簡體   English   中英

如何在iOS上均衡立體聲輸入並將音頻效果僅應用於單個通道?

[英]How to equalize stereo input and apply audio effect only to single channel on iOS?

我需要按以下方式在iOS上處理立體聲音頻文件:

  • 兩個通道應具有相等的強度,即。 使立體聲顯示為單聲道
  • 將單聲道音頻路由到左右聲道
  • 對輸出到正確通道的音頻應用效果

我目前擁有的是:

            +-------------------+
            | AVAudioPlayerNode +------------------------+
            +--------^----------+                        |
                     |                                   |
            +--------+---------+                +--------v---------+
    File ---> AVAudioPCMBuffer |                | AVAudioMixerNode +---> Output
            +--------+---------+                +--------^---------+
                     |                                   |
            +--------v----------+  +-------------------+ |
            | AVAudioPlayerNode +--> AVAudioUnitEffect +-+
            +-------------------+  +-------------------+

該效果是AVAudioUnitEffect的子類。

我在使立體聲輸入顯示為單聲道並將AVAudioPlayerNode輸出到單獨的通道時遇到麻煩。

我試圖將PlayerNodes的音量設置為0.5,平移為-1.0和1.0,但是,由於輸入是立體聲,因此無法產生所需的效果。

使用AVFoundation,我認為我至少有兩個選擇:要么……

(1)均衡PlayerNode的通道,使兩個PlayerNode都顯示為單聲道-之后,我可以使用與之前相同的邏輯:在兩個PlayerNode上具有相同的音量,向左和向右平移另一個音量,並在MixerNode之后對一個PlayerNode施加效果,結果效果僅出現在右輸出通道中。

(2)保持PlayerNodes為立體聲(pan = 0.0),僅在一個PlayerNode上應用效果,然后告訴MixerNode使用一個PlayerNode的兩個通道作為左聲道的源,使用另一個PlayerNode的右聲道。 我猜想,MixerNode將有效地均衡輸入通道,因此當輸入為單聲道且僅從一個輸出通道可聽到效果時,它會出現。

問題是:上述任何一種策略都是可行的,怎么可能? 我還有其他選擇嗎?

我正在為項目使用Swift,但是可以應付Objective-C。


從缺乏回應和我自己的研究來看,在我看來,AVFoundation可能不是走的路。 使用AVFoundation的簡單性很誘人,但我願意接受其他選擇。 目前,我正在研究MTAudioProcessingTap -class,它們可能有用。 仍然感謝您的幫助。

通過使用同時播放的兩個AVPlayer,我設法獲得了預期的結果。 一個AVPlayer的輸入在左聲道平均音頻數據,在右聲道靜音。 反之亦然。 最后,該效果僅應用於一個AVPlayer實例。

由於在AVPlayer實例上應用專有效果非常簡單,因此最大的障礙是如何均衡立體聲輸入。

我發現了兩個相關的問題( 使用MultiChannelMixer和MTAudioProcessingTap平移單聲道信號 ,單聲道音頻立體聲→單聲道的AVPlayer播放 )和一個教程( 使用MTAudioProcessingTap處理AVPlayer的音頻) —我嘗試嘗試的幾乎所有其他教程都提到了該問題。 google),所有這些都表明解決方案可能位於MTAudioProcessingTap中。

可悲的是,MTAudioProcessing接頭(或MediaToolbox的任何其他方面)的官方文檔或多或少 我的意思是,僅在網上找到了一些示例代碼 ,並且通過Xcode找到了標頭(MTAudioProcessingTap.h)。 但是通過上述教程,我設法開始了。

為了使事情變得不太容易,我決定使用Swift,而不是使用Objective-C,因為現有的教程都在其中。 轉換調用還不錯,我什至找到了幾乎可以在Swift 2中創建MTAudioProcessingTap的示例 我確實設法鈎住了處理抽頭並用它輕輕地操縱音頻(嗯,我可以按原樣輸出流,並且至少將其完全清零)。 但是,均衡通道是Accelerate框架 (即vDSP部分)的一項任務。

但是,使用在Swift中大量使用指針(例如:vDSP)的C API 變得相當麻煩 -至少與使用Objective-C的方式相比。 當我最初在Swift中編寫MTAudioProcessingTaps時,這也是一個問題:我無法通過AudioTapContext而不失敗(在Obj-C中獲取上下文就像AudioTapContext *context = (AudioTapContext *)MTAudioProcessingTapGetStorage(tap); ) UnsafeMutablePointers讓我認為Swift不是適合這項工作的工具。

因此,對於處理類,我放棄了Swift,並在Objective-C中對其進行了重構。
而且,如前所述,我正在使用兩個AVPlayer。 所以在AudioPlayerController.swift中,我有:

var left = AudioTap.create(TapType.L)
var right = AudioTap.create(TapType.R)

asset = AVAsset(URL: audioList[index].assetURL!) // audioList is [MPMediaItem]. asset is class property

let leftItem = AVPlayerItem(asset: asset)
let rightItem = AVPlayerItem(asset: asset)

var leftTap: Unmanaged<MTAudioProcessingTapRef>?
var rightTap: Unmanaged<MTAudioProcessingTapRef>?

MTAudioProcessingTapCreate(kCFAllocatorDefault, &left, kMTAudioProcessingTapCreationFlag_PreEffects, &leftTap)
MTAudioProcessingTapCreate(kCFAllocatorDefault, &right, kMTAudioProcessingTapCreationFlag_PreEffects, &rightTap)

let leftParams = AVMutableAudioMixInputParameters(track: asset.tracks[0])
let rightParams = AVMutableAudioMixInputParameters(track: asset.tracks[0])
leftParams.audioTapProcessor = leftTap?.takeUnretainedValue()
rightParams.audioTapProcessor = rightTap?.takeUnretainedValue()

let leftAudioMix = AVMutableAudioMix()
let rightAudioMix = AVMutableAudioMix()
leftAudioMix.inputParameters = [leftParams]
rightAudioMix.inputParameters = [rightParams]
leftItem.audioMix = leftAudioMix
rightItem.audioMix = rightAudioMix

// leftPlayer & rightPlayer are class properties
leftPlayer = AVPlayer(playerItem: leftItem)
rightPlayer = AVPlayer(playerItem: rightItem)
leftPlayer.play()
rightPlayer.play()

我使用“ TapType”來區分通道,它的定義很簡單(在Objective-C中):

typedef NS_ENUM(NSUInteger, TapType) {
    TapTypeL = 0,
    TapTypeR = 1
};

MTAudioProcessingTap回調的創建方法與本教程中的創建方法幾乎相同。 但是,在創建時,我將TapType保存到上下文,以便可以在ProcessCallback中對其進行檢查:

static void tap_InitLeftCallback(MTAudioProcessingTapRef tap, void *clientInfo, void **tapStorageOut) {
    struct AudioTapContext *context = calloc(1, sizeof(AudioTapContext));
    context->channel = TapTypeL;
    *tapStorageOut = context;
}

最后,實際的舉重發生在使用vDSP函數的過程回調中:

static void tap_ProcessCallback(MTAudioProcessingTapRef tap, CMItemCount numberFrames, MTAudioProcessingTapFlags flags, AudioBufferList *bufferListInOut, CMItemCount *numberFramesOut, MTAudioProcessingTapFlags *flagsOut) {
    // output channel is saved in context->channel
    AudioTapContext *context = (AudioTapContext *)MTAudioProcessingTapGetStorage(tap);

    // this fetches the audio for processing (and for output)
    OSStatus status;    
    status = MTAudioProcessingTapGetSourceAudio(tap, numberFrames, bufferListInOut, flagsOut, NULL, numberFramesOut);

    // NB: we assume the audio is interleaved stereo, which means the length of mBuffers is 1 and data alternates between L and R in `size` intervals.
    // If audio wasn’t interleaved, then L would be in mBuffers[0] and R in mBuffers[1]
    uint size = bufferListInOut->mBuffers[0].mDataByteSize / sizeof(float);
    float *left = bufferListInOut->mBuffers[0].mData;
    float *right = left + size;

    // this is where we equalize the stereo
    // basically: L = (L + R) / 2, and R = (L + R) / 2
    // which is the same as: (L + R) * 0.5
    // ”vasm” = add two vectors (L & R), multiply by scalar (0.5)
    float div = 0.5;
    vDSP_vasm(left, 1, right, 1, &div, left, 1, size);
    vDSP_vasm(right, 1, left, 1, &div, right, 1, size);

    // if we would end the processing here the audio would be virtually mono
    // however, we want to use distinct players for each channel, so here we zero out (multiply the data by 0) the other
    float zero = 0;
    if (context->channel == TapTypeL) {
        vDSP_vsmul(right, 1, &zero, right, 1, size);
    } else {
        vDSP_vsmul(left, 1, &zero, left, 1, size);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM