2010-10-20 137 views
8

我想从应用程序包中读取声音文件,将其复制,以最大音量级别(增益值或峰值功率,我不确定其技术名称)播放,然后再将它作为另一个文件写入该包。音频处理:使用音量级别

我做了复制和写作部分。生成的文件与输入文件相同。我在AudioToolbox框架中使用AudioFile服务的AudioFileReadBytes()和AudioFileWriteBytes()函数来做到这一点。因此,我有输入文件的字节和其音频数据格式(通过使用AudioFileGetProperty()与kAudioFilePropertyDataFormat),但我找不到在这些原始文件的最大音量级别播放变量。

为了阐明我的目的,我试图生成另一个音量级别相对于原始音量增加或减少的声音文件,因此我不在乎用户设置的系统音量级别,或者iOS版。

这可能与我提到的框架有关吗?如果没有,是否有其他建议?

感谢


编辑: 通过山姆对于一些音频基础知识答案走,我决定扩大与其他可替代的问题。

我可以使用AudioQueue服务将现有声音文件(它在包中)录制到另一个文件中,并在录制阶段使用音量级别(在框架的帮助下)播放?


更新: 这里就是我如何读取输入文件写入输出。下面的代码降低了“某些”幅度值的声级,但有很多噪音。有趣的是,如果我选择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); 

回答

13

你不会在(Ext)AudioFile中找到幅度缩放操作,因为它是关于你可以做的最简单的DSP。

假设您使用ExtAudioFile将您读取的任何内容转换为32位浮点数。要改变振幅,只需简单乘以:

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服务只是为了获得访问卷属性是严重的,严重矫枉过正。

UPDATE:

在你更新的代码,你是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处理任何输入文件格式,包括立体声v您的代码的例子。单声道

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; 

} 
+0

感谢艺术,我根据您的建议更新了我的代码和问题,但这导致了其他问题。也许我弄错了,但如果你可以用代码片段来看看更新后的问题,它会是完美的。 – cocoatoucher 2010-10-21 13:57:47

+0

我已经编辑了我的答案,并解释了为什么代码不起作用以及代码的一个示例。 – 2010-10-21 15:43:56

+0

艺术,我很感谢这个答案。您不仅向我提供了代码,还帮助我了解发生了什么。非常感谢!请继续分享。也感谢其他人。 – cocoatoucher 2010-10-21 17:57:20

0

对于大多数常见的音频文件格式,没有一个主音量变量。相反,您需要采集(或转换)PCM声音样本,并对每个样本执行至少一些最低限度的数字信号处理(乘法,饱和/限制/ AGC,量化噪声整形等)。

+0

这是坏消息:)如果需要,我宁愿框架处理这种科学操作:) – cocoatoucher 2010-10-20 22:31:29

0

如果声音文件已标准化,则无法使文件变大。除了音频编码不佳的情况外,音量几乎完全是播放引擎的领域。

http://en.wikipedia.org/wiki/Audio_bit_depth

正确存储的音频文件将具有达到或接近可用于文件的位深度最大值峰体积。如果您尝试“降低声音文件的音量”,您基本上只会降低声音质量。

+0

谢谢,这是有道理的。然后我必须扩大这个问题并分享我自己的选择。 – cocoatoucher 2010-10-20 22:30:52

1

我想你应该避免与8位无符号字符工作音频,如果你能。 尝试获取数据为16位或32位,这将避免一些噪音/质量差的问题。

+0

谢谢,我试过Float32,但我仍然有类似的噪音 – cocoatoucher 2010-10-21 14:50:32