1

在openframeworks和本网站http://atastypixel.com/blog/using-remoteio-audio-unit的帮助下,我使用音频单元成功地将麦克风中的音频录制到音频文件中。

我希望能够将文件流式传输回音频单元并播放音频。根据Play an audio file using RemoteIO and Audio Unit我可以使用 ExtAudioFileOpenURL 和 ExtAudioFileRead。但是,如何在缓冲区中播放音频数据?

这是我目前拥有的:

static OSStatus setupAudioFileRead() {
    //construct the file destination URL
    CFURLRef destinationURL = audioSystemFileURL();
    OSStatus status = ExtAudioFileOpenURL(destinationURL, &audioFileRef);
    CFRelease(destinationURL);

    if (checkStatus(status)) { ofLog(OF_LOG_ERROR, "ofxiPhoneSoundStream: Couldn't open file to read"); return status; }

    while( TRUE ) {
        // Try to fill the buffer to capacity.
        UInt32 framesRead = 8000;
        status = ExtAudioFileRead( audioFileRef, &framesRead, &inputBufferList );

        // error check
        if( checkStatus(status) ) { break; }

        // 0 frames read means EOF.
        if( framesRead == 0 ) { break; }

        //play audio???
    }

    return noErr;
}
4

3 回答 3

0

基本上,1.创建一个RemoteIO单元(参见关于如何创建RemoteIO的参考资料);

  1. 创建一个 FilePlayer 音频单元,它是一个专用的音频单元,用于读取音频文件并将文件中的音频数据提供给输出单元,例如步骤 1 中创建的 RemoteIO 单元。要实际使用 FilePlayer,需要进行很多设置(指定播放哪个文件,播放文件的哪一部分等等)都需要在上面完成;

  2. 设置 RemoteIO 单元的 kAudioUnitProperty_SetRenderCallback 和 kAudioUnitProperty_StreamFormat 属性。第一个属性本质上是一个回调函数,RemoteIO 单元从中提取音频数据并播放它。第二个属性必须按照 FilePlayer 支持的 StreamFormat 设置。它可以从 FilePlayer 上调用的 get-property 函数派生。

  3. 在步骤 3 中定义回调集,其中最重要的事情是要求 FilePlayer 渲染到回调提供的缓冲区中,您需要在 FilePlayer 上调用 AudioUnitRender()。

  4. 最后启动 RemoteIO 单元来播放文件。

以上只是在 iOS 上使用音频单元播放文件的基本操作的初步概述。详情可以参考 Chris Adamson 和 Kevin Avila 的Learning Core Audio

于 2013-10-16T02:05:24.837 回答
0

来自作者:http ://atastypixel.com/blog/using-remoteio-audio-unit/ ,如果您向下滚动到播放部分,请尝试以下操作:

static OSStatus playbackCallback(void *inRefCon, 
                                  AudioUnitRenderActionFlags *ioActionFlags, 
                                  const AudioTimeStamp *inTimeStamp, 
                                  UInt32 inBusNumber, 
                                  UInt32 inNumberFrames, 
                                  AudioBufferList *ioData) {    
    // Notes: ioData contains buffers (may be more than one!)
    // Fill them up as much as you can. Remember to set the size value in each buffer to match how
    // much data is in the buffer.

for (int i=0; i < ioData->mNumberBuffers; i++) 
{ 
    AudioBuffer buffer = ioData->mBuffers[i];
    // copy from your whatever buffer data to output buffer
    UInt32 size = min(buffer.mDataByteSize, your buffer.size); 
    memcpy(buffer.mData, your buffer, size);
    buffer.mDataByteSize = size; // indicate how much data we wrote in the buffer

    // To test if your Audio Unit setup is working - comment out the three 
    // lines above and uncomment the for loop below to hear random noise  
    /*
    UInt16 *frameBuffer = buffer.mData;
    for (int j = 0; j < inNumberFrames; j++) {
        frameBuffer[j] = rand();
    }
    */
}
return noErr;
}

如果您只是寻找从 MIC 录制到文件并播放它,Apple 的Speakhere样本可能更适合使用。

于 2012-08-02T22:49:34.323 回答
0

这是一种相对简单的方法,它利用了 Tasty Pixel 博客中提到的音频单元。在录音回调中,您可以使用 ExtAudioFileRead 用文件中的数据填充缓冲区,而不是使用来自麦克风的数据填充缓冲区。我将尝试在下面粘贴一个示例。请注意,这仅适用于 .caf 文件。

在 start 方法中调用 readAudio 或 initAudioFile 函数,它只是获取有关文件的所有信息。

- (void) start {
readAudio();
OSStatus status = AudioOutputUnitStart(audioUnit);
checkStatus(status);
}

现在在 readAudio 方法中初始化音频文件引用。

ExtAudioFileRef fileRef;

void readAudio() {
NSString * name = @"AudioFile";
NSString * source = [[NSBundle mainBundle] pathForResource:name ofType:@"caf"];
const char * cString = [source cStringUsingEncoding:NSASCIIStringEncoding];
CFStringRef str = CFStringCreateWithCString(NULL, cString, kCFStringEncodingMacRoman);
CFURLRef inputFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);

AudioFileID fileID;
OSStatus err = AudioFileOpenURL(inputFileURL, kAudioFileReadPermission, 0, &fileID);
CheckError(err, "AudioFileOpenURL");


err = ExtAudioFileOpenURL(inputFileURL, &fileRef);
CheckError(err, "ExtAudioFileOpenURL");

err = ExtAudioFileSetProperty(fileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(AudioStreamBasicDescription), &audioFormat);
CheckError(err, "ExtAudioFileSetProperty");
}

既然您手头有音频数据,下一步就很容易了。在recordingCallback 中从文件而不是从麦克风中读取数据。

static OSStatus recordingCallback(void *inRefCon,
                              AudioUnitRenderActionFlags *ioActionFlags,
                              const AudioTimeStamp *inTimeStamp,
                              UInt32 inBusNumber,
                              UInt32 inNumberFrames,
                              AudioBufferList *ioData) {

// Because of the way our audio format (setup below) is chosen:
// we only need 1 buffer, since it is mono
// Samples are 16 bits = 2 bytes.
// 1 frame includes only 1 sample

AudioBuffer buffer;

buffer.mNumberChannels = 1;
buffer.mDataByteSize = inNumberFrames * 2;
buffer.mData = malloc( inNumberFrames * 2 );

// Put buffer in a AudioBufferList
AudioBufferList bufferList;
bufferList.mNumberBuffers = 1;
bufferList.mBuffers[0] = buffer;

// Then:
// Obtain recorded samples
OSStatus err = ExtAudioFileRead(fileRef, &inNumberFrames, &bufferList);

// Now, we have the samples we just read sitting in buffers in bufferList
// Process the new data
[iosAudio processAudio:&bufferList];

// release the malloc'ed data in the buffer we created earlier
free(bufferList.mBuffers[0].mData);

return noErr;
}

这对我有用。

于 2015-05-14T00:57:21.930 回答