ExtAudioFileWrite创建静默m4a

时间:2015-06-24 16:02:44

标签: ios objective-c core-audio

我正在创建一个应用程序,需要将可变数量的音频文件合并为一个。为实现这一点,我正在使用多声道混音器音频单元。调音台成功混合音轨,如果我将AUGOph中的I / O单元设置为RemoteIO,它会成功播放声音。

但是,我想将新声音保存到文件中;我认为最好的方法是使用Generic Output而不是RemoteIO。文件成功写入并且长度正确(以秒为单位),但播放时是静音。以下是我创建AUGraph的方法(为了简洁,我会尽可能地删除)

- (void)createGraph
{
    OSStatus result = NewAUGraph(&graph);

    AudioComponentDescription mixerDescription;
    mixerDescription.componentType = kAudioUnitType_Mixer;
    mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
    mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
    mixerDescription.componentFlags = 0;
    mixerDescription.componentFlagsMask = 0;

    AudioComponentDescription remoteIODescription;
    remoteIODescription.componentType = kAudioUnitType_Output;
    remoteIODescription.componentSubType = kAudioUnitSubType_GenericOutput;
    remoteIODescription.componentManufacturer = kAudioUnitManufacturer_Apple;
    remoteIODescription.componentFlags = 0;
    remoteIODescription.componentFlagsMask = 0;

    AUNode iONode, mixerNode;

    result = AUGraphAddNode(graph, &remoteIODescription, &iONode);
    result = AUGraphAddNode(graph, &mixerDescription, &mixerNode);
    result = AUGraphOpen(graph);
    result = AUGraphNodeInfo(graph, mixerNode, NULL, &mixerUnit);
    result = AUGraphNodeInfo(graph, iONode, NULL, &iOUnit);
    result = AUGraphConnectNodeInput(graph, mixerNode, 0, iONode, 0);

    UInt32 busCount = (UInt32)fileCount;

    result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &busCount, sizeof(busCount));

    UInt32 maximumFramesPerSlice = 4096;

    result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maximumFramesPerSlice, sizeof(maximumFramesPerSlice));

    for (UInt16 busNumber = 0; busNumber < busCount; busNumber++)
    {
        AURenderCallbackStruct renderCallback;
        renderCallback.inputProc = &inputRenderCallback;
        renderCallback.inputProcRefCon = (__bridge void *)self;

        result = AUGraphSetNodeInputCallback(graph, mixerNode, busNumber, &renderCallback);

        //sets certain parameters for the mixer; I don't believe this is the cause of the problem
        AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Pan, kAudioUnitScope_Input, busNumber, fileSettings[busNumber].pan, 0);
        AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Volume, kAudioUnitScope_Input, busNumber, fileSettings[busNumber].volume, 0);
        AudioUnitSetParameter(mixerUnit, kMultiChannelMixerParam_Enable, kAudioUnitScope_Input, busNumber, fileSettings[busNumber].enabled, 0);

        if (soundStructs[busNumber].isStereo == YES)
        {
            result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &stereoDescription, sizeof(stereoDescription));
        }
        else
        {
            result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, busNumber, &monoDescription, sizeof(monoDescription));
        }
    }

    Float64 sampleRate = SAMPLE_RATE; // 44100.0
    result = AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sampleRate, sizeof(sampleRate));

    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:ofn];

    AudioStreamBasicDescription destinationFormat;
    memset(&destinationFormat, 0, sizeof(destinationFormat));
    destinationFormat.mChannelsPerFrame = 2;
    destinationFormat.mFormatID = kAudioFormatMPEG4AAC;
    destinationFormat.mFormatFlags = kMPEG4Object_AAC_Main;
    destinationFormat.mSampleRate = sampleRate;

    UInt32 size = sizeof(destinationFormat);
    result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &destinationFormat);

    result = ExtAudioFileCreateWithURL(url, kAudioFileM4AType, &destinationFormat, NULL, kAudioFileFlags_EraseFile, &outputFile);

    AudioStreamBasicDescription clientFormat;
    memset(&clientFormat, 0, sizeof(clientFormat));

    size = sizeof(clientFormat);
    result = AudioUnitGetProperty(iOUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, & clientFormat, &size);

    UInt32 codec = kAppleHardwareAudioCodecManufacturer;
    ExtAudioFileSetProperty(outputFile, kExtAudioFileProperty_CodecManufacturer, sizeof(codec), &codec);

    ExtAudioFileSetProperty(outputFile, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);

    result = AUGraphInitialize(graph);
}

这就是我保存文件的方式

- (void)startGraph
{
    AudioUnitRenderActionFlags flags = 0;
    AudioTimeStamp inTimeStamp;
    memset(&inTimeStamp, 0, sizeof(AudioTimeStamp));
    inTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
    UInt32 busNumber = 0;
    UInt32 numberFrames = 1024;
    inTimeStamp.mSampleTime = 0;
    int channelCount = 2;
    SInt64 totFrms = 0;

    for (int i = 0; i < fileCount; i++) //gets the length for the longest recording in the new track
    {
        SInt64 len = soundStructs[i].totalFrames;
        if (len > totFrms && fileSettings[i].enabled == YES)
            totFrms = len;
    }
    while (totFrms > 0)
    {
        if (totFrms < numberFrames)
            numberFrames = (UInt32)totFrms;
        else
            totFrms -= numberFrames;
        AudioBufferList *bufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList)+sizeof(AudioBuffer)*(channelCount-1));
        bufferList->mNumberBuffers = channelCount;
        for (int j=0; j<channelCount; j++)
        {
            AudioBuffer buffer = {0};
            buffer.mNumberChannels = 1;
            buffer.mDataByteSize = numberFrames*sizeof(SInt32);
            buffer.mData = calloc(numberFrames, sizeof(SInt32));

            bufferList->mBuffers[j] = buffer;
        }
        AudioUnitRender(iOUnit, &flags, &inTimeStamp, busNumber, numberFrames, bufferList);

        OSStatus res = ExtAudioFileWrite(outputFile, numberFrames, bufferList);
        NSAssert(res == noErr, @"Res != noerr");
    }

    ExtAudioFileDispose(outputFile);
}

因为问题已经很长了,我不会添加混音器用于输入的回调函数或用于将文件加载到内存中的方法,因为RemoteIO工作的事实让我相信没有错用这些。那么,为什么我的图形创建的输出文件是静默的?

1 个答案:

答案 0 :(得分:1)

在调用AudioUnitRender之后,您需要为下一次迭代增加时间戳的采样时间。

AudioUnitRender(iOUnit, &flags, &inTimeStamp, busNumber, numberFrames, bufferList);
inTimeStamp.mSampleTime += numberFrames;