【问题标题】:Memory problems with continuously recording audio连续录制音频的内存问题
【发布时间】:2015-03-19 10:36:41
【问题描述】:

在这里,我正在尝试为连续录制的音频系统编写一些代码。然后,当某个幅度阈值被打破时,我会尝试将音频录制一段时间。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <portaudio.h>
#include <sndfile.h>

#define FRAMES_PER_BUFFER (1024)
#define SAMPLE_SIZE (4)

typedef struct
{
    uint16_t formatType;
    uint16_t numberOfChannels;
    uint32_t sampleRate;
    float* recordedSamples;
} AudioData;

AudioData initAudioData(uint32_t sampleRate, uint16_t channels, int type)
{
    AudioData data;
    data.formatType = type;
    data.numberOfChannels = channels;
    data.sampleRate = sampleRate;
    return data;
}

float avg(float *data)
{
    int elems = sizeof(data) / sizeof(data[0]);
    float sum = 0;
    for (int i = 0; i < elems; i++)
    {
        sum += fabs(*(data + i));
    }
    return (float) sum / elems;
}

int main(void)
{
    AudioData data = initAudioData(44100, 2, paFloat32);
    PaStream *stream = NULL;
    PaError err = paNoError;
    int size = FRAMES_PER_BUFFER * data.numberOfChannels * SAMPLE_SIZE;
    float *sampleBlock = malloc(size);
    float *recordedSamples = NULL;
    time_t talking = 0;
    time_t silence = 0;

    if((err = Pa_Initialize())) goto done;
    PaStreamParameters inputParameters =
    {
        .device = Pa_GetDefaultInputDevice(),
        .channelCount = data.numberOfChannels,
        .sampleFormat = data.formatType,
        .suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultHighInputLatency,
        .hostApiSpecificStreamInfo = NULL
    };
    if((err = Pa_OpenStream(&stream, &inputParameters, NULL, data.sampleRate, FRAMES_PER_BUFFER, paClipOff, NULL, NULL))) goto done;
    if((err = Pa_StartStream(stream))) goto done;
    for(int i = 0;;)
    {
        err = Pa_ReadStream(stream, sampleBlock, FRAMES_PER_BUFFER);
        if(avg(sampleBlock) > 0.000550) // talking
        {
            printf("You're talking! %d\n", i);
            i++;
            time(&talking);
            recordedSamples = realloc(recordedSamples, size * i);
            if (recordedSamples) memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size); // problem here writing to memory at i = 16?
            else free(recordedSamples);
        }
        else //silence
        {
            double test = difftime(time(&silence), talking);
            printf("Time diff: %g\n", test);
            if (test >= 1.5)
            {
                // TODO: finish code processing audio snippet
                talking = 0;
                free(recordedSamples); // problem freeing memory?
            }
        }
    }

done:
    free(sampleBlock);
    Pa_Terminate();
    return err;
}

但是,代码有点挑剔。有时当我在 Xcode 中运行我的程序时,我会得到以下输出:

Time diff: 1.4218e+09
You're talking! 0
You're talking! 1
You're talking! 2
You're talking! 3
You're talking! 4
You're talking! 5
You're talking! 6
You're talking! 7
You're talking! 8
You're talking! 9
You're talking! 10
You're talking! 11
You're talking! 12
You're talking! 13
You're talking! 14
You're talking! 15
(lldb)

Xcode 指向这一行是问题所在:

if (recordedSamples) memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size); // problem here writing to memory at i = 16?

其他时候我运行代码,我得到这个错误:

Time diff: 1.4218e+09
You're talking! 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 0
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 1
Time diff: 2
Time diff: 1.4218e+09
CTestEnvironment(55085,0x7fff7938e300) malloc: *** error for object 0x10081ea00: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug

这两个错误都让我有些困惑……有什么建议吗?

【问题讨论】:

  • 你不是在分配recordedSamples,而是试图重新分配或释放它
  • @Lashane 请阅读realloc 文档
  • 这一行:'int elems = sizeof(data) / sizeof(data[0]);'问题是“sizeof(data)”将是指针的大小,而不是浮点数的大小。建议:int elems = sizeof(float) / sizeof(data[0]);
  • 始终检查 malloc 和 realloc 的返回值,以确保操作成功。在 realloc 上,将返回的值保存到临时变量中,检查是否为空,如果不为空,则仅将临时变量复制到实际使用的指针中。否则,如果 realloc 失败,那么指向原始分配内存的指针将会丢失
  • 您的平均函数无法正常工作 - 它只会返回第一个元素的绝对值。 sizeof(data) 正在返回指针的大小,(我认为在您的平台上)与浮点数的大小相同。这意味着elems 将永远是一。您无法以任何方式检查 data 来获取长度 - 您需要将其作为参数传递给您的平均函数。

标签: c audio memory-management deep-copy portaudio


【解决方案1】:

您正在写入超出分配缓冲区的范围:

recordedSamples = realloc(recordedSamples, size * i);
memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size);

realloc() 分配一定数量的字节,这里是size * i。结果指针存储在recordedSamples,其类型为float*

memcpy() 然后尝试将数据写入recordedSamples + ((i - 1) * size。指针算法用于确定应写入的位置。由于 recordedSamples 的类型为 float*recordedSample + X 指向 X 个浮点值(不是 X 个字节)的偏移量。

换句话说,recordedSamples + ((i - 1) * size 指向recordedSamples 之后的内存位置((i - 1) * size * sizeof(float) 字节。这通常不在分配的缓冲区内,因为浮点数大于单个字节。

要解决这个问题,最大的问题是 size 应该是字节数还是浮点数。这取决于您使用的 API 函数,我没有详细研究。

如果是多个floats,那么您必须调整对基本内存管理函数的调用,例如mallocreallocmemcpy,因为它们都对字节进行操作。代替malloc(size),您将调用malloc(size * sizeof(float))

如果size 确实是多个字节,那么将recordedSamples 设为char* 会更合乎逻辑,或者至少在使用字节偏移量进行指针运算之前将其转换,例如memcpy((char*)recordedSamples + ...)

【讨论】:

    【解决方案2】:

    由于平台差异,这些类型的错误很难重新创建,所以我很难准确地知道这里发生了什么,但我会指出你的代码中可能与它有关的一些问题。

    我注意到您在使用 free() 时存在一些问题。

    注意 free(ptr) 不会改变 ptr 的值,所以你后面的错误可能是由以下调用顺序引起的:

    free(recordSamples);
    free(recordSamples);
    

    这可能是因为您可能两次进入测试 >= 1.5 条件,因此两次免费。解决这个问题应该很简单,就是添加:

    recordSamples = NULL; 
    

    当您免费拨打电话时。这样,第二次调用free时指针为NULL,不会报错。

    这种情况的另一个潜在问题是,一个已被释放然后传递给 realloc 的指针会产生未定义的行为。它可以愉快地返回一个无效指针而不会引发任何错误,具体取决于实现。如果是这种情况,那么 memcpy 会失败是有道理的,尽管我承认我不确定这是否真的发生在你的第一个案例中。在引发错误之前,您的某些输出可能没有刷新,因此我们可能无法全面了解错误之前调用的内容。调试器会对此很有用。

    我的建议是确保在释放后 recordSamples 始终设置为 NULL(看起来您的代码中只有两个)并查看是否可以解决问题。如果仍然存在问题,我建议使用 valgrind 之类的工具来获取有关为什么会出现这些内存问题的更多详细信息。

    Valgrind 的工作原理是将系统的 malloc 和 free 替换为具有广泛元数据跟踪功能的自己。它通常可以准确地报告为什么这样的事情可能会失败。

    http://valgrind.org/

    【讨论】:

    • 啊,我不敢相信我忘了这样做。 +1 发现这个错误
    【解决方案3】:
    // Note: I do not have the portaudio.h and sndfile.h so could not compile/test
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>
    #include <time.h>
    
    #include "portaudio.h"
    #include "sndfile.h"
    
    #define FRAMES_PER_BUFFER  (1024)
    #define SAMPLE_SIZE        (4)
    #define NUMBER_OF_CHANNELS (2)
    #define SAMPLE_RATE        (44100)
    #define SIZE               (FRAMES_PER_BUFFER * NUMBER_OF_CHANNELS * SAMPLE_SIZE)
    
    static const int size = SIZE;
    
    struct AudioData_t
    {
        uint16_t formatType;
        uint16_t numberOfChannels;
        uint32_t sampleRate;
        float* recordedSamples;
    };
    
    
    void initAudioData(
        struct AudioData_t *pData,
        uint32_t sampleRate,
        uint16_t channels,
        int type)
    {
        (*pData).formatType = type;
        (*pData).numberOfChannels = channels;
        (*pData).sampleRate = sampleRate;
    } // end function: initAudioData
    
    
    float averageAudioLevel(float *data)
    {
        int elems = size / sizeof(float); // <--
        float sum = 0;
    
        for (int i = 0; i < elems; i++)
        {
            sum += fabs(*(data + i));
        }
        return  sum / elems; // sum is float so result is float
    } // end function: averageAudioLevel
    
    
    int main(void)
    {
        struct AudioData_t data;
        initAudioData(&data, SAMPLE_RATE, NUMBER_OF_CHANNELS, paFloat32);
    
        PaStream *stream = NULL;
        PaError err = paNoError;
    
    
        float *sampleBlock = NULL;
    
        if(NULL == (sampleBlock = malloc(size) ) )
        { // then, malloc failed
            perror( "malloc failed" );
            exit( EXIT_FAILURE );
        }
    
        // implied else, malloc successful
    
        float *recordedSamples = NULL;
    
        time_t talking = 0;
        time_t silence = 0;
    
        if( 0 == (err = Pa_Initialize()))
        { // then init successful
    
            PaStreamParameters inputParameters =
            {
                .device = Pa_GetDefaultInputDevice(),
                .channelCount = data.numberOfChannels,
                .sampleFormat = data.formatType,
                .suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultHighInputLatency,
                .hostApiSpecificStreamInfo = NULL
            };
    
            // if err >0, exit
            if( 0 == (err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, NULL, NULL)))
            { // then success
                if( 0 == (err = Pa_StartStream(stream)) )
                { // then success
    
                    int i = 0;
                    while(1) // this loop never exits
                    {
                        talking = 0;
                        if( 0 == (err = Pa_ReadStream(stream, sampleBlock, FRAMES_PER_BUFFER) ) )
                        {
                            if(averageAudioLevel(sampleBlock) > 0.000550) // talking
                            {
                                printf("You're talking! %d\n", i);
    
                                i++; // counting usable audio samples
    
                                if( !talking ) {talking = time(NULL);} // only do once per audio sample
    
                                // increase allocation for another audio sample (starts at 0 allocated)
                                float *temp;
                                if( NULL == (temp = realloc(recordedSamples, size * i ) )
                                { // then realloc failed
                                    perror( ""realloc failed" ");
                                    free( sampleBlock );
                                    free( recordedSamples );
                                    exit( EXIT_FAILURE );
                                }
    
                                // implied else, realloc successful
    
                                // update the actual allocated memory pointer
                                recordedSamples = temp;
    
                                // save the new sample into array of samples
                                memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size);}
                            } // end if
                        }
    
                        else //silence
                        {
                            if( 0 < i )
                            { // then some samples to evaluate
    
                                double elapsedTime = difftime(time(NULL), talking);
                                printf("Time diff: %g\n", elapsedTime);
    
                                if (elapsedTime >= 1.5)
                                {
                                    // TODO: finish code processing audio snippet
    
                                    // reset time indicators so do not process silence unless proceed by audio sound
                                    talking = 0;
    
                                    // reset audio sample counter
                                    i = 0;
    
                                    // dispose of recorded samples
                                    free( recordedSamples );
    
                                    // prep for next recording
                                    recordedSamples = NULL;
                                } // end if
                            } // end if
                        } // end if
                    } //  end forever loop
                } // end if
            } // end if
        } // end if
    
    
        free( sampleBlock );
        free( recordedSamples );
        Pa_Terminate();
        return err;
    } // end function: main
    

    【讨论】:

    • 这不会导致问题中指定的问题。此外,编译器利用return 值优化...
    • 您的编辑仍然包含我在问题中描述的问题,以及一些语法错误。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多