5

「caf」形式で録音された個々のギターの音符を再生して、ランダムなギター音楽を生成する iPhone アプリを構築しています。これらのノートは、サステインの量に応じて、3 秒から 11 秒まで持続時間が異なります。

私はもともとAVAudioPlayerを再生に使用し、シミュレーターで120 bpmで16分音符を再生すると美しく歌われましたが、ハンドセットでテンポを60 bpm強に上げて1/4音符だけを再生するとすぐに、次のように実行されました犬で、間に合わない。私の高揚感は非常に短命でした。

遅延を減らすために、オーディオ エンジンのテンプレートとして Apple MixerHost プロジェクトを使用して Audio Units 経由の再生を実装しようとしましたが、ボルトで固定してすべてを接続した後も、不正なアクセス エラーが発生し続けました。

何時間も頭を悩ませた後、私はその道をあきらめ、代わりにNovocaineオーディオエンジンをボルトで固定しました.

モデルに接続しようとしてレンガの壁にぶつかりました。

最も基本的なレベルでは、私のモデルは Note オブジェクトの NSDictionary を含む Neck オブジェクトです。

各 Note オブジェクトは、それがギター ネックのどの弦とフレットにあるかを認識しており、独自の AVAudioPlayer を含んでいます。

ユーザー設定で選択したネック サイズに応じて、122 ノート (6 弦 x 22 フレット) または 144 ノート (6 弦 x 24 フレット) のいずれかを含むクロマティック ギター ネックを作成します。

私はこれらのノートを唯一の真実として使用しているため、音楽エンジンによって生成されたすべてのスカラー ノートは、このクロマティック ノート バケットへのポインタです。

@interface Note : NSObject <NSCopying>
{
    NSString *name;
    AVAudioPlayer *soundFilePlayer;
    int stringNumber;
    int fretNumber; 
}

私は常に、選択したスケールのルート ノートまたはコードで再生を開始し、次に再生するノートを生成するので、生成されたノートの 1 つ後ろのノートを常に再生します。このようにして、次に再生するノートは常にキューに入れられ、すぐに使用できます。

これらのノートの再生制御は、次のコードで実現されます。

- (void)runMusicGenerator:(NSNumber *)counter
{
if (self.isRunning) {
    Note *NoteToPlay;
    // pulseRate is the time interval between beats
    // staticNoteLength = 1/4 notes, 1/8th notes, 16th notes, etc.

    float delay = self.pulseRate / [self grabStaticNoteLength];

    // user setting to play single, double or triplet notes. 
    if (self.beatCounter == CONST_BEAT_COUNTER_INIT_VAL) {
        NoteToPlay = [self.GuitarNeck generateNoteToPlayNext];
    } else {
        NoteToPlay = [self.GuitarNeck cloneNote:self.GuitarNeck.NoteToPlayNow];
    }

    self.GuitarNeck.NoteToPlayNow = NoteToPlay;

    [self callOutNoteToPlay];

    [self performSelector:@selector(runDrill:) withObject:NoteToPlay afterDelay:delay];
}

- (Note *)generateNoteToPlayNext
{
    if ((self.musicPaused) || (self.musicStopped)) {
        // grab the root note on the string to resume
        self.NoteToPlayNow = [self grabRootNoteForString];

        //reset the flags
        self.musicPaused = NO;
        self.musicStopped = NO;
    } else {
        // Set NoteRingingOut to NoteToPlayNow
        self.NoteRingingOut = self.NoteToPlayNow;

        // Set NoteToPlaNowy to NoteToPlayNext
        self.NoteToPlayNow = self.NoteToPlayNext;
        if (!self.NoteToPlayNow) {
            self.NoteToPlayNow = [self grabRootNoteForString];

            // now prep the note's audio player for playback
            [self.NoteToPlayNow.soundFilePlayer prepareToPlay];
        }        
    }

    // Load NoteToPlayNext
        self.NoteToPlayNext = [self generateRandomNote];
    }

    - (void)callOutNoteToPlay
    {    
        self.GuitarNeck.NoteToPlayNow.soundFilePlayer.delegate = (id)self;
        [self.GuitarNeck.NoteToPlayNow.soundFilePlayer setVolume:1.0];
        [self.GuitarNeck.NoteToPlayNow.soundFilePlayer setCurrentTime:0];
        [self.GuitarNeck.NoteToPlayNow.soundFilePlayer play];
    }

各ノートの AVAudioPlayer は次のようにロードされます。

- (AVAudioPlayer *)buildStringNotePlayer:(NSString *)nameOfNote
{
    NSString *soundFileName = @"S";
    soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", stringNumber]];
    soundFileName = [soundFileName stringByAppendingString:@"F"];

    if (fretNumber < 10) {
        soundFileName = [soundFileName stringByAppendingString:@"0"];
    }
    soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", fretNumber]];

    NSString *soundPath = [[NSBundle mainBundle] pathForResource:soundFileName ofType:@"caf"];
    NSURL *fileURL = [NSURL fileURLWithPath:soundPath];
    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];

    return notePlayer;
}

これが私がクロッパーになるところです。

ノボカインのGithubページによると……

オーディオの再生

Novocaine *audioManager = [Novocaine audioManager];
[audioManager setOutputBlock:^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels) {
    // All you have to do is put your audio into "audioToPlay".
}];

しかし、ダウンロードしたプロジェクトでは、次のコードを使用してオーディオをロードします...

// AUDIO FILE READING OHHH YEAHHHH
// ========================================
NSURL *inputFileURL = [[NSBundle mainBundle] URLForResource:@"TLC" withExtension:@"mp3"];

fileReader = [[AudioFileReader alloc]
              initWithAudioFileURL:inputFileURL
              samplingRate:audioManager.samplingRate
              numChannels:audioManager.numOutputChannels];

[fileReader play];
fileReader.currentTime = 30.0;

[audioManager setOutputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels)
{
    [fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels];
    NSLog(@"Time: %f", fileReader.currentTime);
}];

最初の方法は float を使用し、2 番目の方法は URL を使用するため、ここで本当に混乱し始めます。

「caf」ファイルをフロートに渡すにはどうすればよいですか? Novocaine の実装方法がわかりません。まだ頭の中が曖昧です。

誰かが私を助けてくれることを願っている私の質問は次のとおりです...

  1. Novocaine オブジェクトは AVAudioPlayer オブジェクトに似ていますか? つまり、自己完結型のオーディオ再生 (/録音/生成) ユニットですか?

  2. ノボカインをそのままモデルに使用できますか? つまり、クロマチック ノートごとに 1 つのノボカイン オブジェクト、またはすべてのクロマチック ノートを含むノボカイン オブジェクトを 1 つ持つ必要がありますか? それとも、代わりにメモに URL を保存して、それを Novocaine プレーヤーに渡しますか?

  3. オーディオが「caf」ファイルで、「audioToPlay」がフロートを取る場合、オーディオを「audioToPlay」に入れるにはどうすればよいですか?

  4. Note.m に Novocaine プロパティを含めて宣言した場合、Novocaine オブジェクトを使用するには、クラスの名前を Note.mm に変更する必要がありますか?

  5. 和音と音程を再現するために、複数の Novocaine オブジェクトを同時に再生するにはどうすればよいですか?

  6. Novocaine オブジェクトの再生をループできますか?

  7. ノートの再生の長さを設定できますか? つまり、10 秒の音符を 1 秒間だけ再生しますか?

  8. Novocaine を使用するように上記のコードを変更できますか?

  9. 私が runMusicGenerator に使用している方法は、プロの基準に達したテンポを維持するために使用する正しいものですか?

4

1 に答える 1

10

Novocaine を使用すると、RemoteIO AudioUnit を手動でセットアップする必要がなくなり、生活が楽になります。これには、一連の CoreAudio 構造体を苦労して埋める必要があり、このオーディオ プロセス コールバックなどの一連のコールバックを提供することが含まれます。

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

代わりに、Novocaine はその実装でそれを処理し、これを実行して設定したブロックを呼び出します。

[audioManager setOutputBlock: ^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels){} ];

あなたが書いたものは何でも再生されaudioToPlayます。

  1. Novocaine が RemoteIO AudioUnit をセットアップします。これは低レベルの CoreAudio API であり、高レベルの AVFoundation とは異なり、予想どおり非常に低レイテンシです。ノボカインが自己完結型であるという点で、あなたは正しい. オーディオをリアルタイムで録音、生成、および処理できます。

  2. Novocaine はシングルトンです。複数の Novocaine インスタンスを持つことはできません。これを行う 1 つの方法は、ギターのサウンドを別のクラスまたは配列に保存し、Novocaine を使用してそれらを再生する一連のメソッドを記述することです。

  3. たくさんのオプションがあります。Novocaine の AudioFileReader を使用して、.caf ファイルを再生できます。これを行うには、サンプル コードに従って、AudioFileReader を割り当ててから、再生する .caf ファイルの URL を渡します。[fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels]次に、コード例に従って、ブロックに固執します。ブロックが呼び出されるたびに、AudioFileReader はディスクからオーディオのチャンクを取得してバッファリングし、audioToPlayその後再生されるように配置します。これにはいくつかの欠点があります。短い音(私が推測するあなたのギターの音など)の場合、繰り返し呼び出しますretrieveFreshAudioパフォーマンスヒットです。一般に、(短いサウンドの場合) ファイル全体をメモリに同期して順次読み込むことをお勧めします。ノボカインは、これを行う方法を (まだ) 提供していません。これを行うには ExtAudioFileServices を使用する必要があります。Apple のサンプル プロジェクト MixerHost で、これを行う方法が詳しく説明されています。

  4. AudioFileReader を使用している場合は、はい。#importObj-C++ ヘッダーから ing する場合、または C++ ヘッダーを ing する場合にのみ、名前を .mm に変更します#include

  5. 前述のとおり、許可される Novocaine インスタンスは 1 つだけです。複数の音源をミックスすることでポリフォニーを実現できます。これは単にバッファを一緒に追加するだけです。異なるピッチで同じギター サウンドの複数のバージョンを作成した場合は、それらをすべてメモリに読み込み、ミックスします。ギターの音を 1 つだけにしたい場合は、演奏しているノートの再生レートをリアルタイムで変更してからミックスダウンする必要があります。

  6. Novocaine は、実際に何を演奏しているかにとらわれず、サンプルをどれだけ長く演奏しているかは気にしません。サウンドをループするには、経過したサンプル数のカウントを維持し、サウンドの最後にいるかどうかを確認してから、そのカウントを 0 に戻す必要があります。

  7. はい。サンプルレートを 44.1k とすると、1 秒のオーディオ = 44100 サンプルになります。44100 に達したら、カウントをリセットします。

  8. はい。こんな感じです。モノラルで 1 秒以上の長さの 4 つのギター サウンドがあり、それらをメモリに読み込んでいると仮定float *guitarCします。*guitarE*guitarG*guitarB;

[audioManager setOutputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels){
    static int count = 0;
    for(int i=0; i<numFrames; ++i){
        //Mono mix each sample of each sound together. Since result can be 4x louder, divide the total amp by 4. 
        //You should be using `vDSP_vadd` from the accelerate framework for added performance.
        data[count] = (guitarC[count] + guitarE[count] + guitarG[count] + guitarB[count]) * 0.25;
        if(++count >= 44100) count = 0;    //Plays the mix for 1 sec
    }
}];
  1. ではない正確に。performSelectorまたは実行ループまたはスレッドでスケジュールされたメカニズムの使用は、正確であることが保証されていません。たとえば、CPU 負荷が変動すると、タイミングの不規則性が発生する場合があります。サンプルの正確なタイミングが必要な場合は、オーディオ ブロックを使用します。
于 2012-12-26T23:10:17.830 に答える