(*
 * Copyright 2008 Savonet team
 *
 * This file is part of OCaml-speex.
 *
 * OCaml-speex is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * OCaml-speex is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OCaml-speex; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *)

(**
  * A speex to wav converter using OCaml-speex.
  *
  * @author Samuel Mimram
  * @author Romaib Beauxis
  *)

let bufsize = 16 * 1024

let src = ref ""
let dst = ref ""


open Unix
open Speex

let output_int chan n =
  output_char chan (char_of_int ((n lsr 0) land 0xff));
  output_char chan (char_of_int ((n lsr 8) land 0xff));
  output_char chan (char_of_int ((n lsr 16) land 0xff));
  output_char chan (char_of_int ((n lsr 24) land 0xff))


let output_short chan n =
  output_char chan (char_of_int ((n lsr 0) land 0xff));
  output_char chan (char_of_int ((n lsr 8) land 0xff))


let usage = "usage: speex2wav [options] source destination"
let float = ref false

let _ =
  Arg.parse 
      [
        "--float", Arg.Bool (fun b -> float := b),
        "Use floats for encoding" ;
      ]
    (
      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 dec,fd = Speex.Wrapper.Decoder.open_file !src in
  let header = Speex.Wrapper.Decoder.header dec in
  let comments = Speex.Wrapper.Decoder.comments dec in
  let chans = header.Header.nb_channels in
  let rate = header.Header.rate in
  let mode = header.Header.mode in
  let print_comment (k,v) = 
    Printf.printf "%s: %s\n" k v
  in
  Printf.printf "Comments:\n";
  List.iter print_comment comments;
  Printf.printf "\n";
  let s_of_m x =
    match x with
      | Speex.Narrowband -> "narrowband"
      | Speex.Wideband -> "wideband"
      | Speex.Ultra_wideband -> "ultra_wideband"
  in
    Printf.printf
      "Input file characteristics: speex codec, %d channels, %d Hz, %s mode\n"
      chans 
      rate 
      (s_of_m mode);
    (* Using speex to decode the ogg. *)
  Printf.printf "\nDecoding...\n";
  flush_all ();
  let tmpdst,oc = Filename.open_temp_file ~mode:[Open_binary] "speex2wav" ".raw" in
      (
          try
            while true do
              (* Convert mono to two identical channels.. *)
              let frames = 
                if !float then
                  if chans = 2 then 
                    List.map (Array.map (fun x -> Array.map int_of_float x))
                      (Wrapper.Decoder.decode_stereo dec)
                  else
                    let f = List.map (Array.map int_of_float) (Wrapper.Decoder.decode dec) in
                    List.map (fun f -> Array.init 2 (fun _ -> f)) f
                else
                  if chans = 2 then
                    Wrapper.Decoder.decode_int_stereo dec
                  else
                    let f = Wrapper.Decoder.decode_int dec in
                    List.map (fun f -> Array.init 2 (fun _ -> f)) f
              in
              let put frame = 
                let s1 = frame.(0) in
                let s2 = frame.(1) in
                Array.iteri (fun n -> fun _ -> 
                                 output_short oc s1.(n);
                                 output_short oc s2.(n))
                               s1;
              in
              List.iter put frames;
            done
          with
            | End_of_stream -> close_out oc
      );
      Printf.printf "Decoding finished, writing WAV..\n";
      Unix.close fd;
      (* Do the wav stuff. *)
      let datalen = (stat tmpdst).st_size in
      let ic = open_in_bin tmpdst in
      let oc = open_out_bin !dst in
        output_string oc "RIFF";
        output_int oc (4 + 24 + 8 + datalen);
        output_string oc "WAVE";
        output_string oc "fmt ";
        output_int oc 16;
        output_short oc 1; (* WAVE_FORMAT_PCM *)
        output_short oc 2; (* channels *)
        output_int oc rate; (* freq *)
        output_int oc (rate * 2 * 2); (* bytes / s *)
        output_short oc (2 * 2); (* block alignment *)
        output_short oc 16; (* bits per sample *)
        output_string oc "data";
        output_int oc datalen;
        (
          let buflen = 256 * 1024 in
          let buf = String.create buflen in
          let r = ref 1 in
          let pos = ref 0 in
            while !r <> 0
            do
              r := input ic buf 0 buflen;
              output oc buf 0 !r;
              pos := !pos + !r;
            done
        );
        close_in ic;
        close_out oc;
        Unix.unlink tmpdst;
        Printf.printf "Done !\n";
        Gc.full_major ()
