ExtAudioFileWrite 创建静音 m4a

Posted

技术标签:

【中文标题】ExtAudioFileWrite 创建静音 m4a【英文标题】:ExtAudioFileWrite creates silent m4a 【发布时间】:2015-06-24 16:02:44 【问题描述】:

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

但是,我想将新声音保存到文件中;我认为最好的方法是使用通用输出而不是 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】:

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

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

【讨论】:

以上是关于ExtAudioFileWrite 创建静音 m4a的主要内容,如果未能解决你的问题,请参考以下文章

使用 ExtAudioFileWrite 在文件末尾写入

静音不会影响文件大小的音频格式

iOS 中的 ExtAudioFileWrite 失败,错误代码为 -66560

从 AudioUnit 格式转换为 ExtAudioFileWrite

使用 ExtAudioFileWrite ios 将流式 mp3 数据包的缓冲区写入 wav 文件

如何将MAV转换成MP3格式,为啥用千千静音后成无效格式了