diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 032e27208618309118848ac5029c826416f7273b..4aebdb0c5453b673cab6b5fce0d2476079951095 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,39 +1,48 @@ - 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" diff --git a/Makefile b/Makefile deleted file mode 100644 index 4a563d9d12760b1e0622c3236d8565329004f72b..0000000000000000000000000000000000000000 --- a/Makefile +++ /dev/null @@ -1,76 +0,0 @@ - -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 $@ - -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 $@ -$(BUILD_LIB)/$(OCAML_MODULE).cmx: src/lib/$(OCAML_MODULE).ml $(BUILD_LIB) - ocamlfind ocamlopt -c -thread -package unix,threads $< -o $@ -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 $@ - -_build/test: - mkdir -p $@ -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 = ' > $@ && \ - grep ^version: misuja.opam | sed 's/version: //' >> $@ && \ - echo 'description = "Midi Sequencer Library Using Jack"' >> $@ && \ - echo 'requires = "threads unix bytes"' >> $@ && \ - echo 'archive(byte) = "misuja.cma"' >> $@ && \ - echo 'archive(byte, plugin) = "misuja.cma"' >> $@ && \ - echo 'archive(native) = "misuja.cmxa"' >> $@ && \ - echo 'archive(native, plugin) = "misuja.cmxs"' >> $@ && \ - cat $@ - -.PHONY: install uninstall -install: lib $(BUILD_LIB)/META - ocamlfind install misuja $(BUILD_LIB)/* -uninstall: - ocamlfind remove misuja - -.merlin: - echo 'S src/lib/' > $@ && \ - echo 'S src/test/' >> $@ && \ - echo 'PKG nonstd unix threads' >> $@ && \ - echo 'B _build/lib/' >> $@ && \ - echo 'B +threads' >> $@ - -.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 diff --git a/README.md b/README.md index c0b098b0bc7dfb7b4d9fa2c938861754af4fe0c6..2b7f7f0961fdb332c8256edf4c8f989c5bd737ed 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/dune-project b/dune-project new file mode 100644 index 0000000000000000000000000000000000000000..a17e9ab8ab3c1b41e4aa26158e5db911fd4f45d6 --- /dev/null +++ b/dune-project @@ -0,0 +1,2 @@ +(lang dune 1.0) +(name misuja) diff --git a/misuja.opam b/misuja.opam index 3b53e6e2c5a1bee2fb804e2ff6669251dd4315d2..528340e0cdcb006194150c48ed7d396160121307 100644 --- a/misuja.opam +++ b/misuja.opam @@ -1,24 +1,14 @@ -opam-version: "1.2" -version: "0.0.0" +opam-version: "2.0" maintainer: "Seb Mondet " authors: "Seb Mondet " 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). +""" diff --git a/src/lib/.ocamlformat b/src/lib/.ocamlformat new file mode 100644 index 0000000000000000000000000000000000000000..6bafbd1c4e234b3b34aa76c3ce359274d0069922 --- /dev/null +++ b/src/lib/.ocamlformat @@ -0,0 +1 @@ +profile=compact diff --git a/src/lib/config/discover.ml b/src/lib/config/discover.ml new file mode 100644 index 0000000000000000000000000000000000000000..02f6c73c1240b6eee951545769bc63a1f6c9c543 --- /dev/null +++ b/src/lib/config/discover.ml @@ -0,0 +1,20 @@ +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 ) diff --git a/src/lib/config/dune b/src/lib/config/dune new file mode 100644 index 0000000000000000000000000000000000000000..21a5f9a549b043b9ee057001a4097cc7ed5c62a1 --- /dev/null +++ b/src/lib/config/dune @@ -0,0 +1,3 @@ +(executable + (name discover) + (libraries dune.configurator)) diff --git a/src/lib/dune b/src/lib/dune new file mode 100644 index 0000000000000000000000000000000000000000..ccd7697487b3bfc3cdf8295ae6cabd8a00ed7c5d --- /dev/null +++ b/src/lib/dune @@ -0,0 +1,16 @@ +(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 %{<}))) diff --git a/src/lib/misuja.ml b/src/lib/misuja.ml index 9e35ba1f8e967eee350746a5517415de189fe2af..3a5d52c7c6069fac4b65242aa9378ec7844075f4 100644 --- a/src/lib/misuja.ml +++ b/src/lib/misuja.ml @@ -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 diff --git a/src/test/.ocamlformat b/src/test/.ocamlformat new file mode 100644 index 0000000000000000000000000000000000000000..6bafbd1c4e234b3b34aa76c3ce359274d0069922 --- /dev/null +++ b/src/test/.ocamlformat @@ -0,0 +1 @@ +profile=compact diff --git a/src/test/dune b/src/test/dune new file mode 100644 index 0000000000000000000000000000000000000000..29c39cf06fd4f14b623ace255a70918d24d59cc1 --- /dev/null +++ b/src/test/dune @@ -0,0 +1,4 @@ +(executable + (name relay) + (libraries threads.posix misuja) +) \ No newline at end of file diff --git a/src/test/relay.ml b/src/test/relay.ml index 541eac63865eb6149e5af34e6e81881aefd6a2a5..d038ece88745dcdb8a72672b146c85f23faf1d05 100644 --- a/src/test/relay.ml +++ b/src/test/relay.ml @@ -1,52 +1,45 @@ - 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 ()