31 votes

Est-il possible d'obtenir un tampon d'octets directement à partir d'un actif audio dans OpenSL ES (pour Android)?

Je voudrais obtenir un tampon d'octets à partir d'un support audio à l'aide de la OpenSL ES FileDescriptor objet, afin que je puisse mettre en file d'attente à plusieurs reprises pour un SimpleBufferQueue, au lieu d'utiliser SL interfaces pour lire/arrêter/rechercher le fichier.

Il y a trois principales raisons pour lesquelles je voudrais gérer les octets d'échantillon directement:

  1. OpenSL utilise un AudioTrack couche play/stop/etc pour le Joueur des Objets. Ce n'est pas seulement introduire des frais généraux, mais il a aussi plusieurs bugs, et rapide démarrages/arrêts du joueur causer beaucoup de problèmes.
  2. J'ai besoin de manipuler des octets de la mémoire tampon directement pour personnaliser les effets DSP.
  3. Les clips, je vais jouer sont de petite taille, et peuvent être chargés en mémoire pour éviter d'e/S de fichier de frais généraux. De Plus, enqueueing mes propres tampons va me permettre de réduire la latence en écriture de 0 pour la sortie de l'évier, et simplement en changeant d'octets d'échantillon quand ils jouent, plutôt que de s'ARRÊTER, faire une PAUSE et de JOUER de la AudioTrack.

Ok, donc les justifications complètes - voici ce que j'ai essayé - je avoir un Exemple de structure qui contient, pour l'essentiel, une entrée et une sortie de piste, et un tableau d'octets pour contenir les échantillons. L'entrée est mon FileDescriptor joueur, et la sortie est un SimpleBufferQueue objet. Voici ma structure:

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;

après l'initialisation d'un lecteur de fichiers fdPlayerObject, et le malloc-ing mémoire pour mon tampon d'octets avec

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

Je suis de son BufferQueue interface avec

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

Puis j'instancie une sortie de joueur:

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

Quand je veux jouer de l'échantillon, je suis en utilisant:

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

Toutefois, cela provoque mon application de gel et arrêt. Quelque chose ne va pas ici. Aussi, je préfère ne pas obtenir des échantillons par le Descripteur de Fichier du BufferQueue à chaque fois. Au lieu de cela, je voudrais stocker de façon permanente dans un tableau d'octets, et mettre en File d'attente que pour la sortie à chaque fois que j'aime.

4voto

PSyton Points 663

Décodage PCM est disponible à l'API de niveau 14 et plus.

Lorsque vous créez décodeur joueur, vous devez définir Android simple tampon de la file d'attente que les données de l'évier:

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

Pour le décodeur de la file d'attente vous avez besoin de mettre en file d'attente un ensemble de vider les tampons de l'Android simple tampon de la file d'attente, qui sera rempli avec des données PCM.

Aussi, vous devez enregistrer un gestionnaire de rappel avec le décodeur de la file d'attente qui sera appelée lorsque des données PCM sera prêt. Le gestionnaire de rappel doit traiter les données PCM, re-enqueue maintenant vide de la mémoire tampon, puis retour. L'application est responsable de garder la trace des décodé tampons; le rappel de la liste des paramètres ne contient pas suffisamment de renseignements à indiquer le tampon est rempli ou un tampon de mise en file d'à côté.

Décoder pour PCM prend en charge la pause initiale et demander. Contrôle du Volume, des effets, des boucles, et des taux de lecture ne sont pas pris en charge.

Lire Décoder l'audio PCM à partir de OpenSL ES pour Android pour plus de détails.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X