espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.48k stars 7.26k forks source link

some noise at every start output of the i2s_write (IDFGH-7021) #8639

Closed Kingwulin closed 3 months ago

Kingwulin commented 2 years ago

Hi,I am using the demo here I modify this demo to make the .wav file output to a speaker over the i2s driver(a hardware max9735a I used) mostly,every time at the starting of the .wav file ,the sound always some noise like "de" How to deal with it ,Thanks

L-KAYA commented 2 years ago

This example uses i2s_write to play raw music that store in flash or header file. Raw music means only audio data in it, but a .wav format music has a protocol header at the beginning, which indicates the sample_rate, data_bit_width and so on, these information are used to decode the following music and shouldn't be played. You can try to parse the header of .wav file and play the music without the header.

Hope it can help~

L-KAYA commented 2 years ago

FYI, you can refer to i2s_audio_recorder_sdcard and check how a .wav header is generated

Kingwulin commented 2 years ago

This example uses i2s_write to play raw music that store in flash or header file. Raw music means only audio data in it, but a .wav format music has a protocol header at the beginning, which indicates the sample_rate, data_bit_width and so on, these information are used to decode the following music and shouldn't be played. You can try to parse the header of .wav file and play the music without the header.

Hope it can help~

think you for your reply below it my code to play a .wav file stora at spiffs, I had read the header of the file ,but the noise still exit and Noise appears at the beginning of playback

typedef struct {
    // The "RIFF" chunk descriptor
    uint8_t ChunkID[4];
    int32_t ChunkSize;
    uint8_t Format[4];
    // The "fmt" sub-chunk
    uint8_t Subchunk1ID[4];
    int32_t Subchunk1Size;
    int16_t AudioFormat;
    int16_t NumChannels;
    int32_t SampleRate;
    int32_t ByteRate;
    int16_t BlockAlign;
    int16_t BitsPerSample;
    // The "data" sub-chunk
    uint8_t Subchunk2ID[4];
    int32_t Subchunk2Size;
} wav_header_t;
static esp_err_t  play_wav(const char *filepath)
{
    FILE *fd = NULL;
    fd = fopen(filepath, "r");

    if (NULL == fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        return ESP_FAIL;
    }
    const size_t chunk_size = 2048;
    uint8_t *buffer = malloc(chunk_size);

    if (NULL == buffer) {
        ESP_LOGE(TAG, "audio data buffer malloc failed");
        fclose(fd);
        return ESP_FAIL;
    }
    int len; 
    /**
     * read head of WAV file
     */
    wav_header_t wav_head;
    len = fread(&wav_head, 1, sizeof(wav_header_t), fd);
    if (len <= 0) {
        ESP_LOGE(TAG, "Read wav header failed");
        fclose(fd);
        return ESP_FAIL;
    }
    if (NULL == strstr((char *)wav_head.Subchunk1ID, "fmt") &&
            NULL == strstr((char *)wav_head.Subchunk2ID, "data")) {
        ESP_LOGE(TAG, "Header of wav format error");
        fclose(fd);
        return ESP_FAIL;
    }

    // ESP_LOGI(TAG, "frame_rate=%d, ch=%d, width=%d", wav_head.SampleRate, wav_head.NumChannels, wav_head.BitsPerSample);

    /**
     * read wave data of WAV file
     */
    size_t write_num = 0;
    size_t cnt;
    //i2s_start(I2S_NUM_0);
    do {
        /* Read file in chunks into the scratch buffer */
        len = fread(buffer, 1, chunk_size, fd);
        if (len <= 0) {
            ESP_LOGI(TAG, "break out");
            //i2s_stop(I2S_NUM_0);
            break;
        }

        //pwm_audio_write(buffer, len, &cnt, 1000 / portTICK_PERIOD_MS);
        i2s_write(I2S_NUM_0, buffer, len, &cnt, 100);
        ESP_LOGI(TAG, "%d,%d",cnt,len);
        write_num += len;
    } while (1);

    free(buffer);
    fclose(fd);
    return ESP_OK;
}
Kingwulin commented 2 years ago

Hello,there is video,There is a "de" sound at the beginning of most tones,and below is my main code

void app_play_num(int index)
{
    char file_buf[16];

    sprintf(file_buf,"/audio/%d.wav",index);

    play_wav(file_buf);
    ESP_LOGI(TAG,"going to player %s",file_buf);
}

void speaker_setTemp2_say(void)
{
    char file_buf[32];

    sprintf(file_buf,"/audio/setTime.wav");
    ESP_LOGI(TAG,"going to player %s",file_buf);
    play_wav(file_buf);   
}
void speaker_du_say(void)
{
    char file_buf[16];

    sprintf(file_buf,"/audio/min.wav");
    ESP_LOGI(TAG,"going to player %s",file_buf);
    play_wav(file_buf);   
}
void app_voice_play(int num)
{
    speaker_setTemp2_say();

    if(num/10)
    {
        app_play_num(num/10); //noise exit at the begin of this code 
        app_play_num(0); //noise exit at the begin of this code 
    }

    app_play_num(num%10); //noise exit at the begin of this code 

    speaker_du_say(); //noise exit at the begin of this code 

}
void app_main(void)
{
    spiffs_driver_init();

    hal_i2s_driver_init();
    //play_wav(MOUNT_POINT"/sample.wav");
    app_voice_play(19); 
    vTaskDelay(100);
    app_voice_play(13);
    vTaskDelay(100);
    app_voice_play(31);
    vTaskDelay(100);
    app_voice_play(39);
    vTaskDelay(100);
    app_voice_play(91);
    vTaskDelay(100);
    app_voice_play(93);
}
L-KAYA commented 2 years ago

Firstly, for the current I2S driver, due to the driver will start automatically after installation, the uninitialized data may be transported, you can try to stop the i2s after installation and calling i2s_zero_dma_buffer to clear the DMA buffer before start and write data. Secondly, just in case the audio itself contains the noise, try to print the beginning data of the sending buffer.

L-KAYA commented 2 years ago

Another notice, the DAC only support 8 bit-width, but most audios are at least 16 bit-width, the audio data need to be right shifted 8 bits

L-KAYA commented 2 years ago

The clipped sound mainly caused by the rapid change of data. I guess the polling time between two i2s_write may be too long which lead to the audio not consecutive, try to remove other codes in the i2s_write loop. and also clear the dma buffer every time after finished sending the data.

Kingwulin commented 2 years ago

Firstly, for the current I2S driver, due to the driver will start automatically after installation, the uninitialized data may be transported, you can try to stop the i2s after installation and calling i2s_zero_dma_buffer to clear the DMA buffer before start and write data. Secondly, just in case the audio itself contains the noise, try to print the beginning data of the sending buffer.

here is the wav file spiffs_image.zip

Kingwulin commented 2 years ago

how to clear the dma buffer

L-KAYA commented 2 years ago

Call i2s_zero_dma_buffer

Kingwulin commented 2 years ago

Call i2s_zero_dma_buffer

Thanks for your reply,would you tell me which file the "i2s_zero_dma_buffer" function is. I can't find the call in the git project

i2s_zero_dma_buffer not found

L-KAYA commented 2 years ago

It's not a file, it's a function declared in components/driver/include/driver/i2s.h line 379.

Kingwulin commented 2 years ago

It's not a file, it's a function declared in components/driver/include/driver/i2s.h line 379.

Thank you! I found event I write the code like this: to make the num output data consecutly,but the 'de' noise is still exit in num

Is it a problem of wav format, is it better for me to change to pcm format?

#include <stdio.h>
#include "driver/i2s.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "esp_spiffs.h"
#include <string.h>
static const char *TAG = "speaker";

#define I2S_SAMPLE_RATE     (16000)
#define SAMPLE_BITS     (16)

#define MASTER_BCK_IO 4
#define MASTER_WS_IO 5
#define DATA_OUT_IO 18
#define DATA_IN_IO 21

#define MOUNT_POINT  "/audio"

static const char *partition_label = "audio";

typedef struct {
    // The "RIFF" chunk descriptor
    uint8_t ChunkID[4];
    int32_t ChunkSize;
    uint8_t Format[4];
    // The "fmt" sub-chunk
    uint8_t Subchunk1ID[4];
    int32_t Subchunk1Size;
    int16_t AudioFormat;
    int16_t NumChannels;
    int32_t SampleRate;
    int32_t ByteRate;
    int16_t BlockAlign;
    int16_t BitsPerSample;
    // The "data" sub-chunk
    uint8_t Subchunk2ID[4];
    int32_t Subchunk2Size;
} wav_header_t;

#define MAX_OP_FILE 10
typedef struct
{
    FILE * fds[MAX_OP_FILE];
    int num_use;
}file_op_t;
file_op_t file_op;
char file_name_buf[MAX_OP_FILE][16];

static esp_err_t  play_wav(const char *filepath)
{
    FILE *fd = NULL;
    //struct stat file_stat;

    // if (stat(filepath, &file_stat) == -1) {
    //     ESP_LOGE(TAG, "Failed to stat file : %s", filepath);
    //     return ESP_FAIL;
    // }

    // ESP_LOGI(TAG, "file stat info: %s (%ld bytes)...", filepath, file_stat.st_size);
    fd = fopen(filepath, "r");

    if (NULL == fd) {
        ESP_LOGE(TAG, "Failed to read existing file : %s", filepath);
        return ESP_FAIL;
    }
    const size_t chunk_size = 4096;
    uint8_t *buffer = malloc(chunk_size);

    if (NULL == buffer) {
        ESP_LOGE(TAG, "audio data buffer malloc failed");
        fclose(fd);
        return ESP_FAIL;
    }
    int len; 
    /**
     * read head of WAV file
     */
    wav_header_t wav_head;
    len = fread(&wav_head, 1, sizeof(wav_header_t), fd);
    if (len <= 0) {
        ESP_LOGE(TAG, "Read wav header failed");
        fclose(fd);
        return ESP_FAIL;
    }
    if (NULL == strstr((char *)wav_head.Subchunk1ID, "fmt") &&
            NULL == strstr((char *)wav_head.Subchunk2ID, "data")) {
        ESP_LOGE(TAG, "Header of wav format error");
        fclose(fd);
        return ESP_FAIL;
    }

    // ESP_LOGI(TAG, "frame_rate=%d, ch=%d, width=%d", wav_head.SampleRate, wav_head.NumChannels, wav_head.BitsPerSample);

    /**
     * read wave data of WAV file
     */
    size_t write_num = 0;
    size_t cnt;
    //i2s_start(I2S_NUM_0);
    i2s_zero_dma_buffer(I2S_NUM_0);
    do {
        /* Read file in chunks into the scratch buffer */
        len = fread(buffer, 1, chunk_size, fd);
        if (len <= 0) {
            ESP_LOGI(TAG, "break out");
            //i2s_stop(I2S_NUM_0);

            break;
        }

        //pwm_audio_write(buffer, len, &cnt, 1000 / portTICK_PERIOD_MS);
        i2s_write(I2S_NUM_0, buffer, len, &cnt, 100);
        ESP_LOGI(TAG, "%d,%d",cnt,len);
        write_num += len;
    } while (1);

    free(buffer);
    fclose(fd);
    return ESP_OK;
}
void spiffs_driver_init(void)
{
    ESP_LOGI(TAG, "Initializing SPIFFS");

    esp_vfs_spiffs_conf_t conf = {
      .base_path = MOUNT_POINT,
      .partition_label = partition_label,
      .max_files = 5,
      .format_if_mount_failed = false
    };

    // Use settings defined above to initialize and mount SPIFFS filesystem.
    // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(conf.partition_label, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }
}

void hal_i2s_driver_init(void)
{
    esp_err_t err;
    // master driver installed and send data
    i2s_config_t master_i2s_config = {
        .mode = I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX,
        .sample_rate = I2S_SAMPLE_RATE,
        .bits_per_sample = SAMPLE_BITS,
        .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
        .communication_format = I2S_COMM_FORMAT_STAND_I2S,
        .dma_buf_count = 6,
        .dma_buf_len = 200,
        .use_apll = 0,
        .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1 ,
    };
    i2s_pin_config_t master_pin_config = {
        .bck_io_num = MASTER_BCK_IO,
        .ws_io_num = MASTER_WS_IO,
        .data_out_num = DATA_OUT_IO,
        .data_in_num = DATA_IN_IO
    };  

    err = i2s_driver_install(I2S_NUM_0, &master_i2s_config, 0, NULL);
    ESP_LOGI(TAG,"i2s_driver_install status %d",err);
    err = i2s_set_pin(I2S_NUM_0, &master_pin_config);
    ESP_LOGI(TAG,"i2s_set_pin status %d",err);
    i2s_set_clk(I2S_NUM_0, I2S_SAMPLE_RATE, (i2s_bits_per_sample_t)16, (i2s_channel_t)1);
}

void wav_num_set(int num)
{
    file_op.num_use = 0;
    if(num/10)
    {
        //app_play_num(num/10); //noise exit at the begin of this code 
        sprintf(file_name_buf[file_op.num_use++],"/audio/%d.wav",num/10);
        //app_play_num(0); //noise exit at the begin of this code 
        sprintf(file_name_buf[file_op.num_use++],"/audio/0.wav");
    }

    //app_play_num(num%10); //noise exit at the begin of this code 
    sprintf(file_name_buf[file_op.num_use++],"/audio/%d.wav",num%10);
}

static esp_err_t wav_num_player(void)
{
    int i,j=0;
    wav_header_t wav_head;
    int len;
    for(i=0;i<file_op.num_use;i++)
    {
        file_op.fds[i] = NULL;
        file_op.fds[i] = fopen(file_name_buf[i], "r");
        if (NULL == file_op.fds[i]) {
            ESP_LOGE(TAG, "Failed to read existing file : %s", file_name_buf[i]);
            return ESP_FAIL;
        }

        len = fread(&wav_head, 1, sizeof(wav_header_t), file_op.fds[i]);
        if (len <= 0) {
            ESP_LOGE(TAG, "Read wav header failed");
            fclose(file_op.fds[i]);
            return ESP_FAIL;
        }
        if (NULL == strstr((char *)wav_head.Subchunk1ID, "fmt") &&
                NULL == strstr((char *)wav_head.Subchunk2ID, "data")) {
            ESP_LOGE(TAG, "Header of wav format error");
            fclose(file_op.fds[i]);
            return ESP_FAIL;
        }
        ESP_LOGI(TAG, "frame_rate=%d, ch=%d, width=%d", wav_head.SampleRate, wav_head.NumChannels, wav_head.BitsPerSample);
    }

    const size_t chunk_size = 4096;
    uint8_t *buffer = malloc(chunk_size);

    size_t write_num = 0;
    size_t cnt;

    do {
        /* Read file in chunks into the scratch buffer */
        len = fread(buffer, 1, chunk_size, file_op.fds[j]);
        if(j != file_op.num_use-1)
        {
            if (len < chunk_size) {
                j++;
                len+=fread(&buffer[len], 1, chunk_size-len, file_op.fds[j]);

            }
        }
        else
        {
            if (len <= 0) {

                ESP_LOGI(TAG, "break out");

                break;
            }
        }

        //pwm_audio_write(buffer, len, &cnt, 1000 / portTICK_PERIOD_MS);
        i2s_write(I2S_NUM_0, buffer, len, &cnt, 100);
        ESP_LOGI(TAG, "%d,%d",cnt,len);
        write_num += len;
    } while (1);

    free(buffer);
    for(i=0;i<file_op.num_use;i++)
    {   
        fclose(file_op.fds[i]);
    }
    return ESP_OK;  
}

size_t sounder_speaker(char * p_data,unsigned int data_len)
{
    size_t i2s_bytes_write = 0;
    i2s_write(I2S_NUM_0, p_data, data_len, &i2s_bytes_write, 1000 / portTICK_PERIOD_MS);
    return i2s_bytes_write;
}

//char *file_arr[5] = {"shi","001","002","ktw","shd"};

void app_play_num(int index)
{
    char file_buf[16];

    sprintf(file_buf,"/audio/%d.wav",index);

    play_wav(file_buf);
    ESP_LOGI(TAG,"going to player %s",file_buf);
}

void speaker_setTemp2_say(void)
{
    char file_buf[32];

    sprintf(file_buf,"/audio/setTime.wav");
    ESP_LOGI(TAG,"going to player %s",file_buf);
    play_wav(file_buf);   
}
void speaker_du_say(void)
{
    char file_buf[16];

    sprintf(file_buf,"/audio/min.wav");
    ESP_LOGI(TAG,"going to player %s",file_buf);
    play_wav(file_buf);   
}
void app_voice_play(int num)
{
    wav_num_set(num);
    speaker_setTemp2_say();

    wav_num_player();

    speaker_du_say(); //noise exit at the begin of this code 

}
void app_main(void)
{
    spiffs_driver_init();

    hal_i2s_driver_init();
    //play_wav(MOUNT_POINT"/sample.wav");
    app_voice_play(19); 
    vTaskDelay(100);
    app_voice_play(13);
    vTaskDelay(100);
    app_voice_play(31);
    vTaskDelay(100);
    app_voice_play(39);
    vTaskDelay(100);
    app_voice_play(91);
    vTaskDelay(100);
    app_voice_play(93);

}
L-KAYA commented 2 years ago

You can put your wav file into a software named audacity, it can provide the wave shape and also able to play the audio.

Alvin1Zhang commented 3 months ago

Thanks for reporting, feel free to reopen.