连续录制音频的内存问题
Memory problems with continuously recording audio
在这里,我正在尝试为连续录制的音频系统编写一些代码。然后,当某个幅度阈值被打破时,我会尝试在一定时间内录制音频。
#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
这两个错误让我有些困惑...有什么建议吗?
// 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
由于平台差异,这些类型的错误很难重现,所以我很难准确知道这里发生了什么,但我会指出一些可能与此有关的代码问题.
我注意到您对 free() 的使用存在一些问题。
请注意 free(ptr) 不会更改 ptr 的值,因此您后面的错误可能是由以下调用顺序引起的:
free(recordSamples);
free(recordSamples);
发生这种情况可能是因为您可能两次进入测试 >= 1.5 条件,因此两次免费。解决这个问题应该很简单,就是添加:
recordSamples = NULL;
只要你免费打电话。这样,当你第二次调用 free 时,指针为 NULL,你就不会报错。
与此完全相同的情况的另一个潜在问题是,已释放然后传递给 realloc 的指针将产生未定义的行为。它可以愉快地 return 一个无效的指针而不会抛出任何错误,这取决于实现。如果是这种情况,那么 memcpy 会失败是有道理的,尽管我不确定这是否真的发生在您的第一个案例中。在抛出错误之前,您的某些输出可能没有刷新,因此我们可能无法全面了解错误发生之前调用的内容。调试器对此很有用。
我的建议是确保 recordSamples 在释放后始终设置为 NULL(看起来您的代码中只有两个),看看是否能解决问题。如果仍然存在问题,我建议使用 valgrind 之类的工具来获取有关为什么会出现这些内存问题的更多详细信息。
Valgrind 的工作原理是将系统的 malloc 和 free 替换为具有广泛元数据跟踪功能的自身。它通常可以准确地报告为什么这样的事情可能会失败。
您正在超出已分配缓冲区的写入范围:
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
的数字,那么你必须调整对malloc
、realloc
和memcpy
等基本内存管理函数的调用,因为所有对字节进行操作。要代替 malloc(size)
你会调用 malloc(size * sizeof(float))
.
如果 size
确实是一个字节数,那么将 recordedSamples
设为 char*
更合乎逻辑,或者至少在使用字节偏移量进行指针运算之前对其进行转换, 比如 memcpy((char*)recordedSamples + ...)
.
在这里,我正在尝试为连续录制的音频系统编写一些代码。然后,当某个幅度阈值被打破时,我会尝试在一定时间内录制音频。
#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
这两个错误让我有些困惑...有什么建议吗?
// 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
由于平台差异,这些类型的错误很难重现,所以我很难准确知道这里发生了什么,但我会指出一些可能与此有关的代码问题.
我注意到您对 free() 的使用存在一些问题。
请注意 free(ptr) 不会更改 ptr 的值,因此您后面的错误可能是由以下调用顺序引起的:
free(recordSamples);
free(recordSamples);
发生这种情况可能是因为您可能两次进入测试 >= 1.5 条件,因此两次免费。解决这个问题应该很简单,就是添加:
recordSamples = NULL;
只要你免费打电话。这样,当你第二次调用 free 时,指针为 NULL,你就不会报错。
与此完全相同的情况的另一个潜在问题是,已释放然后传递给 realloc 的指针将产生未定义的行为。它可以愉快地 return 一个无效的指针而不会抛出任何错误,这取决于实现。如果是这种情况,那么 memcpy 会失败是有道理的,尽管我不确定这是否真的发生在您的第一个案例中。在抛出错误之前,您的某些输出可能没有刷新,因此我们可能无法全面了解错误发生之前调用的内容。调试器对此很有用。
我的建议是确保 recordSamples 在释放后始终设置为 NULL(看起来您的代码中只有两个),看看是否能解决问题。如果仍然存在问题,我建议使用 valgrind 之类的工具来获取有关为什么会出现这些内存问题的更多详细信息。
Valgrind 的工作原理是将系统的 malloc 和 free 替换为具有广泛元数据跟踪功能的自身。它通常可以准确地报告为什么这样的事情可能会失败。
您正在超出已分配缓冲区的写入范围:
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
的数字,那么你必须调整对malloc
、realloc
和memcpy
等基本内存管理函数的调用,因为所有对字节进行操作。要代替 malloc(size)
你会调用 malloc(size * sizeof(float))
.
如果 size
确实是一个字节数,那么将 recordedSamples
设为 char*
更合乎逻辑,或者至少在使用字节偏移量进行指针运算之前对其进行转换, 比如 memcpy((char*)recordedSamples + ...)
.