24

iPhone で簡単なオーディオ シーケンサーをプログラムしたいのですが、正確なタイミングを取得できません。ここ数日、AudioServicesPlaySystemSound と AVAudioPlayer と OpenAL から AudioQueues に至るまで、iphone で可能なすべてのオーディオ技術を試しました。

前回の試みでは、openAL を使用し、サウンドを複数のバッファにロードして、必要なときにいつでも再生できる CocosDension サウンド エンジンを試しました。基本的なコードは次のとおりです。

初期化:

int channelGroups[1];
channelGroups[0] = 8;
soundEngine = [[CDSoundEngine alloc] init:channelGroups channelGroupTotal:1];

int i=0;
for(NSString *soundName in [NSArray arrayWithObjects:@"base1", @"snare1", @"hihat1", @"dit", @"snare", nil])
{
    [soundEngine loadBuffer:i fileName:soundName fileType:@"wav"];
    i++;
}

[NSTimer scheduledTimerWithTimeInterval:0.14 target:self selector:@selector(drumLoop:) userInfo:nil repeats:YES];

初期化では、サウンド エンジンを作成し、いくつかのサウンドを異なるバッファーにロードしてから、NSTimer でシーケンサー ループを確立します。

オーディオ ループ:

- (void)drumLoop:(NSTimer *)timer
{
for(int track=0; track<4; track++)
{
    unsigned char note=pattern[track][step];
    if(note)
        [soundEngine playSound:note-1 channelGroupId:0 pitch:1.0f pan:.5 gain:1.0 loop:NO];
}

if(++step>=16)
    step=0;

}

それだけで、正常に動作しますが、タイミングが不安定で不安定です。他の何かが発生するとすぐに (ビューでの ig 描画)、同期が取れなくなります。

私がサウンドエンジンとopenALを理解しているように、バッファは(initコードで)ロードされ、すぐに開始する準備ができていalSourcePlay(source);ます.NSTimerに問題があるのでしょうか?

現在、アプリストアには数十のサウンド シーケンサー アプリがあり、それらは正確なタイミングを持っています。Ig "idrum" は、ズームと描画を行うと 180 bpm でも完全に安定したビートを持っています。したがって、解決策があるはずです。

誰にもアイデアはありますか?

事前に助けてくれてありがとう!

よろしくお願いします、

ウォルキー


ご回答有難うございます。それは私をさらに一歩進めましたが、残念ながら目標には達しませんでした. これが私がしたことです:

nextBeat=[[NSDate alloc] initWithTimeIntervalSinceNow:0.1];
[NSThread detachNewThreadSelector:@selector(drumLoop:) toTarget:self withObject:nil];

初期化では、次のビートの時間を保存し、新しいスレッドを作成します。

- (void)drumLoop:(id)info
{
    [NSThread setThreadPriority:1.0];

    while(1)
    {
        for(int track=0; track<4; track++)
        {
            unsigned char note=pattern[track][step];
            if(note)
                [soundEngine playSound:note-1 channelGroupId:0 pitch:1.0f pan:.5 gain:1.0 loop:NO];
        }

        if(++step>=16)
            step=0;     

        NSDate *newNextBeat=[[NSDate alloc] initWithTimeInterval:0.1 sinceDate:nextBeat];
        [nextBeat release];
        nextBeat=newNextBeat;
        [NSThread sleepUntilDate:nextBeat];
    }
}

シーケンス ループでは、スレッドの優先度をできるだけ高く設定し、無限ループに入ります。サウンドを再生した後、次のビートの次の絶対時間を計算し、その時間までスレッドをスリープ状態にします。

繰り返しますが、これは機能し、NSThread なしで試した場合よりも安定して動作しますが、何か他のことが発生した場合、特に GUI の問題が発生した場合でも不安定です。

iphone で NSThread を使用してリアルタイムの応答を取得する方法はありますか?

よろしくお願いします、

ウォルキー

4

9 に答える 9

10

NSTimer は、いつ起動するかについてまったく保証されていません。実行ループで起動時間をスケジュールし、実行ループがタイマーに近づくと、タイマーのいずれかが期限を過ぎていないかどうかを確認します。その場合、セレクターを実行します。さまざまなタスクに最適です。これには役に立たない。

ここでのステ​​ップ 1 は、オーディオ処理を独自のスレッドに移動し、UI スレッドを終了する必要があることです。タイミングについては、通常の C アプローチを使用して独自のタイミング エンジンを構築できますが、CAAnimation、特に CAMediaTiming を調べることから始めます。

Cocoa には、メイン スレッドでのみ実行するように設計された多くの機能があることに注意してください。たとえば、バックグラウンド スレッドで UI 作業を行わないでください。一般に、ドキュメントを注意深く読んで、スレッドセーフについて何と言っているかを確認してください。しかし、一般的に、スレッド間の通信があまりない場合 (IMO ではほとんどの場合は存在しないはずです)、スレッドは Cocoa では非常に簡単です。NSスレッドを見てください。

于 2009-05-25T15:31:49.997 に答える
7

私はremoteIO出力を使用して同様のことをしています。私はNSTimerに依存していません。レンダー コールバックで提供されるタイムスタンプを使用して、すべてのタイミングを計算します。iPhoneのhzレートがどれほど正確かはわかりませんが、44100hzにかなり近いと確信しているので、現在のサンプル数に基づいて次のビートをいつロードする必要があるかを計算します.

リモート io を使用するサンプル プロジェクトはここにあります。レンダー コールバックの inTimeStamp 引数を見てください。

編集:このアプローチの動作例(およびアプリストアでは、ここで見つけることができます)

于 2009-07-26T05:54:07.273 に答える
5

AudioFileServices API を使用して、RemoteIO AudioUnit と、swing バッファー (読み取り用に 1 つ、書き込み用に 1 つのバッファー) を埋めるバックグラウンド スレッドを使用することにしました。その後、バッファは AudioUnit スレッドで処理および混合されます。AudioUnit スレッドは、次のスイング バッファのロードを開始する必要があるときに、bgnd スレッドに通知します。すべての処理は C で行われ、posix スレッド API を使用しました。すべての UI は ObjC にありました。

IMO の AudioUnit/AudioFileServices アプローチは、最大限の柔軟性と制御を提供します。

乾杯、

ベン

于 2009-11-06T17:00:25.913 に答える
4

ここでいくつかの良い答えがありましたが、私にとってはうまくいった解決策のコードを提供したいと思いました. これを調査し始めたとき、実際にゲームの実行ループがどのように機能するかを調べたところ、mach_absolute_time.

ここで何をするかについて少し読むことができますが、短いのは、ナノ秒の精度で時間を返すことです。ただし、返される数値は完全な時間ではなく、使用している CPU によって異なるため、mach_timebase_info_data_t最初に構造体を作成し、それを使用して時間を正規化する必要があります。

// Gives a numerator and denominator that you can apply to mach_absolute_time to
// get the actual nanoseconds
mach_timebase_info_data_t info;
mach_timebase_info(&info);

uint64_t currentTime = mach_absolute_time();

currentTime *= info.numer;
currentTime /= info.denom;

16 分音符ごとにティックさせたい場合は、次のようにします。

uint64_t interval = (1000 * 1000 * 1000) / 16;
uint64_t nextTime = currentTime + interval;

この時点で、currentTimeにはいくつかのナノ秒が含まれており、ナノ秒が経過するたびにティックする必要があります。intervalこれは に保存されnextTimeます。その後、次のような while ループを設定できます。

while (_running) {
    if (currentTime >= nextTime) {
        // Do some work, play the sound files or whatever you like
        nextTime += interval;
    }

    currentTime = mach_absolute_time();
    currentTime *= info.numer;
    currentTime /= info.denom;
}

内容はmach_timebase_info少しわかりにくいですが、いったんそこに入ると、非常にうまく機能します。私のアプリでは非常に優れたパフォーマンスを発揮します。また、これをメインスレッドで実行したくないことにも注意してください。そのため、独自のスレッドに振り分けることが賢明です。上記のすべてのコードを と呼ばれる独自のメソッドに入れ、次のrunようなもので開始できます。

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

ここに表示されているすべてのコードは、私がオープン ソース化したプロジェクトを単純化したものです。乾杯。

于 2012-09-15T05:15:10.430 に答える
2

タイミングにアプローチする最も正確な方法は、オーディオ サンプルをカウントし、特定の数のサンプルが経過したときに必要なことを行うことです。とにかく、出力サンプルレートはサウンドに関連するすべての基礎であるため、これがマスタークロックです。

各サンプルをチェックする必要はありません。数ミリ秒ごとにこれを行うだけで十分です。

于 2011-02-17T17:11:28.280 に答える
1

ループ内の「Dosomework」部分の経過時間を測定し、nextTimeからこの期間を差し引くと、精度が大幅に向上します。

while (loop == YES)
{
    timerInterval = adjustedTimerInterval ;

    startTime = CFAbsoluteTimeGetCurrent() ;

    if (delegate != nil)
    {
        [delegate timerFired] ; // do some work
    }

    endTime = CFAbsoluteTimeGetCurrent() ;

    diffTime = endTime - startTime ; // measure how long the call took. This result has to be subtracted from the interval!

    endTime = CFAbsoluteTimeGetCurrent() + timerInterval-diffTime ;

    while (CFAbsoluteTimeGetCurrent() < endTime)
    {
        // wait until the waiting interval has elapsed
    }
}
于 2013-02-05T16:50:15.540 に答える
1

オーディオ セッションをアクティブにする前に、オーディオ セッションの kAudioSessionProperty_PreferredHardwareIOBufferDuration を数ミリ秒 (0.005 秒など) に設定すると、リアルタイムの応答性が向上する可能性があります。これにより、RemoteIO は短いコールバック バッファをより頻繁に (リアルタイム スレッドで) 要求するようになります。これらのリアルタイム オーディオ コールバックにあまり時間をかけないでください。時間をかけないと、アプリのオーディオ スレッドとすべてのオーディオが強制終了されます。

より短い RemoteIO コールバック バッファをカウントするだけで、NSTimer を使用するよりも 10 倍正確で待ち時間が短くなります。また、オーディオ コールバック バッファ内のサンプルを数えてサウンド ミックスの開始位置を決めると、サブミリ秒の相対タイミングが得られます。

于 2012-09-16T20:46:50.357 に答える
0

時間管理のより良いアプローチは、bpm 設定 (たとえば 120) を用意し、代わりにそれをオフにすることだと思いました。音楽の作成/作成/音楽アプリケーションでは、分と秒の測定はほとんど役に立ちません。

シーケンスアプリを見ると、それらはすべて時間ではなくビートで進んでいます。反対に、波形エディターを見ると、分と秒が使用されています。

これをコード単位で実装する最善の方法については確信が持てませんが、このアプローチにより、今後の多くの頭痛の種が解消されると思います。

于 2009-07-26T05:59:55.840 に答える