2010-03-16 6 views
6

나는 wav 파일의 진폭을 처리하고 몇 가지 십진수로 스케일링을하고 있습니다. 나는 언어의 뉘앙스를 다루려고 노력하면서 메모리 효율적인 방법으로 파일을 읽고 다시 쓰는 방법에 대해 머리를 쓰려고 노력하고있다. (나는 C 언어에 익숙하지 않다.) 파일은 8 비트 또는 16 비트 형식 일 수 있습니다. 이 방법을 생각한 방법은 먼저 header data을 사전 정의 된 구조체로 읽고 버퍼에서 데이터 덩어리를 읽는 루프에서 실제 데이터를 처리하고 필요한 모든 작업을 수행 한 다음 그런 다음 출력에 기록하십시오. ,C와 오디오 wav 파일을 처리

#include <stdio.h> 
#include <stdlib.h> 


typedef struct header 
{ 
    char chunk_id[4]; 
    int chunk_size; 
    char format[4]; 
    char subchunk1_id[4]; 
    int subchunk1_size; 
    short int audio_format; 
    short int num_channels; 
    int sample_rate; 
    int byte_rate; 
    short int block_align; 
    short int bits_per_sample; 
    short int extra_param_size; 
    char subchunk2_id[4]; 
    int subchunk2_size; 
} header; 

typedef struct header* header_p; 

void scale_wav_file(char * input, float factor, int is_8bit) 
{ 
    FILE * infile = fopen(input, "rb"); 
    FILE * outfile = fopen("outfile.wav", "wb"); 

    int BUFSIZE = 4000, i, MAX_8BIT_AMP = 255, MAX_16BIT_AMP = 32678; 

    // used for processing 8-bit file 
    unsigned char inbuff8[BUFSIZE], outbuff8[BUFSIZE]; 

    // used for processing 16-bit file 
    short int inbuff16[BUFSIZE], outbuff16[BUFSIZE]; 

    // header_p points to a header struct that contains the file's metadata fields 
    header_p meta = (header_p)malloc(sizeof(header)); 

    if (infile) 
    { 

     // read and write header data 
     fread(meta, 1, sizeof(header), infile); 
     fwrite(meta, 1, sizeof(meta), outfile); 

     while (!feof(infile)) 
     { 
      if (is_8bit) 
      { 
       fread(inbuff8, 1, BUFSIZE, infile); 
      } else { 
       fread(inbuff16, 1, BUFSIZE, infile);  
      } 

      // scale amplitude for 8/16 bits 
      for (i=0; i < BUFSIZE; ++i) 
      { 
       if (is_8bit) 
       { 
        outbuff8[i] = factor * inbuff8[i]; 
        if ((int)outbuff8[i] > MAX_8BIT_AMP) 
        { 
         outbuff8[i] = MAX_8BIT_AMP; 
        } 
       } else { 
        outbuff16[i] = factor * inbuff16[i]; 
        if ((int)outbuff16[i] > MAX_16BIT_AMP) 
        { 
         outbuff16[i] = MAX_16BIT_AMP; 
        } else if ((int)outbuff16[i] < -MAX_16BIT_AMP) { 
         outbuff16[i] = -MAX_16BIT_AMP; 
        } 
       } 
      } 

      // write to output file for 8/16 bit 
      if (is_8bit) 
      { 
       fwrite(outbuff8, 1, BUFSIZE, outfile); 
      } else { 
       fwrite(outbuff16, 1, BUFSIZE, outfile); 
      } 
     } 
    } 

    // cleanup 
    if (infile) { fclose(infile); } 
    if (outfile) { fclose(outfile); } 
    if (meta) { free(meta); } 
} 

int main (int argc, char const *argv[]) 
{ 
    char infile[] = "file.wav"; 
    float factor = 0.5; 
    scale_wav_file(infile, factor, 0); 
    return 0; 
} 

나는 (A를 40Mb 파일, 1K 정도에 의해) 끝에서 다른 파일 크기를 받고 있어요, 나는 이것이 내가 출력에 전체 버퍼를 쓰고 있어요 때문이다 의심 전체 버퍼 크기를 채우기 전에 파일이 종료되었을 수 있습니다. 또한 출력 파일이 엉망입니다 - 재생 또는 열리지 않습니다 - 그래서 나는 아마 모든 일을 잘못하고 있어요. 내가 엉망이되고있는 곳의 어떤 조언도 위대 할 것이다. 감사!

+1

는 입력 및 출력 파일은 다른이 크기는 출력 파일이 입력보다 크거나 작습니까? – bta

+1

출력이 더 크다 – sa125

답변

8

1이 다른 지점에 바이트 대신 16 비트 샘플을 읽고 : 확장 할 때

while (!feof(infile)) 
    { 
     if (is_8bit) 
     { 
      fread(inbuff8, 1, BUFSIZE, infile); 
     } else { 
      fread(inbuff16, 1, BUFSIZE, infile); // <-- should be BUFSIZE*2  
     } 

2 당신은 값을 포화하지 않는, 예를 원래의 16 비트 샘플 = 32000 및 계수 = 1.5는 최대 값을 32767로 고정하는 대신 정수 값을 감쌀 것입니다.

RIFF 및 다른 헤더는 전혀 보지 않습니다. WAV 파일에서 오디오 데이터 다음에 정보 용 바닥 글 또는 추가 헤더가 오는 것이 가능합니다. 즉, header 구조체가 너무 정적입니다. 또한 8 비트 샘플을 나타내는 매개 변수 대신 파일에서 WAV 형식을 읽어야합니다.

4이는 일어나지 않을 것입니다 :

   outbuff16[i] = factor * inbuff16[i]; 
       if ((int)outbuff16[i] > MAX_16BIT_AMP) 

8 비트/16 비트 값이 없을 것보다 32,768분의 255 컴퓨터가 메모리에 마법의 비트를 삽입하는 경우를 제외하고 정수를 오버플로 : P

오디오 샘플에 서명되어 있으므로 범위는 -128, 127 및 -32768, 32767입니다. 오버플로 검사는 곱셈 표현식에서 발생해야합니다. 또한 구성 가능하고 고려해야 할 부동 소수점 - 반올림 모드에 대한 가정을하고 있습니다. if(roundf(factor * inbuff16[i]) > 32767 || roundf(factor * inbuff16[i]) < -32768)과 같은 것일 수도 있습니다.

fread의 결과를 저장하지 않으므로 출력 파일에 너무 많은 샘플을 쓸 것입니다.

마지막으로, 당신은 바퀴를 재발 명하고 있습니다. 학습을위한 것 인 한, 괜찮습니다. 그렇지 않으면 기존 라이브러리를 사용해야합니다.

+1

나는 당신이 대부분의 것을 잡았다 고 생각한다 - 나는 sizeof (메타)가 헤더로서 메타를 쓸 때 잘못되었다고 덧붙인다. 포인터 - sizeof (header) 또는 sizeof (* meta) 여야합니다. – Dipstick

+0

그 위대한 의견, 나는 이러한 것들을 밖으로 시도 할 것입니다 – sa125

+0

: 항목 3, 당신은 또한 모두가 단점을 가지고 좋아하는 파도 편집기의 출력을 검사에 따라 가정을 할 수 없습니다. libsndfile의 작업 중 상당 부분은 다양한 웨이브 편집기에서 이상하게 작동하는 것으로 나타났습니다. libsndfile 라이센스에 익숙하다면이 모든 것들이 던져 넣은 초기화 물건들을 가지고 sf_read_floats와 sf_write_floats를 줄일 수 있습니다. – kibibu

1

데이터를 올바르게 다시 쓰는지 확인하려면 16 진수 편집기에서 원본 파일과 출력 파일을 살펴 보는 것이 좋습니다. 결과 파일이 재생되거나 열리지 않으면 출력 파일의 헤더가 올바르지 않습니다.

또 다른 옵션은 오디오 프로세싱 로직을 제거하고 소스 파일을 내부 버퍼로 읽어 들이고 파일에 기록하는 것입니다. 코드가 이러한 방식으로 유효한 출력 파일을 생성 할 수 있으면 문제를 처리 코드로 좁힐 수 있습니다.

40MB보다 작은 파일로 시작할 수도 있습니다. 그 밖의 것이 없다면, 그 입력 파일의 복사본을 만들고 오디오를 몇 초 정도 자릅니다. 작은 파일을 수동으로 검사하기가 더 쉽습니다.

편집 : fread()fwrite() 필요에 통화가 반환 값을 확인해야합니다. 이 함수는 읽거나 쓰는 요소의 수를 반환하며, 두 함수 중 하나를 호출하면 예상보다 작은 값이 반환되면 파일 크기 차이의 원인이 될 수 있습니다.

또한 fread의 두 번째 매개 변수는 바이트 단위입니다. 따라서 전체 버퍼를 읽기 - 채우기를 원할 경우 fread(inbuff16, sizeof(inbuff16[0]), BUFSIZE, infile);과 같은 것을 말해야합니다. 현재 코드는 BUFSIZE 바이트 (우연히 8 비트의 경우 작동하지만, 명확하게하기 위해 변경하는 것이 좋습니다)로만 읽습니다.

+0

당신이 파일 크기에 대해 옳다면, 그것은 단지 그것이 작동 하는지를 확인하기 위해서만 축소 할 가치가있다. – sa125

5

사운드 파일을 읽고 쓰는 데 라이브러리를 사용하는 것이 훨씬 좋습니다. 예 : libsndfile. 그 웹 페이지는 여러분이 볼 수있는 "다른 비슷한 프로젝트"의 목록을 가지고 있습니다. 라이브러리를 사용하는 방법을 배우려면 sndfile-tools이 좋은 코드 예제가 될 수 있습니다.

0

가능하면 C 응용 프로그램 전용이 아닌 한 C 언어가 아닌 다른 언어를보고 싶을 수 있습니다.

  • 예를 들어 파이썬에는 wav 파일을 쉽게 쓰는 &이라는 좋은 wav 패키지가 있습니다.
  • 전문적으로나 학술적으로 사용하기 위해 먼저 wav 파일을 매우 쉽게 읽을 수있는 MATLAB (직접적으로 단일 표현식으로 작동되는 벡터로 직접 이동)이 있습니다.
1

이 다음 줄은 또한 WAV 헤더를 읽기 위해 필요하지 않습니다는 (대신 "표준"(44), 헤더 48 바이트를하게) : 당신이 말할 때

short int extra_param_size;