有些人建议从头到尾读取音频数据并创建一个从头到尾写的副本,然后简单地播放反转的音频数据。
iOS现有示例是如何完成的吗?
我找到了一个名为MixerHost的示例项目,它在某些时候使用了一个
AudioUnitSampleType
保存已从文件中读取的音频数据,并将其分配给缓冲区。
这被定义为:
typedef SInt32 AudioUnitSampleType;
#define kAudioUnitSampleFractionBits 24
根据Apple的说法:
音频单元和其他音频的规范音频样本类型 iPhone OS中的处理是8.24位的非交错线性PCM 定点样本。
因此换句话说,它包含非交错的线性PCM音频数据。
但我无法弄清楚这些数据的读取位置以及存储位置。这是加载音频数据并缓冲它的代码:
- (void) readAudioFilesIntoMemory {
for (int audioFile = 0; audioFile < NUM_FILES; ++audioFile) {
NSLog (@"readAudioFilesIntoMemory - file %i", audioFile);
// Instantiate an extended audio file object.
ExtAudioFileRef audioFileObject = 0;
// Open an audio file and associate it with the extended audio file object.
OSStatus result = ExtAudioFileOpenURL (sourceURLArray[audioFile], &audioFileObject);
if (noErr != result || NULL == audioFileObject) {[self printErrorMessage: @"ExtAudioFileOpenURL" withStatus: result]; return;}
// Get the audio file's length in frames.
UInt64 totalFramesInFile = 0;
UInt32 frameLengthPropertySize = sizeof (totalFramesInFile);
result = ExtAudioFileGetProperty (
audioFileObject,
kExtAudioFileProperty_FileLengthFrames,
&frameLengthPropertySize,
&totalFramesInFile
);
if (noErr != result) {[self printErrorMessage: @"ExtAudioFileGetProperty (audio file length in frames)" withStatus: result]; return;}
// Assign the frame count to the soundStructArray instance variable
soundStructArray[audioFile].frameCount = totalFramesInFile;
// Get the audio file's number of channels.
AudioStreamBasicDescription fileAudioFormat = {0};
UInt32 formatPropertySize = sizeof (fileAudioFormat);
result = ExtAudioFileGetProperty (
audioFileObject,
kExtAudioFileProperty_FileDataFormat,
&formatPropertySize,
&fileAudioFormat
);
if (noErr != result) {[self printErrorMessage: @"ExtAudioFileGetProperty (file audio format)" withStatus: result]; return;}
UInt32 channelCount = fileAudioFormat.mChannelsPerFrame;
// Allocate memory in the soundStructArray instance variable to hold the left channel,
// or mono, audio data
soundStructArray[audioFile].audioDataLeft =
(AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));
AudioStreamBasicDescription importFormat = {0};
if (2 == channelCount) {
soundStructArray[audioFile].isStereo = YES;
// Sound is stereo, so allocate memory in the soundStructArray instance variable to
// hold the right channel audio data
soundStructArray[audioFile].audioDataRight =
(AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));
importFormat = stereoStreamFormat;
} else if (1 == channelCount) {
soundStructArray[audioFile].isStereo = NO;
importFormat = monoStreamFormat;
} else {
NSLog (@"*** WARNING: File format not supported - wrong number of channels");
ExtAudioFileDispose (audioFileObject);
return;
}
// Assign the appropriate mixer input bus stream data format to the extended audio
// file object. This is the format used for the audio data placed into the audio
// buffer in the SoundStruct data structure, which is in turn used in the
// inputRenderCallback callback function.
result = ExtAudioFileSetProperty (
audioFileObject,
kExtAudioFileProperty_ClientDataFormat,
sizeof (importFormat),
&importFormat
);
if (noErr != result) {[self printErrorMessage: @"ExtAudioFileSetProperty (client data format)" withStatus: result]; return;}
// Set up an AudioBufferList struct, which has two roles:
//
// 1. It gives the ExtAudioFileRead function the configuration it
// needs to correctly provide the data to the buffer.
//
// 2. It points to the soundStructArray[audioFile].audioDataLeft buffer, so
// that audio data obtained from disk using the ExtAudioFileRead function
// goes to that buffer
// Allocate memory for the buffer list struct according to the number of
// channels it represents.
AudioBufferList *bufferList;
bufferList = (AudioBufferList *) malloc (
sizeof (AudioBufferList) + sizeof (AudioBuffer) * (channelCount - 1)
);
if (NULL == bufferList) {NSLog (@"*** malloc failure for allocating bufferList memory"); return;}
// initialize the mNumberBuffers member
bufferList->mNumberBuffers = channelCount;
// initialize the mBuffers member to 0
AudioBuffer emptyBuffer = {0};
size_t arrayIndex;
for (arrayIndex = 0; arrayIndex < channelCount; arrayIndex++) {
bufferList->mBuffers[arrayIndex] = emptyBuffer;
}
// set up the AudioBuffer structs in the buffer list
bufferList->mBuffers[0].mNumberChannels = 1;
bufferList->mBuffers[0].mDataByteSize = totalFramesInFile * sizeof (AudioUnitSampleType);
bufferList->mBuffers[0].mData = soundStructArray[audioFile].audioDataLeft;
if (2 == channelCount) {
bufferList->mBuffers[1].mNumberChannels = 1;
bufferList->mBuffers[1].mDataByteSize = totalFramesInFile * sizeof (AudioUnitSampleType);
bufferList->mBuffers[1].mData = soundStructArray[audioFile].audioDataRight;
}
// Perform a synchronous, sequential read of the audio data out of the file and
// into the soundStructArray[audioFile].audioDataLeft and (if stereo) .audioDataRight members.
UInt32 numberOfPacketsToRead = (UInt32) totalFramesInFile;
result = ExtAudioFileRead (
audioFileObject,
&numberOfPacketsToRead,
bufferList
);
free (bufferList);
if (noErr != result) {
[self printErrorMessage: @"ExtAudioFileRead failure - " withStatus: result];
// If reading from the file failed, then free the memory for the sound buffer.
free (soundStructArray[audioFile].audioDataLeft);
soundStructArray[audioFile].audioDataLeft = 0;
if (2 == channelCount) {
free (soundStructArray[audioFile].audioDataRight);
soundStructArray[audioFile].audioDataRight = 0;
}
ExtAudioFileDispose (audioFileObject);
return;
}
NSLog (@"Finished reading file %i into memory", audioFile);
// Set the sample index to zero, so that playback starts at the
// beginning of the sound.
soundStructArray[audioFile].sampleNumber = 0;
// Dispose of the extended audio file object, which also
// closes the associated file.
ExtAudioFileDispose (audioFileObject);
}
}
哪个部分包含必须反转的音频样本数组?是AudioUnitSampleType
吗?
bufferList->mBuffers[0].mData = soundStructArray[audioFile].audioDataLeft;
注意:audioDataLeft定义为AudioUnitSampleType
,它是一个SInt32但不是数组。
我在Core Audio Mailing list中找到了一条线索:
嗯,据我所知,与iPh * n *无关(除非有些音频 API已被省略 - 我不是该程序的成员)。公平, AudioFile.h和ExtendedAudioFile.h应该为您提供 需要读取或写入咖啡馆并访问其流/频道。 基本上,你想要向后阅读每个频道/流,所以,如果你 不需要音频文件的属性,这是非常简单的 一旦你掌握了该频道的数据,假设它不在 压缩格式。考虑到咖啡馆的格式数量 代表,这可能需要比你更多的代码行 思维。一旦掌握了未压缩的数据,它应该是 就像翻转一个字符串一样简单。然后你当然会替换 文件的数据与反转的数据,或者你可以只提供 音频输出(或您发送反向信号的任何地方)读数 无论你有什么流回来。
这是我尝试过的,但是当我将反向缓冲区分配给两个通道的mData时,我什么都没听到:
AudioUnitSampleType *leftData = soundStructArray[audioFile].audioDataLeft;
AudioUnitSampleType *reversedData = (AudioUnitSampleType *) calloc (totalFramesInFile, sizeof (AudioUnitSampleType));
UInt64 j = 0;
for (UInt64 i = (totalFramesInFile - 1); i > -1; i--) {
reversedData[j] = leftData[i];
j++;
}
答案 0 :(得分:2)
我参与了一个示例应用,它会记录用户说的内容并向后播放。我使用CoreAudio来实现这一目标。 Link to app code
/ * 每个样本的大小为16位(2字节)(单声道)。 您可以通过从录制结束开始将其复制到不同的缓冲区中,一次加载每个样本 向后看。当您到达数据的开头时,您已经反转了数据并且播放将被反转。 * /
// set up output file
AudioFileID outputAudioFile;
AudioStreamBasicDescription myPCMFormat;
myPCMFormat.mSampleRate = 16000.00;
myPCMFormat.mFormatID = kAudioFormatLinearPCM ;
myPCMFormat.mFormatFlags = kAudioFormatFlagsCanonical;
myPCMFormat.mChannelsPerFrame = 1;
myPCMFormat.mFramesPerPacket = 1;
myPCMFormat.mBitsPerChannel = 16;
myPCMFormat.mBytesPerPacket = 2;
myPCMFormat.mBytesPerFrame = 2;
AudioFileCreateWithURL((__bridge CFURLRef)self.flippedAudioUrl,
kAudioFileCAFType,
&myPCMFormat,
kAudioFileFlags_EraseFile,
&outputAudioFile);
// set up input file
AudioFileID inputAudioFile;
OSStatus theErr = noErr;
UInt64 fileDataSize = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
theErr = AudioFileOpenURL((__bridge CFURLRef)self.recordedAudioUrl, kAudioFileReadPermission, 0, &inputAudioFile);
thePropertySize = sizeof(fileDataSize);
theErr = AudioFileGetProperty(inputAudioFile, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
UInt32 dataSize = fileDataSize;
void* theData = malloc(dataSize);
//Read data into buffer
UInt32 readPoint = dataSize;
UInt32 writePoint = 0;
while( readPoint > 0 )
{
UInt32 bytesToRead = 2;
AudioFileReadBytes( inputAudioFile, false, readPoint, &bytesToRead, theData );
AudioFileWriteBytes( outputAudioFile, false, writePoint, &bytesToRead, theData );
writePoint += 2;
readPoint -= 2;
}
free(theData);
AudioFileClose(inputAudioFile);
AudioFileClose(outputAudioFile);
希望这会有所帮助。
答案 1 :(得分:0)
通常,在使用ASBD时,这些字段描述了由此描述表示的缓冲区中的样本数据的完整布局 - 通常这些缓冲区由AudioBufferList中包含的AudioBuffer表示。
但是,当ASBD具有kAudioFormatFlagIsNonInterleaved标志时,AudioBufferList具有不同的结构和语义。在这种情况下,ASBD字段将描述列表中包含的一个AudioBuffers的格式,并且列表中的每个AudioBuffer被确定为具有单个(单声道)音频数据通道。然后,ASBD的mChannelsPerFrame将指示AudioBufferList中包含的AudioBuffers的总数 - 其中每个缓冲区包含一个通道。这主要用于此列表的AudioUnit(和AudioConverter)表示 - 并且不会在此结构的AudioHardware用法中找到。
答案 2 :(得分:0)
您不必分配单独的缓冲区来存储反转数据,这可能需要相当多的CPU,具体取决于声音的长度。要向后播放声音,只需将sampleNumber计数器从totalFramesInFile - 1开始。
您可以像这样修改MixerHost,以达到预期的效果。
将soundStructArray[audioFile].sampleNumber = 0;
替换为
soundStructArray[audioFile].sampleNumber = totalFramesInFile - 1;
make sampleNumber SInt32而不是UInt32。
用这个替换你写出样本的循环。
for (UInt32 frameNumber = 0; frameNumber < inNumberFrames; ++frameNumber) { outSamplesChannelLeft[frameNumber] = dataInLeft[sampleNumber]; if (isStereo) outSamplesChannelRight[frameNumber] = dataInRight[sampleNumber]; if (--sampleNumber < 0) sampleNumber = frameTotalForSound - 1; }
这有效地使它向后播放。嗯。自从我听过MixerHost音乐以来已经有一段时间了。我必须承认我觉得这很令人愉快。