顯示具有 Audio 標籤的文章。 顯示所有文章
顯示具有 Audio 標籤的文章。 顯示所有文章

2015年12月7日 星期一

Audio Wave Format Specification

WAV File - Waveform Audio File Format (WAVE, or more commonly known as WAV due to its filename extension).
是微软與IBM公司所開發在個人電腦儲存音訊串流的編碼格式,它受到Windows平台的應用軟體之廣泛支援,也是使用者經常使用的指定規格之一。WAV音頻格式沒有經過壓縮(原則上只有在類比轉數位這個過程會失真),所以音質不會出現失真的情況,但相對地,它的檔案體積在各種音頻格式中是比較大的。一般音樂CD片也是由WAV檔編輯而成。



底下的網頁講的也蠻不錯的
http://soundfile.sapp.org/doc/WaveFormat/

Little Endian注意事項:
裏面的Size存放格式都是Little Endian, 因此讀入後要轉換一下, 在fdk-aac的範例程式轉換函式入下:
static uint32_t read_int32(struct wav_reader* wr) {
        uint32_t value = 0;
        value |= fgetc(wr->wav) <<  0;
        value |= fgetc(wr->wav) <<  8;
        value |= fgetc(wr->wav) << 16;
        value |= fgetc(wr->wav) << 24;
        return value;
}
你也可以在include byteswap.h後使用bswap_32()函式
#include <stdio.h>
#include <stdlib.h>
#include <byteswap.h>

int main(int argc, char** argv){
        unsigned int a = 0x46000600;
        printf("%08x\n", bswap_32(a));
}


底下的範例檔案是使用ffmpeg將pcm資料轉成wav檔,整個wav檔的大小為778318bytes

52, 49, 46, 46代表"RIFF".
000be046代表扣掉目前的8個bytes後面的資料長度, 所以是778310+8=778318bytes.
57, 41 56, 45代表"WAVE".
66, 6d, 74, 20代表"fmt ".
00000010代表後面有接著16bytes的fmt資料(01 00 01 00 11 2b 00 00 22 56 00 00 02 00 10 00)
    0001 => Audio Format = 1(PCM)
    0001 => NumChannels = 1 channel
    00002b11 => 11025 Sample Rate
    00005622 => 22050 ByteRate
    0002 => BlockAlign = 2
    0010 => BitsPerSample = 16
4c, 49, 53, 54代表"LIST".
0000001a代表後面有接著26bytes的list資料(49 4e 46 4f 49 53 46 54 0e 00 00 00 4c 61 76 66 35 37 2e 31 39 2e 31 30 30 00)
64, 61, 74, 61代表"data".
000be000代表後面接著778240的資料. 所以778240+78=778318bytes

參考:
https://www.aelius.com/njh/wavemetatools/

2015年12月1日 星期二

Alsa Audio Capture

最近在試Camera Digital Microphone, 它基本上是走Alsa的架構,
因此找個Alsa Audio Capture的範例程式來做實驗.
參考資料來自於: Linux Journal Introduction to Sound Programming with ALSA

/*

   This example reads from the default PCM device
   and writes to standard output for 5 seconds of data.

 */

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

int main() {
        long loops;
        int rc;
        int size;
        snd_pcm_t *handle;
        snd_pcm_hw_params_t *params;
        unsigned int val;
        int dir;
        snd_pcm_uframes_t frames;
        char *buffer;

        /* Open PCM device for recording (capture). */
        rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
        if (rc < 0) {
                fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc));
                exit(1);
        }

        /* Allocate a hardware parameters object. */
        snd_pcm_hw_params_alloca(&params);

        /* Fill it in with default values. */
        snd_pcm_hw_params_any(handle, params);

        /* Set the desired hardware parameters. */

        /* Interleaved mode */
        snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);

        /* Signed 16-bit little-endian format */
        snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);

        /* Two channels (stereo) */
        snd_pcm_hw_params_set_channels(handle, params, 2);

        /* 44100 bits/second sampling rate (CD quality) */
        val = 44100;
        snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir);

        /* Set period size to 32 frames. */
        frames = 32;
        snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);

        /* Write the parameters to the driver */
        rc = snd_pcm_hw_params(handle, params);
        if (rc < 0) {
                fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc));
                exit(1);
        }

        /* Use a buffer large enough to hold one period */
        snd_pcm_hw_params_get_period_size(params, &frames, &dir);
        size = frames * 4; /* 2 bytes/sample, 2 channels */
        buffer = (char *) malloc(size);

        /* We want to loop for 5 seconds */
        snd_pcm_hw_params_get_period_time(params, &val, &dir);
        loops = 5000000 / val;

        while (loops > 0) {
                loops--;
                rc = snd_pcm_readi(handle, buffer, frames);
                if (rc == -EPIPE) {
                        /* EPIPE means overrun */
                        fprintf(stderr, "overrun occurred\n");
                        snd_pcm_prepare(handle);
                } else if (rc < 0) {
                        fprintf(stderr, "error from read: %s\n", snd_strerror(rc));
                } else if (rc != (int)frames) {
                        fprintf(stderr, "short read, read %d frames\n", rc);
                }
                rc = write(1, buffer, size);
                if (rc != size)
                        fprintf(stderr, "short write: wrote %d bytes\n", rc);
        }

        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        free(buffer);

        return 0;
}

目前錄出來的PCM聲音檔, 我是在ubuntu上用mplayer去播放
mplayer -rawaudio samplesize=2:channels=2:rate=11025 -demuxer rawaudio test.raw

使用ffmpeg將PCM檔轉WAV檔
ffmpeg -f s16le -ar 11025 -ac 2 -i ./test.pcm ./test.wav

  • -f s16le … signed 16-bit little endian samples
  • -ar 11025 … sample rate 11025Hz
  • -ac 2 … 2 channels (stereo)
  • -i test.pcm … input file
  • test.wav … output file
snd_pcm_readi這裏有遇到一個有趣的問題, 它回傳的值是audio frames的數量.
當你的資料是設定為16bits, 2 channles時, 一個frames的大小為4 bytes.

一個小故事讓我們明白資金流通的意義

“又是炎熱小鎮慵懶的一天。太陽高掛,街道無人,每個人都債台高築,靠信用度日。這時,從外地來了一位有錢的旅客,他進了一家旅館,拿出一張1000 元鈔票放在櫃檯,說想先看看房間,挑一間合適的過夜,就在此人上樓的時候---- 店主抓了這張1000 元鈔,跑到隔壁屠戶那裡支付了他欠的肉錢...