Skip to content

Instantly share code, notes, and snippets.

@kurogelee
Created April 6, 2014 12:34
Show Gist options
  • Save kurogelee/10005408 to your computer and use it in GitHub Desktop.
Save kurogelee/10005408 to your computer and use it in GitHub Desktop.
ポケット・ミクを操作する関数を作ってみる ref: http://qiita.com/kurogelee/items/c5443167b355cd021407
(ns pokemiku.core
(:import [javax.sound.midi MidiSystem ShortMessage SysexMessage MidiDevice Receiver]
[javax.xml.bind DatatypeConverter]))
(defn ^MidiDevice get-device []
(some->> (seq (MidiSystem/getMidiDeviceInfo))
(filter #(re-find #"NSX-39" (:name (bean %))))
(filter #(re-find #"External" (:description (bean %))))
first
(MidiSystem/getMidiDevice)))
(defn note [^Receiver r data1 data2]
(.send r (ShortMessage. ShortMessage/NOTE_ON data1 data2) -1))
(defn note-off [^Receiver r data1 data2]
(.send r (ShortMessage. ShortMessage/NOTE_OFF data1 data2) -1))
(defn vibrato
([^Receiver r] (vibrato r 127))
([^Receiver r vol] (.send r (ShortMessage. ShortMessage/CONTROL_CHANGE 1 vol) -1)))
(defn vibrato-off [^Receiver r] (vibrato r 0))
(defn pitch-bend [^Receiver r n]
(when (<= -64 n 63)
(.send r (ShortMessage. ShortMessage/PITCH_BEND 1 (+ 64 n)) -1)))
(defn pitch-bend-off [^Receiver r] (pitch-bend r 0))
(defn- small-char? [x] (re-find #"[ぁぃぅぇぉゃゅょ]" (str x)))
(def ^:private base-chars "あいうえおかきくけこがぎぐげごきゃきゅきょぎゃぎゅぎょさすぃすせそざずぃずぜぞしゃししゅしぇしょじゃじじゅじぇじょたてぃとぅてとだでぃどぅでどてゅでゅちゃちちゅちぇちょつぁつぃつつぇつぉなにぬねのにゃにゅにょはひふへほばびぶべぼぱぴぷぺぽひゃひゅひょびゃびゅびょぴゃぴゅぴょふぁふぃふゅふぇふぉまみむめもみゃみゅみょやゆよらりるれろりゃりゅりょわうぃうぇうぉんmNJn")
(defn split-chars [s]
(let [s (str (apply str s) " ")]
(->> (map #(cond (small-char? %2) (str %1 %2) (not (small-char? %1)) (str %1)) s (drop 1 s))
(filter some?))))
(def ^:private char-map
(->> (zipmap (split-chars base-chars) (range))
(merge {"づぁ" 26 "づぃ" 27 "づ" 28 "づぇ" 29 "づぉ" 30 "ゐ" 120 "ゑ" 121 "を" 122 "N\\" 123 "ぢ" 37 "ヴ" 78})))
(def ^:private bin-map
(->> (mapcat (fn [[k v]] [(keyword k) (DatatypeConverter/parseHexBinary (format "F0437909110A00%02XF7" v))]) char-map)
(apply hash-map)))
(defn set-char [^Receiver r ch]
(when-let [data (bin-map (if (char? ch) (keyword (str ch)) (keyword ch)))]
(.send r (SysexMessage. data (count data)) -1)))
(ns pokemiku.util
(:require [pokemiku.core :refer :all])
(:import [java.util.concurrent Executors ScheduledExecutorService ScheduledFuture TimeUnit]
[javax.sound.midi ShortMessage]))
(def base-octave [60 62 64 65 67 69 71])
(defn play [tempo & notes]
(let [n (partition 4 notes)
base 120
tempo (atom tempo)
n (atom n)
sum (atom 0)
pool (Executors/newScheduledThreadPool 1)
f #(if (nil? @n) (.shutdown pool)
;; char pitch length volume
(let [[c p l v] (first @n)]
(if (zero? @sum)
(condp = c
:V (vibrato % v)
:B (pitch-bend % v)
:T (reset! tempo v)
:R (note-off % p v)
(do (set-char % c) (note % p v)))
(when (< @sum 2) (set-char % (first (second @n))))) ; set-charを即座に反映するため
(swap! sum inc)
(when (<= (* base l (/ 60 @tempo)) @sum)
(vibrato-off %) (pitch-bend-off %) (reset! sum 0) (swap! n next))))]
(with-open [dev (get-device)]
(.open dev)
(let [r (.getReceiver dev) rate (Math/round (double (/ 1000000 base)))]
(doto pool
(.scheduleAtFixedRate #(f r) 0 rate TimeUnit/MICROSECONDS)
(.awaitTermination 1 TimeUnit/DAYS))))))
(play 60 :ど 72 1 127 :V 1 1 127 :R 72 1/2 0 :れ 74 1/2 127 :B 1 1/5 2 :み 76 1 127 :T 0 0 120 :ど 72 1 127 :V 1 1 127 :れ 74 1/2 127 :B 1 1/5 2 :み 76 1 127)
(apply play 150 (interleave
(split-chars (repeat 3 "どれみふぁそらし"))
(concat (map #(- % 12) base-octave) base-octave (map #(+ 12 %) base-octave))
(repeat 1) (repeat 127)))
(play 150 :ど 84 1/5 127 :V 1 2 127)
(apply play 120
(interleave (cycle "みく")
(let [c [76 77 74 72]] (concat c (map inc c) (reverse c) (map inc (reverse c) )))
(cycle [1/2 1/3 1 2/3]) (repeat 127)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment