File: encode.ml

package info (click to toggle)
ocaml-flac 0.3.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 280 kB
  • sloc: ansic: 1,082; ml: 803; makefile: 8
file content (156 lines) | stat: -rw-r--r-- 4,487 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
let src = ref ""
let dst = ref ""
let buflen = ref 1024
let flush_outchan = flush

let input_string chan len =
  let ans = Bytes.create len in
  really_input chan ans 0 len;
  Bytes.to_string ans

let input_int chan =
  let buf = input_string chan 4 in
  int_of_char buf.[0]
  + (int_of_char buf.[1] lsl 8)
  + (int_of_char buf.[2] lsl 16)
  + (int_of_char buf.[3] lsl 24)

let input_short chan =
  let buf = input_string chan 2 in
  int_of_char buf.[0] + (int_of_char buf.[1] lsl 8)

let compression = ref 5
let ogg = ref false
let usage = "usage: encode [options] source destination"

let _ =
  Arg.parse
    [
      ( "--compression",
        Arg.Int (fun b -> compression := b),
        "Compression level." );
      ("--ogg", Arg.Bool (fun b -> ogg := b), "Encoder in ogg format.");
    ]
    (let pnum = ref (-1) in
     fun s ->
       incr pnum;
       match !pnum with
         | 0 -> src := s
         | 1 -> dst := s
         | _ ->
             Printf.eprintf "Error: too many arguments\n";
             exit 1)
    usage;
  if !src = "" || !dst = "" then (
    Printf.printf "%s\n" usage;
    exit 1);
  let ic = open_in_bin !src in
  (* TODO: improve! *)
  if input_string ic 4 <> "RIFF" then invalid_arg "No RIFF tag";
  ignore (input_string ic 4);
  if input_string ic 4 <> "WAVE" then invalid_arg "No WAVE tag";
  if input_string ic 4 <> "fmt " then invalid_arg "No fmt tag";
  let _ = input_int ic in
  let _ = input_short ic in
  (* TODO: should be 1 *)
  let channels = input_short ic in
  let infreq = input_int ic in
  let _ = input_int ic in
  (* bytes / s *)
  let _ = input_short ic in
  (* block align *)
  let bits = input_short ic in
  if bits <> 16 then failwith "only s16le is supported for now..";
  let params =
    {
      Flac.Encoder.channels;
      sample_rate = infreq;
      bits_per_sample = bits;
      compression_level = Some !compression;
      total_samples = None;
    }
  in
  let comments = [("TITLE", "Encoding example")] in
  let encode, finish =
    if not !ogg then (
      let enc = Flac.Encoder.File.create ~comments params !dst in
      let encode buf =
        Flac.Encoder.process enc.Flac.Encoder.File.enc
          enc.Flac.Encoder.File.callbacks buf
      in
      let finish () =
        Flac.Encoder.finish enc.Flac.Encoder.File.enc
          enc.Flac.Encoder.File.callbacks;
        Unix.close enc.Flac.Encoder.File.fd
      in
      (encode, finish))
    else (
      let os = Ogg.Stream.create () in
      let oc = open_out !dst in
      let s_o_f (h, b) = h ^ b in
      let flush s =
        let rec f v =
          try
            let v = v ^ s_o_f (Ogg.Stream.flush_page s) in
            f v
          with Ogg.Not_enough_data -> v
        in
        f ""
      in
      let pageout s = s_o_f (Ogg.Stream.get_page s) in
      let pagesout s =
        let rec f v =
          try
            let n = pageout s in
            f (v ^ n)
          with Ogg.Not_enough_data -> v
        in
        f ""
      in
      let enc, p, l = Flac_ogg.Encoder.create ~comments params os in
      Ogg.Stream.put_packet os p;
      output_string oc (flush os);
      List.iter (Ogg.Stream.put_packet os) l;
      output_string oc (flush os);
      flush_outchan oc;
      let encode buf =
        Flac.Encoder.process enc Flac_ogg.Encoder.callbacks buf;
        output_string oc (pagesout os)
      in
      let finish () =
        Flac.Encoder.finish enc Flac_ogg.Encoder.callbacks;
        output_string oc (flush os);
        close_out oc
      in
      (encode, finish))
  in
  let start = Unix.time () in
  Printf.printf "Input detected: PCM WAVE %d channels, %d Hz, %d bits\n%!"
    channels infreq bits;
  Printf.printf
    "Encoding to: %s %d channels, %d Hz, compression level: %d\n\
     Please wait...\n\
     %!"
    (if !ogg then "OGG/FLAC" else "FLAC")
    channels infreq !compression;
  while input_string ic 4 <> "data" do
    let len = input_int ic in
    really_input ic (Bytes.create len) 0 len
  done;
  (* This ensures the actual audio data will start on a new page, as per
   * spec. *)
  let buflen = channels * bits / 8 * !buflen in
  let buf = Bytes.create buflen in
  begin
    try
      while true do
        really_input ic buf 0 (Bytes.length buf);
        encode (Flac.Encoder.from_s16le (Bytes.to_string buf) channels)
      done
    with End_of_file -> ()
  end;
  finish ();
  close_in ic;
  Printf.printf "Finished in %.0f seconds.\n" (Unix.time () -. start);
  Gc.full_major ();
  Gc.full_major ()