Last active
August 29, 2015 14:08
-
-
Save fukuroder/980fa46f77e3ab2de752 to your computer and use it in GitHub Desktop.
OCamlでWAVファイル読み込んでALSAで再生するテスト
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| (* | |
| * 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