M5Sound 😎
-
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 toSINE
,SQUARE
,SAWTOOTH
,TRIANGLE
orNOISE
. 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. -
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 addcheckRotation()
to my system library! -
Hey Rop
never resting - it seems. Very nice! Just tried it successfully with my old rotary phone.
Cheers
Felix -
☑ Valid DTMF. (Didn't have an easy means of testing...)
-
I'm pretty sure it can do other tones as well. I chose blue for a reason... :)
-
@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 ranM5.IMU.Init()
(which seems to set power things on the MPU, so there's probably a reason it's not inM5.begin()
, haven't read the datasheet.)checkRotation
returns abool
and takes the number of milliseconds between checks. It returnstrue
if it has already rotated and cleared the display for you and needs you to set things up again. So myloop()
now doesif (checkRotation(1000)) doButtons()
-
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_hz
andC_major
examples, just so that not all examples are somewhat advanced bits of code. ButPiano
, like theDTMF_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(); }
-
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?
-
@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?
-
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.