• Уважаемые посетители сайта esp8266.ru!
    Мы отказались от размещения рекламы на страницах форума для большего комфорта пользователей.
    Вы можете оказать посильную поддержку администрации форума. Данные средства пойдут на оплату услуг облачных провайдеров для сайта esp8266.ru
  • Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

Делюсь опытом Запись аудио с микрофона

unreg

Member
Ну вот при содействии форумчан удалось таки записать звук с микрофона. ESP32-wrover-b, Микрофон на плате max9814 (OUT подключен к GPIO36 (SENSOR VP)), и microSD-карта class 10 8Гб. Звук пишется довольно качественно для голоса, длина записи может регулироваться конечным размером аудиоданных :rolleyes: не стал заморачиваться с кнопками, цель была не в этом. Формат файла 8 бит, 8КГц.
Скетч:
Код:
#include <Arduino.h>
#include <driver/adc.h>

#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define AUDIO_BUFFER_MAX 51200 //эмпирически выбрал. склеек не слышно. буфер успевает записаться на SD пока другой заполняется новыми аудио данными

uint8_t audioBuffer[AUDIO_BUFFER_MAX];

uint8_t audioBufferTmp[AUDIO_BUFFER_MAX];

uint32_t bufferPointer = 0;

bool transmitNow = false;
bool recordStarting = false;

hw_timer_t * timer = NULL; // our timer
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

fs::File fContent;

int touch_sensor_value = 0;
const int VALUE_THRESHOLD = 70;

volatile bool audio_buffer_ready = false;
int interruptCounter = 0;
size_t audio_data_size = 0;

byte  header[44] = {};

bool startRecording = true;

void setup() {

    Serial.begin(115200);
    if (!SD.begin()) {
        Serial.println("Card Mount Failed");
        startRecording = false;
        return;
    }
    startRecording = true;
    if (SD.exists("/file.wav"))
        SD.remove("/file.wav");
   
    Serial.println("Ready");

    adc1_config_width(ADC_WIDTH_12Bit);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db);

    fContent = SD.open("/file.wav", FILE_WRITE);
    fContent.write((const byte *)header, sizeof(header));
    Serial.println("Rec started file.wav");

    timer = timerBegin(0, 80, true); 
    timerAttachInterrupt(timer, &onTimer, true); 
    timerAlarmWrite(timer, 125, true);
    timerAlarmEnable(timer);
}

void IRAM_ATTR onTimer() {

    portENTER_CRITICAL_ISR(&timerMux);
    uint16_t sample = adc1_get_raw(ADC1_CHANNEL_0);
    uint8_t value = map(sample, 0, 4095, 0, 255);
    audioBuffer[bufferPointer] = value;
    bufferPointer++;
    if (bufferPointer == AUDIO_BUFFER_MAX)
    {
        memcpy(audioBufferTmp, audioBuffer, AUDIO_BUFFER_MAX);
        bufferPointer = 0;
        audio_buffer_ready = true;
    }
    portEXIT_CRITICAL_ISR(&timerMux);
}

void loop() {
    if (!startRecording)
        return;
    if (audio_buffer_ready)
    {
        audio_buffer_ready = false;
        audio_data_size += fContent.write((const uint8_t *)audioBufferTmp, sizeof(audioBufferTmp));
    }
   
    if (audio_data_size > 500000) //500000 это тот самый размер аудиоданных (~1 мин)
    {
        startRecording = false;
        timerAlarmDisable(timer);
        CreateWavHeader(header, audio_data_size);
        fContent.seek(0);
        fContent.write((const byte *)header, sizeof(header));
        fContent.close();
        SD.end(); //корректно отключаем SD, я уже одну убил
        Serial.println("Done!");
        Serial.print("Audio Data size:");
        Serial.println(audio_data_size, DEC);
    }
   
}

void CreateWavHeader(byte* header, int waveDataSize) {
    header[0] = 'R';
    header[1] = 'I';
    header[2] = 'F';
    header[3] = 'F';
    unsigned int fileSizeMinus8 = waveDataSize + 44 - 8;
    header[4] = (byte)(fileSizeMinus8 & 0xFF);
    header[5] = (byte)((fileSizeMinus8 >> 8) & 0xFF);
    header[6] = (byte)((fileSizeMinus8 >> 16) & 0xFF);
    header[7] = (byte)((fileSizeMinus8 >> 24) & 0xFF);
    header[8] = 'W';
    header[9] = 'A';
    header[10] = 'V';
    header[11] = 'E';
    header[12] = 'f';
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = 0x10; 
    header[17] = 0x00;
    header[18] = 0x00;
    header[19] = 0x00;
    header[20] = 0x01;
    header[21] = 0x00;
    header[22] = 0x01;
    header[23] = 0x00;
    header[24] = 0x40;
    header[25] = 0x1F;
    header[26] = 0x00;
    header[27] = 0x00;
    header[28] = 0x40;
    header[29] = 0x1F;
    header[30] = 0x00;
    header[31] = 0x00;
    header[32] = 0x01;
    header[33] = 0x00;
    header[34] = 0x08;
    header[35] = 0x00;
    header[36] = 'd';
    header[37] = 'a';
    header[38] = 't';
    header[39] = 'a';
    header[40] = (byte)(waveDataSize & 0xFF);
    header[41] = (byte)((waveDataSize >> 8) & 0xFF);
    header[42] = (byte)((waveDataSize >> 16) & 0xFF);
    header[43] = (byte)((waveDataSize >> 24) & 0xFF);
}

uint16_t changeEndianess(uint16_t val)
{
    return (val << 8) | ((val >> 8) & 0x00ff);
}
Прошу предложения, замечания и 12-ти битный wav-файл (они вообще бывают и проигрываются стандартными плеерами?). Не допер как собрать чанк формата и не нашел конвертера который бы мог сделать 12 бит, а так хоть хексом подгляжу.
 

nikolz

Well-known member
Ну вот при содействии форумчан удалось таки записать звук с микрофона. ESP32-wrover-b, Микрофон на плате max9814 (OUT подключен к GPIO36 (SENSOR VP)), и microSD-карта class 10 8Гб. Звук пишется довольно качественно для голоса, длина записи может регулироваться конечным размером аудиоданных :rolleyes: не стал заморачиваться с кнопками, цель была не в этом. Формат файла 8 бит, 8КГц.
Скетч:
Код:
#include <Arduino.h>
#include <driver/adc.h>

#include "FS.h"
#include "SD.h"
#include "SPI.h"

#define AUDIO_BUFFER_MAX 51200 //эмпирически выбрал. склеек не слышно. буфер успевает записаться на SD пока другой заполняется новыми аудио данными

uint8_t audioBuffer[AUDIO_BUFFER_MAX];

uint8_t audioBufferTmp[AUDIO_BUFFER_MAX];

uint32_t bufferPointer = 0;

bool transmitNow = false;
bool recordStarting = false;

hw_timer_t * timer = NULL; // our timer
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

fs::File fContent;

int touch_sensor_value = 0;
const int VALUE_THRESHOLD = 70;

volatile bool audio_buffer_ready = false;
int interruptCounter = 0;
size_t audio_data_size = 0;

byte  header[44] = {};

bool startRecording = true;

void setup() {

    Serial.begin(115200);
    if (!SD.begin()) {
        Serial.println("Card Mount Failed");
        startRecording = false;
        return;
    }
    startRecording = true;
    if (SD.exists("/file.wav"))
        SD.remove("/file.wav");
  
    Serial.println("Ready");

    adc1_config_width(ADC_WIDTH_12Bit);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_11db);

    fContent = SD.open("/file.wav", FILE_WRITE);
    fContent.write((const byte *)header, sizeof(header));
    Serial.println("Rec started file.wav");

    timer = timerBegin(0, 80, true);
    timerAttachInterrupt(timer, &onTimer, true);
    timerAlarmWrite(timer, 125, true);
    timerAlarmEnable(timer);
}

void IRAM_ATTR onTimer() {

    portENTER_CRITICAL_ISR(&timerMux);
    uint16_t sample = adc1_get_raw(ADC1_CHANNEL_0);
    uint8_t value = map(sample, 0, 4095, 0, 255);
    audioBuffer[bufferPointer] = value;
    bufferPointer++;
    if (bufferPointer == AUDIO_BUFFER_MAX)
    {
        memcpy(audioBufferTmp, audioBuffer, AUDIO_BUFFER_MAX);
        bufferPointer = 0;
        audio_buffer_ready = true;
    }
    portEXIT_CRITICAL_ISR(&timerMux);
}

void loop() {
    if (!startRecording)
        return;
    if (audio_buffer_ready)
    {
        audio_buffer_ready = false;
        audio_data_size += fContent.write((const uint8_t *)audioBufferTmp, sizeof(audioBufferTmp));
    }
  
    if (audio_data_size > 500000) //500000 это тот самый размер аудиоданных (~1 мин)
    {
        startRecording = false;
        timerAlarmDisable(timer);
        CreateWavHeader(header, audio_data_size);
        fContent.seek(0);
        fContent.write((const byte *)header, sizeof(header));
        fContent.close();
        SD.end(); //корректно отключаем SD, я уже одну убил
        Serial.println("Done!");
        Serial.print("Audio Data size:");
        Serial.println(audio_data_size, DEC);
    }
  
}

void CreateWavHeader(byte* header, int waveDataSize) {
    header[0] = 'R';
    header[1] = 'I';
    header[2] = 'F';
    header[3] = 'F';
    unsigned int fileSizeMinus8 = waveDataSize + 44 - 8;
    header[4] = (byte)(fileSizeMinus8 & 0xFF);
    header[5] = (byte)((fileSizeMinus8 >> 8) & 0xFF);
    header[6] = (byte)((fileSizeMinus8 >> 16) & 0xFF);
    header[7] = (byte)((fileSizeMinus8 >> 24) & 0xFF);
    header[8] = 'W';
    header[9] = 'A';
    header[10] = 'V';
    header[11] = 'E';
    header[12] = 'f';
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = 0x10;
    header[17] = 0x00;
    header[18] = 0x00;
    header[19] = 0x00;
    header[20] = 0x01;
    header[21] = 0x00;
    header[22] = 0x01;
    header[23] = 0x00;
    header[24] = 0x40;
    header[25] = 0x1F;
    header[26] = 0x00;
    header[27] = 0x00;
    header[28] = 0x40;
    header[29] = 0x1F;
    header[30] = 0x00;
    header[31] = 0x00;
    header[32] = 0x01;
    header[33] = 0x00;
    header[34] = 0x08;
    header[35] = 0x00;
    header[36] = 'd';
    header[37] = 'a';
    header[38] = 't';
    header[39] = 'a';
    header[40] = (byte)(waveDataSize & 0xFF);
    header[41] = (byte)((waveDataSize >> 8) & 0xFF);
    header[42] = (byte)((waveDataSize >> 16) & 0xFF);
    header[43] = (byte)((waveDataSize >> 24) & 0xFF);
}

uint16_t changeEndianess(uint16_t val)
{
    return (val << 8) | ((val >> 8) & 0x00ff);
}
Прошу предложения, замечания и 12-ти битный wav-файл (они вообще бывают и проигрываются стандартными плеерами?). Не допер как собрать чанк формата и не нашел конвертера который бы мог сделать 12 бит, а так хоть хексом подгляжу.
вопрос уточните.
Вы же принимаете 12 бит.
укажите что у вас буфер 16 битный и пишите в него.
или что-то другое надо?
 

unreg

Member
Да, принимаю12 бит, точнее 4 байта инт со значимыми 12 битами, потом масштабирую на 8. это потому что не получается проиграть полученный 12битный. То ли я не правильно заголовок составляю, то ли в принципе 12 бит не играются тем же виндоус плеером. Дело в том что 12 бит понятное дело это 2 байта. выравниваются по левому краю, плюс младший байт со старшим переставляются при записи в файл. Я все это проделывал, но плеер не распознает формат, чую заголовок формирую не правильно и подглядеть некуда
 

unreg

Member
качал разные конвертеры так ни олин из них 12 ьитный вав делать не могет
 

nikolz

Well-known member
качал разные конвертеры так ни олин из них 12 ьитный вав делать не могет
что мешает делать 16 бит? т е 12 бит помещаете в 16 бит.будет громкость меньше.
если надо громкость повысить то 12 бит двигаете влево на 4 бита и пишите в 16 бит.
 

unreg

Member
а заголовок в чанке формат тоже под 16 бит? там есть два байта под bitPerSample, ну и другие поля зависящие от этого значения...Предлагаете заголовок под 16 бит собрать? Определяться будет как 16 бит а пофакту 12. Надо попробовать
 

nikolz

Well-known member
а заголовок в чанке формат тоже под 16 бит? там есть два байта под bitPerSample, ну и другие поля зависящие от этого значения...Предлагаете заголовок под 16 бит собрать? Определяться будет как 16 бит а пофакту 12. Надо попробовать
чем отличается 12 бит от 16 бит? тем что сигнал в 12 битах в 16 раз слабее (вместо крика будет шопот).
другое дело что если будете воспроизводить через ШИМ то качество будет что с 12 что с 16 что с 8 практически одинаковое
 

unreg

Member
да просто хотелось стандарта придерживаться. воспроизводить буду через цап. заказал td1387
 

nikolz

Well-known member
да просто хотелось стандарта придерживаться. воспроизводить буду через цап. заказал td1387
вы же с микрофона вводите голос. качество будет не очень
если воспроизводите сами то зачем вам заголовки
или вы сжимаете данные?
тогда тем более зачем 16 бит.
надо изначально определиться вам с качеством и разрядностью чтобы избыточность не гонять впустую.
 

nikolz

Well-known member
чтобы не тратить память надо определить буфер в байтах
и упаковывать два отсчета в три байта.
 

unreg

Member
Воспроизводить я буду не только то что с микрофона, но и качественные короткие сэмплы. Устройство многофункциональное по задумке. А на счет чистого потока это прямо мысли читаете :) тем более что реализовал уже такое через встроеный цап, битности не хватает
 

nikolz

Well-known member
Воспроизводить я буду не только то что с микрофона, но и качественные короткие сэмплы. Устройство многофункциональное по задумке. А на счет чистого потока это прямо мысли читаете :) тем более что реализовал уже такое через встроеный цап, битности не хватает
а как вы установили, что битности не хватает?
я например делал говорящий прибор и гонял 8 и 12 бит через ШИМ и ЦАП результат получился одинаковый.
 

unreg

Member
я так понял лучше чем качество в 8 бит там и не получить. поэтому наверное одинаково и получалось
 

unreg

Member
а ну поэтому я цап 16 бит i2s заказал. чтоб уж гонять так гонять)) 8 бит встроеного для моих целей не хватает
 

unreg

Member
да мне не только мои 12 гонять, мне надо будет нормальные качественные данные проигрывать 16/44. микрофон лишь часть задумки и для него хватит и 12
 
Сверху Снизу