2015-01-21 16 views
10

Hier Aufnahme Ich versuche, einige Codes für ein kontinuierlich Aufzeichnung von Audio-System zu schreiben. Ich versuche dann, den Ton für eine bestimmte Zeit aufzuzeichnen, wenn eine bestimmte Amplitudenschwelle gebrochen ist.Speicherprobleme mit kontinuierlich 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; 
} 

Allerdings ist der Code etwas knifflig. Manchmal, wenn ich mein Programm in Xcode laufen, ich folgende Ausgabe:

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) 

Mit Xcode auf dieser Linie zeigt das Problem zu sein:

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

Andere Male habe ich den Code ausführen, bekomme ich diesen Fehler :

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 

Beide Fehler verwirren mich etwas ... irgendwelche Vorschläge?

+0

Sie ordnen 'recordedSamples' nicht zu, versuchen aber, sie neu zu verteilen oder zu löschen –

+0

@Lashane Bitte lesen Sie die' realloc' Dokumentation – syb0rg

+0

diese Zeile: 'int elems = sizeof (data)/sizeof (data [0]);' hat das Problem, dass 'sizeof (data)' die Größe eines Zeigers hat und nicht die Größe eines Floats. vorschlagen: int elems = sizeof (float)/sizeof (Daten [0]); – user3629249

Antwort

6

Sie schreiben außerhalb der Grenzen des zugewiesenen Puffer:

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

realloc() ordnet eine bestimmte Anzahl von Bytes, hier size * i. Der resultierende Zeiger wird in recordedSamples mit dem Typ float* gespeichert.

Die memcpy() versucht dann Daten in recordedSamples + ((i - 1) * size zu schreiben. Zeigerarithmetik wird verwendet, um den Ort zu bestimmen, in den geschrieben werden soll. Da recordedSamples vom Typ float* ist, zeigt recordedSample + X auf einen Offset von X-Float-Werten (nicht X Bytes).

Mit anderen Worten, recordedSamples + ((i - 1) * size zeigt auf den Speicherort ((i - 1) * size * sizeof(float) Bytes nach recordedSamples. Dies ist normalerweise nicht innerhalb des zugewiesenen Puffers, da Floats größer als ein einzelnes Byte sind.

Um dies zu beheben, ist die große Frage, ob size eine Anzahl von Bytes oder eine Anzahl von Floats sein soll. Das hängt von den API-Funktionen ab, die Sie verwenden, ich habe es nicht im Detail untersucht.

Wenn es eine Reihe von floats ist, dann müssen Sie die Anrufe zu den grundlegenden Speicherverwaltungsfunktionen einstellen wie malloc, realloc und memcpy, weil die alle auf Bytes arbeiten. Um statt malloc(size) würden Sie malloc(size * sizeof(float)) anrufen.

Wenn size ist in der Tat eine Anzahl von Bytes, dann wäre es logischer, recordedSamples ein char* zu machen oder zumindest wirft sie vor Zeigerarithmetik mit Byteversätze tun, wie 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

Das verursacht die in der Frage angegebenen Probleme nicht. Außerdem verwendet der Compiler die 'return'-Wert-Optimierung ... – syb0rg

+0

Ihre Bearbeitung enthält immer noch die Probleme, die ich in meiner Frage beschrieben habe, sowie einige Syntaxfehler. – syb0rg

3

Diese Arten von Fehlern sind schwierig aufgrund Plattform Unterschiede zu erstellen, es ist so schwer für mich, genau zu wissen, was hier passiert, aber ich werde einige Probleme mit Ihrem Code darauf hin, dass möglicherweise etwas zu tun haben könnte damit.

Ich habe einige Probleme mit der Nutzung von freien bemerkt().

Beachten Sie, dass frei (ptr), nicht den Wert von ptr ändern, damit Du kann letztere Fehler durch die folgende Sequenz von Anrufen verursacht werden:

free(recordSamples); 
free(recordSamples); 

dies geschehen kann, weil Sie den Test eingeben werden könnten > = 1,5 Bedingung zweimal, und daher eine doppelte frei. Die Lösung dieses Problems sollte so einfach sein, ist die Zugabe:

recordSamples = NULL; 

, wann immer Sie kostenlos anrufen. Auf diese Weise ist der Zeiger NULL, wenn Sie ein zweites Mal kostenlos anrufen, und Sie erhalten keinen Fehler.

Ein weiteres mögliches Problem mit diesem gleichen Fall ist, dass ein Zeiger, und dann in realloc befreit worden ist, übergab undefiniertes Verhalten schaffen. Es könnte glücklich einen ungültigen Zeiger zurückgeben, ohne irgendwelche Fehler zu werfen, abhängig von der Implementierung. Wenn das der Fall ist, macht es Sinn, dass ein Memcpy würde scheitern, obwohl zugegebenermaßen bin ich nicht sicher, ob dies tatsächlich in Ihrem ersten Fall geschieht. Es ist möglich, dass einige Ihrer Ausgabe nicht gespült werden, bevor der Fehler ausgelöst wird, so dass wir nicht ein vollständiges Bild von bekommen, was vor den Fehlern aufgerufen wird. Ein Debugger wäre dafür nützlich.

Meine Empfehlung ist, sicherzustellen, dass recordSamples immer nach einem freien NULL gesetzt wird (sieht aus wie es nur zwei in Ihrem Code sind) und sehen, ob das die Probleme behebt. Wenn es noch Probleme gibt, empfehle ich ein Tool wie Valgrind zu verwenden, um mehr Details zu erhalten, warum diese Speicherprobleme auftreten.

Valgrind arbeitet nach dem System des malloc und frei mit seinem eigenen zu ersetzen, dem umfangreichen Metadaten-Tracking hat. Es kann oft genau berichten, warum so etwas scheitern könnte.

http://valgrind.org/

+1

Ahh, ich kann nicht glauben, dass ich das vergessen habe. +1, um diesen Fehler zu fangen – syb0rg