我想从应用程序包中读取一个声音文件,复制它,以最大音量播放(增益值或峰值功率,我不确定它的技术名称),然后将其写入另一个文件再次到 bundle 。
我做了复制和写作部分。生成的文件与输入文件相同。我使用 AudioToolbox 框架中 AudioFile 服务的 AudioFileReadBytes() 和 AudioFileWriteBytes() 函数来执行此操作。
所以,我有输入文件的字节及其音频数据格式(通过使用 AudioFileGetProperty() 和 kAudioFilePropertyDataFormat),但我找不到其中的变量来播放原始文件的最大音量级别。
为了澄清我的目的,我正在尝试生成另一个声音文件,其音量相对于原始文件有所增加或减少,所以我不关心由用户或iOS设置的系统音量.
这可能与我提到的框架有关吗?如果没有,有其他建议吗?
谢谢
<小时/>编辑: 浏览 Sam 关于一些音频基础知识的回答后,我决定用另一种选择来扩展问题。
我可以使用 AudioQueue 服务将现有声音文件(位于 bundle 中)录制到另一个文件并在录制阶段使用音量级别(在框架的帮助下)播放吗?
<小时/>更新: 这是我读取输入文件并写入输出的方式。下面的代码降低了“某些”幅度值的声级,但产生了大量噪音。有趣的是,如果我选择 0.5 作为幅度值,它会增加而不是降低声音级别,但当我使用 0.1 作为幅度值时,它会降低声音。这两种情况都涉及令人不安的噪音。我认为这就是艺术谈论标准化的原因,但我对标准化一无所知。
AudioFileID inFileID;
CFURLRef inURL = [self inSoundURL];
AudioFileOpenURL(inURL, kAudioFileReadPermission, kAudioFileWAVEType, &inFileID)
UInt32 fileSize = [self audioFileSize:inFileID];
Float32 *inData = malloc(fileSize * sizeof(Float32)); //I used Float32 type with jv42's suggestion
AudioFileReadBytes(inFileID, false, 0, &fileSize, inData);
Float32 *outData = malloc(fileSize * sizeof(Float32));
//Art's suggestion, if I've correctly understood him
float ampScale = 0.5f; //this will reduce the 'volume' by -6db
for (int i = 0; i < fileSize; i++) {
outData[i] = (Float32)(inData[i] * ampScale);
}
AudioStreamBasicDescription outDataFormat = {0};
[self audioDataFormat:inFileID];
AudioFileID outFileID;
CFURLRef outURL = [self outSoundURL];
AudioFileCreateWithURL(outURL, kAudioFileWAVEType, &outDataFormat, kAudioFileFlags_EraseFile, &outFileID)
AudioFileWriteBytes(outFileID, false, 0, &fileSize, outData);
AudioFileClose(outFileID);
AudioFileClose(inFileID);
最佳答案
您不会在 (Ext)AudioFile 中找到幅度缩放操作,因为它是您可以执行的最简单的 DSP。
假设您使用 ExtAudioFile 将读取的任何内容转换为 32 位 float 。要改变幅度,只需乘以:
float ampScale = 0.5f; //this will reduce the 'volume' by -6db
for (int ii=0; ii<numSamples; ++ii) {
*sampOut = *sampIn * ampScale;
sampOut++; sampIn++;
}
要增加增益,您只需使用 > 1.f 的比例即可。例如,2.f 的 ampScale 将为您提供 +6dB 的增益。
如果要标准化,则必须对音频进行两次传递:一次确定振幅最大的样本。然后另一个实际应用您计算的增益。
仅仅为了访问音量属性而使用 AudioQueue 服务是严重的,严重的过度杀伤力。
更新:
在更新后的代码中,您将每个字节乘以 0.5,而不是每个样本。这是对您的代码的快速修复,但是请参阅下面我的注释。我不会做你正在做的事情。
...
// create short pointers to our byte data
int16_t *inDataShort = (int16_t *)inData;
int16_t *outDataShort = (int16_t *)inData;
int16_t ampScale = 2;
for (int i = 0; i < fileSize; i++) {
outDataShort[i] = inDataShort[i] / ampScale;
}
...
当然,这不是最好的方法:它假设您的文件是小端 16 位有符号线性 PCM。 (大多数 WAV 文件是,但不是 AIFF、m4a、mp3 等)我会使用 ExtAudioFile API 而不是 AudioFile API,因为这会将您正在读取的任何格式转换为您想要在代码中使用的任何格式。通常最简单的做法是以 32 位浮点形式读取样本。以下是使用 ExtAudioAPI 处理任何输入文件格式(包括立体声和单声道)的代码示例
void ScaleAudioFileAmplitude(NSURL *theURL, float ampScale) {
OSStatus err = noErr;
ExtAudioFileRef audiofile;
ExtAudioFileOpenURL((CFURLRef)theURL, &audiofile);
assert(audiofile);
// get some info about the file's format.
AudioStreamBasicDescription fileFormat;
UInt32 size = sizeof(fileFormat);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileDataFormat, &size, &fileFormat);
// we'll need to know what type of file it is later when we write
AudioFileID aFile;
size = sizeof(aFile);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_AudioFile, &size, &aFile);
AudioFileTypeID fileType;
size = sizeof(fileType);
err = AudioFileGetProperty(aFile, kAudioFilePropertyFileFormat, &size, &fileType);
// tell the ExtAudioFile API what format we want samples back in
AudioStreamBasicDescription clientFormat;
bzero(&clientFormat, sizeof(clientFormat));
clientFormat.mChannelsPerFrame = fileFormat.mChannelsPerFrame;
clientFormat.mBytesPerFrame = 4;
clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame;
clientFormat.mFramesPerPacket = 1;
clientFormat.mBitsPerChannel = 32;
clientFormat.mFormatID = kAudioFormatLinearPCM;
clientFormat.mSampleRate = fileFormat.mSampleRate;
clientFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved;
err = ExtAudioFileSetProperty(audiofile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
// find out how many frames we need to read
SInt64 numFrames = 0;
size = sizeof(numFrames);
err = ExtAudioFileGetProperty(audiofile, kExtAudioFileProperty_FileLengthFrames, &size, &numFrames);
// create the buffers for reading in data
AudioBufferList *bufferList = malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (clientFormat.mChannelsPerFrame - 1));
bufferList->mNumberBuffers = clientFormat.mChannelsPerFrame;
for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
bufferList->mBuffers[ii].mDataByteSize = sizeof(float) * numFrames;
bufferList->mBuffers[ii].mNumberChannels = 1;
bufferList->mBuffers[ii].mData = malloc(bufferList->mBuffers[ii].mDataByteSize);
}
// read in the data
UInt32 rFrames = (UInt32)numFrames;
err = ExtAudioFileRead(audiofile, &rFrames, bufferList);
// close the file
err = ExtAudioFileDispose(audiofile);
// process the audio
for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
float *fBuf = (float *)bufferList->mBuffers[ii].mData;
for (int jj=0; jj < rFrames; ++jj) {
*fBuf = *fBuf * ampScale;
fBuf++;
}
}
// open the file for writing
err = ExtAudioFileCreateWithURL((CFURLRef)theURL, fileType, &fileFormat, NULL, kAudioFileFlags_EraseFile, &audiofile);
// tell the ExtAudioFile API what format we'll be sending samples in
err = ExtAudioFileSetProperty(audiofile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
// write the data
err = ExtAudioFileWrite(audiofile, rFrames, bufferList);
// close the file
ExtAudioFileDispose(audiofile);
// destroy the buffers
for (int ii=0; ii < bufferList->mNumberBuffers; ++ii) {
free(bufferList->mBuffers[ii].mData);
}
free(bufferList);
bufferList = NULL;
}
关于iphone - 音频处理: Playing with volume level,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3982723/