2012-05-27 13 views
32

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

私はサンプルを管理したい理由を三つの主要な理由がありますが、直接バイト:

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

わかりましたので、正当性は - ここで私が試したものだ - 私は、基本的に、入力と出力のトラックが含まれていサンプル構造体、およびサンプルを保持するためにバイト配列を持っています。入力は私の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; 

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

でfdPlayerObjectファイルプレーヤーを初期化し、そして私のバイトのバッファのためのmalloc-INGのメモリの後、私は

// get the buffer queue interface 
result = (*(sample->fdPlayerObject))->GetInterface(sample->fdPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(sample->fdBufferQueue)); 
とのBufferQueueインターフェイスを取得しています

出力プレーヤー

をインスタンス化します。
// 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から毎回サンプルを取得したくないと思います。代わりに、私は永久にそれをバイト配列に格納したいと思います。

+2

こんにちはkhiner、Javaバイトまたは短い配列のアセットフォルダから.wavファイルを読み込み、これをさらに処理するのに役立ちますか? –

+0

私がどれくらい助けてくれるのかよく分かりませんが、記録のためだけにNDKを使っていますか?あなたのコードはC++になっているのですか? – Erhannis

+2

はい、私はNDKを使用していますが、これは実際には純粋なCです。この質問は長い間歯に入っています。私はこれに関して数多くの進歩を遂げており、まともな答えを加えようとしています。 – khiner

答えて

5

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

// 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の簡単なバッファキューに空のバッファのセットをエンキュー必要があります:あなたはデコーダプレーヤーを作成すると、データがシンクとして

あなたは、Androidの簡単なバッファキューを設定する必要はありPCMデータを使用します。

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

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

Read オーディオをPCMからデコードする詳細は、OpenSL ES for Androidを参照してください。

関連する問題