Skip to content

Instantly share code, notes, and snippets.

@fukuroder
Last active August 29, 2015 14:08
Show Gist options
  • Select an option

  • Save fukuroder/980fa46f77e3ab2de752 to your computer and use it in GitHub Desktop.

Select an option

Save fukuroder/980fa46f77e3ab2de752 to your computer and use it in GitHub Desktop.
OCamlでWAVファイル読み込んでALSAで再生するテスト
(*
* ocaml_alsa_test.ml
*
* Created by fukuroda (https://github.com/fukuroder)
*)
(* sudo apt-get install libalsa-ocaml-dev *)
(* ocamlfind ocamlopt -o ocaml_alsa_test ocaml_alsa_test.ml -package alsa -linkpkg *)
(* 例外 *)
exception InvalidWavFormat of string
(* 文字列読み込み *)
let read_string f n = (
let buf = Buffer.create n in (
Buffer.add_channel buf f n;
Buffer.contents buf
)
)
(* 4byte読み込み *)
let read_byte4 f = (
let rec read_byte4_sub n = (
let v = Int64.of_int (input_byte f) in
let v = Int64.shift_left v (n*8) in
(match n with
| 3 -> v
| _ -> Int64.logor v (read_byte4_sub (n+1))
)
) in read_byte4_sub 0
)
(* 2byte読み込み *)
let read_byte2 f = (
let a1 = input_byte f and a2 = input_byte f in (
(a2 lsl 8) lor a1
)
)
(* frame読み込み *)
let read_frame buf f pos = (
buf.[4*pos] <- input_char f;
buf.[4*pos+1] <- input_char f;
buf.[4*pos+2] <- input_char f;
buf.[4*pos+3] <- input_char f
)
(* block読み込み *)
let read_block buf f block_size = (
let rec read_block_sub pos = (
match (block_size-pos) with
| 0 -> ()
| _ -> read_frame buf f pos;
read_block_sub (pos+1)
) in read_block_sub 0
)
(* WAVヘッダの読み込みとframe数取得 *)
let get_frames wav = (
(* RIFFヘッダ *)
let data = read_string wav 4 in (
Printf.printf "%s\n" data;
if data <> "RIFF" then raise (InvalidWavFormat "RIFFでない")
);
(* ファイルサイズ - 8 *)
let data = read_byte4 wav in (
Printf.printf "%Ld\n" data
);
(* WAVEヘッダ *)
let data = read_string wav 4 in (
Printf.printf "%s\n" data;
if data <> "WAVE" then raise (InvalidWavFormat "WAVEでない")
);
(* fmtチャンク *)
let data = read_string wav 4 in (
Printf.printf "%s\n" data;
if data <> "fmt " then raise (InvalidWavFormat "fmt でない")
);
(* fmtチャンクのバイト数 *)
let data = read_byte4 wav in (
Printf.printf "%Ld\n" data;
if data <> 16L then raise (InvalidWavFormat "fmtチャンクのバイト数が16でない")
);
(* フォーマットID *)
let data = read_byte2 wav in (
Printf.printf "%d\n" data;
if data <> 1 then raise (InvalidWavFormat "PCMでない")
);
(* チャンネル数 *)
let data = read_byte2 wav in (
Printf.printf "%d\n" data;
if data <> 2 then raise (InvalidWavFormat "stereoでない")
);
(* サンプリングレート *)
let data = read_byte4 wav in (
Printf.printf "%Ld\n" data;
if data <> 44100L then raise (InvalidWavFormat "サンプリングレートが44100[kHz]でない")
);
(* データ速度 *)
let data = read_byte4 wav in (
Printf.printf "%Ld\n" data;
if data <> 176400L then raise (InvalidWavFormat "データ速度が176400[byte/sec]でない")
);
(* ブロックサイズ *)
let data = read_byte2 wav in (
Printf.printf "%d\n" data;
if data <> 4 then raise (InvalidWavFormat "ブロックサイズが4でない")
);
(* サンプルあたりのビット数 *)
let data = read_byte2 wav in (
Printf.printf "%d\n" data;
if data <> 16 then raise (InvalidWavFormat "サンプルあたりのビット数が16でない")
);
(* dataチャンク *)
let data = read_string wav 4 in (
Printf.printf "%s\n" data;
if data <> "data" then raise (InvalidWavFormat "dataでない")
);
(* 波形データのバイト数 *)
let data = read_byte4 wav in (
Printf.printf "%Ld\n" data;
flush stdout;
Int64.div data 4L (* frame数 *)
)
)
(* デバイスを開く *)
let open_default_pcm = (
(* オーディオデバイスを開く *)
let pcm = Alsa.Pcm.open_pcm "hw:0,0" [Alsa.Pcm.Playback] [] in (
(* 設定 *)
let params = Alsa.Pcm.get_params pcm in (
Alsa.Pcm.set_access pcm params Alsa.Pcm.Access_rw_interleaved;
Alsa.Pcm.set_format pcm params Alsa.Pcm.Format_s16_le;
ignore (Alsa.Pcm.set_rate_near pcm params 44100 Alsa.Dir_eq);
Alsa.Pcm.set_channels pcm params 2;
Alsa.Pcm.set_buffer_size pcm params (1024*4);
Alsa.Pcm.set_params pcm params;
);
pcm
)
)
(* PCM書き込み *)
let write_pcm pcm wav frames = (
let buffer = String.create (2*2*1024)
and blocks = Int64.div frames 1024L
and last_block_size = Int64.to_int (Int64.rem frames 1024L) in (
let rec write_pcm_sub blocks = (
let block_size = match blocks with
| 0L -> last_block_size
| _ -> 1024 in (
read_block buffer wav block_size;
ignore (Alsa.Pcm.writei pcm buffer 0 block_size);
match blocks with
| 0L -> ()
| _ -> write_pcm_sub (Int64.sub blocks 1L)
)
) in write_pcm_sub blocks
)
)
(* entry *)
let () = (
let wav = open_in_bin "kmb.wav" in (
(try
let frames = get_frames wav (* フレーム数取得 *)
and pcm = open_default_pcm (* オーディオデバイスを開く *)
in (
write_pcm pcm wav frames; (* 書き込み *)
Alsa.Pcm.drain pcm;
Alsa.Pcm.close pcm (* オーディオデバイスを閉じる *)
)
with
InvalidWavFormat(s) -> Printf.printf "InvalidWavFormat %s\n" s
);
(* ファイルを閉じる *)
close_in wav;
Printf.printf "OK\n"
)
)
(* 結果
RIFF
27570180
WAVE
fmt
16
1
2
44100
176400
4
16
data
27570144
OK
*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment