1. 程式人生 > >PCM音訊檔案(.wav)壓縮成GSM6.10(.wav)

PCM音訊檔案(.wav)壓縮成GSM6.10(.wav)

GSM的輸入是幀資料,一幀(20毫秒)由取樣頻率為8 kHz的帶符號的160個樣本組成,每個樣本為13位或者16位的線性PCM碼。GSM編碼器可把一幀(160×16位)的資料壓縮成260位的GSM幀,壓縮後的資料率為1625位元組,相當於13 kbps。由於260位不是8位的整數倍,因此編碼器輸出的GSM幀為264位的線性PCM碼。取樣頻率為8 kHz、每個樣本為16位的未壓縮的話音資料率為128 kbps,使用GSM壓縮後的資料率為: (264bit×8000樣本/s) / 160樣本 = 13.2 kbps

GSM的壓縮比:128:13.2 = 9.7,近似於10:1。

void setGsmHeader( FILE *outFile, ULONG dataSize)
{
 ULONG   flen = 0;
 INTELADPCM_HEADER   gsmhead;
 
 gsmhead.riff.chunkid = WAV_ID_RIFF;//4BYTE
 int gsmDataSize = 0;
 if(dataSize % 640 == 0) {
  gsmDataSize = (((int)dataSize/640) * 65 + 1) & 0xFFFFFFFE;
 }
 else {
  gsmDataSize = (((int)dataSize/640 + 1) * 65 + 1) & 0xFFFFFFFE;
 }
 gsmhead.riff.chunksize = gsmDataSize + sizeof(INTELADPCM_HEADER) - 8;
 gsmhead.riff.wave_id = WAV_ID_WAVE;//4BYTE
 gsmhead.chunkid = WAV_ID_FMT;//4BYTE
 gsmhead.chunksize = 0x14;//2BYTE
 gsmhead.wformattag = 0x31;//2BYTE
 gsmhead.nchannels = 1;//2BYTE
 gsmhead.nsamplespersec = 8000;//4BYTE
 gsmhead.navgbytespersec = 1625;//4BYTE
 gsmhead.nblockalign = 0x41;//2BYTE
 gsmhead.wbitspersample = 0;//2BYTE  
 gsmhead.cbsize = 2;//2BYTE
 gsmhead.wsamplesperblock = 0x140;// 2BYTE:320
 
 gsmhead.fact.chunkid = WAV_ID_FACT;
 gsmhead.fact.chunksize = 4;
 gsmhead.fact.datalength = dataSize;
 gsmhead.data.ID = WAV_ID_DATA;
 gsmhead.data.Size = gsmDataSize;
 
 fwrite( &gsmhead, sizeof(gsmhead), 1, outFile);

}

int gsmEncoder(char* strFilePath,
                         char* strOriginalPCM,
                         char* strCompressFileName){
    FILE *sfp;
    FILE *dfp;
    gsm handle;
    long samples;
    long total_out;
    gsm_signal linear[GSM_BLOCK_SIZE];
    gsm_frame frame;
    int out_size;
    int rc;

    // 圧縮するファイルのパスとファイル名を連線する。
    char fileFullPath[FILE_NAME_LEN] = {'0',};
    fileFullPathCat(fileFullPath, strFilePath, strOriginalPCM);
    sfp = fopen(fileFullPath, "rb");
    if (!sfp)
    {
        perror("open fails");
        exit(1);
    }

    /* Read .wav file header info and calulate output size */
    WAVEFMT waveFmt;
    checkWaveFileFormat(sfp, &waveFmt);
    samples = waveFmt.dataLength;

    // 圧縮されたファイルのパスとファイル名を連線する。
    memset(fileFullPath, 0, sizeof(fileFullPath));
    fileFullPathCat(fileFullPath, strFilePath, strCompressFileName);
    dfp = fopen(fileFullPath, "wb+");

    /* Create the GSM codec object and option it for wave framing */
    handle = gsm_create();
    if (!handle)
    {
       perror("cannot create gsm codec");
       exit(1);
    }

    (void )gsm_option(handle, GSM_OPT_WAV49, &F_wav_fmt);

    /* Write the .wav file header */
    setGsmHeader(dfp, samples);

    /* Compress the audio */

    total_out = 0;
    while (samples > 0)
    {
  /* Read two frames worth of samples and convert to linear */
  rc = fread(linear, (size_t )1, sizeof (linear), sfp);
  if (rc < 0)
  {
   perror("error reading input");
   exit(1);
  }
  samples -= rc;
  if (rc < sizeof (linear))
  {
   memset((char *)linear + rc, LINEAR_ZERO, sizeof (linear) - rc);
  }

  /* Encode the even half and write short (32-byte) frame */
  gsm_encode(handle, &linear[0], frame);
  out_size = sizeof (frame) - 1;
  rc = fwrite(frame, (size_t )1, out_size, dfp);
  if (rc != out_size)
  {
   perror("error writing output");
   exit(1);
  }
  total_out += rc;

  /* Encode the odd half and write long (33-byte) frame */
  gsm_encode(handle, &linear[160], frame);
  out_size = sizeof (frame);
  rc = fwrite(frame, (size_t )1, out_size, dfp);
  if (rc != out_size)
  {
   perror("error writing output");
   exit(1);
  }
  total_out += rc;
    }

    /* Pad output to even number of bytes */
    if (total_out & 0x1)
    {
  frame[0] = 0x00;
  rc = fwrite(frame, (size_t )1, 1, dfp);
  if (rc != 1)
  {
   perror("error writing output");
   exit(1);
  }
  total_out += rc;
    }

    /* Clean up */
    gsm_destroy(handle);

    return 0;
}