2014-04-15 4 views
4

PortAudio library을 사용하여 오디오를 캡처 한 코드를 가지고 WAV codec에 패키지하려고합니다. 그런 다음 해당 파일을 FLAC file codec으로 변환하려고 시도하지만 올바른 .wav 파일이 아닙니다.잘못된 WAV 파일 생성

wav.h :

#include <stdint.h> 
#include <string.h> 

/** 
* @struct WaveHeader 
* @brief A basic WAVE header 
*/ 

typedef struct 
{ 
    char RIFF_marker[4]; 
    uint32_t file_size; 
    char filetype_header[4]; 
    char format_marker[4]; 
    uint32_t data_header_length; 
    uint16_t format_type; 
    uint16_t number_of_channels; 
    uint32_t sample_rate; 
    uint32_t bytes_per_second; 
    uint16_t bytes_per_frame; 
    uint16_t bits_per_sample; 
} WaveHeader; 

WaveHeader *genericWAVHeader(uint32_t sample_rate, uint16_t bit_depth, uint16_t channels); 
WaveHeader *retrieveWAVHeader(const void *ptr); 
int writeWAVHeader(FILE* fd, WaveHeader *hdr); 
int recordWAV(const char *fileName, WaveHeader *hdr, uint32_t duration); 

capture.c :

#include <stdio.h> 
#include <stdlib.h> 
#include <portaudio.h> 
#include <unistd.h> 
#include <math.h> 
#include "wav.h" 

typedef struct 
{ 
    int frameIndex; /* Index into sample array. */ 
    int maxFrameIndex; 
    char* recordedSamples; 
} PAData; 

WaveHeader *genericWAVHeader(uint32_t sample_rate, uint16_t bit_depth, uint16_t channels) 
{ 
    WaveHeader *hdr = malloc(sizeof(*hdr)); 
    if (!hdr) return NULL; 

    memcpy(&hdr->RIFF_marker, "RIFF", 4); 
    memcpy(&hdr->filetype_header, "WAVE", 4); 
    memcpy(&hdr->format_marker, "fmt ", 4); 
    hdr->data_header_length = 16; 
    hdr->format_type = 1; 
    hdr->number_of_channels = channels; 
    hdr->sample_rate = sample_rate; 
    hdr->bytes_per_second = sample_rate * channels * bit_depth/8; 
    hdr->bytes_per_frame = channels * bit_depth/8; 
    hdr->bits_per_sample = bit_depth; 

    return hdr; 
} 
int writeWAVHeader(FILE* fd, WaveHeader *hdr) 
{ 
    if (!hdr) return -1; 

    fwrite(&hdr->RIFF_marker, sizeof(*hdr->RIFF_marker), sizeof(&hdr->RIFF_marker), fd); 
    fwrite(&hdr->file_size, sizeof(hdr->file_size), sizeof(&hdr->file_size), fd); 
    fwrite(&hdr->filetype_header, sizeof(*hdr->filetype_header), sizeof(&hdr->filetype_header), fd); 
    fwrite(&hdr->format_marker, sizeof(*hdr->format_marker), sizeof(&hdr->format_marker), fd); 
    fwrite(&hdr->data_header_length, sizeof(hdr->data_header_length), sizeof(&hdr->data_header_length), fd); 
    fwrite(&hdr->format_type, sizeof(hdr->format_type), sizeof(&hdr->format_type), fd); 
    fwrite(&hdr->number_of_channels, sizeof(hdr->number_of_channels), sizeof(&hdr->number_of_channels), fd); 
    fwrite(&hdr->sample_rate, sizeof(hdr->sample_rate), sizeof(&hdr->sample_rate), fd); 
    fwrite(&hdr->bytes_per_second, sizeof(hdr->bytes_per_second), sizeof(&hdr->bytes_per_second), fd); 
    fwrite(&hdr->bytes_per_frame, sizeof(hdr->bytes_per_frame), sizeof(&hdr->bytes_per_frame), fd); 
    fwrite(&hdr->bits_per_sample, sizeof(hdr->bits_per_sample), sizeof(&hdr->bits_per_sample), fd); 
    fwrite("data", 1, sizeof("data") - 1, fd); 

    uint32_t data_size = hdr->file_size - 36; 
    fwrite(&data_size, sizeof(data_size), sizeof(&data_size), fd); 

    return 0; 
} 

static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) 
{ 
    PAData *data = (PAData*) userData; 
    const char *rptr = (const char*) inputBuffer; 
    char *wptr = &data->recordedSamples[data->frameIndex * 2 /*channels*/]; 
    long framesToCalc; 
    int finished; 
    unsigned long framesLeft = data->maxFrameIndex - data->frameIndex; 

    (void) outputBuffer; /* Prevent unused variable warnings. */ 
    (void) timeInfo; 
    (void) statusFlags; 
    (void) userData; 

    if(framesLeft < framesPerBuffer) 
    { 
     framesToCalc = framesLeft; 
     finished = paComplete; 
    } 
    else 
    { 
     framesToCalc = framesPerBuffer; 
     finished = paContinue; 
    } 

    if(!inputBuffer) 
    { 
     for(long i = 0; i < framesToCalc; i++) 
     { 
      *wptr++ = 0; /* left */ 
      *wptr++ = 0; /* right */ 
     } 
    } 
    else 
    { 
     for(long i = 0; i < framesToCalc; i++) 
     { 
      *wptr++ = *rptr++; /* left */ 
      *wptr++ = *rptr++; /* right */ 
     } 
    } 
    data->frameIndex += framesToCalc; 
    return finished; 
} 

int recordWAV(const char *fileName, WaveHeader *hdr, uint32_t duration) 
{ 
    PaStreamParameters inputParameters; 
    PaStream* stream; 
    PaError err = paNoError; 
    PAData data; 
    int totalFrames; 
    int numSamples; 
    int numBytes; 
    char max, val; 
    double average; 
    printf("%d", hdr->bytes_per_second); 
    data.maxFrameIndex = totalFrames = duration * hdr->sample_rate; /* Record for a few seconds. */ 
    data.frameIndex = 0; 
    numSamples = totalFrames * hdr->number_of_channels; 
    numBytes = numSamples; 
    data.recordedSamples = malloc(numBytes); 
    if(!data.recordedSamples) 
    { 
     printf("Could not allocate record array.\n"); 
     goto done; 
    } 
    for(int i = 0; i < numSamples; i++) data.recordedSamples[i] = 0; 

    if((err = Pa_Initialize())) goto done; 

    inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */ 
    if (inputParameters.device == paNoDevice) { 
     fprintf(stderr,"Error: No default input device.\n"); 
     goto done; 
    } 
    inputParameters.channelCount = 2;     /* stereo input */ 
    inputParameters.sampleFormat = 1; 
    inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency; 
    inputParameters.hostApiSpecificStreamInfo = NULL; 

    /* Record some audio. -------------------------------------------- */ 
    err = Pa_OpenStream(&stream, &inputParameters, NULL, hdr->sample_rate, paFramesPerBufferUnspecified, paClipOff, recordCallback, &data); 
    if(err) goto done; 
    if((err = Pa_StartStream(stream))) goto done; 
    puts("Now recording!! Please speak into the microphone."); 

    while((err = Pa_IsStreamActive(stream)) == 1) 
    { 
     Pa_Sleep(1000); 
     printf("index = %d\n", data.frameIndex); 
    } 
    if(err < 0) goto done; 

    err = Pa_CloseStream(stream); 
    if(err) goto done; 

    /* Measure maximum peak amplitude. */ 
    max = 0; 
    average = 0.0; 
    for(int i = 0; i < numSamples; i++) 
    { 
     val = data.recordedSamples[i]; 
     val = abs(val); 
     if(val > max) 
     { 
      max = val; 
     } 
     average += val; 
    } 

    average /= (double)numSamples; 

    { 
     FILE* fid = fopen(fileName, "wb"); 
     if(!fid) printf("Could not open file."); 
     else 
     { 
      writeWAVHeader(fid, hdr); 
      fwrite(data.recordedSamples, hdr->number_of_channels, totalFrames, fid); 
      fclose(fid); 
     } 
    } 

done: 
    Pa_Terminate(); 
    if(data.recordedSamples)  /* Sure it is NULL or valid. */ 
     free(data.recordedSamples); 
    if(err) 
    { 
     fprintf(stderr, "An error occured while using the portaudio stream\n"); 
     fprintf(stderr, "Error number: %d\n", err); 
     fprintf(stderr, "Error message: %s\n", Pa_GetErrorText(err)); 
     err = 1;   /* Always return 0 or 1, but no other return codes. */ 
    } 
    return err; 
} 

I 파일에서 실행 일부 테스트 :

$ xxd -g1 test.wav | head 
0000000: 52 49 46 46 00 00 00 30 00 00 00 30 57 41 56 45 RIFF...0...0WAVE 
0000010: 66 6d 74 20 10 00 00 00 01 00 02 00 e0 ab 00 00 fmt ............ 
0000020: 80 af 02 00 04 00 10 00 57 41 56 45 66 6d 74 20 ........WAVEfmt 
0000030: 66 6d 74 20 10 00 00 00 10 00 00 00 01 00 02 00 fmt ............ 
0000040: e0 ab 00 00 80 af 02 00 04 00 10 00 00 00 00 80 ................ 
0000050: 37 3d 04 10 00 00 03 00 01 00 02 00 e0 ab 00 00 7=.............. 
0000060: 80 af 02 00 04 00 10 00 02 00 e0 ab 00 00 80 af ................ 
0000070: 02 00 04 00 10 00 00 00 e0 ab 00 00 80 af 02 00 ................ 
0000080: 04 00 10 00 00 00 00 80 37 3d 04 10 00 00 03 00 ........7=...... 
0000090: 00 00 00 00 00 00 00 00 80 af 02 00 04 00 10 00 ................ 
$ file test.wav 
test.wav: RIFF (little-endian) data 
$ stat -f %s test.wav 
stat: %s: bad format 

꽤 확실하지 않다 내가 잘못 가고있는 곳. 데이터를 올바르게 쓰지 않습니까?

+0

WAV 파일 형식이 온라인 상태입니다. 바이트 단위로 크롤링하고 무엇이 잘못되었는지 알아냅니다. –

+0

'fwrite'를주의하여 실행하십시오. 거기에 오류가 있습니다. 특히 세 번째 매개 변수는 주소가되어서는 안됩니다. 실제로는 '1'이어야합니다. – ooga

+0

두 번째 4 바이트의 길이는 리틀 엔디안이어야합니다. "WAVE"는 바이트 8에서 시작하기로되어 있습니다. 그 이상의 것을 시도하지 않았습니다. –

답변

2

나는 그것의 나머지에 대해 잘 모르지만, 당신의 fwrite들과 같이해야한다 :

fwrite(hdr->RIFF_marker,  sizeof(hdr->RIFF_marker),  1, fd); 
fwrite(&hdr->file_size,   sizeof(hdr->file_size),   1, fd); 
fwrite(hdr->filetype_header, sizeof(hdr->filetype_header), 1, fd); 
fwrite(hdr->format_marker,  sizeof(hdr->format_marker),  1, fd); 
fwrite(&hdr->data_header_length, sizeof(hdr->data_header_length), 1, fd); 
fwrite(&hdr->format_type,  sizeof(hdr->format_type),  1, fd); 
fwrite(&hdr->number_of_channels, sizeof(hdr->number_of_channels), 1, fd); 
fwrite(&hdr->sample_rate,  sizeof(hdr->sample_rate),  1, fd); 
fwrite(&hdr->bytes_per_second, sizeof(hdr->bytes_per_second), 1, fd); 
fwrite(&hdr->bytes_per_frame, sizeof(hdr->bytes_per_frame), 1, fd); 
fwrite(&hdr->bits_per_sample, sizeof(hdr->bits_per_sample), 1, fd); 
fwrite("data", 4, 1, fd); 
+0

이것은'.wav' 파일을 생성하지만 그 안에있는 모든 파일은 정적입니다. – syb0rg

+1

@ syb0rg 그 코드는 데이터를 올바르게 기록하지만 wav 파일의 구조는 보지 않았습니다. 그걸 확인해야 할거야. – ooga

+0

이것이 유효한'.wav' 파일을 생성했기 때문에 내 질문에 답했습니다. – syb0rg