2

J'ai travaillé sur la lecture d'un asset audio en utilisant AVAssetReader pour que je puisse plus tard lire l'audio avec un AUGraph avec un rappel AudioUnit. J'ai le rappel AUGraph et AudioUnit fonctionnant mais il lit des dossiers du disque et si le dossier est trop grand cela prendrait trop de mémoire et écraserait l'application. Donc, je lis plutôt l'actif directement et seulement une taille limitée. Je vais ensuite le gérer comme un double tampon et obtenir le AUGraph ce dont il a besoin quand il en a besoin.Comment vous ajoutez à un AudioBufferList avec un AVAssetReader?

(Note: J'aimerais savoir si je peux utiliser les services de file d'attente audio et utilisez toujours un AUGraph avec rappel AudioUnit si la mémoire me est gérée par les cadres iOS.)

Mon problème est que je n'ai pas une bonne compréhension des tableaux, des structures et des pointeurs en C. La partie où j'ai besoin d'aide prend l'AudioBufferList individuel qui tient sur un seul AudioBuffer et ajoute ces données à une autre AudioBufferList qui conserve toutes les données à utiliser plus tard. Je crois que j'ai besoin d'utiliser memcpy mais il n'est pas clair comment l'utiliser ou même initialiser un AudioBufferList à mes fins. J'utilise MixerHost pour référence qui est le projet exemple d'Apple qui lit dans le fichier à partir du disque.

J'ai téléchargé mon travail en cours si vous souhaitez le charger dans Xcode. J'ai trouvé la plupart de ce dont j'ai besoin pour faire cela et une fois que j'ai les données recueillies en un seul endroit, je devrais être bon à faire.

Exemple de projet: MyAssetReader.zip

Dans l'en-tête, vous pouvez voir que je déclare la bufferList comme un pointeur vers la struct.

@interface MyAssetReader : NSObject { 
    BOOL reading; 
    signed long sampleTotal; 
    Float64 totalDuration; 

    AudioBufferList *bufferList; // How should this be handled? 
} 

J'allouent bufferList cette façon, en grande partie empruntant MixerHost ...

UInt32 channelCount = [asset.tracks count]; 

if (channelCount > 1) { 
    NSLog(@"We have more than 1 channel!"); 
} 

bufferList = (AudioBufferList *) malloc (
             sizeof (AudioBufferList) + sizeof (AudioBuffer) * (channelCount - 1) 
             ); 

if (NULL == bufferList) {NSLog (@"*** malloc failure for allocating bufferList memory"); return;} 

// initialize the mNumberBuffers member 
bufferList->mNumberBuffers = channelCount; 

// initialize the mBuffers member to 0 
AudioBuffer emptyBuffer = {0}; 
size_t arrayIndex; 
for (arrayIndex = 0; arrayIndex < channelCount; arrayIndex++) { 
    // set up the AudioBuffer structs in the buffer list 
    bufferList->mBuffers[arrayIndex] = emptyBuffer; 
    bufferList->mBuffers[arrayIndex].mNumberChannels = 1; 
    // How should mData be initialized??? 
    bufferList->mBuffers[arrayIndex].mData   = malloc(sizeof(AudioUnitSampleType)); 
} 

Enfin, je boucle à travers les lectures.

int frameCount = 0; 

CMSampleBufferRef nextBuffer; 
while (assetReader.status == AVAssetReaderStatusReading) { 
    nextBuffer = [assetReaderOutput copyNextSampleBuffer]; 

    AudioBufferList localBufferList; 
    CMBlockBufferRef blockBuffer;  
    CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &localBufferList, sizeof(localBufferList), NULL, NULL, 
                  kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); 

    // increase the number of total bites 
    bufferList->mBuffers[0].mDataByteSize += localBufferList.mBuffers[0].mDataByteSize; 

    // carefully copy the data into the buffer list 
    memcpy(bufferList->mBuffers[0].mData + frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType)); 

    // get information about duration and position 
    //CMSampleBufferGet 
    CMItemCount sampleCount = CMSampleBufferGetNumSamples(nextBuffer); 
    Float64 duration = CMTimeGetSeconds(CMSampleBufferGetDuration(nextBuffer)); 
    Float64 presTime = CMTimeGetSeconds(CMSampleBufferGetPresentationTimeStamp(nextBuffer)); 

    if (isnan(duration)) duration = 0.0; 
    if (isnan(presTime)) presTime = 0.0; 

    //NSLog(@"sampleCount: %ld", sampleCount); 
    //NSLog(@"duration: %f", duration); 
    //NSLog(@"presTime: %f", presTime); 

    self.sampleTotal += sampleCount; 
    self.totalDuration += duration; 
    frameCount++; 

    free(nextBuffer); 
} 

Je ne suis pas sûr de la ce que je manipule mDataByteSize et MDATA, en particulier avec memcpy. Puisque mData est un pointeur vide, c'est une zone compliquée.

memcpy(bufferList->mBuffers[0].mData + frameCount, localBufferList.mBuffers[0].mData, sizeof(AudioUnitSampleType)); 

Dans cette ligne, je pense qu'il devrait être copier la valeur à partir des données localBufferList à la position dans le bufferList plus le nombre de cadres pour positionner le pointeur où il doit écrire les données. J'ai quelques idées sur ce que je dois changer pour que cela fonctionne.

  • Comme un pointeur vide est juste 1 et non la taille du pointeur pour un AudioUnitSampleType je peux avoir besoin de multiplier aussi par sizeof (AudioUnitSampleType) pour obtenir le memcpy dans la bonne position
  • Je ne peux pas être en utilisant malloc correctement pour préparer MDATA mais comme je ne suis pas sûr du nombre de cadres, il y aura je ne suis pas sûr de ce qu'il faut faire pour l'initialiser

Actuellement quand je lance cette application, il termine cette fonction avec un pointeur non valide pour bufferList .

J'apprécie votre aide pour me permettre de mieux comprendre comment gérer une AudioBufferList.

+0

J'ai appris plus sur les pointeurs void et comment utiliser malloc et memcpy qui semblent être un élément clé de la compréhension de la structure AudioBufferList. J'aurai besoin de savoir comment conserver les valeurs dans un tableau dynamique en C. Je lis des fonctions comme celles-ci qui ont été ajoutées avec C99. Je continue à lire à ce sujet. – Brennan

+0

Je pense que je vais accumuler les valeurs de AudioBuffer à un tableau qui est développé avec ralloc mais je peux le faire avec des blocs de 100 afin que realloc ne soit pas appelé à chaque itération. Ensuite, je peux créer un tableau de taille dynamique après la boucle, utiliser memcpy pour remplir ce tableau avec les valeurs accumulées, puis définir mData sur le tampon avec ce tableau. – Brennan

Répondre

3

J'ai trouvé ma propre réponse.J'ai décidé d'utiliser un objet NSMutableData qui me permet d'ajouter des octets à partir de CMSampleBufferRef après avoir appelé CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer pour obtenir une AudioBufferList.

 [data appendBytes:localBufferList.mBuffers[0].mData length:localBufferList.mBuffers[0].mDataByteSize]; 

Une fois la boucle de lecture terminée, j'ai toutes les données dans mon objet NSMutableData. Je crée et remplis l'AudioBufferList de cette façon.

audioBufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList)); 
if (NULL == audioBufferList) { 
    NSLog (@"*** malloc failure for allocating audioBufferList memory"); 
    [data release]; 
    return; 
} 
audioBufferList->mNumberBuffers = 1; 
audioBufferList->mBuffers[0].mNumberChannels = channelCount; 
audioBufferList->mBuffers[0].mDataByteSize = [data length]; 
audioBufferList->mBuffers[0].mData = (AudioUnitSampleType *)malloc([data length]); 
if (NULL == audioBufferList->mBuffers[0].mData) { 
    NSLog (@"*** malloc failure for allocating mData memory"); 
    [data release]; 
    return; 
} 
memcpy(audioBufferList->mBuffers[0].mData, [data mutableBytes], [data length]); 
[data release]; 

J'apprécierais un peu de révision de code sur comment j'utilise malloc pour créer la structure et la peupler. J'obtiens une erreur EXC_BAD_ACCESS sporadiquement mais je ne peux pas identifier où l'erreur est juste pour le moment. Puisque j'utilise malloc sur la structure je ne devrais pas avoir à le conserver n'importe où. J'appelle "libre" pour libérer des éléments enfants dans la structure et enfin la structure elle-même partout où j'utilise malloc.

+0

pouvez-vous s'il vous plaît regarder ceci: - https://stackoverflow.com/q/49003226/8527224 –

Questions connexes