module FFT = Audio.Mono.Analyze.FFT
let polyphony = 1
let mchan = 0
let oss_out = true

let list_diff cmp l1 l2 =
  List.fold_left
    (fun acc x ->
      if not (List.exists (cmp x) l2) then
        x::acc
      else
        acc
    ) [] l1

let rec list_head_n n l =
  if n = 0 then
    []
  else
    match l with
      | h::t -> h::(list_head_n (n-1) t)
      | [] -> []

let () =
  let fname = Sys.argv.(1) in
  let f = new MMMad.reader_of_file fname in
  let oss = new MMOSS.writer f#channels f#sample_rate in
  let wav = new Audio.IO.Writer.to_wav_file f#channels f#sample_rate "out.wav" in
  let mid = new MIDI.IO.Writer.to_file f#sample_rate "out.mid" in
  let fft_n = 11 in
  let fft = FFT.init fft_n in
  let blen = FFT.duration fft in
  let buf = Audio.create f#channels blen in
  let agc = Audio.Effect.auto_gain_control f#channels f#sample_rate ~kup:0.9 ~kdown:0.7 ~rms_target:2. () in
  let adsr = Audio.Mono.Effect.ADSR.make f#sample_rate (0.02,0.01,0.9,0.05) in
  let synth = new Synth.saw ~adsr f#sample_rate in
  let loop = ref true in
  let prevnotes = ref [] in
  synth#set_volume 0.1;
  while !loop do
    let r = f#read buf 0 blen in
    agc#process buf 0 blen;
    loop := r <> 0;
    let notes = FFT.notes f#sample_rate fft ~note_min:(Audio.Note.create 0 4) ~volume_min:0.01 ~filter_harmonics:false (Audio.to_mono buf) 0 blen in
    let notes = List.sort (fun (_,v1) (_,v2) -> if v1 < v2 then 1 else -1) notes in
    let notes = list_head_n polyphony notes in
    (* Printf.printf "Notes: %d\n%!" (List.length notes); *)
    let ncmp (n1,_) (n2,_) = n1 = n2 in
    List.iter (fun (n,v) -> synth#note_off n 1.; mid#note_off mchan n (10. *. v)) (list_diff ncmp !prevnotes notes);
    List.iter (fun (n,v) -> synth#note_on n 1.; mid#note_on mchan n (10. *. v)) (list_diff ncmp notes !prevnotes);
    prevnotes := notes;
    synth#fill_add buf 0 blen;
    mid#advance blen;
    Audio.amplify 2. buf 0 blen;
    wav#write buf 0 blen;
    if oss_out then
      oss#write buf 0 blen
  done;
  wav#close;
  mid#close;
  oss#close;
  f#close
