...
 
Commits (5)
variables:
BUILD_SCRIPT: >
sudo apt-get update -qq &&
opam config exec -- opam remote add --priority 100 mothership https://github.com/ocaml/opam-repository.git &&
sudo apt-get install -y pkg-config libjack-jackd2-dev &&
opam config exec -- opam remote add mothership https://github.com/ocaml/opam-repository.git &&
opam config exec -- opam update --yes &&
opam config exec -- opam upgrade --yes &&
opam config exec -- opam pin --yes -n add misuja https://gitlab.com/smondet/misuja.git &&
opam config exec -- opam depext --yes misuja &&
opam config exec -- opam install --yes depext &&
opam config exec -- opam pin --yes -n add misuja . &&
opam config exec -- opam depext --install --yes misuja &&
opam config exec -- opam install --yes misuja &&
opam config exec -- make lib &&
opam config exec -- make tests
opam config exec -- dune build @install src/test/relay.exe
BUILD_DOC: >
sudo apt-get install -y pandoc &&
opam config exec -- opam install --yes odoc &&
opam config exec -- make doc &&
opam config exec -- dune build @doc &&
mkdir -p public &&
cp -r _build/doc/html/* public/
cp -r _build/default/_doc/_html/* public/
ocaml:4030:
image: ocaml/opam:ubuntu-16.04_ocaml-4.03.0
image: ocaml/opam2:4.03
script:
- bash -c "$BUILD_SCRIPT"
ocaml:4042:
image: ocaml/opam:ubuntu-16.04_ocaml-4.04.2
image: ocaml/opam2:4.04
script:
- bash -c "$BUILD_SCRIPT"
ocaml:4050:
image: ocaml/opam:ubuntu-16.04_ocaml-4.05.0
image: ocaml/opam2:4.05
script:
- bash -c "$BUILD_SCRIPT"
ocaml:4060:
image: ocaml/opam2:4.06
script:
- bash -c "$BUILD_SCRIPT"
ocaml:4070:
image: ocaml/opam2:4.07
script:
- bash -c "$BUILD_SCRIPT"
pages:
image: ocaml/opam:ubuntu-16.04_ocaml-4.05.0
image: ocaml/opam2:4.07
script:
- bash -c "$BUILD_SCRIPT"
- bash -c "$BUILD_DOC"
......
BUILD_LIB=_build/lib
$(BUILD_LIB):
mkdir -p _build/lib
GCC_FLAGS=-pthread -pedantic -Wall -Wextra -Wpointer-arith -Wbad-function-cast
C_MODULE=misuja_jack_seq
$(BUILD_LIB)/$(C_MODULE).o: src/lib/$(C_MODULE).c _build/lib
gcc -I `ocamlc -where` $(GCC_FLAGS) -pthread -ljack -fPIC -c src/lib/$(C_MODULE).c -o [email protected]
OCAML_MODULE=misuja
$(BUILD_LIB)/$(OCAML_MODULE).cmo: src/lib/$(OCAML_MODULE).ml $(BUILD_LIB)
ocamlfind ocamlc -c -bin-annot -thread -package unix,threads $< -o [email protected]
$(BUILD_LIB)/$(OCAML_MODULE).cmx: src/lib/$(OCAML_MODULE).ml $(BUILD_LIB)
ocamlfind ocamlopt -c -thread -package unix,threads $< -o [email protected]
BINARY_STUFF=$(BUILD_LIB)/$(C_MODULE).o $(BUILD_LIB)/$(OCAML_MODULE).cmx $(BUILD_LIB)/$(OCAML_MODULE).cmo
$(BUILD_LIB)/misuja.cma: $(BINARY_STUFF)
ocamlmklib -ljack $(BINARY_STUFF) -o $(BUILD_LIB)/misuja
$(BUILD_LIB)/misuja.cmxs: $(BUILD_LIB)/misuja.cma $(BINARY_STUFF)
ocamlopt $(BUILD_LIB)/misuja.cmxa -I $(BUILD_LIB) -shared -o [email protected]
_build/test:
mkdir -p [email protected]
BUILD_RELAY_OPTIONS=-package unix,threads -linkpkg -thread -I ../../$(BUILD_LIB)
_build/test/relay.byte: $(BUILD_LIB)/misuja.cma _build/test src/test/relay.ml
cp src/test/relay.ml _build/test/ && \
cd _build/test/ && \
ocamlfind ocamlc $(BUILD_RELAY_OPTIONS) misuja.cma relay.ml -o relay.byte
_build/test/relay.native: $(BUILD_LIB)/misuja.cma _build/test src/test/relay.ml
cp src/test/relay.ml _build/test/ && \
cd _build/test/ && \
ocamlfind ocamlopt $(BUILD_RELAY_OPTIONS) misuja.cmxa relay.ml -o relay.native
.PHONY: tests lib
lib: $(BUILD_LIB)/misuja.cma $(BUILD_LIB)/misuja.cmxs
tests: lib _build/test/relay.byte _build/test/relay.native
$(BUILD_LIB)/META:
printf 'version = ' > [email protected] && \
grep ^version: misuja.opam | sed 's/version: //' >> [email protected] && \
echo 'description = "Midi Sequencer Library Using Jack"' >> [email protected] && \
echo 'requires = "threads unix bytes"' >> [email protected] && \
echo 'archive(byte) = "misuja.cma"' >> [email protected] && \
echo 'archive(byte, plugin) = "misuja.cma"' >> [email protected] && \
echo 'archive(native) = "misuja.cmxa"' >> [email protected] && \
echo 'archive(native, plugin) = "misuja.cmxs"' >> [email protected] && \
cat [email protected]
.PHONY: install uninstall
install: lib $(BUILD_LIB)/META
ocamlfind install misuja $(BUILD_LIB)/*
uninstall:
ocamlfind remove misuja
.merlin:
echo 'S src/lib/' > [email protected] && \
echo 'S src/test/' >> [email protected] && \
echo 'PKG nonstd unix threads' >> [email protected] && \
echo 'B _build/lib/' >> [email protected] && \
echo 'B +threads' >> [email protected]
.PHONY: clean
clean:
rm -fr _build $(BUILD_LIB)
.PHONY: doc
doc:
odoc compile --package Misuja _build/lib/misuja.cmt -o _build/doc/misuja.odoc && \
mkdir -p _build/doc/html/ && \
odoc html _build/doc/misuja.odoc -o _build/doc/html/ && \
odoc css -o _build/doc/html/ && \
pandoc -i README.md -c odoc.css -o _build/doc/html/index.html && \
echo Done
......@@ -18,35 +18,35 @@ Build/Install
To build/install the library:
make lib
make install
opam pin add misuja . -kgit
or better:
or to build locally:
opam pin add misuja . -kgit
dune build @install
The library requires the development files of the JACK library, see the
`depexts` field in the `opam/opam` file; the package is named
`depexts` field in the `misuja.opam` file; the package is named
[`jack-audio-connection-kit-devel`](https://pkgs.org/download/jack-audio-connection-kit-devel)
on most RPM-based distributions, and
[`libjack-jackd2-dev`](https://packages.ubuntu.com/xenial/libjack-jackd2-dev) on
Ubuntu/Debian.
On Nix, this may look like:
nix-shell -p pkg-config -p libjack2 --run 'dune build @install src/test/relay.exe'
Tests
-----
Build them:
make tests
dune build @install src/test/relay.exe
then:
LD_LIBRARY_PATH=_build/lib/ _build/test/relay.byte
_build/default/src/test/relay.exe
or
_build/test/relay.native
The test opens a Jack-midi client with 10 input and 10 output ports; it just
relays everything it gets as input but in the case of “note-on/off” MIDI events
it “power-harmonizes” them (it makes just “power chords,” i.e. it adds fifths
......
(lang dune 1.0)
(name misuja)
opam-version: "1.2"
version: "0.0.0"
opam-version: "2.0"
maintainer: "Seb Mondet <[email protected]>"
authors: "Seb Mondet <[email protected]>"
homepage: "https://gitlab.com/smondet/misuja"
bug-reports: "https://gitlab.com/smondet/misuja/issues"
dev-repo: "https://gitlab.com/smondet/misuja.git"
dev-repo: "git+https://gitlab.com/smondet/misuja.git"
license: "MIT"
build: [
[make "lib"]
]
install: [
[make "install"]
]
remove: [
[make "uninstall"]
]
build: [ "dune" "build" "-p" name "-j" jobs ]
depends: [
"ocamlfind" {build}
"ocaml" {>= "4.03.0"}
"dune" {build & >= "1.8.2"}
"base-unix"
]
depexts: [
......@@ -34,6 +24,10 @@ depexts: [
# Cf. https://github.com/ocaml/opam-repository/pull/10167 for OSX:
[["homebrew" "osx"] ["jack"]]
]
available: [
ocaml-version >= "4.03.0"
]
synopsis: "A library to use JACK-MIDI"
description: """
Misuja is a low-latency “MIDI communications thread” implemented in C
which is manipulated with an OCaml API: `Misuja.Sequencer. (the
process communicates with the Sequencer thread through ring-buffers
provided by the Jack API).
"""
profile=compact
module C = Configurator.V1
let () =
C.main ~name:"jack" (fun c ->
let default : C.Pkg_config.package_conf =
{libs= ["-ljack"]; cflags= ["-I/usr/include/jack"]}
in
let conf =
match C.Pkg_config.get c with
| None ->
default
| Some pc -> (
match C.Pkg_config.query pc ~package:"jack" with
| None ->
default
| Some a ->
a )
in
C.Flags.write_sexp "c_flags.sexp" conf.cflags ;
C.Flags.write_sexp "c_library_flags.sexp" conf.libs )
(executable
(name discover)
(libraries dune.configurator))
(library
(name misuja)
(public_name misuja)
(libraries unix)
(c_names misuja_jack_seq)
(c_flags
(:include c_flags.sexp))
(c_library_flags
(:include c_library_flags.sexp)))
(rule
(targets c_flags.sexp c_library_flags.sexp)
(deps
(:< config/discover.exe))
(action
(run %{<})))
......@@ -23,7 +23,6 @@
(* OTHER DEALINGS IN THE SOFTWARE. *)
(******************************************************************************)
(**
OCaml types and functions providing high level access to a jack midi
......@@ -33,10 +32,12 @@
*)
module Sequencer = struct
(** The sequencer object *)
type t
external make :
name:string -> input_ports:string array -> output_ports:string array -> t
= "ml_jackseq_make"
(**
The sequencer constructor
should be called as {[
......@@ -47,24 +48,18 @@ module Sequencer = struct
in
]}
*)
external make:
name: string -> input_ports: string array -> output_ports: string array -> t
= "ml_jackseq_make"
external close : t -> unit = "ml_jackseq_close"
(** Close the sequencer ({i ie} jack client) *)
external close:
t -> unit
= "ml_jackseq_close"
(** Put an event in the output buffer, it will be really output at next
jack frame (which means quasi immediately) *)
external output_event:
external output_event :
t -> port:int -> stat:int -> chan:int -> dat1:int -> dat2:int -> unit
= "ml_jackseq_output_event_bytecode" "ml_jackseq_output_event"
(** Put an event in the output buffer, it will be really output at next
jack frame (which means quasi immediately) *)
(** Get all events in input buffer and clear it *)
external get_input:
t -> (int*int*int*int*int) array
external get_input :
t -> (int * int * int * int * int) array
= "ml_jackseq_get_input"
(** Get all events in input buffer and clear it *)
end
(executable
(name relay)
(libraries threads.posix misuja)
)
\ No newline at end of file
open Printf
module Array = ArrayLabels
let prf fmt = ksprintf (printf "%s%!") fmt
let line fmt = ksprintf (prf "%s\n%!") fmt
let test_jack_seq () =
let test_jack_seq () =
let open Misuja in
let seq =
let input_ports =
Array.init 10 ~f:(sprintf "in%d") in
let output_ports =
Array.init 10 ~f:(sprintf "out%d") in
Sequencer.make
~name:"JackSeqTest" ~input_ports ~output_ports
let input_ports = Array.init 10 ~f:(sprintf "in%d") in
let output_ports = Array.init 10 ~f:(sprintf "out%d") in
Sequencer.make ~name:"JackSeqTest" ~input_ports ~output_ports
in
for i = 0 to 25000000 do
Thread.delay 0.02;
Thread.delay 0.02 ;
let input = Sequencer.get_input seq in
Array.iter input ~f:begin fun (port, stat, chan, dat1, dat2) ->
Printf.printf "[%d] port:%d stat:%x chan:%d dat1:%d dat2:%d\n%!" i
port stat chan dat1 dat2;
let high_level =
match stat with
| rs when ((0x80<= rs) && (rs <= 0x8F)) -> `Note_off (rs, dat1 , dat2)
| rs when ((0x90<= rs) && (rs <= 0x9F)) ->
if dat2 = 0 (* If velocity = 0 -> note off ! *)
then `Note_off (rs, dat1 , dat2)
else `Note_on (rs, dat1 , dat2)
| other -> `None
in
begin match high_level with
| `None ->
Sequencer.output_event seq ~port ~stat ~chan ~dat1 ~dat2
| `Note_on (rs, dat1, dat2)
| `Note_off (rs, dat1, dat2) ->
Sequencer.output_event seq ~port ~stat ~chan ~dat1 ~dat2;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 7) ~dat2;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 12) ~dat2;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 19) ~dat2;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 24) ~dat2;
end;
end;
done;
Sequencer.close seq;
Unix.sleep 3;
Array.iter input ~f:(fun (port, stat, chan, dat1, dat2) ->
Printf.printf "[%d] port:%d stat:%x chan:%d dat1:%d dat2:%d\n%!" i port
stat chan dat1 dat2 ;
let high_level =
match stat with
| rs when 0x80 <= rs && rs <= 0x8F -> `Note_off (rs, dat1, dat2)
| rs when 0x90 <= rs && rs <= 0x9F ->
if dat2 = 0 (* If velocity = 0 -> note off ! *) then
`Note_off (rs, dat1, dat2)
else `Note_on (rs, dat1, dat2)
| _other -> `None
in
match high_level with
| `None -> Sequencer.output_event seq ~port ~stat ~chan ~dat1 ~dat2
| `Note_on (_, dat1, dat2) | `Note_off (_, dat1, dat2) ->
Sequencer.output_event seq ~port ~stat ~chan ~dat1 ~dat2 ;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 7) ~dat2 ;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 12)
~dat2 ;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 19)
~dat2 ;
Sequencer.output_event seq ~port ~stat ~chan ~dat1:(dat1 + 24)
~dat2 )
done ;
Sequencer.close seq ;
Unix.sleep 3 ;
()
let () = test_jack_seq ()
\ No newline at end of file
let () = test_jack_seq ()