-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
1,851 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
let () = Printexc.record_backtrace true | ||
|
||
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 progress_bar = | ||
let spin = ref 0 in | ||
fun title pos tot -> | ||
let nbeq = 40 in | ||
let n = min (100. *. float_of_int pos /. float_of_int tot) 100. in | ||
Printf.printf "\r%s " title; | ||
if tot > 0 then begin | ||
Printf.printf "%6.2f%% [" n; | ||
let e = int_of_float (n /. 100. *. float_of_int nbeq) in | ||
for _ = 1 to e do | ||
Printf.printf "=" | ||
done; | ||
if e != nbeq then Printf.printf ">"; | ||
for _ = e + 2 to nbeq do | ||
Printf.printf " " | ||
done; | ||
Printf.printf "] " | ||
end; | ||
incr spin; | ||
if !spin > 4 then spin := 1; | ||
Printf.printf "%c%!" | ||
(if tot > 0 && n = 100. then ' ' | ||
else ( | ||
match !spin with | ||
| 1 -> '|' | ||
| 2 -> '/' | ||
| 3 -> '-' | ||
| 4 -> '\\' | ||
| _ -> failwith "this did not happen")) | ||
|
||
let infile = ref "input.flac" | ||
let outfile = ref "output.raw" | ||
let ogg = ref false | ||
|
||
let () = | ||
Arg.parse | ||
[ | ||
("-o", Arg.Set_string outfile, "Output file"); | ||
("-i", Arg.Set_string infile, "Input file"); | ||
("-ogg", Arg.Bool (fun x -> ogg := x), "Ogg/flac file"); | ||
] | ||
ignore "decode [options]" | ||
|
||
let process () = | ||
let fd = | ||
Printf.printf "Opening input file %S\n%!" !infile; | ||
Unix.openfile !infile [Unix.O_RDONLY] 0o640 | ||
in | ||
let oc = | ||
Printf.printf "Opening output file %S\n%!" !outfile; | ||
open_out !outfile | ||
in | ||
let ret = Buffer.create 1024 in | ||
let write x = Buffer.add_string ret (Flac.Decoder.to_s16le x) in | ||
let get () = | ||
let ans = Buffer.contents ret in | ||
Buffer.reset ret; | ||
ans | ||
in | ||
let process, info, comments = | ||
if not !ogg then ( | ||
let h = Flac.Decoder.File.create_from_fd ~write fd in | ||
let process () = | ||
Flac.Decoder.process h.Flac.Decoder.File.dec; | ||
Flac.Decoder.state h.Flac.Decoder.File.dec | ||
in | ||
(process, h.Flac.Decoder.File.info, h.Flac.Decoder.File.comments)) | ||
else ( | ||
let sync = Ogg.Sync.create (Unix.read fd) in | ||
let test_flac () = | ||
(* Get First page *) | ||
let page = Ogg.Sync.read sync in | ||
(* Check wether this is a b_o_s *) | ||
if not (Ogg.Page.bos page) then raise Flac.Decoder.Not_flac; | ||
(* Create a stream with this ID *) | ||
let serial = Ogg.Page.serialno page in | ||
Printf.printf "Testing stream %nx\n" serial; | ||
let os = Ogg.Stream.create ~serial () in | ||
Ogg.Stream.put_page os page; | ||
let packet = Ogg.Stream.peek_packet os in | ||
(* Test header. Do not catch anything, first page should be sufficient *) | ||
if not (Flac_ogg.Decoder.check_packet packet) then raise Not_found; | ||
Printf.printf "Got a flac stream !\n"; | ||
let fill () = | ||
let page = Ogg.Sync.read sync in | ||
if Ogg.Page.serialno page = serial then Ogg.Stream.put_page os page | ||
in | ||
let dec, info, meta = Flac_ogg.Decoder.create ~fill ~write os in | ||
let rec process () = | ||
try | ||
Flac.Decoder.process dec; | ||
Flac.Decoder.state dec | ||
with | ||
| Ogg.End_of_stream -> `End_of_stream | ||
| Ogg.Not_enough_data -> ( | ||
try | ||
fill (); | ||
process () | ||
with Ogg.End_of_stream | Ogg.Not_enough_data -> `End_of_stream) | ||
in | ||
(process, info, meta) | ||
in | ||
(* Now find a flac stream *) | ||
let rec init () = | ||
try test_flac () with | ||
| Not_found -> | ||
Printf.printf "This stream was not flac..\n"; | ||
init () | ||
| Flac.Decoder.Not_flac -> | ||
Printf.printf "No flac stream was found..\n%!"; | ||
raise Flac.Decoder.Not_flac | ||
in | ||
init ()) | ||
in | ||
Printf.printf "Stream info:\n"; | ||
Printf.printf "sample rate: %i\n" info.Flac.Decoder.sample_rate; | ||
Printf.printf "bits per sample: %i\n" info.Flac.Decoder.bits_per_sample; | ||
Printf.printf "channels: %i\n" info.Flac.Decoder.channels; | ||
Printf.printf "total samples: %s\n" | ||
(Int64.to_string info.Flac.Decoder.total_samples); | ||
Printf.printf "md5sum: "; | ||
String.iter | ||
(fun c -> Printf.printf "%x" (int_of_char c)) | ||
info.Flac.Decoder.md5sum; | ||
Printf.printf "\n"; | ||
if info.Flac.Decoder.bits_per_sample <> 16 then | ||
failwith "Unsupported bits per sample."; | ||
let srate = info.Flac.Decoder.sample_rate in | ||
let chans = info.Flac.Decoder.channels in | ||
let datalen = Int64.to_int info.Flac.Decoder.total_samples * chans * 2 in | ||
let () = | ||
match comments with | ||
| None -> Printf.printf "No comment found..\n" | ||
| Some (vendor, comments) -> | ||
Printf.printf "Metadata:\n"; | ||
List.iter (fun (x, y) -> Printf.printf "%s: %s\n" x y) comments; | ||
Printf.printf "VENDOR: %s\n" vendor | ||
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 chans; | ||
(* channels *) | ||
output_int oc srate; | ||
(* freq *) | ||
output_int oc (srate * chans * 2); | ||
(* bytes / s *) | ||
output_short oc (chans * 2); | ||
(* block alignment *) | ||
output_short oc 16; | ||
(* bits per sample *) | ||
output_string oc "data"; | ||
output_int oc datalen; | ||
let pos = ref 0 in | ||
let rec decode () = | ||
let state = process () in | ||
let ret = get () in | ||
pos := !pos + String.length ret; | ||
progress_bar "Decoding FLAC file:" !pos datalen; | ||
output_string oc ret; | ||
flush oc; | ||
match state with `End_of_stream -> Printf.printf "\n" | _ -> decode () | ||
in | ||
decode (); | ||
Printf.printf "\n"; | ||
close_out oc; | ||
Unix.close fd | ||
|
||
let () = | ||
process (); | ||
(* We have global root values | ||
* so we need to do two full major.. *) | ||
Gc.full_major (); | ||
Gc.full_major () |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
(executable | ||
(name decode) | ||
(modules decode) | ||
(optional) | ||
(libraries flac.ogg)) | ||
|
||
(executable | ||
(name encode) | ||
(modules encode) | ||
(optional) | ||
(libraries flac.ogg)) | ||
|
||
(rule | ||
(alias citest) | ||
(target src.wav) | ||
(action | ||
(run | ||
ffmpeg | ||
-hide_banner | ||
-loglevel | ||
error | ||
-f | ||
lavfi | ||
-i | ||
"sine=frequency=220:duration=5" | ||
-ac | ||
2 | ||
%{target}))) | ||
|
||
(rule | ||
(alias citest) | ||
(deps ./src.wav) | ||
(action | ||
(progn | ||
(run ./encode.exe ./src.wav ./src.flac) | ||
(run ./decode.exe -i ./src.flac -o ./dst.wav) | ||
(run ./encode.exe --ogg true ./src.wav ./dst.ogg) | ||
(run ./decode.exe -ogg true -i ./dst.ogg -o ./ogg-dst.wav)))) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
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 buf in | ||
let finish () = | ||
Flac.Encoder.finish enc.Flac.Encoder.File.enc; | ||
Unix.close enc.Flac.Encoder.File.fd | ||
in | ||
(encode, finish)) | ||
else ( | ||
let oc = open_out !dst in | ||
let write_page (header, body) = | ||
output_string oc header; | ||
output_string oc body | ||
in | ||
let serialno = Random.nativeint Nativeint.max_int in | ||
let { Flac_ogg.Encoder.encoder; first_pages } = | ||
Flac_ogg.Encoder.create ~comments ~serialno ~write:write_page params | ||
in | ||
List.iter write_page first_pages; | ||
let encode = Flac.Encoder.process encoder in | ||
let finish () = Flac.Encoder.finish encoder 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 () |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
(executable | ||
(name opus2wav) | ||
(modules opus2wav) | ||
(libraries opus)) | ||
|
||
(executable | ||
(name wav2opus) | ||
(modules wav2opus) | ||
(libraries opus)) |
Oops, something went wrong.