2010-10-20 127 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