pax_global_header00006660000000000000000000000064142615627600014523gustar00rootroot0000000000000052 comment=3e1a566ba0e761f79afa5bbc89e34fd5556edbe3 ocaml-opus-0.2.2/000077500000000000000000000000001426156276000136035ustar00rootroot00000000000000ocaml-opus-0.2.2/.github/000077500000000000000000000000001426156276000151435ustar00rootroot00000000000000ocaml-opus-0.2.2/.github/workflows/000077500000000000000000000000001426156276000172005ustar00rootroot00000000000000ocaml-opus-0.2.2/.github/workflows/doc.yml000066400000000000000000000010671426156276000204740ustar00rootroot00000000000000name: Build doc on: push: branches: - master jobs: build_doc: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v2 - name: Setup OCaml uses: avsm/setup-ocaml@v2 - name: Pin locally run: opam pin -y add -n . - name: Install locally run: opam install -y odoc opus - name: Build doc run: opam exec dune build @doc - name: Deploy doc uses: JamesIves/github-pages-deploy-action@4.1.6 with: branch: gh-pages folder: _build/default/_doc/_html ocaml-opus-0.2.2/.github/workflows/main.yml000066400000000000000000000007171426156276000206540ustar00rootroot00000000000000name: CI on: [push] jobs: cancel_previous_run: runs-on: ubuntu-latest steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.4.0 with: access_token: ${{ github.token }} build: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] steps: - name: Build and test module uses: savonet/build-and-test-ocaml-module@main ocaml-opus-0.2.2/.gitignore000066400000000000000000000000721426156276000155720ustar00rootroot00000000000000*~ _build *.byte *.native _tests .merlin *.install .*.sw* ocaml-opus-0.2.2/.ocamlformat000066400000000000000000000003371426156276000161130ustar00rootroot00000000000000version=0.19.0 profile = conventional break-separators = after space-around-lists = false doc-comments = before match-indent = 2 match-indent-nested = always parens-ite exp-grouping = preserve module-item-spacing = compact ocaml-opus-0.2.2/.travis-ci.sh000077500000000000000000000007661426156276000161320ustar00rootroot00000000000000# Hacking the build into Travis-CI "C" environment # See http://anil.recoil.org/2013/09/30/travis-and-ocaml.html OPAM_PACKAGES='ocamlfind base-bytes' export OPAMYES=1 opam init if [ -n "${OPAM_SWITCH}" ]; then opam switch ${OPAM_SWITCH} fi eval `opam config env` opam install -q -y ${OPAM_PACKAGES} # compile ocaml-ogg git clone https://github.com/savonet/ocaml-ogg.git cd ocaml-ogg && opam pin add . -y && cd .. # compile & run tests ./bootstrap && ./configure && make && make -C examples test ocaml-opus-0.2.2/CHANGES.md000066400000000000000000000007771426156276000152100ustar00rootroot000000000000000.2.2 (28-06-2022) ===== - Added bigarray API. - Fix memory leak in header packets. 0.2.1 (02-01-2022) ===== - Fix compilation on big-endian architectures (#5, #6). 0.2.0 (08-03-2021) ===== * Set minimum opus version to 1.3.0 * Switch to dune 0.1.3 (19-09-2019) ===== * Added Set_phase_inversion_disabled 0.1.2 (02-02-2017) ===== * Fix mono decoding. 0.1.1 (03-08-2015) ===== * Changed types to match new ocaml-ogg's API. Should be fully backward compatible. 0.1.0 (18-02-2013) ===== * Initial release. ocaml-opus-0.2.2/COPYING000066400000000000000000000431311426156276000146400ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ocaml-opus-0.2.2/README.md000066400000000000000000000010661426156276000150650ustar00rootroot00000000000000ocaml-opus ============ This package contains an OCaml interface for the `opus` library Please read the COPYING file before using this software. Prerequisites: ============== - ocaml - libopus - findlib - ocaml-ogg >= 0.7.0 - dune >= 2.0 Compilation: ============ ``` $ dune build ``` This should build both the native and the byte-code version of the extension library. Installation: ============= Via `opam`: ``` $ opam install opus ``` Via `dune` (for developers): ``` $ dune install ``` This should install the library file in the appropriate place. ocaml-opus-0.2.2/dune-project000066400000000000000000000007471426156276000161350ustar00rootroot00000000000000(lang dune 2.8) (version 0.2.2) (name opus) (source (github savonet/ocaml-opus)) (license GPL-2.0) (authors "The Savonet Team ") (maintainers "The Savonet Team ") (generate_opam_files true) (use_standard_c_and_cxx_flags false) (package (name opus) (synopsis "Bindings to libopus") (depends conf-libogg conf-libopus conf-pkg-config (ocaml (>= 4.08.0)) dune dune-configurator (ogg (>= 0.7.1))) ) ocaml-opus-0.2.2/examples/000077500000000000000000000000001426156276000154215ustar00rootroot00000000000000ocaml-opus-0.2.2/examples/dune000066400000000000000000000002111426156276000162710ustar00rootroot00000000000000(executable (name opus2wav) (modules opus2wav) (libraries opus)) (executable (name wav2opus) (modules wav2opus) (libraries opus)) ocaml-opus-0.2.2/examples/opus2wav.ml000066400000000000000000000112201426156276000175350ustar00rootroot00000000000000(* * Copyright 2003 Savonet team * * This file is part of OCaml-Opus. * * OCaml-Opus 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-Opus 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-Opus; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (** * An opus to wav converter using OCaml-Opus. * * @author Samuel Mimram *) let src = ref "" let dst = ref "" 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: opus2wav [options] source destination" let use_ba = ref false let () = Arg.parse [("-ba", Arg.Set use_ba, "Use big arrays")] (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 sync, fd = Ogg.Sync.create_from_file !src in Printf.printf "Checking file.\n%!"; let os, p1 = let page = Ogg.Sync.read sync in assert (Ogg.Page.bos page); 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.get_packet os in assert (Opus.Decoder.check_packet packet); Printf.printf "Found an opus stream!\n%!"; (os, packet) in let page = Ogg.Sync.read sync in Ogg.Stream.put_page os page; let p2 = Ogg.Stream.get_packet os in let samplerate = 48000 in Printf.printf "Creating decoder...\n%!"; let dec = Opus.Decoder.create ~samplerate p1 p2 in let chans = Opus.Decoder.channels dec in Printf.printf "Channels: %d\n%!" chans; let vendor, comments = Opus.Decoder.comments dec in Printf.printf "Vendor: %s\nComments:\n%!" vendor; List.iter (fun (l, v) -> Printf.printf "- %s = %s\n%!" l v) comments; Printf.printf "done.\n%!"; Printf.printf "Decoding...%!"; let max_frame_size = 960 * 6 in let buflen = max_frame_size in let outbuf = Array.make chans ([||] : float array) in let decode () = if !use_ba then ( let buf = Array.init chans (fun _ -> Bigarray.Array1.create Bigarray.float32 Bigarray.c_layout buflen) in let len = Opus.Decoder.decode_float_ba dec os buf 0 buflen in for c = 0 to chans - 1 do let pcm = Array.make len 0. in for i = 0 to len - 1 do pcm.(i) <- buf.(c).{i} done; outbuf.(c) <- pcm done) else ( let buf = Array.init chans (fun _ -> Array.make buflen 0.) in let len = Opus.Decoder.decode_float dec os buf 0 buflen in for c = 0 to chans - 1 do outbuf.(c) <- Array.append outbuf.(c) (Array.sub buf.(c) 0 len) done) in (try while true do try decode () with Ogg.Not_enough_data -> let page = Ogg.Sync.read sync in if Ogg.Page.serialno page = Ogg.Stream.serialno os then Ogg.Stream.put_page os page done with Ogg.End_of_stream -> ()); Printf.printf "done.\n%!"; Unix.close fd; let len = Array.length outbuf.(0) in let datalen = 2 * len 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 samplerate; (* freq *) output_int oc (samplerate * 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; for i = 0 to len - 1 do for c = 0 to chans - 1 do let x = outbuf.(c).(i) in let x = int_of_float (x *. 32767.) in output_short oc x done done; close_out oc; Gc.full_major () ocaml-opus-0.2.2/examples/wav2opus.ml000066400000000000000000000127531426156276000175510ustar00rootroot00000000000000(* * Copyright 2003 Savonet team * * This file is part of OCaml-opus. * * OCaml-opus 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-opus 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-opus; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (** * An wav to ogg converter using OCaml-opus. * * @author Samuel Mimram, and many others... *) open Opus let src = ref "" let dst = ref "" let buflen = ref 4096 let input_string chan len = let ans = Bytes.create len in (* TODO: check length *) ignore (input chan ans 0 len); Bytes.unsafe_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 usage = "usage: wav2ogg [options] source destination" let use_ba = ref false let _ = Arg.parse [ ( "--buflen", Arg.Int (fun i -> buflen := i), "Size of chunks successively encoded" ); ("-ba", Arg.Set use_ba, "Use big arrays"); ] (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 let oc = open_out_bin !dst 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 let fos buf = let len = String.length buf / (2 * channels) in let ans = Array.init channels (fun _ -> Array.make len 0.) in for i = 0 to len - 1 do for c = 0 to channels - 1 do let n = int_of_char buf.[(2 * channels * i) + (2 * c)] + (int_of_char buf.[(2 * channels * i) + (2 * c) + 1] lsl 8) in let n = if n land (1 lsl 15) = 0 then n else (n land 0b111111111111111) - 32768 in ans.(c).(i) <- float n /. 32768.; ans.(c).(i) <- max (-1.) (min 1. ans.(c).(i)) done done; ans in let os = Ogg.Stream.create () in let enc = Encoder.create ~samplerate:infreq ~channels ~application:`Audio os in Ogg.Stream.put_packet os (Opus.Encoder.header enc); let ph, pb = Ogg.Stream.flush_page os in output_string oc (ph ^ pb); Ogg.Stream.put_packet os (Opus.Encoder.comments enc); let ph, pb = Ogg.Stream.flush_page os in output_string oc (ph ^ pb); let rem = ref (Array.make channels [||]) in let encode buf = let encoded, buf = if !use_ba then ( let buf = fos buf in let buf = Array.mapi (fun c d -> Array.append d buf.(c)) !rem in let bbuf = Array.map (fun d -> Bigarray.Array1.of_array Bigarray.float32 Bigarray.c_layout d) buf in let encoded = Encoder.encode_float_ba enc bbuf 0 (Bigarray.Array1.dim bbuf.(0)) in (encoded, buf)) else ( let buf = fos buf in let buf = Array.mapi (fun c d -> Array.append d buf.(c)) !rem in let encoded = Encoder.encode_float enc buf 0 (Array.length buf.(0)) in (encoded, buf)) in rem := Array.map (fun d -> Array.sub d encoded (Array.length d - encoded)) buf 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: ogg/opus %d channels, %d Hz\nPlease wait...\n%!" channels infreq; (* skip headers *) let rec aux () = let tag = input_string ic 4 in match tag with | "LIST" -> let n = input_int ic in let _ = input_string ic n in aux () | "data" -> () | _ -> invalid_arg "No data tag" in aux (); let buflen = !buflen in let buf = Bytes.create buflen in begin try while true do try really_input ic buf 0 buflen; encode (Bytes.unsafe_to_string buf); while true do let ph, pb = Ogg.Stream.get_page os in output_string oc (ph ^ pb) done with Ogg.Not_enough_data -> () done with End_of_file -> () end; Encoder.eos enc; begin try while true do let ph, pb = Ogg.Stream.get_page os in output_string oc (ph ^ pb) done with Ogg.Not_enough_data -> () end; close_in ic; close_out oc; Printf.printf "Finished in %.0f seconds.\n" (Unix.time () -. start); Gc.full_major () ocaml-opus-0.2.2/opus.opam000066400000000000000000000014331426156276000154500ustar00rootroot00000000000000# This file is generated by dune, edit dune-project instead opam-version: "2.0" version: "0.2.2" synopsis: "Bindings to libopus" maintainer: ["The Savonet Team "] authors: ["The Savonet Team "] license: "GPL-2.0" homepage: "https://github.com/savonet/ocaml-opus" bug-reports: "https://github.com/savonet/ocaml-opus/issues" depends: [ "conf-libogg" "conf-libopus" "conf-pkg-config" "ocaml" {>= "4.08.0"} "dune" {>= "2.8"} "dune-configurator" "ogg" {>= "0.7.1"} "odoc" {with-doc} ] build: [ ["dune" "subst"] {dev} [ "dune" "build" "-p" name "-j" jobs "@install" "@runtest" {with-test} "@doc" {with-doc} ] ] dev-repo: "git+https://github.com/savonet/ocaml-opus.git" ocaml-opus-0.2.2/src/000077500000000000000000000000001426156276000143725ustar00rootroot00000000000000ocaml-opus-0.2.2/src/config/000077500000000000000000000000001426156276000156375ustar00rootroot00000000000000ocaml-opus-0.2.2/src/config/discover.ml000066400000000000000000000013301426156276000200040ustar00rootroot00000000000000module C = Configurator.V1 external is_big_endian : unit -> bool = "ocaml_mm_is_big_endian" let () = C.main ~name:"opus-pkg-config" (fun c -> C.C_define.gen_header_file c ~fname:"config.h" [("BIGENDIAN", Switch (is_big_endian ()))]; let default : C.Pkg_config.package_conf = { libs = ["-lopus"; "-logg"]; cflags = [] } in let conf = match C.Pkg_config.get c with | None -> default | Some pc -> ( match C.Pkg_config.query pc ~package:"opus ogg" with | None -> default | Some deps -> deps) in C.Flags.write_sexp "c_flags.sexp" conf.cflags; C.Flags.write_sexp "c_library_flags.sexp" conf.libs) ocaml-opus-0.2.2/src/config/dune000066400000000000000000000001611426156276000165130ustar00rootroot00000000000000(executable (name discover) (foreign_stubs (language c) (names endianess)) (libraries dune.configurator)) ocaml-opus-0.2.2/src/config/endianess.c000066400000000000000000000006161426156276000177570ustar00rootroot00000000000000#include #include enum { OCAML_MM_LITTLE_ENDIAN = 0x0100, OCAML_MM_BIG_ENDIAN = 0x0001, }; static const union { unsigned char bytes[2]; uint16_t value; } host_order = { { 0, 1 } }; CAMLprim value ocaml_mm_is_big_endian(value unit) { CAMLparam0(); if (host_order.value == OCAML_MM_BIG_ENDIAN) CAMLreturn(Val_bool(1)); CAMLreturn(Val_bool(0)); } ocaml-opus-0.2.2/src/dune000066400000000000000000000010661426156276000152530ustar00rootroot00000000000000(library (name opus) (public_name opus) (synopsis "OCaml bindings for libopus") (libraries bigarray ogg) (modules opus) (foreign_stubs (language c) (names opus_stubs) (extra_deps "config.h") (flags (:include c_flags.sexp))) (c_library_flags (:include c_library_flags.sexp))) (library (name opus_decoder) (public_name opus.decoder) (synopsis "Opus decoder for the ogg-decoder library") (libraries ogg.decoder opus) (modules opus_decoder)) (rule (targets config.h c_flags.sexp c_library_flags.sexp) (action (run ./config/discover.exe))) ocaml-opus-0.2.2/src/opus.ml000066400000000000000000000140631426156276000157160ustar00rootroot00000000000000exception Buffer_too_small exception Internal_error exception Invalid_packet exception Unimplemented exception Invalid_state exception Alloc_fail let () = Callback.register_exception "opus_exn_buffer_too_small" Buffer_too_small; Callback.register_exception "opus_exn_internal_error" Internal_error; Callback.register_exception "opus_exn_invalid_packet" Invalid_packet; Callback.register_exception "opus_exn_unimplemented" Unimplemented; Callback.register_exception "opus_exn_invalid_state" Invalid_state; Callback.register_exception "opus_exn_alloc_fail" Alloc_fail let recommended_frame_size = 960 * 6 external version_string : unit -> string = "ocaml_opus_version_string" let version_string = version_string () type max_bandwidth = [ `Narrow_band | `Medium_band | `Wide_band | `Super_wide_band | `Full_band ] type bandwidth = [ `Auto | max_bandwidth ] type generic_control = [ `Reset_state | `Get_final_range of int ref | `Get_pitch of int ref | `Get_bandwidth of bandwidth ref | `Set_lsb_depth of int | `Get_lsb_depth of int ref | `Set_phase_inversion_disabled of bool ] module Decoder = struct type control = [ generic_control | `Set_gain of int | `Get_gain of int ref ] external check_packet : Ogg.Stream.packet -> bool = "ocaml_opus_packet_check_header" external channels : Ogg.Stream.packet -> int = "ocaml_opus_decoder_channels" external comments : Ogg.Stream.packet -> string * string array = "ocaml_opus_comments" let comments p = let vendor, comments = comments p in let comments = Array.map (fun s -> let n = String.index s '=' in (String.sub s 0 n, String.sub s (n + 1) (String.length s - n - 1))) comments in let comments = Array.to_list comments in (vendor, comments) type decoder type t = { header : Ogg.Stream.packet; comments : Ogg.Stream.packet; decoder : decoder; } external create : samplerate:int -> channels:int -> decoder = "ocaml_opus_decoder_create" let create ?(samplerate = 48000) p1 p2 = if not (check_packet p1) then raise Invalid_packet; let decoder = create ~samplerate ~channels:(channels p1) in { header = p1; comments = p2; decoder } external apply_control : control -> decoder -> unit = "ocaml_opus_decoder_ctl" let apply_control control t = apply_control control t.decoder external decode_float : decoder -> Ogg.Stream.stream -> float array array -> int -> int -> bool -> int = "ocaml_opus_decoder_decode_float_byte" "ocaml_opus_decoder_decode_float" let decode_float ?(decode_fec = false) t os buf ofs len = decode_float t.decoder os buf ofs len decode_fec external decode_float_ba : decoder -> Ogg.Stream.stream -> (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array -> int -> int -> bool -> int = "ocaml_opus_decoder_decode_float_ba_byte" "ocaml_opus_decoder_decode_float_ba" let decode_float_ba ?(decode_fec = false) t os buf ofs len = decode_float_ba t.decoder os buf ofs len decode_fec let comments t = comments t.comments let channels t = channels t.header end module Encoder = struct type application = [ `Voip | `Audio | `Restricted_lowdelay ] type signal = [ `Auto | `Voice | `Music ] type bitrate = [ `Auto | `Bitrate_max | `Bitrate of int ] type control = [ generic_control | `Set_complexity of int | `Get_complexity of int ref | `Set_bitrate of bitrate | `Get_bitrate of bitrate ref | `Set_vbr of bool | `Get_vbr of bool ref | `Set_vbr_constraint of bool | `Get_vbr_constraint of bool ref | `Set_force_channels of bool | `Get_force_channels of bool ref | `Set_max_bandwidth of max_bandwidth | `Get_max_bandwidth of max_bandwidth | `Set_bandwidth of bandwidth | `Set_signal of signal | `Get_signal of signal ref | `Set_application of application | `Get_application of application | `Get_samplerate of int | `Get_lookhead of int | `Set_inband_fec of bool | `Get_inband_fec of bool ref | `Set_packet_loss_perc of int | `Get_packet_loss_perc of int ref | `Set_dtx of bool | `Get_dtx of bool ref ] type encoder type t = { header : Ogg.Stream.packet; comments : Ogg.Stream.packet; os : Ogg.Stream.stream; samplerate : int; enc : encoder; } external create : pre_skip:int -> comments:string array -> gain:int -> samplerate:int -> channels:int -> application:application -> encoder * Ogg.Stream.packet * Ogg.Stream.packet = "ocaml_opus_encoder_create_byte" "ocaml_opus_encoder_create" let create ?(pre_skip = 3840) ?(comments = []) ?(gain = 0) ~samplerate ~channels ~application os = let comments = List.map (fun (label, value) -> Printf.sprintf "%s=%s" label value) comments in let comments = Array.of_list comments in let enc, p1, p2 = create ~pre_skip ~comments ~gain ~samplerate ~channels ~application in { os; header = p1; comments = p2; samplerate; enc } let header enc = enc.header let comments enc = enc.comments external apply_control : control -> encoder -> unit = "ocaml_opus_encoder_ctl" let apply_control control enc = apply_control control enc.enc external encode_float : frame_size:int -> encoder -> Ogg.Stream.stream -> float array array -> int -> int -> int = "ocaml_opus_encode_float_byte" "ocaml_opus_encode_float" external encode_float_ba : frame_size:int -> encoder -> Ogg.Stream.stream -> (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array -> int -> int -> int = "ocaml_opus_encode_float_ba_byte" "ocaml_opus_encode_float_ba" let mk_encode_float fn ?(frame_size = 20.) t = let frame_size = frame_size *. float t.samplerate /. 1000. in fn ~frame_size:(int_of_float frame_size) t.enc t.os let encode_float = mk_encode_float encode_float let encode_float_ba = mk_encode_float encode_float_ba external eos : Ogg.Stream.stream -> encoder -> unit = "ocaml_opus_encode_eos" let eos t = eos t.os t.enc end ocaml-opus-0.2.2/src/opus.mli000066400000000000000000000060561426156276000160720ustar00rootroot00000000000000exception Buffer_too_small exception Internal_error exception Invalid_packet exception Unimplemented exception Invalid_state exception Alloc_fail (** Recommended size of a frame in sample. Buffers for decoding are typically of this size. *) val recommended_frame_size : int val version_string : string type max_bandwidth = [ `Narrow_band | `Medium_band | `Wide_band | `Super_wide_band | `Full_band ] type bandwidth = [ `Auto | max_bandwidth ] type generic_control = [ `Reset_state | `Get_final_range of int ref | `Get_pitch of int ref | `Get_bandwidth of bandwidth ref | `Set_lsb_depth of int | `Get_lsb_depth of int ref | `Set_phase_inversion_disabled of bool ] module Decoder : sig type control = [ generic_control | `Set_gain of int | `Get_gain of int ref ] type t val check_packet : Ogg.Stream.packet -> bool (** Create a decoder with given samplerate an number of channels. *) val create : ?samplerate:int -> Ogg.Stream.packet -> Ogg.Stream.packet -> t val comments : t -> string * (string * string) list val channels : t -> int val apply_control : control -> t -> unit val decode_float : ?decode_fec:bool -> t -> Ogg.Stream.stream -> float array array -> int -> int -> int val decode_float_ba : ?decode_fec:bool -> t -> Ogg.Stream.stream -> (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array -> int -> int -> int end module Encoder : sig type application = [ `Voip | `Audio | `Restricted_lowdelay ] type signal = [ `Auto | `Voice | `Music ] type bitrate = [ `Auto | `Bitrate_max | `Bitrate of int ] type control = [ generic_control | `Set_complexity of int | `Get_complexity of int ref | `Set_bitrate of bitrate | `Get_bitrate of bitrate ref | `Set_vbr of bool | `Get_vbr of bool ref | `Set_vbr_constraint of bool | `Get_vbr_constraint of bool ref | `Set_force_channels of bool | `Get_force_channels of bool ref | `Set_max_bandwidth of max_bandwidth | `Get_max_bandwidth of max_bandwidth | `Set_bandwidth of bandwidth | `Set_signal of signal | `Get_signal of signal ref | `Set_application of application | `Get_application of application | `Get_samplerate of int | `Get_lookhead of int | `Set_inband_fec of bool | `Get_inband_fec of bool ref | `Set_packet_loss_perc of int | `Get_packet_loss_perc of int ref | `Set_dtx of bool | `Get_dtx of bool ref ] type t val create : ?pre_skip:int -> ?comments:(string * string) list -> ?gain:int -> samplerate:int -> channels:int -> application:application -> Ogg.Stream.stream -> t val header : t -> Ogg.Stream.packet val comments : t -> Ogg.Stream.packet val apply_control : control -> t -> unit val encode_float : ?frame_size:float -> t -> float array array -> int -> int -> int val encode_float_ba : ?frame_size:float -> t -> (float, Bigarray.float32_elt, Bigarray.c_layout) Bigarray.Array1.t array -> int -> int -> int val eos : t -> unit end ocaml-opus-0.2.2/src/opus_decoder.ml000066400000000000000000000057251426156276000174100ustar00rootroot00000000000000(* * Copyright 2003-2011 Savonet team * * This file is part of ocaml-opus. * * ocaml-opus 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-opus 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-opus; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * *) let check = Opus.Decoder.check_packet let buflen = Opus.recommended_frame_size let decoder_samplerate = ref 48000 let decoder os = let decoder = ref None in let packet1 = ref None in let packet2 = ref None in let os = ref os in let init () = match !decoder with | None -> let packet1 = match !packet1 with | None -> let p = Ogg.Stream.get_packet !os in packet1 := Some p; p | Some p -> p in let packet2 = match !packet2 with | None -> let p = Ogg.Stream.get_packet !os in packet2 := Some p; p | Some p -> p in let dec = Opus.Decoder.create ~samplerate:!decoder_samplerate packet1 packet2 in let chans = Opus.Decoder.channels dec in let meta = Opus.Decoder.comments dec in decoder := Some (dec, chans, meta); (dec, chans, meta) | Some dec -> dec in let info () = let _, chans, meta = init () in ({ Ogg_decoder.channels = chans; sample_rate = !decoder_samplerate }, meta) in let restart new_os = os := new_os; decoder := None; ignore (init ()) in let decode ~decode_float ~make_float ~sub_float feed = let dec, chans, _ = init () in let chan _ = make_float buflen in let buf = Array.init chans chan in let ret = decode_float dec !os buf 0 buflen in feed (Array.map (fun x -> sub_float x 0 ret) buf) in let decoder ~decode_float ~make_float ~sub_float = { Ogg_decoder.name = "opus"; info; decode = decode ~decode_float ~make_float ~sub_float; restart; samples_of_granulepos = (fun x -> x); } in Ogg_decoder.Audio_both ( decoder ~decode_float:Opus.Decoder.decode_float ~make_float:(fun len -> Array.make len 0.) ~sub_float:Array.sub, decoder ~decode_float:Opus.Decoder.decode_float_ba ~make_float:(Bigarray.Array1.create Bigarray.float32 Bigarray.c_layout) ~sub_float:Bigarray.Array1.sub ) let register () = Hashtbl.add Ogg_decoder.ogg_decoders "opus" (check, decoder) ocaml-opus-0.2.2/src/opus_decoder.mli000066400000000000000000000017061426156276000175540ustar00rootroot00000000000000(* * Copyright 2003-2011 Savonet team * * This file is part of Ocaml-opus. * * Ocaml-opus 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-opus 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-opus; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *) (** This module provides a opus decoder for * the [Ogg_demuxer] module. *) val decoder_samplerate : int ref (** Register the opus decoder *) val register : unit -> unit ocaml-opus-0.2.2/src/opus_stubs.c000066400000000000000000000671771426156276000167660ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include #define caml_acquire_runtime_system caml_leave_blocking_section #define caml_release_runtime_system caml_enter_blocking_section #include #include #include #include #include #include #include #include "config.h" #ifndef Bytes_val #define Bytes_val String_val #endif #ifdef BIGENDIAN // code from bits/byteswap.h (C) 1997, 1998 Free Software Foundation, Inc. #define int32le_to_native(x) \ ((((x)&0xff000000) >> 24) | (((x)&0x00ff0000) >> 8) | \ (((x)&0x0000ff00) << 8) | (((x)&0x000000ff) << 24)) #define int16le_to_native(x) ((((x) >> 8) & 0xff) | (((x)&0xff) << 8)) #else #define int32le_to_native(x) x #define int16le_to_native(x) x #endif /* polymorphic variant utility macro */ #define get_var(x) caml_hash_variant(#x) /* Macros to convert variants to controls. */ #define set_ctl(tag, variant, handle, fn, name, v) \ if (tag == get_var(variant)) { \ check(fn(handle, name(Int_val(v)))); \ CAMLreturn(Val_unit); \ } #define set_value_ctl(tag, variant, handle, fn, name, v, convert) \ if (tag == get_var(variant)) { \ check(fn(handle, name(convert(v)))); \ CAMLreturn(Val_unit); \ } #define get_ctl(tag, variant, handle, fn, name, v, type) \ if (tag == get_var(variant)) { \ type name = (type)Int_val(Field(v, 0)); \ check(fn(handle, name(&name))); \ Store_field(v, 0, Val_int(name)); \ CAMLreturn(Val_unit); \ } #define get_value_ctl(tag, variant, handle, fn, name, v, type, convert) \ if (tag == get_var(variant)) { \ type name = (type)Int_val(Field(v, 0)); \ check(fn(handle, name(&name))); \ Store_field(v, 0, convert(name)); \ CAMLreturn(Val_unit); \ } static void check(int ret) { if (ret < 0) switch (ret) { case OPUS_BAD_ARG: caml_invalid_argument("opus"); case OPUS_BUFFER_TOO_SMALL: caml_raise_constant(*caml_named_value("opus_exn_buffer_too_small")); case OPUS_INTERNAL_ERROR: caml_raise_constant(*caml_named_value("opus_exn_internal_error")); case OPUS_INVALID_PACKET: caml_raise_constant(*caml_named_value("opus_exn_invalid_packet")); case OPUS_UNIMPLEMENTED: caml_raise_constant(*caml_named_value("opus_exn_unimplemented")); case OPUS_INVALID_STATE: caml_raise_constant(*caml_named_value("opus_exn_invalid_state")); case OPUS_ALLOC_FAIL: caml_raise_constant(*caml_named_value("opus_exn_alloc_fail")); default: caml_failwith("Unknown opus error"); } } CAMLprim value ocaml_opus_version_string(value unit) { CAMLparam0(); CAMLreturn(caml_copy_string(opus_get_version_string())); } /***** Decoder ******/ #define Dec_val(v) (*(OpusDecoder **)Data_custom_val(v)) static void finalize_dec(value v) { OpusDecoder *dec = Dec_val(v); opus_decoder_destroy(dec); } static struct custom_operations dec_ops = { "ocaml_opus_dec", finalize_dec, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default}; CAMLprim value ocaml_opus_decoder_create(value _sr, value _chans) { CAMLparam0(); CAMLlocal1(ans); opus_int32 sr = Int_val(_sr); int chans = Int_val(_chans); int ret = 0; OpusDecoder *dec; dec = opus_decoder_create(sr, chans, &ret); check(ret); ans = caml_alloc_custom(&dec_ops, sizeof(OpusDecoder *), 0, 1); Dec_val(ans) = dec; CAMLreturn(ans); } CAMLprim value ocaml_opus_packet_check_header(value packet) { CAMLparam1(packet); ogg_packet *op = Packet_val(packet); int ans = 0; if (op->bytes >= 8 && !memcmp(op->packet, "OpusHead", 8)) ans = 1; CAMLreturn(Val_bool(ans)); } CAMLprim value ocaml_opus_decoder_channels(value packet) { CAMLparam1(packet); ogg_packet *op = Packet_val(packet); uint8_t *data = op->packet; uint8_t version = *(data + 8); if (op->bytes <= 10 || memcmp(op->packet, "OpusHead", 8)) caml_invalid_argument("Wrong header data."); if (version != 1) caml_invalid_argument("Wrong header version."); uint8_t ret = *(data + 9); CAMLreturn(Val_int(ret)); } CAMLprim value ocaml_opus_comments(value packet) { CAMLparam1(packet); CAMLlocal2(ans, comments); ogg_packet *op = Packet_val(packet); if (!(op->bytes >= 8 && !memcmp(op->packet, "OpusTags", 8))) check(OPUS_INVALID_PACKET); ans = caml_alloc_tuple(2); int off = 8; /* Vendor */ if (off + 4 > op->bytes) check(OPUS_INVALID_PACKET); opus_int32 vendor_length = int32le_to_native((opus_int32) * (op->packet + off)); off += 4; if (off + vendor_length > op->bytes) check(OPUS_INVALID_PACKET); Store_field(ans, 0, caml_alloc_string(vendor_length)); memcpy(Bytes_val(Field(ans, 0)), op->packet + off, vendor_length); off += vendor_length; /* Comments */ if (off + 4 > op->bytes) check(OPUS_INVALID_PACKET); opus_int32 comments_length = int32le_to_native((opus_int32) * (op->packet + off)); off += 4; comments = caml_alloc_tuple(comments_length); Store_field(ans, 1, comments); opus_int32 i, len; for (i = 0; i < comments_length; i++) { if (off + 4 > op->bytes) check(OPUS_INVALID_PACKET); len = int32le_to_native((opus_int32) * (op->packet + off)); off += 4; if (off + len > op->bytes) check(OPUS_INVALID_PACKET); Store_field(comments, i, caml_alloc_string(len)); memcpy(Bytes_val(Field(comments, i)), op->packet + off, len); off += len; } CAMLreturn(ans); } static opus_int32 bandwidth_of_value(value v) { if (v == get_var(Auto)) return OPUS_AUTO; if (v == get_var(Narrow_band)) return OPUS_BANDWIDTH_NARROWBAND; if (v == get_var(Medium_band)) return OPUS_BANDWIDTH_MEDIUMBAND; if (v == get_var(Wide_band)) return OPUS_BANDWIDTH_WIDEBAND; if (v == get_var(Super_wide_band)) return OPUS_BANDWIDTH_SUPERWIDEBAND; if (v == get_var(Full_band)) return OPUS_BANDWIDTH_FULLBAND; caml_failwith("Unknown opus error"); } static value value_of_bandwidth(opus_int32 a) { switch (a) { case OPUS_AUTO: return get_var(Auto); case OPUS_BANDWIDTH_NARROWBAND: return get_var(Narrow_band); case OPUS_BANDWIDTH_MEDIUMBAND: return get_var(Medium_band); case OPUS_BANDWIDTH_WIDEBAND: return get_var(Wide_band); case OPUS_BANDWIDTH_SUPERWIDEBAND: return get_var(Super_wide_band); case OPUS_BANDWIDTH_FULLBAND: return get_var(Full_band); default: caml_failwith("Unknown opus error"); } } CAMLprim value ocaml_opus_decoder_ctl(value ctl, value _dec) { CAMLparam2(_dec, ctl); CAMLlocal2(tag, v); OpusDecoder *dec = Dec_val(_dec); if (Is_long(ctl)) { // Only ctl without argument here is reset state.. opus_decoder_ctl(dec, OPUS_RESET_STATE); CAMLreturn(Val_unit); } else { v = Field(ctl, 1); tag = Field(ctl, 0); /* Generic controls. */ get_ctl(tag, Get_final_range, dec, opus_decoder_ctl, OPUS_GET_FINAL_RANGE, v, opus_uint32); get_ctl(tag, Get_pitch, dec, opus_decoder_ctl, OPUS_GET_PITCH, v, opus_int32); get_value_ctl(tag, Get_bandwidth, dec, opus_decoder_ctl, OPUS_GET_BANDWIDTH, v, opus_int32, value_of_bandwidth); set_ctl(tag, Set_lsb_depth, dec, opus_decoder_ctl, OPUS_SET_LSB_DEPTH, v); get_ctl(tag, Get_lsb_depth, dec, opus_decoder_ctl, OPUS_GET_LSB_DEPTH, v, opus_int32); #ifdef OPUS_SET_PHASE_INVERSION_DISABLED set_ctl(tag, Set_phase_inversion_disabled, dec, opus_decoder_ctl, OPUS_SET_PHASE_INVERSION_DISABLED, v); #endif /* Decoder controls. */ get_ctl(tag, Get_gain, dec, opus_decoder_ctl, OPUS_GET_GAIN, v, opus_int32); set_ctl(tag, Set_gain, dec, opus_decoder_ctl, OPUS_SET_GAIN, v); } caml_failwith("Unknown opus error"); } CAMLprim value ocaml_opus_decoder_decode_float(value _dec, value _os, value buf, value _ofs, value _len, value _fec) { CAMLparam3(_dec, _os, buf); CAMLlocal1(chan); ogg_stream_state *os = Stream_state_val(_os); ogg_packet op; OpusDecoder *dec = Dec_val(_dec); int decode_fec = Int_val(_fec); int ofs = Int_val(_ofs); int len = Int_val(_len); int total_samples = 0; int ret; int chans = Wosize_val(buf); float *pcm = malloc(chans * len * sizeof(float)); if (pcm == NULL) caml_raise_out_of_memory(); int i, c; while (total_samples < len) { ret = ogg_stream_packetout(os, &op); /* returned values are: * 1: ok * 0: not enough data. in this case * we return the number of samples * decoded if > 0 and raise * Ogg_not_enough_data otherwise * -1: out of sync */ if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); if (ret == 0) { free(pcm); if (total_samples > 0) { CAMLreturn(Val_int(total_samples)); } else { caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); } } if (chans != opus_packet_get_nb_channels(op.packet)) caml_invalid_argument("Wrong number of channels."); caml_release_runtime_system(); ret = opus_decode_float(dec, op.packet, op.bytes, pcm, len, decode_fec); caml_acquire_runtime_system(); if (ret < 0) { free(pcm); check(ret); } for (c = 0; c < chans; c++) { chan = Field(buf, c); for (i = 0; i < ret; i++) Store_double_field(chan, ofs + total_samples + i, pcm[i * chans + c]); } total_samples += ret; len -= ret; } free(pcm); CAMLreturn(Val_int(total_samples)); } CAMLprim value ocaml_opus_decoder_decode_float_byte(value *argv, int argn) { return ocaml_opus_decoder_decode_float(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } CAMLprim value ocaml_opus_decoder_decode_float_ba(value _dec, value _os, value buf, value _ofs, value _len, value _fec) { CAMLparam3(_dec, _os, buf); CAMLlocal1(chan); ogg_stream_state *os = Stream_state_val(_os); ogg_packet op; OpusDecoder *dec = Dec_val(_dec); int decode_fec = Int_val(_fec); int ofs = Int_val(_ofs); int len = Int_val(_len); int total_samples = 0; int ret; int chans = Wosize_val(buf); float *pcm = malloc(chans * len * sizeof(float)); if (pcm == NULL) caml_raise_out_of_memory(); int i, c; while (total_samples < len) { ret = ogg_stream_packetout(os, &op); /* returned values are: * 1: ok * 0: not enough data. in this case * we return the number of samples * decoded if > 0 and raise * Ogg_not_enough_data otherwise * -1: out of sync */ if (ret == -1) caml_raise_constant(*caml_named_value("ogg_exn_out_of_sync")); if (ret == 0) { free(pcm); if (total_samples > 0) { CAMLreturn(Val_int(total_samples)); } else { caml_raise_constant(*caml_named_value("ogg_exn_not_enough_data")); } } if (chans != opus_packet_get_nb_channels(op.packet)) caml_invalid_argument("Wrong number of channels."); caml_release_runtime_system(); ret = opus_decode_float(dec, op.packet, op.bytes, pcm, len, decode_fec); caml_acquire_runtime_system(); if (ret < 0) { free(pcm); check(ret); } for (c = 0; c < chans; c++) { chan = Field(buf, c); for (i = 0; i < ret; i++) ((float *)Caml_ba_data_val(chan))[ofs + total_samples + i] = pcm[i * chans + c]; } total_samples += ret; len -= ret; } free(pcm); CAMLreturn(Val_int(total_samples)); } CAMLprim value ocaml_opus_decoder_decode_float_ba_byte(value *argv, int argn) { return ocaml_opus_decoder_decode_float_ba(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } /***** Encoder *****/ typedef struct encoder_t { OpusEncoder *encoder; int samplerate_ratio; ogg_int64_t granulepos; ogg_int64_t packetno; } encoder_t; #define Enc_val(v) (*(encoder_t **)Data_custom_val(v)) static void finalize_enc(value v) { encoder_t *enc = Enc_val(v); opus_encoder_destroy(enc->encoder); free(enc); } static struct custom_operations enc_ops = { "ocaml_opus_enc", finalize_enc, custom_compare_default, custom_hash_default, custom_serialize_default, custom_deserialize_default}; static opus_int32 application_of_value(value v) { if (v == get_var(Voip)) return OPUS_APPLICATION_VOIP; if (v == get_var(Audio)) return OPUS_APPLICATION_AUDIO; if (v == get_var(Restricted_lowdelay)) return OPUS_APPLICATION_RESTRICTED_LOWDELAY; caml_failwith("Unknown opus error"); } static value value_of_application(opus_int32 a) { switch (a) { case OPUS_APPLICATION_VOIP: return get_var(Voip); case OPUS_APPLICATION_AUDIO: return get_var(Audio); case OPUS_APPLICATION_RESTRICTED_LOWDELAY: return get_var(Restricted_lowdelay); default: caml_failwith("Unknown opus error"); } } static unsigned char header_packet[19] = { /* Identifier. */ 'O', 'p', 'u', 's', 'H', 'e', 'a', 'd', /* version, channels count, pre-skip (16 bits, unsigned, * little endian) */ 1, 2, 0, 0, /* Samperate (32 bits, unsigned, little endian) */ 0, 0, 0, 0, /* output gain (16 bits, signed, little endian), channels mapping familly, * stream count (always 0 in this implementation) */ 0, 0, 0}; static void pack_header(ogg_packet *op, opus_int32 sr, int channels, opus_int16 pre_skip, opus_int16 gain) { op->bytes = sizeof(header_packet); op->packet = header_packet; /* Now fill data. */ op->packet[9] = channels; opus_int16 pre_skip_native = int16le_to_native(pre_skip); memcpy(op->packet + 10, &pre_skip_native, sizeof(opus_int16)); opus_int32 sr_native = int32le_to_native(sr); memcpy(op->packet + 12, &sr_native, sizeof(opus_int32)); opus_int16 gain_native = int16le_to_native(gain); memcpy(op->packet + 16, &gain_native, sizeof(opus_int16)); op->b_o_s = 1; op->e_o_s = op->granulepos = op->packetno = 0; } static void pack_comments(ogg_packet *op, char *vendor, value comments) { int i; long pos = 0; opus_int32 vendor_length = strlen(vendor); char *comment; opus_int32 comments_len = Wosize_val(comments); opus_int32 comment_length; op->bytes = 8 + 4 + vendor_length + 4; for (i = 0; i < Wosize_val(comments); i++) op->bytes += 4 + caml_string_length(Field(comments, i)); op->packet = malloc(op->bytes); if (op->packet == NULL) caml_raise_out_of_memory(); /* Identifier. */ memcpy(op->packet, "OpusTags", 8); pos += 8; /* Vendor. */ opus_int32 vendor_length_native = int32le_to_native(vendor_length); memcpy(op->packet + 8, &vendor_length_native, sizeof(opus_int32)); memcpy(op->packet + 12, vendor, vendor_length); pos += 4 + vendor_length; /* Comments length. */ memcpy(op->packet + pos, &comments_len, sizeof(opus_int32)); pos += 4; /* Comments. */ for (i = 0; i < comments_len; i++) { comment = (char *)Bytes_val(Field(comments, i)); comment_length = caml_string_length(Field(comments, i)); opus_int32 comment_length_native = int32le_to_native(comment_length); memcpy(op->packet + pos, &comment_length_native, sizeof(opus_int32)); memcpy(op->packet + pos + 4, comment, comment_length); pos += 4 + comment_length; } op->e_o_s = op->b_o_s = op->granulepos = 0; op->packetno = 1; } CAMLprim value ocaml_opus_encoder_create(value _skip, value _comments, value _gain, value _sr, value _chans, value _application) { CAMLparam0(); CAMLlocal2(_enc, ans); opus_int32 sr = Int_val(_sr); int chans = Int_val(_chans); int ret = 0; int app = application_of_value(_application); encoder_t *enc = malloc(sizeof(encoder_t)); if (enc == NULL) caml_raise_out_of_memory(); /* First encoded packet is the third one. */ enc->packetno = 1; enc->granulepos = 0; /* Value samplerates are: 48000, 24000, 16000, 12000, 8000 * so this value is always an integer. */ enc->samplerate_ratio = 48000 / sr; ogg_packet header; pack_header(&header, sr, chans, Int_val(_skip), Int_val(_gain)); ogg_packet comments; pack_comments(&comments, "ocaml-opus by the Savonet Team.", _comments); enc->encoder = opus_encoder_create(sr, chans, app, &ret); check(ret); _enc = caml_alloc_custom(&enc_ops, sizeof(encoder_t *), 0, 1); Enc_val(_enc) = enc; ans = caml_alloc_tuple(3); Store_field(ans, 0, _enc); Store_field(ans, 1, value_of_packet(&header)); Store_field(ans, 2, value_of_packet(&comments)); free(comments.packet); CAMLreturn(ans); } CAMLprim value ocaml_opus_encoder_create_byte(value *argv, int argn) { return ocaml_opus_encoder_create(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } static opus_int32 bitrate_of_value(value v) { if (Is_long(v)) { if (v == get_var(Auto)) return OPUS_AUTO; if (v == get_var(Bitrate_max)) return OPUS_BITRATE_MAX; } else { if (Field(v, 0) == get_var(Bitrate)) return Int_val(Field(v, 1)); } caml_failwith("Unknown opus error"); } CAMLprim value value_of_bitrate(opus_int32 a) { CAMLparam0(); CAMLlocal1(ret); switch (a) { case OPUS_AUTO: CAMLreturn(get_var(Auto)); case OPUS_BITRATE_MAX: CAMLreturn(get_var(Voice)); default: ret = caml_alloc_tuple(2); Store_field(ret, 0, get_var(Bitrate)); Store_field(ret, 1, Val_int(a)); CAMLreturn(ret); } } static opus_int32 signal_of_value(value v) { if (v == get_var(Auto)) return OPUS_AUTO; if (v == get_var(Voice)) return OPUS_SIGNAL_VOICE; if (v == get_var(Music)) return OPUS_SIGNAL_MUSIC; caml_failwith("Unknown opus error"); } static value value_of_signal(opus_int32 a) { switch (a) { case OPUS_AUTO: return get_var(Auto); case OPUS_SIGNAL_VOICE: return get_var(Voice); case OPUS_SIGNAL_MUSIC: return get_var(Music); default: caml_failwith("Unknown opus error"); } } CAMLprim value ocaml_opus_encoder_ctl(value ctl, value _enc) { CAMLparam2(_enc, ctl); CAMLlocal2(tag, v); encoder_t *handler = Enc_val(_enc); OpusEncoder *enc = handler->encoder; if (Is_long(ctl)) { // Only ctl without argument here is reset state.. opus_encoder_ctl(enc, OPUS_RESET_STATE); CAMLreturn(Val_unit); } else { v = Field(ctl, 1); tag = Field(ctl, 0); /* Generic controls. */ get_ctl(tag, Get_final_range, enc, opus_encoder_ctl, OPUS_GET_FINAL_RANGE, v, opus_uint32); get_ctl(tag, Get_pitch, enc, opus_encoder_ctl, OPUS_GET_PITCH, v, opus_int32); get_value_ctl(tag, Get_bandwidth, enc, opus_encoder_ctl, OPUS_GET_BANDWIDTH, v, opus_int32, value_of_bandwidth); set_ctl(tag, Set_lsb_depth, enc, opus_encoder_ctl, OPUS_SET_LSB_DEPTH, v); get_ctl(tag, Get_lsb_depth, enc, opus_encoder_ctl, OPUS_GET_LSB_DEPTH, v, opus_int32); #ifdef OPUS_SET_PHASE_INVERSION_DISABLED set_ctl(tag, Set_phase_inversion_disabled, enc, opus_encoder_ctl, OPUS_SET_PHASE_INVERSION_DISABLED, v); #endif /* Encoder controls. */ set_ctl(tag, Set_complexity, enc, opus_encoder_ctl, OPUS_SET_COMPLEXITY, v); get_ctl(tag, Get_complexity, enc, opus_encoder_ctl, OPUS_GET_COMPLEXITY, v, opus_int32); set_ctl(tag, Set_vbr, enc, opus_encoder_ctl, OPUS_SET_VBR, v); get_ctl(tag, Get_vbr, enc, opus_encoder_ctl, OPUS_GET_VBR, v, opus_int32); set_ctl(tag, Set_vbr_constraint, enc, opus_encoder_ctl, OPUS_SET_VBR_CONSTRAINT, v); get_ctl(tag, Get_vbr_constraint, enc, opus_encoder_ctl, OPUS_GET_VBR_CONSTRAINT, v, opus_int32); set_ctl(tag, Set_force_channels, enc, opus_encoder_ctl, OPUS_SET_FORCE_CHANNELS, v); get_ctl(tag, Get_force_channels, enc, opus_encoder_ctl, OPUS_GET_FORCE_CHANNELS, v, opus_int32); get_ctl(tag, Get_samplerate, enc, opus_encoder_ctl, OPUS_GET_SAMPLE_RATE, v, opus_int32); get_ctl(tag, Get_lookhead, enc, opus_encoder_ctl, OPUS_GET_LOOKAHEAD, v, opus_int32); set_ctl(tag, Set_inband_fec, enc, opus_encoder_ctl, OPUS_SET_INBAND_FEC, v); get_ctl(tag, Get_inband_fec, enc, opus_encoder_ctl, OPUS_GET_INBAND_FEC, v, opus_int32); set_ctl(tag, Set_packet_loss_perc, enc, opus_encoder_ctl, OPUS_SET_PACKET_LOSS_PERC, v); get_ctl(tag, Get_packet_loss_perc, enc, opus_encoder_ctl, OPUS_GET_PACKET_LOSS_PERC, v, opus_int32); set_ctl(tag, Set_dtx, enc, opus_encoder_ctl, OPUS_SET_DTX, v); get_ctl(tag, Get_dtx, enc, opus_encoder_ctl, OPUS_GET_DTX, v, opus_int32); /* These guys have polynmorphic variant as argument.. */ set_value_ctl(tag, Set_bitrate, enc, opus_encoder_ctl, OPUS_SET_BITRATE, v, bitrate_of_value); get_value_ctl(tag, Get_bitrate, enc, opus_encoder_ctl, OPUS_GET_BITRATE, v, opus_int32, value_of_bitrate); set_value_ctl(tag, Set_max_bandwidth, enc, opus_encoder_ctl, OPUS_SET_MAX_BANDWIDTH, v, bandwidth_of_value); get_value_ctl(tag, Get_max_bandwidth, enc, opus_encoder_ctl, OPUS_GET_MAX_BANDWIDTH, v, opus_int32, value_of_bandwidth); set_value_ctl(tag, Set_bandwidth, enc, opus_encoder_ctl, OPUS_SET_BANDWIDTH, v, bandwidth_of_value); set_value_ctl(tag, Set_signal, enc, opus_encoder_ctl, OPUS_SET_SIGNAL, v, signal_of_value); get_value_ctl(tag, Get_signal, enc, opus_encoder_ctl, OPUS_GET_SIGNAL, v, opus_int32, value_of_signal); set_value_ctl(tag, Set_application, enc, opus_encoder_ctl, OPUS_SET_APPLICATION, v, application_of_value); get_value_ctl(tag, Get_application, enc, opus_encoder_ctl, OPUS_GET_APPLICATION, v, opus_int32, value_of_application); } caml_failwith("Unknown opus error"); } CAMLprim value ocaml_opus_encode_float(value _frame_size, value _enc, value _os, value buf, value _off, value _len) { CAMLparam3(_enc, buf, _os); encoder_t *handler = Enc_val(_enc); OpusEncoder *enc = handler->encoder; ogg_stream_state *os = Stream_state_val(_os); ogg_packet op; int off = Int_val(_off); int len = Int_val(_len); int frame_size = Int_val(_frame_size); if (len < frame_size) caml_raise_constant(*caml_named_value("opus_exn_buffer_too_small")); int chans = Wosize_val(buf); /* This is the recommended value */ int max_data_bytes = 4000; unsigned char *data = malloc(max_data_bytes); if (data == NULL) caml_raise_out_of_memory(); float *pcm = malloc(chans * frame_size * sizeof(float)); if (pcm == NULL) caml_raise_out_of_memory(); int i, j, c; int ret; int loops = len / frame_size; for (i = 0; i < loops; i++) { for (j = 0; j < frame_size; j++) for (c = 0; c < chans; c++) pcm[chans * j + c] = Double_field(Field(buf, c), off + j + i * frame_size); caml_release_runtime_system(); ret = opus_encode_float(enc, pcm, frame_size, data, max_data_bytes); caml_acquire_runtime_system(); if (ret < 0) { free(pcm); free(data); check(ret); } /* From the documentation: If the return value is 1 byte, * then the packet does not need to be transmitted (DTX). */ if (ret < 2) continue; handler->granulepos += frame_size * handler->samplerate_ratio; handler->packetno++; op.bytes = ret; op.packet = data; op.b_o_s = op.e_o_s = 0; op.packetno = handler->packetno; op.granulepos = handler->granulepos; if (ogg_stream_packetin(os, &op) != 0) { free(pcm); free(data); caml_raise_constant(*caml_named_value("ogg_exn_internal_error")); } } free(pcm); free(data); CAMLreturn(Val_int(loops * frame_size)); } CAMLprim value ocaml_opus_encode_float_byte(value *argv, int argn) { return ocaml_opus_encode_float(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } CAMLprim value ocaml_opus_encode_float_ba(value _frame_size, value _enc, value _os, value buf, value _ofs, value _len) { CAMLparam3(_enc, buf, _os); encoder_t *handler = Enc_val(_enc); OpusEncoder *enc = handler->encoder; ogg_stream_state *os = Stream_state_val(_os); ogg_packet op; int len = Int_val(_len); int ofs = Int_val(_ofs); int chans = Wosize_val(buf); int frame_size = Int_val(_frame_size); if (chans == 0) CAMLreturn(Val_int(0)); if (Caml_ba_array_val(Field(buf, 0))->dim[0] < ofs + len) caml_failwith("Invalid length or offset!"); if (len < frame_size) caml_raise_constant(*caml_named_value("opus_exn_buffer_too_small")); /* This is the recommended value */ int max_data_bytes = 4000; unsigned char *data = malloc(max_data_bytes); if (data == NULL) caml_raise_out_of_memory(); float *pcm = malloc(chans * frame_size * sizeof(float)); if (pcm == NULL) caml_raise_out_of_memory(); int i, j, c; int ret; int loops = len / frame_size; for (i = 0; i < loops; i++) { for (j = 0; j < frame_size; j++) for (c = 0; c < chans; c++) pcm[chans * j + c] = ((float *)Caml_ba_data_val( Field(buf, c)))[j + i * frame_size + ofs]; caml_release_runtime_system(); ret = opus_encode_float(enc, pcm, frame_size, data, max_data_bytes); caml_acquire_runtime_system(); if (ret < 0) { free(pcm); free(data); check(ret); } /* From the documentation: If the return value is 1 byte, * then the packet does not need to be transmitted (DTX). */ if (ret < 2) continue; handler->granulepos += frame_size * handler->samplerate_ratio; handler->packetno++; op.bytes = ret; op.packet = data; op.b_o_s = op.e_o_s = 0; op.packetno = handler->packetno; op.granulepos = handler->granulepos; if (ogg_stream_packetin(os, &op) != 0) { free(pcm); free(data); caml_raise_constant(*caml_named_value("ogg_exn_internal_error")); } } free(pcm); free(data); CAMLreturn(Val_int(loops * frame_size)); } CAMLprim value ocaml_opus_encode_float_ba_byte(value *argv, int argn) { return ocaml_opus_encode_float_ba(argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]); } CAMLprim value ocaml_opus_encode_eos(value _os, value _enc) { CAMLparam2(_os, _enc); ogg_stream_state *os = Stream_state_val(_os); ogg_packet op; encoder_t *handler = Enc_val(_enc); handler->packetno++; op.bytes = 0; op.packet = NULL; op.b_o_s = 0; op.e_o_s = 1; op.packetno = handler->packetno; op.granulepos = handler->granulepos; if (ogg_stream_packetin(os, &op) != 0) caml_raise_constant(*caml_named_value("ogg_exn_internal_error")); ; CAMLreturn(Val_unit); } ocaml-opus-0.2.2/test/000077500000000000000000000000001426156276000145625ustar00rootroot00000000000000ocaml-opus-0.2.2/test/dune000066400000000000000000000006511426156276000154420ustar00rootroot00000000000000(executable (name gen_wav) (modules gen_wav)) (rule (alias runtest) (package opus) (deps (:gen_wav ./gen_wav.exe) (:opus2wav ../examples/opus2wav.exe) (:wav2opus ../examples/wav2opus.exe)) (action (progn (run %{gen_wav}) (run %{wav2opus} gen.wav output.ogg) (run %{wav2opus} -ba gen.wav output-ba.ogg) (run %{opus2wav} output.ogg output.wav) (run %{opus2wav} -ba output-ba.ogg output-ba.wav)))) ocaml-opus-0.2.2/test/gen_wav.ml000066400000000000000000000021621426156276000165430ustar00rootroot00000000000000let 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 () = let dst = "gen.wav" in let chans = 2 in let samples = 4096 * 12 in let datalen = samples * chans * 2 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 48000; (* freq *) output_int oc (48000 * 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; for i = 0 to samples - 1 do for c = 0 to chans - 1 do output_short oc (i * c * 32767) done done; close_out oc