【问题标题】:How can I store the 50ms before and after an audio event in a circular buffer?如何将音频事件前后的 50 毫秒存储在循环缓冲区中?
【发布时间】:2019-10-07 00:48:17
【问题描述】:

我正在处理一个包含 17 小时音频 .wav(16 位 PCM,192khz)的数据集,以模拟将嵌入 ESP32、Arduino DUE 或 RASP 中的“实时”处理,具体取决于结果。

我现在该如何处理?

首先我将 17 小时的文件剪成 1 分钟的样本,然后我用 C 语言创建了一个程序,将该文件转换为 .CSV(跳过 .wav 的整个头部并仅获取日期字段)。

PS:我选择 CSV 是为了更好地处理数据,以便在 Scilab 中执行测试以验证算法。

有了这个生成的 .CSV 文件,我在第二个程序中运行它,打开这个文件并用 130 毫秒(24900 个值)填充一个循环缓冲区,当缓冲区已满时,代码开始计算 RMS(均方根) 在 10ms 重叠的移动窗口中,窗口大小为 30ms 。当我得到一个大于 1000 的值时,它被认为是一个事件。

您可以在下面看到问题的说明:

这里显示了一个事件前后 50 毫秒的窗口,我的意思是:

PS:Inicio、Fim 和 Janela 分别表示 Start、End、Window。

我的问题是:

我应该如何保存事件前后的这 50 毫秒,因为事件可以发生在缓冲区中的任何位置?如果事件持续超过一个窗口,我该怎么办?

一些数据帮助理解:

130ms = 24900 values ​​from my .csv file
50ms = 9600   values
30ms = 5700   values
10ms = 1920   values

我搜索了几个来源,但大多数 DSP 参考书目和数据结构对这些主题的处理都是肤浅的,只是说明什么是循环缓冲区,而不是如何以有用的方式处理它。

这是我的代码草图,它似乎对问题采取了错误的方法,但我真的不知道如何继续,在这种情况下,我创建了一个从 1 到 100 的数据集以简化调试:

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<math.h>

    // Define the size of window 50ms
    #define window_size 3 // 30ms
    #define buffer_size 13 // 130ms = 50ms + 30ms + 50ms

    int main()
    {
        //define variables.
        int buffer[buffer_size]={0}; // create the buffer with 150ms;
        int write = 0;
        int i = 0, j = 0;
        int read = 0;
        int read1 =0;
        int write1 = 0;
        int counter_elements = 0;
        int number_lines = 0;
        int save_line = 0;
        char c;
        char str[1024];     // array to hold characters in a conversion of char to int.
        int inicio = 0, fim = 0;
        //RMS
        int soma_quadrado = 0;
        int rms = 0;
        int pre_amostragem[5] = {0};

        //Define variaveis referentes a leitura do arquivo e manipulacoes do mesmo.
        FILE * fp;
        FILE * LOG;
        FILE * log_rms_final;

        // Open the file and verify is NULL.
        if((fp = fopen("generator.txt","r")) == NULL)
        { // Define o nome do csv para abrir
            printf("Error! Can't open the file.\n");
            exit(1);
        }
        // store rms values
         LOG = fopen("RMSValues.csv", "a");
        // store the 50ms after and before a event.
        log_rms_final = fopen("Log_RMS.csv","a");

        int lines = 0;
        while(!feof(fp))
        {
            fgets(str,1024,fp); //reads 1024 characters and store in str.
            buffer[write] = atoi(str);
            write = (write + 1) % buffer_size; // circular
            counter_elements++; // sum 

        c = fgetc(fp);
        if(c == '\n')
        {
            lines++;
        }
        printf("%d\n", lines);
            //if buffer is full
            if(counter_elements == buffer_size)
            {
                // window
                read1 = read; 
                for(i = 0; i < window_size; i++)
                {
                    //square and sum.
                    soma_quadrado += buffer[read1]*buffer[read1];
                    read1 = (read1 + 1) % buffer_size;
                }

                // RMS 
                rms = sqrt(soma_quadrado/window_size);

                fprintf(LOG, "\n %d", rms); // store

                if(rms > 1000)
                {
                    printf("rms: %d\n",rms);

                    // store the 50ms befor a event and the window.
                    write1 = write;
                    for(j = 0 ; j < 5; j++)
                    {

                        write1 = (write1 + (buffer_size - 1)) % buffer_size;
                        pre_amostragem[j] = buffer[write1];
                    }

                    fprintf(log_rms_final,"%s","\n");

                    for(j = 4; j >= 0; j--)
                    {
                        fprintf(log_rms_final,"%d - pre \n",pre_amostragem[j]);
                    }

                    fprintf(log_rms_final,"%s","\n");
    /*
                    for(j = 0; j < window_size; j++)
                    {

                        fprintf(log_rms_final,"%d - janela\n",buffer[read1]);
                        read1 = (read1 + 1) % buffer_size;
                    }
    */
                    fprintf(log_rms_final,"%s","\n");

                    //store the 50ms after a event.

                    /*
                    fseek(log_rms_final,save_line - 3,save_line);

                    for(j = 0; j < 5; j++){

                        fgets(str,1024,fp);
                        fprintf(log_rms_final,"%d - pós \n",atoi(str));

                    }
                    */
                }

                soma_quadrado = 0;
                rms = 0;


                read = (read + 1) % buffer_size;
                counter_elements = counter_elements - 2;

            }
            soma_quadrado = 0;
            rms = 0;

        }

    fclose(fp);
    fclose(LOG);
    fclose(log_rms_final);
    return 0;
    }

一些 cmets 是葡萄牙语,但它们与理解问题无关。

【问题讨论】:

  • C 还是 C++?选择一个。
  • 你知道频率+频道数。您知道当前正在为您的窗口查看的样本数量。如果您的事件发生 - 您可以建立之前 50 毫秒的帧。当您的事件停止时,您可以在 50 毫秒后保存帧。
  • 我个人会将数据的“窗口”大小保留在缓冲区中。如果我没有找到事件的开始,请将除最后 50mS 之外的所有内容放到缓冲区的开头,然后用新数据填充缓冲区的其余部分。冲洗,重复。是的,你检查了一些数据两次,但它可以防止你错过一个事件的开始或让你的事件离开缓冲区的末尾。
  • @broman C,我已经包含了 c++,因为我在 c 中找不到太多关于此的内容。
  • 在你的“草图”中,你的缓冲区大小需要是 t / sample-rate 的长度,你的“窗口”有超过 13 个样本。做什么是事件持续多个“窗口”需要有关系统约束的信息,但实际上您只需要一个“窗口”来进行 50 毫秒事件开始监控,之后数据可以流式传输到任意长度的缓冲区或到存储媒体,直到检测到事件结束。

标签: c embedded signal-processing audio-processing


【解决方案1】:

我在这里给你一个解决方案的算法。

  1. 始终在循环缓冲区中记录 50 毫秒(甚至 60 毫秒)的数据。
  2. 如果检测到启动事件,
    • 将前 50 毫秒从循环缓冲区复制到最终缓冲区
    • 继续将接收到的数据写入最终缓冲区的 50 毫秒位置。
  3. 如果您检测到结束事件。
    • 继续写入最终缓冲区 50 毫秒以上。
    • 再次开始写入循环缓冲区。
  4. 如果您有多个事件,则需要有多个最终缓冲区,并且可以重复该过程。

正如下面 cmets 中所述,此解决方案也适用于 > 50 ms 的窗口大小。您需要相应地选择最终缓冲区的大小。

【讨论】:

  • 很好的答案,但是问题包括“如果事件持续超过一个窗口,我该怎么办?”这里没有解决。除了更大的“最坏情况”窗口之外,我不确定是否有一个好的答案。将数据直接存储到具有足够容量的大容量存储设备而不是有限且必然较短的“窗口”缓冲区将是我的方法。如果没有大容量存储,那么您必须选择一个缓冲区大小并接受它可能会失败并可能处理失败。
  • 通过超过 1 个窗口,我知道事件大小超过 50 毫秒。此处给出的解决方案将允许任何事件大小。只有最终的缓冲区大小需要增加。循环缓冲区可以保持不变。
  • 我同意,但问题是指“窗口”覆盖整个事件(示例中为 200 毫秒)。持续时间更长的事件需要更大的缓冲区“最终”缓冲区,但如果最大事件长度是不确定的,则您必须选择一些“最坏情况”长度先验并消耗您的资源可能没有,对于相对罕见的情况,或者我建议使用某种流媒体。我的意思很简单,答案并没有直接解决问题。如果没有关于系统约束的更具体信息,就不可能给出具体的解决方案。
  • 如果事件长度是indefinite,没有特定的任意缓冲区长度就足够了。我认为没有更多信息是无法回答的,但这些问题也许可以解决(或者这些 cmets 已经充分涵盖了这些问题)。
  • 我对这个问题的看法是,在所有事件的非常大的记录(几小时)中,所需的“最终”大小很小(大约几毫秒或几秒)。由于 OP 使用的是微控制器,他可能没有使用malloc,因此需要提前确定窗口大小和支持的窗口数量。
猜你喜欢
  • 2018-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-07
  • 2018-12-17
相关资源
最近更新 更多