33

OpenSL ES FileDescriptorオブジェクトを使用してオーディオアセットからバイトバッファーを取得したいので、SLインターフェイスを使用してファイルを再生/停止/シークする代わりに、SimpleBufferQueueに繰り返しエンキューできます。

サンプルバイトを直接管理したい主な理由は3つあります。

  1. OpenSLは、AudioTrackレイヤーを使用して、プレーヤーオブジェクトの再生/停止などを行います。これは、不要なオーバーヘッドをもたらすだけでなく、いくつかのバグもあり、プレーヤーの迅速な開始/停止は多くの問題を引き起こします。
  2. カスタムDSPエフェクトを使用するには、バイトバッファを直接操作する必要があります。
  3. 再生するクリップは小さく、ファイルI/Oのオーバーヘッドを回避するためにすべてメモリにロードできます。さらに、自分のバッファーをキューに入れると、AudioTrackを停止、一時停止、再生するのではなく、出力シンクに0を書き込み、再生中にサンプルバイトに切り替えるだけでレイテンシーを減らすことができます。

さて、正当化が完了しました-これが私が試したことです-基本的に、入力トラックと出力トラック、およびサンプルを保持するためのバイト配列を含むSample構造体があります。入力は私のFileDescriptorプレーヤーであり、出力はSimpleBufferQueueオブジェクトです。これが私の構造です:

typedef struct Sample_ {
    // buffer to hold all samples
    short *buffer;      
    int totalSamples;

    SLObjectItf fdPlayerObject;
    // file descriptor player interfaces
    SLPlayItf fdPlayerPlay;
    SLSeekItf fdPlayerSeek;
    SLMuteSoloItf fdPlayerMuteSolo;
    SLVolumeItf fdPlayerVolume;
    SLAndroidSimpleBufferQueueItf fdBufferQueue;

    SLObjectItf outputPlayerObject; 
    SLPlayItf outputPlayerPlay; 
    // output buffer interfaces
    SLAndroidSimpleBufferQueueItf outputBufferQueue;        
} Sample;

ファイルプレーヤーfdPlayerObjectを初期化し、バイトバッファのメモリをmallocした後

sample->buffer = malloc(sizeof(short)*sample->totalSamples);

私はそのBufferQueueインターフェースを取得しています

// get the buffer queue interface
result = (*(sample->fdPlayerObject))->GetInterface(sample->fdPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(sample->fdBufferQueue));

次に、出力プレーヤーをインスタンス化します。

// create audio player for output buffer queue
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->outputPlayerObject), &outputAudioSrc, &audioSnk,
                                               1, ids1, req1);

// realize the output player
result = (*(sample->outputPlayerObject))->Realize(sample->outputPlayerObject, SL_BOOLEAN_FALSE);
assert(result == SL_RESULT_SUCCESS);

// get the play interface
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_PLAY, &(sample->outputPlayerPlay));
assert(result == SL_RESULT_SUCCESS);

// get the buffer queue interface for output
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                   &(sample->outputBufferQueue));
assert(result == SL_RESULT_SUCCESS);    

  // set the player's state to playing
result = (*(sample->outputPlayerPlay))->SetPlayState(sample->outputPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(result == SL_RESULT_SUCCESS);

サンプルを再生したいときは、以下を使用しています。

Sample *sample = &samples[sampleNum];
// THIS WORKS FOR SIMPLY PLAYING THE SAMPLE, BUT I WANT THE BUFFER DIRECTLY 
//    if (sample->fdPlayerPlay != NULL) {
//        // set the player's state to playing
//        (*(sample->fdPlayerPlay))->SetPlayState(sample->fdPlayerPlay, SL_PLAYSTATE_PLAYING);
//    }

// fill buffer with the samples from the file descriptor
(*(sample->fdBufferQueue))->Enqueue(sample->fdBufferQueue, sample->buffer,sample->totalSamples*sizeof(short));
// write the buffer to the outputBufferQueue, which is already playing
(*(sample->outputBufferQueue))->Enqueue(sample->outputBufferQueue, sample->buffer, sample->totalSamples*sizeof(short));

ただし、これによりアプリがフリーズしてシャットダウンします。ここは何かがおかしい。 また、ファイル記述子のBufferQueueから毎回サンプルを取得したくないと思います。代わりに、それをバイト配列に永続的に格納し、いつでも出力にエンキューしたいと思います。

4

1 に答える 1

6

PCMへのデコードは、APIレベル14以降で利用できます。

デコーダープレーヤーを作成するときは、Androidのシンプルなバッファーキューをデータシンクとして設定する必要があります。

// For init use something like this:
SLDataLocator_AndroidFD locatorIn = {SL_DATALOCATOR_ANDROIDFD, decriptor, start, length};
SLDataFormat_MIME dataFormat = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
SLDataSource audioSrc = {&locatorIn, &dataFormat};

SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataSink audioSnk = { &loc_bq, NULL };

const SLInterfaceID ids[2] = {SL_IID_PLAY, SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->fdPlayerObject), &outputAudioSrc, &audioSnk, 2, ids1, req1);

デコーダーキューの場合、空のバッファーのセットをAndroidシンプルバッファーキューにエンキューする必要があります。このキューにはPCMデータが入力されます。

また、PCMデータの準備ができたときに呼び出されるデコーダーキューにコールバックハンドラーを登録する必要があります。コールバックハンドラーは、PCMデータを処理し、空になったバッファーを再エンキューしてから戻る必要があります。アプリケーションは、デコードされたバッファを追跡する責任があります。コールバックパラメータリストには、どのバッファがいっぱいになったのか、または次にどのバッファをエンキューするのかを示すのに十分な情報が含まれていません。

PCMへのデコードは、一時停止と初期シークをサポートします。ボリュームコントロール、エフェクト、ループ、および再生レートはサポートされていません。

詳細については、 OpenSL ESforAndroidからPCMへのオーディオのデコードをお読みください。

于 2013-09-18T04:22:31.440 に答える