🤖Have you ever tried Chat.M5Stack.com before asking??😎
    M5Stack Community
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Register
    • Login

    M5Sound 😎

    Core 2
    3
    10
    16.4k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • RopR
      Rop
      last edited by

      I just wrote a simple polyphonic background synthesizer library for the Core2:

      #include <M5Core2.h>
      
      Synth a;
      
      void setup() {
        M5.begin();
        a.freq = 1000;
        a.gain = 0.4;
      }
      
      void loop() {
        M5.update();
        if (M5.Buttons.event == E_TOUCH) a.start();
        if (M5.Buttons.event == E_RELEASE) a.stop();
      }
      

      You can have multiple Synth instances, their output is mixed. Every synth has attack, decay, sustain release envelope, and the waveform member can be set to SINE, SQUARE, SAWTOOTH, TRIANGLE or NOISE. Sound will continue as long as M5.update() is called. Default attack and release at 5 ms, so no ugly clicks.

      Under 200 lines in the cpp file...

      A few more convenience features coming, buffering to improve a bit still, but this is the core.

      Do play with the DTMF_dialer example, and try holding it sideways...

      It's all on the M5Sound branch of my fork.

      1 Reply Last reply Reply Quote 1
      • vkichlineV
        vkichline
        last edited by

        Nice, efficient implementation of the dtmf sample. I see btn.userData came in handy!
        The audio quality is great, completely free of pops and clicks.
        I think I'll add checkRotation() to my system library!

        RopR 1 Reply Last reply Reply Quote 0
        • felmueF
          felmue
          last edited by

          Hey Rop

          never resting - it seems. Very nice! Just tried it successfully with my old rotary phone.

          Cheers
          Felix

          GPIO translation table M5Stack / M5Core2
          Information about various M5Stack products.
          Code examples

          RopR 1 Reply Last reply Reply Quote 0
          • RopR
            Rop @felmue
            last edited by

            @felmue

            ☑ Valid DTMF. (Didn't have an easy means of testing...)

            1 Reply Last reply Reply Quote 0
            • RopR
              Rop
              last edited by

              I'm pretty sure it can do other tones as well. I chose blue for a reason... :)

              1 Reply Last reply Reply Quote 0
              • RopR
                Rop @vkichline
                last edited by

                @vkichline Made checkRotation more generic:

                bool checkRotation(uint16_t msec) {
                  if (millis() - rotationLastChecked < msec) return false;
                  rotationLastChecked = millis();
                  const float threshold = 0.85;
                  float ax, ay, az;
                  M5.IMU.getAccelData(&ax, &ay, &az);
                  uint8_t newRotation;
                  if      (ay >  threshold) newRotation = 1;
                  else if (ay < -threshold) newRotation = 3;
                  else if (ax >  threshold) newRotation = 2;
                  else if (ax < -threshold) newRotation = 0;
                  else return false;
                  if (M5.Lcd.rotation == newRotation) return false;
                  columns = newRotation % 2 ? 4 : 3;
                  M5.Lcd.clearDisplay();
                  M5.Lcd.setRotation(newRotation);
                  return true;
                }
                

                All it needs is a global uint32_t rotationLastChecked and for you to have ran M5.IMU.Init() (which seems to set power things on the MPU, so there's probably a reason it's not in M5.begin(), haven't read the datasheet.)

                checkRotation returns a bool and takes the number of milliseconds between checks. It returns true if it has already rotated and cleared the display for you and needs you to set things up again. So my loop() now does if (checkRotation(1000)) doButtons()

                1 Reply Last reply Reply Quote 1
                • RopR
                  Rop
                  last edited by

                  Just checked in the new improvements. There is now a polyphonic piano demo. Only two notes can be sustained – if you touch at different screen heights – because of the touch-sensor limitation, but keys that were hit before keep sounding for a bit. Shows M5Sound has no problem with 14 concurrent Synth instances.

                  To make this satisfying, I needed to be able to move my finger from key to key (glissando), which M5Button didn't let me do. Enter M5.Buttons.pianoMode = true. Now you can slip off one button and onto the next. The price to pay is that gesture recognition is switched off in pianoMode.

                  (The DTMF dialer is also cooler in pianoMode, so changed that too.)

                  Also there's now 1000_hzand C_major examples, just so that not all examples are somewhat advanced bits of code. But Piano, like the DTMF_dialer, is still pretty small and quite readable for a stylish polyphonic piano:

                  /*
                  
                  Polyphonic synthesizer. Lingering notes will continue to sound as new notes
                  are played. If you place one finger high on the screen and another low, you
                  can even play two notes at the same time. As you can see you can have many
                  synths going at the same time.
                  
                  */
                  
                  #include <M5Core2.h>
                  
                  #define WKW         40
                  #define WKH        240
                  #define BKW         30
                  #define BKH        160
                  #define WHITE_KEY  {WHITE, NODRAW, BLACK}
                  #define BLACK_KEY  {BLACK, NODRAW, NODRAW}
                  #define NUM_KEYS    14
                  
                  float notes[NUM_KEYS] = { NOTE_F4 , NOTE_G4 , NOTE_A4 , NOTE_B4 , NOTE_C5,
                                            NOTE_D5 , NOTE_E5 , NOTE_F5 , NOTE_Gb4, NOTE_Ab4,
                                            NOTE_Bb4, NOTE_Db5, NOTE_Eb5, NOTE_Gb5 };
                  
                  // (waveform, freq, attack, decay, sustain, release)
                  Synth synth[NUM_KEYS] = Synth(TRIANGLE, 0, 50, 300, 0.7, 1000);
                  
                  Button f_(   0, 0, WKW, WKH, false, "f" , WHITE_KEY);
                  Button g_(  40, 0, WKW, WKH, false, "g" , WHITE_KEY);
                  Button A_(  80, 0, WKW, WKH, false, "A" , WHITE_KEY);
                  Button B_( 120, 0, WKW, WKH, false, "B" , WHITE_KEY);
                  Button C_( 160, 0, WKW, WKH, false, "C" , WHITE_KEY);
                  Button D_( 200, 0, WKW, WKH, false, "D" , WHITE_KEY);
                  Button E_( 240, 0, WKW, WKH, false, "E" , WHITE_KEY);
                  Button F_( 280, 0, WKW, WKH, false, "F" , WHITE_KEY);
                  Button gb(  25, 0, BKW, BKH, false, "gb", BLACK_KEY);
                  Button Ab(  65, 0, BKW, BKH, false, "Ab", BLACK_KEY);
                  Button Bb( 105, 0, BKW, BKH, false, "Bb", BLACK_KEY);
                  Button Db( 185, 0, BKW, BKH, false, "Db", BLACK_KEY);
                  Button Eb( 225, 0, BKW, BKH, false, "Eb", BLACK_KEY);
                  Button Gb( 305, 0,  15, BKH, false, "Gb", BLACK_KEY);
                  
                  void setup() {
                    M5.begin();
                    M5.Buttons.draw();
                  
                    // Prettier with top of keys as straight line
                    M5.Lcd.fillRect(0, 0, 320, 5, BLACK);
                  
                    // Trick to make sure buttons do not draw over eachother anymore
                    M5.Buttons.drawFn = nullptr;
                  
                    // So that you can swipe from one button to another
                    M5.Buttons.pianoMode = true;
                  
                    // Set up syths with their notes
                    for (uint8_t n = 0; n < 14; n++) {
                      synth[n].freq = notes[n];
                    }
                  
                    M5.Buttons.addHandler(pressKey  , E_TOUCH);
                    M5.Buttons.addHandler(releaseKey, E_RELEASE);
                  }
                  
                  void loop() {
                    M5.update();
                  }
                  
                  void pressKey(Event& e) {
                    // instanceIndex() -4 because of background, BtnA, BtnB and BtnC.
                    uint8_t key = e.button->instanceIndex() - 4;
                    synth[key].start();
                  }
                  
                  
                  void releaseKey(Event& e) {
                    uint8_t key = e.button->instanceIndex() - 4;
                    synth[key].stop();
                  }
                  
                  1 Reply Last reply Reply Quote 0
                  • vkichlineV
                    vkichline
                    last edited by

                    Thanks for the checkRotation function!
                    I've just glanced at the Sound stuff (very nice!) but am still working on Touch.
                    I wrote a test to determine what the minimal effective touch button size is (https://github.com/vkichline/ButtonSizeTest).
                    When I moved it from PlatformIO (for development) to Arduino (for publishing) I moved from your master branch to the M5Sound branch. The app built, but did not run in M5Sound. (It drew one button and stopped. I haven't looked into it yet.)

                    Are you making touch changes in M5Sound as well?

                    RopR 1 Reply Last reply Reply Quote 0
                    • RopR
                      Rop @vkichline
                      last edited by

                      @vkichline Yes I have been making a few changes, just what I needed for M5Sound. Which did include pianoMode, which by necessity changes a bit of a voodoo state-machine. My examples all still work I think, no?

                      1 Reply Last reply Reply Quote 0
                      • vkichlineV
                        vkichline
                        last edited by

                        Rop, all the included examples run fine.
                        I found that the problem is that Button::_hidden is uninitialized. I will open an issue.

                        Now I can easily add hit and miss sounds to my button testing app. Will update soon.

                        1 Reply Last reply Reply Quote 0
                        • First post
                          Last post