2015-01-21 27 views
10

Qui sto cercando di scrivere del codice per un sistema audio in registrazione continua. Sto quindi tentando di registrare l'audio per un certo periodo di tempo quando viene interrotta una determinata soglia di ampiezza.Problemi di memoria con registrazione continua audio

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <string.h> 
#include <time.h> 
#include <portaudio.h> 
#include <sndfile.h> 

#define FRAMES_PER_BUFFER (1024) 
#define SAMPLE_SIZE (4) 

typedef struct 
{ 
    uint16_t formatType; 
    uint16_t numberOfChannels; 
    uint32_t sampleRate; 
    float* recordedSamples; 
} AudioData; 

AudioData initAudioData(uint32_t sampleRate, uint16_t channels, int type) 
{ 
    AudioData data; 
    data.formatType = type; 
    data.numberOfChannels = channels; 
    data.sampleRate = sampleRate; 
    return data; 
} 

float avg(float *data) 
{ 
    int elems = sizeof(data)/sizeof(data[0]); 
    float sum = 0; 
    for (int i = 0; i < elems; i++) 
    { 
     sum += fabs(*(data + i)); 
    } 
    return (float) sum/elems; 
} 

int main(void) 
{ 
    AudioData data = initAudioData(44100, 2, paFloat32); 
    PaStream *stream = NULL; 
    PaError err = paNoError; 
    int size = FRAMES_PER_BUFFER * data.numberOfChannels * SAMPLE_SIZE; 
    float *sampleBlock = malloc(size); 
    float *recordedSamples = NULL; 
    time_t talking = 0; 
    time_t silence = 0; 

    if((err = Pa_Initialize())) goto done; 
    PaStreamParameters inputParameters = 
    { 
     .device = Pa_GetDefaultInputDevice(), 
     .channelCount = data.numberOfChannels, 
     .sampleFormat = data.formatType, 
     .suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultHighInputLatency, 
     .hostApiSpecificStreamInfo = NULL 
    }; 
    if((err = Pa_OpenStream(&stream, &inputParameters, NULL, data.sampleRate, FRAMES_PER_BUFFER, paClipOff, NULL, NULL))) goto done; 
    if((err = Pa_StartStream(stream))) goto done; 
    for(int i = 0;;) 
    { 
     err = Pa_ReadStream(stream, sampleBlock, FRAMES_PER_BUFFER); 
     if(avg(sampleBlock) > 0.000550) // talking 
     { 
      printf("You're talking! %d\n", i); 
      i++; 
      time(&talking); 
      recordedSamples = realloc(recordedSamples, size * i); 
      if (recordedSamples) memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size); // problem here writing to memory at i = 16? 
      else free(recordedSamples); 
     } 
     else //silence 
     { 
      double test = difftime(time(&silence), talking); 
      printf("Time diff: %g\n", test); 
      if (test >= 1.5) 
      { 
       // TODO: finish code processing audio snippet 
       talking = 0; 
       free(recordedSamples); // problem freeing memory? 
      } 
     } 
    } 

done: 
    free(sampleBlock); 
    Pa_Terminate(); 
    return err; 
} 

Tuttavia, il codice è essere un po 'schizzinoso. A volte, quando eseguo il mio programma in Xcode, ottengo il seguente output:

Time diff: 1.4218e+09 
You're talking! 0 
You're talking! 1 
You're talking! 2 
You're talking! 3 
You're talking! 4 
You're talking! 5 
You're talking! 6 
You're talking! 7 
You're talking! 8 
You're talking! 9 
You're talking! 10 
You're talking! 11 
You're talking! 12 
You're talking! 13 
You're talking! 14 
You're talking! 15 
(lldb) 

Con Xcode che punta a questa linea è il problema:

if (recordedSamples) memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size); // problem here writing to memory at i = 16? 

Altre volte ho eseguito il codice, ottengo questo errore :

Time diff: 1.4218e+09 
You're talking! 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 0 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 1 
Time diff: 2 
Time diff: 1.4218e+09 
CTestEnvironment(55085,0x7fff7938e300) malloc: *** error for object 0x10081ea00: pointer being freed was not allocated 
*** set a breakpoint in malloc_error_break to debug 

Entrambi gli errori sono un po 'confuse me ... qualche suggerimento?

+0

non sei assegnazione 'recordedSamples' ma cercando di riallocare o libero che –

+0

@Lashane Si prega di leggere la documentazione' realloc' – syb0rg

+0

questa linea: 'int elems = sizeof (dati)/sizeof (dati [0]);' ha il problema che 'sizeof (data)' sarà la dimensione di un puntatore, piuttosto che la dimensione di un float. suggerire: int elems = sizeof (float)/sizeof (data [0]); – user3629249

risposta

6

state scrivendo fuori dai limiti del buffer allocato:

recordedSamples = realloc(recordedSamples, size * i); 
memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size); 

realloc() alloca un certo numero di byte, qui size * i. Il puntatore risultante viene memorizzato in recordedSamples, che ha tipo float*.

Il memcpy() tenta quindi di scrivere i dati su recordedSamples + ((i - 1) * size. L'aritmetica del puntatore viene utilizzata per determinare la posizione in cui deve essere scritto. Dal momento che recordedSamples è di tipo float*, recordedSample + X punti a un offset di valori float X (non X byte).

In altre parole, recordedSamples + ((i - 1) * size punta alla posizione di memoria ((i - 1) * size * sizeof(float) byte dopo recordedSamples. Questo di solito non è all'interno del buffer allocato, poiché i float sono più grandi di un singolo byte.

Per risolvere questo problema, la grande domanda è se size deve essere un numero di byte o un numero di float. Questo dipende dalle funzioni API che stai utilizzando, non l'ho esaminato in dettaglio.

Se si tratta di una serie di floats, poi si deve regolare le chiamate alle funzioni di base di gestione della memoria come malloc, realloc e memcpy, perché il tutto operano su byte. Al posto di malloc(size) chiamereste malloc(size * sizeof(float)).

Se size è davvero un numero di byte, allora sarebbe più logico fare recordedSamples un char*, o almeno lo gettò prima di fare l'aritmetica dei puntatori con scostamenti di byte, come memcpy((char*)recordedSamples + ...).

0
// Note: I do not have the portaudio.h and sndfile.h so could not compile/test 


#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
#include <string.h> 
#include <time.h> 

#include "portaudio.h" 
#include "sndfile.h" 

#define FRAMES_PER_BUFFER (1024) 
#define SAMPLE_SIZE  (4) 
#define NUMBER_OF_CHANNELS (2) 
#define SAMPLE_RATE  (44100) 
#define SIZE    (FRAMES_PER_BUFFER * NUMBER_OF_CHANNELS * SAMPLE_SIZE) 

static const int size = SIZE; 

struct AudioData_t 
{ 
    uint16_t formatType; 
    uint16_t numberOfChannels; 
    uint32_t sampleRate; 
    float* recordedSamples; 
}; 


void initAudioData(
    struct AudioData_t *pData, 
    uint32_t sampleRate, 
    uint16_t channels, 
    int type) 
{ 
    (*pData).formatType = type; 
    (*pData).numberOfChannels = channels; 
    (*pData).sampleRate = sampleRate; 
} // end function: initAudioData 


float averageAudioLevel(float *data) 
{ 
    int elems = size/sizeof(float); // <-- 
    float sum = 0; 

    for (int i = 0; i < elems; i++) 
    { 
     sum += fabs(*(data + i)); 
    } 
    return sum/elems; // sum is float so result is float 
} // end function: averageAudioLevel 


int main(void) 
{ 
    struct AudioData_t data; 
    initAudioData(&data, SAMPLE_RATE, NUMBER_OF_CHANNELS, paFloat32); 

    PaStream *stream = NULL; 
    PaError err = paNoError; 


    float *sampleBlock = NULL; 

    if(NULL == (sampleBlock = malloc(size))) 
    { // then, malloc failed 
     perror("malloc failed"); 
     exit(EXIT_FAILURE); 
    } 

    // implied else, malloc successful 

    float *recordedSamples = NULL; 

    time_t talking = 0; 
    time_t silence = 0; 

    if(0 == (err = Pa_Initialize())) 
    { // then init successful 

     PaStreamParameters inputParameters = 
     { 
      .device = Pa_GetDefaultInputDevice(), 
      .channelCount = data.numberOfChannels, 
      .sampleFormat = data.formatType, 
      .suggestedLatency = Pa_GetDeviceInfo(Pa_GetDefaultInputDevice())->defaultHighInputLatency, 
      .hostApiSpecificStreamInfo = NULL 
     }; 

     // if err >0, exit 
     if(0 == (err = Pa_OpenStream(&stream, &inputParameters, NULL, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff, NULL, NULL))) 
     { // then success 
      if(0 == (err = Pa_StartStream(stream))) 
      { // then success 

       int i = 0; 
       while(1) // this loop never exits 
       { 
        talking = 0; 
        if(0 == (err = Pa_ReadStream(stream, sampleBlock, FRAMES_PER_BUFFER))) 
        { 
         if(averageAudioLevel(sampleBlock) > 0.000550) // talking 
         { 
          printf("You're talking! %d\n", i); 

          i++; // counting usable audio samples 

          if(!talking) {talking = time(NULL);} // only do once per audio sample 

          // increase allocation for another audio sample (starts at 0 allocated) 
          float *temp; 
          if(NULL == (temp = realloc(recordedSamples, size * i)) 
          { // then realloc failed 
           perror(""realloc failed" "); 
           free(sampleBlock); 
           free(recordedSamples); 
           exit(EXIT_FAILURE); 
          } 

          // implied else, realloc successful 

          // update the actual allocated memory pointer 
          recordedSamples = temp; 

          // save the new sample into array of samples 
          memcpy(recordedSamples + ((i - 1) * size), sampleBlock, size);} 
         } // end if 
        } 

        else //silence 
        { 
         if(0 < i) 
         { // then some samples to evaluate 

          double elapsedTime = difftime(time(NULL), talking); 
          printf("Time diff: %g\n", elapsedTime); 

          if (elapsedTime >= 1.5) 
          { 
           // TODO: finish code processing audio snippet 

           // reset time indicators so do not process silence unless proceed by audio sound 
           talking = 0; 

           // reset audio sample counter 
           i = 0; 

           // dispose of recorded samples 
           free(recordedSamples); 

           // prep for next recording 
           recordedSamples = NULL; 
          } // end if 
         } // end if 
        } // end if 
       } // end forever loop 
      } // end if 
     } // end if 
    } // end if 


    free(sampleBlock); 
    free(recordedSamples); 
    Pa_Terminate(); 
    return err; 
} // end function: main 
+0

Ciò non causa i problemi specificati nella domanda. Inoltre, il compilatore utilizza l'ottimizzazione del valore 'return' ... – syb0rg

+0

La tua modifica contiene ancora i problemi che ho descritto nella mia domanda, così come alcuni errori di sintassi. – syb0rg

3

Questi tipi di insetti sono difficili da ricreare a causa di differenze di piattaforma, quindi è difficile per me sapere esattamente ciò che sta succedendo qui, ma io sottolineare alcuni problemi con il codice che potrebbero avere a che fare con esso.

Ho notato alcuni problemi con l'utilizzo di free().

notare che la connessione (PTR) non cambia il valore del PTR, così il vostro quest'ultimo errore può essere causato dalla seguente sequenza di chiamate:

free(recordSamples); 
free(recordSamples); 

Questo può accadere perché si potrebbe essere entrare il test > = 1,5 condizione due volte e quindi una doppia libera. La soluzione di questo problema dovrebbe essere il più semplice è l'aggiunta di:

recordSamples = NULL; 

ogni volta che si chiama libero. In questo modo, il puntatore è NULL quando si chiama free una seconda volta e non si ottiene un errore.

Un altro problema potenziale con questo stesso caso è che un puntatore che è stato liberato e quindi passato in realloc creerà un comportamento non definito. Potrebbe tranquillamente restituire un puntatore non valido senza generare errori, a seconda dell'implementazione. Se è così, ha senso che una memcpy fallirebbe, anche se, ammettiamolo, non sono sicuro che ciò avvenga effettivamente nel primo caso. È possibile che alcuni dei tuoi output non vengano scaricati prima che venga generato l'errore, quindi potremmo non avere una visione completa di ciò che viene chiamato prima degli errori. Un debugger sarebbe utile per questo.

Il mio consiglio è quello di assicurarsi che recordSamples sia sempre impostato su NULL dopo un libero (sembra che ce ne siano solo due nel codice) e vedere se questo risolve i problemi. Se ci sono ancora problemi, ti consiglio di utilizzare uno strumento come valgrind per ottenere maggiori dettagli sul motivo per cui si verificano questi problemi di memoria.

Valgrind funziona sostituendo il malloc del sistema e gratuitamente con il proprio che dispone di un monitoraggio completo dei metadati. Spesso può segnalare esattamente perché qualcosa del genere potrebbe fallire.

http://valgrind.org/

+1

Ahh, non posso credere di aver dimenticato di farlo. +1 per aver catturato quell'errore – syb0rg