2012-05-27 6 views
32

Vorrei ottenere un buffer di byte da una risorsa audio utilizzando l'oggetto FileDescriptor OpenSL ES, quindi posso accodarlo ripetutamente a un SimpleBufferQueue, invece di utilizzare le interfacce SL per riprodurre/arrestare/cercare il file.E 'possibile ottenere un buffer di byte direttamente da una risorsa audio in OpenSL ES (per Android)?

Ci sono tre ragioni principali per cui vorrei gestire direttamente i byte di esempio:

  1. OpenSL utilizza uno strato AudioTrack per play/stop/etc per il giocatore gli oggetti. Ciò non solo introduce un sovraccarico indesiderato, ma ha anche diversi bug e gli avviamenti/arresti rapidi del lettore causano molti problemi.
  2. Ho bisogno di manipolare il buffer di byte direttamente per effetti DSP personalizzati.
  3. Le clip che sto per riprodurre sono piccole e possono essere caricate in memoria per evitare l'overhead di I/O dei file. Inoltre, l'accodamento dei miei buffer personali mi consentirà di ridurre la latenza scrivendo gli 0 nel sink di output e semplicemente passando ai sample byte durante la riproduzione, piuttosto che STOPPING, PAUSING e PLAYing AudioTrack.

Ok, quindi giustificazioni completi - ecco cosa ho provato - Ho una Esempio struct che contiene, in sostanza, una traccia di ingresso e uscita, e una matrice di byte per contenere i campioni. L'input è il mio lettore FileDescriptor e l'output è un oggetto SimpleBufferQueue. Ecco il mio struct:

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; 

dopo aver inizializzato un riproduttore di file fdPlayerObject e malloc-zione di memoria per il mio buffer di byte con

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

che sto ricevendo la sua interfaccia BufferQueue con

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

Quindi istanziato un lettore di uscita :

// 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); 

Quando voglio giocare il campione, io sto usando:

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)); 

Tuttavia, questo fa sì che la mia app per congelare e si spegnerà. Qualcosa non va, qui. Inoltre,, preferirei non ottenere ogni volta gli esempi dal BufferQueue del File Descriptor. Invece, mi piacerebbe memorizzarlo in modo permanente in un array di byte e accodarlo all'output ogni volta che mi piace.

+2

Ciao khiner, ti aiuterà a leggere i file .wav dalla cartella delle risorse in un byte Java o in un array breve e poi elaborarli ulteriormente? –

+0

Non sono sicuro di quanto posso aiutare, ma solo per il record - stai usando l'NDK? È per questo che il tuo codice è in C++? – Erhannis

+2

Sì, sto usando NDK, e questo in realtà è puro C. Questa domanda sta diventando a lungo nel dente. Ho fatto un sacco di progressi su questo e andrò in giro ad aggiungere una risposta decente. – khiner

risposta

5

La decodifica su PCM è disponibile al livello API 14 e versioni successive.

Quando si crea giocatore decoder si desidera impostare coda del buffer semplice Android come il sink di dati:

// 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); 

Per coda decoder è necessario accodare una serie di buffer vuoti al semplice coda di buffer di Android, che sarà riempito con dati PCM.

Inoltre è necessario registrare un gestore di richiamata con la coda del decodificatore che verrà richiamata quando i dati PCM saranno pronti. Il gestore di callback deve elaborare i dati PCM, re-accodare il buffer ora vuoto e quindi restituire.L'applicazione è responsabile per tenere traccia dei buffer decodificati; l'elenco dei parametri di callback non include informazioni sufficienti per indicare quale buffer è stato riempito o quale buffer accodare successivamente.

Decodifica su PCM supporta la pausa e la ricerca iniziale. Controllo del volume, effetti, loop e velocità di riproduzione non sono supportati.

Leggi Decodifica audio su PCM da OpenSL ES for Android per ulteriori dettagli.

Problemi correlati