#1 2023-03-05 15:24:07

Dreamlord
Учасник
Зареєстрований: 2023-03-05
Повідомлень: 4

Артефакты при воспроизведении звука на ESP32 по I2S

Приветствую всех. Впервые столкнулся с проблемой , которую не смог решить гуглом и чтением существующих тем на форумах. Итак делаю проект барабанного триггерного модуля  , который воспроизводит звук бас-бочки ( звук в формате wav сохранен в памяти программ ) по сигналу с триггера ( как это выглядит на практике можно посмотреть тут https://youtu.be/mbOvT2CbM3Y?t=93 ) . На данный момент проект существует на платформе Teensy 3.2 + Teensy Audio Board и работает прекрасно . Решил перенести на ESP32 в связке с ЦАП pcm5102 . Поскольку для ESP32 нет аудио-библиотеки , которая бы работала подобно Teensy Audio Library пришлось нужную функцию написать самому. Вернее адаптировать под себя найденный пример на просторах интернета. Cуть проста -  инициализируется интерфейс I2S , которому в loop() передается порциями данные аудио из wav файла  , и когда все данные переданы воспроизведение останавливается сбросом флага. Так вот каждый раз уже после вывода всех аудио данных звучит довольно ощутимый щелчек ( аудио файл и снимок из cubase ниже )  , как при отключении усилителя  обычно происходит. Так же и в начале  есть подобный эффект  , но гораздо менее ощутимый и слышеш только  в наушниках ( семпл бас-бочки имеет плавное нарастание и спад громкости в начале и конце соотв. так что этот артефакт результат не самого аудио файла  , а именно аппаратной или программной части ESP32 ) . Так вот у меня вопрос - чем это вызванно и как с этим бороться ? В документации по I2S на сайте Espressif я не нашел функции I2S.Mute() или что нибудь подобное . Кроме того попутный вопрос - как правильно "наложить " воспроизведение двух таких семплов друг на друга со смещением по времени . Т.е. допустим у меня уже запустилось воспроизведение семпла один раз и пока оно длится приходит второй сигнал от триггера и нужно запустить еще раз вопроизведение этого же семпла . Изначально я думал в этой ситуации при повторном срабатывании триггера просто возвращать указатель на аудио-данные wav файла в начало и делать так кадый раз пока не перестанут идти сигналы с триггера и файл просто не доиграет до конца. Но теперь мне кажется , что это не лучшая идея т.к. резкое прерывание воспроизведения будет вызывать подобные искажения с щелчками или еще чем похуже. Спасибо заранее за помощь.
изображение_viber_2023-03-05_13-17-54-538.jpg

Аудио файл записан в линейный вход звуковой карты с выхода ЦАП
https://on.soundcloud.com/drGnZ

//------------------------------------------------------------------------------------------------------------------------

    #include "driver/i2s.h"                     
    #include "Kick.h"                 // файл wav                
//------------------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------------------

       
    bool             isPlaing = true ;             // флаг состояния воспроизведения ( играет/не играет ) 
    static const i2s_port_t i2s_num = I2S_NUM_0;   // i2s номер порта 
    unsigned const char* TheData;                  // указатель на данные wav файла
    uint32_t DataIdx=0;                            // значение смещения указателя "TheData" в процессе передачи порции данных интерфейсу  I2S
    const  uint8_t* nameWav ;   
    struct WavHeader_Struct
    
    {
      //   RIFF Section    
      char RIFFSectionID[4];      // "RIFF"
      uint32_t Size;              // Size of entire file less 8
      char RiffFormat[4];         // "WAVE"
      
      //   Format Section    
      char FormatSectionID[4];    // letters "fmt"
      uint32_t FormatSize;        // Size of format section less 8
      uint16_t FormatID;          // 1=uncompressed PCM
      uint16_t NumChannels;       // 1=mono,2=stereo
      uint32_t SampleRate;        // 44100, 16000, 8000 etc.
      uint32_t ByteRate;          // =SampleRate * Channels * (BitsPerSample/8)
      uint16_t BlockAlign;        // =Channels * (BitsPerSample/8)
      uint16_t BitsPerSample;     // 8,16,24 or 32
    
      // Data Section
      char DataSectionID[4];      // The letters "data"
      uint32_t DataSize;          // Size of the data that follows
    }WavHeader;
    
//------------------------------------------------------------------------------------------------------------------------
uint32_t currenttime ;
uint32_t looptime ;
//------------------------------------------------------------------------------------------------------------------------
// I2S configuration structures

static const i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = 44100,                            // Note, this will be changed later
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,       // high interrupt priority
    .dma_buf_count = 8,                             // 8 buffers
    .dma_buf_len = 1024,                            // 1K per buffer, so 8K of buffer space
    .use_apll=0,
    .tx_desc_auto_clear= true, 
    .fixed_mclk=-1    
};

// These are the physical wiring connections to our I2S decoder board/chip from the esp32, there are other connections
// required for the chips mentioned at the top (but not to the ESP32), please visit the page mentioned at the top for
// further information regarding these other connections.

static const i2s_pin_config_t pin_config = {
    .bck_io_num = 26,                                 // The bit clock connectiom, goes to pin 27 of ESP32
    .ws_io_num = 25,                                  // Word select, also known as word select or left right clock
    .data_out_num = 27,                               // Data out from the ESP32, connect to DIN on 38357A
    .data_in_num = I2S_PIN_NO_CHANGE                  // we are not interested in I2S data into the ESP32
};

//------------------------------------------------------------------------------------------------------------------------


void setup() {
  

        memcpy(&WavHeader,&rawData,44);                           // копируем "заголовочную" часть wav файла в структуру 
        i2s_driver_install(i2s_num, &i2s_config, 0, NULL);        // Инициализация I2S
        i2s_set_pin(i2s_num, &pin_config);                        // Назначаем пины I2S 
        i2s_set_sample_rates(i2s_num, WavHeader.SampleRate); 
                                                                 
        TheData=rawData;                                           
        nameWav=TheData+44;                                       // смешаем указатель на начало аудио-данных wav файла 
    
}

void loop()
{
      playWav();
  
    }

    
void playWav(void){
  
     size_t BytesWritten; 

     while(DataIdx!=WavHeader.DataSize && isPlaing )
     {
            i2s_write(i2s_num,TheData+DataIdx,4,&BytesWritten,1);     //  выводим данные на I2S , если звуковые данные являются представлением двух байт на каждный канал ,
                                                                     //   мы отдаем интерфейсу четыре байта за один раз  
            DataIdx+=4;                                             // смещение указателя на четыре байта в массиве данных wav
    
      
    
       if (DataIdx>=WavHeader.DataSize){                         // если вывели все данные 
                                                                 // возвращаем указатель в началао массива данных 
       DataIdx=0;
     
        isPlaing=false;                                         // сбрасываем флаг для остановки воспроизведения . Если не сбрасывать , файл будет воспроизводиться зацикленно 

        
       }
     }
   }

Неактивний

#2 2023-03-05 16:03:31

Honey
Учасник
З Київ
Зареєстрований: 2020-09-26
Повідомлень: 433

Re: Артефакты при воспроизведении звука на ESP32 по I2S

Розкажіть, що буде, якщо до або після циклу while додати рядок:
if (!isPlaing) i2s_write(i2s_num,TheData+WavHeader.DataSize-4,4,&BytesWritten,1);

Неактивний

#3 2023-03-05 23:15:45

Dreamlord
Учасник
Зареєстрований: 2023-03-05
Повідомлень: 4

Re: Артефакты при воспроизведении звука на ESP32 по I2S

Honey пише:

Розкажіть, що буде, якщо до або після циклу while додати рядок:
if (!isPlaing) i2s_write(i2s_num,TheData+WavHeader.DataSize-4,4,&BytesWritten,1);

Завтра попробую и отпишусь о результате . Кстати сегодня попробовал связать Teensy и PCM5102 , тоже были артефакты , но не такие , возможно дело в длинных проводах между платами. Или отсутствие MCLK . Потоковое адуио играет нормально ( пробовал интернет-радио воспроизводить ) а вот с короткими семплами беда какая-то.

Неактивний

#4 2023-03-06 21:29:13

Dreamlord
Учасник
Зареєстрований: 2023-03-05
Повідомлень: 4

Re: Артефакты при воспроизведении звука на ESP32 по I2S

Попробовал - результат тот же. Мало того , я пробовал изначально обрезать воспроизведение на 6 и 10 кБайт задавая условие
while(DataIdx!=(WavHeader.DataSize - 6144)  && isPlaing ) при общем объеме wav файла около 19кБайт и результат тот же. Причем звук бочки меняется естетсвенно , а вот артефакт в конце остается тот же и звучит с тем же интервалом после звука бочки . Teensy + PCM5102 сегодня попробовал с максимально короткими проводами - все нормально. Так что ЦАП исправен и проблема именно в ESP . Есть в наличии ESP8266 попробую еще на ней испытать этот код .

Неактивний

#5 2023-03-20 01:01:31

Dreamlord
Учасник
Зареєстрований: 2023-03-05
Повідомлень: 4

Re: Артефакты при воспроизведении звука на ESP32 по I2S

Не знаю , что именно было причиной  , ибо этот же семпл на ESP8266 c библиотекой ESP8266-Audio нормально играл . Но почему-то этот же код  , скомпилированный для есп32 , давал подобный эффект. В итоге я решил попробовать другой семпл и все стало нормально . Так что вопрос решен .

Неактивний

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму