Skip to content

Instantly share code, notes, and snippets.

@CelliesProjects
Last active March 13, 2021 19:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CelliesProjects/cbd8aaaeb2a40b76cdc4b5930b6fa0e9 to your computer and use it in GitHub Desktop.
Save CelliesProjects/cbd8aaaeb2a40b76cdc4b5930b6fa0e9 to your computer and use it in GitHub Desktop.
POC voice recorder for M5Stack Fire with a M5 Go Base. Compiles in the Arduino IDE.
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
/* this example has to be compiled with PSRAM enabled. */
#include <M5Stack.h>
#include <driver/adc.h>
#define MICROPHONE 34
#define SPEAKER 25
#define BACKLIGHT 32
#define BUFFER_SIZE 1000*1000
#define SAMPLING_FREQUENCY 22050
#define LOWNOISE true /* set to false to enable backlight dimming */
TFT_eSPI tft = TFT_eSPI();
// set log level to debug for output
void logMemory() {
log_d("Used PSRAM: %d from: %d", ESP.getPsramSize() - ESP.getFreePsram(), ESP.getPsramSize());
}
int8_t* sampleBuffer;
uint32_t allocatedSamples;
uint32_t currentSample{0};
uint32_t lastSample{0};
static hw_timer_t * sampleTimer{NULL}; /* only one timer is (re)used for both sampling and playback */
unsigned int sampling_period_us = round(1000000 * (1.0 / SAMPLING_FREQUENCY));
bool samplerRunning{false};
static void IRAM_ATTR _sampleISR() {
if (currentSample >= BUFFER_SIZE) {
timerEnd(sampleTimer);
sampleTimer = NULL;
lastSample = currentSample;
}
else
sampleBuffer[currentSample++] = adc1_get_raw(ADC1_GPIO34_CHANNEL) >> 1; // shifted 1 bit to the right because we set ADC_WIDTH_BIT_9
}
bool startSampler() {
if (NULL != sampleTimer) return false;
delay(170); /* to not record the click from releasing the button */
currentSample = 0;
sampleTimer = timerBegin(0, 80, true);
timerAttachInterrupt(sampleTimer, &_sampleISR, true);
timerAlarmWrite(sampleTimer, sampling_period_us, true);
timerAlarmEnable(sampleTimer);
samplerRunning = true;
return true;
}
void setup() {
pinMode(MICROPHONE, INPUT);
pinMode(SPEAKER, OUTPUT);
logMemory();
tft.init();
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);
tft.setTextSize(2);
tft.drawString("M5-Stack voicerecorder", 10, 5, 2);
if (LOWNOISE) {
pinMode(BACKLIGHT, OUTPUT);
digitalWrite(BACKLIGHT, HIGH); // This gives the least noise
} else {
ledcAttachPin(BACKLIGHT, 0);
ledcSetup(0, 1300, 16);
ledcWrite(0, 0xFFFF / 16); // Dimming the BACKLIGHT will produce more base noise
}
if (!ESP.getPsramSize()) {
tft.setCursor(55, 40);
tft.print("NO PSRAM FOUND!");
while (1) delay(100);
}
sampleBuffer = (int8_t*)ps_malloc(BUFFER_SIZE * sizeof(int8_t));
if (!sampleBuffer) {
Serial.println("Could not allocate sample buffer - SYSTEM HALTED");
while (1) delay(1000);
}
tft.setCursor(15, 40);
tft.printf("%3.1fkHz %5.1fkB %6.2fs", SAMPLING_FREQUENCY / 1000.0, (BUFFER_SIZE * sizeof(int8_t)) / 1000.0, BUFFER_SIZE / (float)SAMPLING_FREQUENCY);
tft.drawString("REC", 45, 200, 2);
tft.drawString("PLAY", 130, 200, 2);
tft.drawString("STOP", 220, 200, 2);
adc1_config_width(ADC_WIDTH_BIT_9);
}
void loop() {
M5.update();
if (!sampleTimer && M5.BtnA.pressedFor(5)) startSampler();
if (!sampleTimer && M5.BtnB.pressedFor(5)) startPlayback();
uint32_t pos = currentSample;
tft.setCursor(85, 100);
tft.printf("%3lu%% %6.2fs", map(pos, 0, BUFFER_SIZE - 1, 0, 100), pos / (float)SAMPLING_FREQUENCY);
tft.setCursor(60, 130);
tft.printf(" %7i/%7i", pos, BUFFER_SIZE);
if (sampleTimer && M5.BtnC.pressedFor(5)) {
if (samplerRunning) {
lastSample = currentSample;
samplerRunning = false;
}
timerAlarmDisable(sampleTimer);
timerEnd(sampleTimer);
sampleTimer = NULL;
}
delay(5);
}
void IRAM_ATTR _playThroughDAC_ISR() {
if (currentSample >= lastSample) {
timerEnd(sampleTimer);
sampleTimer = NULL;
dacWrite(SPEAKER, 0);
}
else
dacWrite(SPEAKER, sampleBuffer[currentSample++]);
}
bool startPlayback() {
if (NULL != sampleTimer) return false;
currentSample = 0;
sampleTimer = timerBegin(0, 80, true);
timerAttachInterrupt(sampleTimer, &_playThroughDAC_ISR, true);
timerAlarmWrite(sampleTimer, sampling_period_us, true);
timerAlarmEnable(sampleTimer);
return true;
}
@Robino
Copy link

Robino commented Mar 6, 2021

Hello,
sorry to worry you but I try without success :-( your programm on my M5Stack Fire.
My issue is when I click on Button A and I try B to play nothing is playing....
Perhaps you need more info from me?

Thanks for your help

@CelliesProjects
Copy link
Author

You are right, does not run anymore.

Just tried to compile and fix it. Did not work.

This code was a proof of concept.
I am currently working on a new version.

@CelliesProjects
Copy link
Author

Should be fixed in with the latest commit to https://github.com/CelliesProjects/voiceRecorderM5

@Robino
Copy link

Robino commented Mar 8, 2021

Thanks for your help, I have at the starting point : NO PSRAM FOUND. Perhaps I make a bad thing ?

@CelliesProjects
Copy link
Author

CelliesProjects commented Mar 8, 2021

You will need the M5Go base. https://docs.m5stack.com/#/en/base/m5go_bottom

And also PSRAM because without PSRAM you will end up with only 3-4 seconds of recording capacity.

@Robino
Copy link

Robino commented Mar 8, 2021

Wahoo it works like a charm (I just forget to add build_flags=-DBOARD_HAS_PSRAM to enable the PSRAM). Thanks a lot for your great project.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment