连续录制音频时出现内存问题

时间:2015-01-21 01:39:29

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

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

#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

这两个错误让我有点困惑......有什么建议吗?

3 个答案:

答案 0 :(得分:6)

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

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指向((i - 1) * size * sizeof(float)之后的内存位置recordedSamples字节。这通常不在分配的缓冲区内,因为浮点数大于单个字节。

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

如果它是floats的数量,那么您必须调整对基本内存管理功能的调用,例如mallocreallocmemcpy,因为全部操作字节。要取代malloc(size)而不是malloc(size * sizeof(float))

如果size确实是一个字节数,那么使recordedSamples成为char*更合乎逻辑,或者至少在使用字节偏移进行指针算法之前抛出它,如memcpy((char*)recordedSamples + ...)

答案 1 :(得分:3)

由于平台差异,这些类型的错误难以重新创建,因此我很难准确了解这里发生的事情,但我会指出您的代码可能有些事情要做的一些问题用它。

我注意到你使用free()会遇到一些问题。

请注意,free(ptr)不会更改ptr的值,因此后一个错误可能由以下调用序列引起:

free(recordSamples);
free(recordSamples);

这可能会发生,因为您可能会两次输入测试&gt; = 1.5条件,因此可以免费获得双倍。解决这个问题应该就是添加:

recordSamples = NULL; 

无论何时免费通话。这样,当您第二次调用free时,指针为NULL,并且您不会收到错误。

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

我的建议是确保recordSamples在空闲后总是设置为NULL(看起来代码中只有两个),看看是否能解决问题。如果仍然存在问题,我建议使用像valgrind这样的工具来获取有关这些内存问题发生原因的更多详细信息。

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

http://valgrind.org/

答案 2 :(得分:0)

// 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