diff --git a/accompaniator_web/base_app/forms.py b/accompaniator_web/base_app/forms.py
index 9ce1aac..6870c0d 100644
--- a/accompaniator_web/base_app/forms.py
+++ b/accompaniator_web/base_app/forms.py
@@ -4,9 +4,9 @@
class FeedbackForm(forms.ModelForm):
- song_name = forms.CharField()
- rating = forms.Select()
+ song_name = forms.CharField(widget=forms.HiddenInput(), required=False)
+ mark = forms.IntegerField(widget=forms.HiddenInput(), required=False, initial=-1)
class Meta:
model = Feedback
- fields = ('song_name', 'rating',)
+ fields = ('song_name', 'mark',)
diff --git a/accompaniator_web/base_app/migrations/0003_auto_20180522_0732.py b/accompaniator_web/base_app/migrations/0003_auto_20180522_0732.py
new file mode 100644
index 0000000..27ea825
--- /dev/null
+++ b/accompaniator_web/base_app/migrations/0003_auto_20180522_0732.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.0.2 on 2018-05-22 07:32
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('base_app', '0002_feedback_song_name'),
+ ]
+
+ operations = [
+ migrations.RenameField(
+ model_name='feedback',
+ old_name='rating',
+ new_name='mark',
+ ),
+ ]
diff --git a/accompaniator_web/base_app/models.py b/accompaniator_web/base_app/models.py
index ae17850..deb0bcd 100644
--- a/accompaniator_web/base_app/models.py
+++ b/accompaniator_web/base_app/models.py
@@ -4,4 +4,4 @@
class Feedback(models.Model):
song_name = models.CharField(max_length=32)
session_key = models.CharField(max_length=32)
- rating = models.SmallIntegerField()
+ mark = models.SmallIntegerField()
diff --git a/accompaniator_web/base_app/views.py b/accompaniator_web/base_app/views.py
index 6c3d6f6..995bfe9 100644
--- a/accompaniator_web/base_app/views.py
+++ b/accompaniator_web/base_app/views.py
@@ -44,11 +44,12 @@ def results(request):
if request.method == 'POST':
form = FeedbackForm(request.POST, request.FILES)
+ print(form)
if form.is_valid():
instance = form.save(commit=False)
instance.session_key = session_key
instance.save()
- return HttpResponseRedirect('/results')
+ return HttpResponseRedirect('/home')
else:
print(form.errors)
diff --git a/accompaniator_web/static/js/home.js b/accompaniator_web/static/js/home.js
index f8b1939..0292e56 100644
--- a/accompaniator_web/static/js/home.js
+++ b/accompaniator_web/static/js/home.js
@@ -16,7 +16,7 @@ var state = "init";
var delay = [];
var send_queve = [];
var text_ws;
-var server_name = 'ws://demos.kaazing.com/echo';
+var server_name = 'ws://localhost:8110/ws'; // 'ws://demos.kaazing.com/echo'; for testing
TextWS();
function startUserMedia(stream) {
@@ -40,9 +40,12 @@ function TextWS() {
text_ws.onmessage = function (ev) {
msg = ev.data;
console.log(msg)
- if (typeof(msg) == "string") {
+ if (msg[0] != "R") {
text_ws.send(msg);
}
+ else{
+ Push(msg);
+ }
};
text_ws.onclose = function () {
TextWS();
@@ -51,6 +54,7 @@ function TextWS() {
function Push(blob) {
+ if (blob)
var url = URL.createObjectURL(blob);
var au = document.createElement('audio');
@@ -203,6 +207,7 @@ function Stop() {
state = 'record';
}, 1000);
ws.close();
+ text_ws.close();
}
@@ -213,7 +218,11 @@ function Recording() {
var s = ev.data;
_now = new Date().getTime();
//console.log("Delay is", _now - delay.pop(), "ms.");
- Push(s);
+ if (s[0] == "R") {
+ Push(s);
+ }else{
+ text_ws.send(s);
+ }
};
queve = [];
frames = [];
diff --git a/accompaniator_web/static/js/result.js b/accompaniator_web/static/js/result.js
index 72bc23d..94901ef 100644
--- a/accompaniator_web/static/js/result.js
+++ b/accompaniator_web/static/js/result.js
@@ -10,5 +10,5 @@ function star(num, star) {
for (var i = num + 1; i <= 5; i++) {
document.getElementById("star" + i).innerHTML = '
';
}
- document.getElementById("mark").innerHTML = num;
+ document.getElementById("id_mark").value = num;
}
diff --git a/accompaniator_web/templates/base_app/results.html b/accompaniator_web/templates/base_app/results.html
index 0a26fd2..bad1c87 100644
--- a/accompaniator_web/templates/base_app/results.html
+++ b/accompaniator_web/templates/base_app/results.html
@@ -15,15 +15,12 @@
Оцените нас
-
-
- {% for i in stars_range %}
+
+ {% for i in stars_range %}
-

@@ -31,18 +28,22 @@ Оцените нас
{% endfor %}
-
+
-
diff --git a/accompaniator_web/tornado_server.py b/accompaniator_web/tornado_server.py
new file mode 100644
index 0000000..3a97c56
--- /dev/null
+++ b/accompaniator_web/tornado_server.py
@@ -0,0 +1,96 @@
+# import sys
+# sys.path.append("../")
+import numpy as np
+import io
+import scipy.io.wavfile
+
+import tornado.websocket
+from tornado import ioloop
+from tornado.web import url
+
+from production import accompanist
+from multiprocessing import Queue, Process
+
+from datetime import datetime
+import time
+
+buffer_size = 256
+time_between_delay_measurings_in_secs = 1.5
+
+
+def send_time_stamps(websocket):
+ while websocket.running is True:
+ now = datetime.now()
+ output = now.strftime("%Y-%m-%d %H:%M:%S")
+ try:
+ websocket.write_message(output, binary=False)
+ except tornado.iostream.StreamClosedError or tornado.websocket.WebSocketClosedError:
+ break
+ time.sleep(time_between_delay_measurings_in_secs)
+
+
+class WebSocketHandler(tornado.websocket.WebSocketHandler):
+
+ def check_origin(self, origin):
+ return True
+
+ def initialize(self):
+ self.in_queue = Queue()
+ self.accompanist = None
+ self.last_sent_time_stamp = None
+ self.running = False
+
+ def open(self):
+ self.accompanist = accompanist.Accompanist()
+ self.accompanist.set_websocket(self)
+ self.accompanist.set_queue_in(self.in_queue)
+
+ self.accompanist.run()
+
+ self.running = True
+ self.time_send_process = Process(target=send_time_stamps, args=(self,))
+ self.time_send_process.start()
+
+ def on_message(self, message):
+ if 'RIFF' in str(message):
+ s = io.BytesIO(message)
+ _, samplesints = scipy.io.wavfile.read(s)
+ samples = samplesints.astype(np.float32, order='C') / 32768.0
+ for i in range(0, len(samples) // buffer_size - 1):
+ self.in_queue.put(samples[i * buffer_size:(i + 1) * buffer_size])
+
+ else: # it's a time string!
+ time_stamp = datetime.strptime(message, "%Y-%m-%d %H:%M:%S")
+ delay = (datetime.now() - time_stamp).total_seconds()
+ self.accompanist.set_web_delay(delay)
+
+ def on_close(self):
+ print("stopping")
+ self.running = False
+ self.time_send_process.join()
+ self.accompanist.stop()
+ print("stopped")
+
+ def send_audio(self, message):
+ print("delay ok!")
+ output = io.BytesIO()
+ scipy.io.wavfile.write(output, 44100, message)
+ try:
+ self.write_message(output.getvalue(), binary=True)
+ except tornado.iostream.StreamClosedError or tornado.websocket.WebSocketClosedError:
+ print("Caught this fucker")
+
+ def send_time(self):
+ pass
+
+
+def main():
+ app = tornado.web.Application([url(r"/ws", WebSocketHandler)])
+ return app
+
+
+if __name__ == "__main__":
+ app = main()
+ http_server = tornado.httpserver.HTTPServer(app)
+ http_server.listen(8110, address='0.0.0.0')
+ ioloop.IOLoop.instance().start()
diff --git a/docker/accompaniator/Dockerfile b/docker/accompaniator/Dockerfile
index 02efcdb..869fd80 100644
--- a/docker/accompaniator/Dockerfile
+++ b/docker/accompaniator/Dockerfile
@@ -30,3 +30,4 @@ ADD . /build/
RUN mv asound.conf /etc/asound.conf
RUN cat ./.bashrc >> ~/.bashrc
+RUN mv piano_and_ultrasound.sf2 /piano_and_ultrasound.sf2
diff --git a/docker/accompaniator/piano_and_ultrasound.sf2 b/docker/accompaniator/piano_and_ultrasound.sf2
new file mode 100644
index 0000000..f33675a
Binary files /dev/null and b/docker/accompaniator/piano_and_ultrasound.sf2 differ
diff --git a/docker/accompaniator/requirements.txt b/docker/accompaniator/requirements.txt
index cd9ab53..3100224 100644
--- a/docker/accompaniator/requirements.txt
+++ b/docker/accompaniator/requirements.txt
@@ -22,4 +22,6 @@ coverage
tqdm
# Web stuff
-django
\ No newline at end of file
+django
+tornado
+six
diff --git a/docker/accompaniator/startup.sh b/docker/accompaniator/startup.sh
index 8db7b36..a6f582e 100755
--- a/docker/accompaniator/startup.sh
+++ b/docker/accompaniator/startup.sh
@@ -11,7 +11,7 @@ else
fi
cd accompaniator
-git checkout ACCOMPANIST-154_site_on_django
+git checkout web_tornado_server
cd accompaniator_web
# Start django
@@ -24,4 +24,15 @@ else
echo "Django ran successfully"
fi
+# Start tornado
+cd ..
+nohup python -m accompaniator_web.tornado_server &
+status=$?
+if [ $status -ne 0 ]; then
+ echo "Failed to start tornado: $status"
+ exit $status
+else
+ echo "Tornado ran successfully"
+fi
+
bash
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
index c473ad3..07e59c6 100644
--- a/docker/docker-compose.yml
+++ b/docker/docker-compose.yml
@@ -11,10 +11,9 @@ services:
- /tmp/.X11-unix:/tmp/.X11-unix
- /run/dbus/:/run/dbus/:rw
- /dev/shm:/dev/shm
- ports:
- - 8000:8000
accompaniator-dev:
+ tty: true
image: accompaniator-dev
build: accompaniator-dev
devices:
@@ -25,6 +24,7 @@ services:
- /dev/shm:/dev/shm
- ./../:/build/accompaniator
ports:
+ - 8110:8110
- 8000:8000
accompaniator-prod:
@@ -37,6 +37,7 @@ services:
- /run/dbus/:/run/dbus/:rw
- /dev/shm:/dev/shm
ports:
+ - 8110:8110
- 8000:8000
accompaniator-test:
diff --git a/ml/NN_model.h5 b/ml/NN_model.h5
new file mode 100644
index 0000000..9a1f7cb
Binary files /dev/null and b/ml/NN_model.h5 differ
diff --git a/ml/NN_model.ipynb b/ml/NN_model.ipynb
new file mode 100644
index 0000000..12f9c05
--- /dev/null
+++ b/ml/NN_model.ipynb
@@ -0,0 +1,398 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [],
+ "source": [
+ "import pickle\n",
+ "import ml.structures\n",
+ "import ml.dataset.corpus as corpus\n",
+ "import numpy as np\n",
+ "\n",
+ "import pandas as pd\n",
+ "\n",
+ "%matplotlib inline\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "from sklearn.cross_validation import train_test_split\n",
+ "from sklearn.linear_model import LogisticRegressionCV\n",
+ "from keras.models import load_model\n",
+ "\n",
+ "from keras.layers import LSTM\n",
+ "from keras.layers import Embedding\n",
+ "from keras.optimizers import RMSprop\n",
+ "\n",
+ "\n",
+ "from keras.models import Sequential\n",
+ "from keras.layers.core import Dense, Activation\n",
+ "from keras.utils import np_utils\n",
+ "from keras.engine.topology import Input\n",
+ "\n",
+ "from sklearn.metrics import accuracy_score\n",
+ "from keras.layers import concatenate, Merge\n",
+ "from keras.layers.merge import Concatenate\n",
+ "\n",
+ "import keras\n",
+ "from keras.utils import to_categorical\n",
+ "\n",
+ "from matplotlib import pyplot as plt\n",
+ "from IPython.display import clear_output"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def make_16(chord):\n",
+ " if (chord.duration > 0):\n",
+ " c = 16 / (128 / chord.duration)\n",
+ " else:\n",
+ " return 0, [0]\n",
+ " pre_array = np.array([note.number % 12 for note in chord.notes])\n",
+ " array = set()\n",
+ " for el in pre_array:\n",
+ " array.add(el)\n",
+ " if len(array) == 4:\n",
+ " break\n",
+ " array = np.array(list(array))\n",
+ " array = np.sort(array)\n",
+ " if len(array) == 0:\n",
+ " array = [0]\n",
+ " if c >= 1:\n",
+ " return 1, np.tile(array, (int(c), 1))\n",
+ " else:\n",
+ " return 0, [0]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def cut_song(song, notes, chords):\n",
+ " chords_track = []\n",
+ " notes_track = np.array([])\n",
+ " flag = 0\n",
+ " for note in song.tracks[0].chords:\n",
+ " if (make_16(note)[0] == 0):\n",
+ " flag = 1\n",
+ " notes_track = np.append(notes_track, make_16(note)[1])\n",
+ " for chord in song.tracks[1].chords:\n",
+ " if (make_16(chord)[0] == 0):\n",
+ " flag = 1\n",
+ " chords_track.append(make_16(chord)[1])\n",
+ " if flag == 1:\n",
+ " return 0, notes, chords\n",
+ " return 1, notes_track, chords_track"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def norm_array(X, size):\n",
+ " for j in range(len(X)):\n",
+ " X[j] = np.unique(np.array(X[j]))[:size]\n",
+ " array = np.zeros(size-len(X[j])) - 1\n",
+ " X[j] = np.append(X[j], array)\n",
+ " X = np.array(X)\n",
+ " return X"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_X_Y(chords):\n",
+ " real_chords = []\n",
+ " y = []\n",
+ " for i, subchord in enumerate(chords):\n",
+ " if i != len(chords) - 1:\n",
+ " for ssubchord in subchord:\n",
+ " real_chords.append(ssubchord)\n",
+ " size = len(subchord)\n",
+ " for j in range(size):\n",
+ " y.append(chords[i+1][-1])\n",
+ " return real_chords, y"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def notes_to_np(notes):\n",
+ " for i in range(len(notes)):\n",
+ " notes[i] = np.array(notes[i])\n",
+ " notes = np.array(notes)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_unique_chords(X, unique_chords):\n",
+ " for x in X:\n",
+ " unique_chords = np.append(unique_chords, np.array(x))\n",
+ " return unique_chords"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def create_coded(X):\n",
+ " return np.zeros(len(X))\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def encode(X, coded_X):\n",
+ " for j, k in enumerate(X):\n",
+ " for i, unique_chord in enumerate(unique_chords):\n",
+ " if np.array_equal(k, unique_chord):\n",
+ " coded_X[j] = i"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {
+ "scrolled": false
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "1\n",
+ "Epoch 1/1\n",
+ "446/446 [==============================] - 7s 16ms/step - loss: 3.8680 - acc: 0.2422\n",
+ "446/446 [==============================] - 2s 4ms/step\n",
+ "current_accuracy: 0.33183856502242154\n",
+ "2\n",
+ "Epoch 1/1\n",
+ "286/286 [==============================] - 3s 10ms/step - loss: 2.1689 - acc: 0.3671\n",
+ "286/286 [==============================] - 1s 4ms/step\n",
+ "current_accuracy: 0.3620218579234973\n",
+ "3\n",
+ "Epoch 1/1\n",
+ "90/90 [==============================] - 1s 10ms/step - loss: 5.5782 - acc: 0.1333\n",
+ "90/90 [==============================] - 0s 4ms/step\n",
+ "current_accuracy: 0.33698296836982966\n",
+ "4\n",
+ "Epoch 1/1\n",
+ "90/90 [==============================] - 1s 9ms/step - loss: 4.7041 - acc: 0.1000\n",
+ "90/90 [==============================] - 0s 4ms/step\n",
+ "current_accuracy: 0.3190789473684211\n",
+ "5\n",
+ "Epoch 1/1\n",
+ "94/94 [==============================] - 1s 11ms/step - loss: 3.9138 - acc: 0.3723\n",
+ "94/94 [==============================] - 0s 5ms/step\n",
+ "current_accuracy: 0.3230616302186879\n",
+ "6\n",
+ "Epoch 1/1\n",
+ "66/94 [====================>.........] - ETA: 0s - loss: 2.7468 - acc: 0.3636"
+ ]
+ },
+ {
+ "ename": "KeyboardInterrupt",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
+ "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 58\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mi\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m27100\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 59\u001b[0;31m \u001b[0mmerged_model\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfit\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mX_notes\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mepochs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mverbose\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 60\u001b[0m \u001b[0macc\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mmerged_model\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mevaluate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mX\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mX_notes\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatch_size\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m*\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0mall_len\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0my\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/keras/models.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)\u001b[0m\n\u001b[1;32m 961\u001b[0m \u001b[0minitial_epoch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_epoch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 962\u001b[0m \u001b[0msteps_per_epoch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msteps_per_epoch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 963\u001b[0;31m validation_steps=validation_steps)\n\u001b[0m\u001b[1;32m 964\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 965\u001b[0m def evaluate(self, x=None, y=None,\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/keras/engine/training.py\u001b[0m in \u001b[0;36mfit\u001b[0;34m(self, x, y, batch_size, epochs, verbose, callbacks, validation_split, validation_data, shuffle, class_weight, sample_weight, initial_epoch, steps_per_epoch, validation_steps, **kwargs)\u001b[0m\n\u001b[1;32m 1703\u001b[0m \u001b[0minitial_epoch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minitial_epoch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1704\u001b[0m \u001b[0msteps_per_epoch\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0msteps_per_epoch\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1705\u001b[0;31m validation_steps=validation_steps)\n\u001b[0m\u001b[1;32m 1706\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1707\u001b[0m def evaluate(self, x=None, y=None,\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/keras/engine/training.py\u001b[0m in \u001b[0;36m_fit_loop\u001b[0;34m(self, f, ins, out_labels, batch_size, epochs, verbose, callbacks, val_f, val_ins, shuffle, callback_metrics, initial_epoch, steps_per_epoch, validation_steps)\u001b[0m\n\u001b[1;32m 1233\u001b[0m \u001b[0mins_batch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mins_batch\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mi\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtoarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1234\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1235\u001b[0;31m \u001b[0mouts\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mins_batch\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1236\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mouts\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlist\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1237\u001b[0m \u001b[0mouts\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0mouts\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/keras/backend/tensorflow_backend.py\u001b[0m in \u001b[0;36m__call__\u001b[0;34m(self, inputs)\u001b[0m\n\u001b[1;32m 2476\u001b[0m \u001b[0msession\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mget_session\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2477\u001b[0m updated = session.run(fetches=fetches, feed_dict=feed_dict,\n\u001b[0;32m-> 2478\u001b[0;31m **self.session_kwargs)\n\u001b[0m\u001b[1;32m 2479\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mupdated\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0mlen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0moutputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2480\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36mrun\u001b[0;34m(self, fetches, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 903\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 904\u001b[0m result = self._run(None, fetches, feed_dict, options_ptr,\n\u001b[0;32m--> 905\u001b[0;31m run_metadata_ptr)\n\u001b[0m\u001b[1;32m 906\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mrun_metadata\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 907\u001b[0m \u001b[0mproto_data\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtf_session\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mTF_GetBuffer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrun_metadata_ptr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_run\u001b[0;34m(self, handle, fetches, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 1135\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mfinal_fetches\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mfinal_targets\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mhandle\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0mfeed_dict_tensor\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1136\u001b[0m results = self._do_run(handle, final_targets, final_fetches,\n\u001b[0;32m-> 1137\u001b[0;31m feed_dict_tensor, options, run_metadata)\n\u001b[0m\u001b[1;32m 1138\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1139\u001b[0m \u001b[0mresults\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_do_run\u001b[0;34m(self, handle, target_list, fetch_list, feed_dict, options, run_metadata)\u001b[0m\n\u001b[1;32m 1353\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mhandle\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1354\u001b[0m return self._do_call(_run_fn, self._session, feeds, fetches, targets,\n\u001b[0;32m-> 1355\u001b[0;31m options, run_metadata)\n\u001b[0m\u001b[1;32m 1356\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1357\u001b[0m \u001b[0;32mreturn\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_do_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0m_prun_fn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_session\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfeeds\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfetches\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_do_call\u001b[0;34m(self, fn, *args)\u001b[0m\n\u001b[1;32m 1359\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_do_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1360\u001b[0m \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1361\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mfn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1362\u001b[0m \u001b[0;32mexcept\u001b[0m \u001b[0merrors\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mOpError\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1363\u001b[0m \u001b[0mmessage\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcompat\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mas_text\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmessage\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;32m/usr/local/lib/python3.5/dist-packages/tensorflow/python/client/session.py\u001b[0m in \u001b[0;36m_run_fn\u001b[0;34m(session, feed_dict, fetch_list, target_list, options, run_metadata)\u001b[0m\n\u001b[1;32m 1338\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1339\u001b[0m return tf_session.TF_Run(session, options, feed_dict, fetch_list,\n\u001b[0;32m-> 1340\u001b[0;31m target_list, status, run_metadata)\n\u001b[0m\u001b[1;32m 1341\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1342\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0m_prun_fn\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msession\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfeed_dict\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfetch_list\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
+ ]
+ }
+ ],
+ "source": [
+ "model_chords = Sequential()\n",
+ "model_notes = Sequential()\n",
+ "\n",
+ "model_chords.add(Embedding(794, 20, input_length=1, batch_input_shape=(1,1)))\n",
+ "model_notes.add(Embedding(128, 20, input_length=1, batch_input_shape=(1,1)))\n",
+ "\n",
+ "merged_model = Sequential()\n",
+ "merged = Merge([model_chords, model_notes])\n",
+ "merged_model.add(merged)\n",
+ "merged_model.add(LSTM(20, stateful=True))\n",
+ "merged_model.add(Dense(794, activation='softmax'))\n",
+ "merged_model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])\n",
+ "\n",
+ "y_real = []\n",
+ "y_pred = []\n",
+ "unique_chords = np.zeros(4) - 1\n",
+ "all_len = 0\n",
+ "acc = 0\n",
+ "\n",
+ "with open('gtp_dataset_final.pickle', 'rb') as input_file:\n",
+ " i = 0\n",
+ " while True:\n",
+ " try:\n",
+ " i += 1\n",
+ " song = ml.structures.Song()\n",
+ " song.undump(input_file)\n",
+ " chords = []\n",
+ " notes = []\n",
+ " flag_notes_chords = cut_song(song, notes, chords)\n",
+ " if (flag_notes_chords[0] == 0) :\n",
+ " continue\n",
+ " notes = flag_notes_chords[1]\n",
+ " chords = flag_notes_chords[2]\n",
+ " X = []\n",
+ " Y = []\n",
+ " X, Y = create_X_Y(chords)\n",
+ " X = norm_array(X, 4)\n",
+ " Y = norm_array(Y, 4)\n",
+ " notes_to_np(notes)\n",
+ " unique_chords = create_unique_chords(X, unique_chords)\n",
+ " unique_chords = create_unique_chords(Y, unique_chords)\n",
+ " unique_chords = unique_chords.reshape((-1, 4))\n",
+ " unique_chords = np.unique(unique_chords, axis=0)\n",
+ " unique_chords = list(unique_chords)\n",
+ " coded_X = []\n",
+ " coded_Y = []\n",
+ " coded_X = create_coded(X)\n",
+ " coded_Y = create_coded(Y)\n",
+ " encode(X, coded_X)\n",
+ " encode(Y, coded_Y)\n",
+ " X = np.array(coded_X)\n",
+ " y = np.array(coded_Y)\n",
+ " X_notes = notes\n",
+ " X_notes = X_notes[:len(X)]\n",
+ " X = X[:len(X_notes)]\n",
+ " y = y[:len(X)]\n",
+ " print(i)\n",
+ " if (i < 27100):\n",
+ " merged_model.fit([X, X_notes], y, epochs=1, batch_size=1, verbose=1)\n",
+ " acc += merged_model.evaluate([X, X_notes], y, batch_size=1)[1] * len(y)\n",
+ " all_len += len(y)\n",
+ " print(\"current_accuracy: \", acc / all_len)\n",
+ " merged_model.reset_states()\n",
+ " else:\n",
+ " y_pred.append(merged_model.predict([X, X_notes], batch_size=1, verbose=1).argmax(axis=1))\n",
+ " y_real.append(y)\n",
+ " \n",
+ " \n",
+ " except EOFError:\n",
+ " break"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "test_accuracy: 0.07658367475575166\n"
+ ]
+ }
+ ],
+ "source": [
+ "from sklearn.metrics import accuracy_score\n",
+ "\n",
+ "test_acc = 0\n",
+ "test_len = 0\n",
+ "\n",
+ "for y_r, y_p in zip(y_real, y_pred):\n",
+ " test_acc += accuracy_score(y_r, y_p) * len(y_r)\n",
+ " test_len += len(y_r)\n",
+ " \n",
+ "print(\"test_accuracy:\", test_acc / test_len)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df = pd.DataFrame(unique_chords)\n",
+ "df.to_csv(\"vocab.csv\")"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x = pd.read_csv(\"vocab.csv\")\n",
+ "x = x.values.T[1:].T"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "x"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "merged_model.save(\"NN_model.h5\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.5.2"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/ml/tmp.py b/ml/tmp.py
new file mode 100644
index 0000000..22c7725
--- /dev/null
+++ b/ml/tmp.py
@@ -0,0 +1,47 @@
+import pickle
+import numpy as np
+
+import pickle
+import numpy as np
+
+
+
+from sklearn.cross_validation import train_test_split
+from sklearn.linear_model import LogisticRegressionCV
+
+from keras.layers import LSTM
+from keras.layers import Embedding
+from keras.optimizers import RMSprop
+
+
+from keras.models import Sequential
+from keras.layers.core import Dense, Activation
+from keras.utils import np_utils
+from keras.engine.topology import Input
+
+from sklearn.metrics import accuracy_score
+from keras.layers import Concatenate, Merge
+
+import keras
+from keras.utils import to_categorical
+
+
+model_chords = Sequential()
+model_notes = Sequential()
+
+model_chords.add(Embedding(10000, 20, input_length=1, batch_input_shape=(1,1)))
+model_notes.add(Embedding(128, 20, input_length=1, batch_input_shape=(1,1)))
+
+model = Sequential()
+merged = Merge([model_chords, model_notes])
+model.add(merged)
+model.add(LSTM(20, stateful=True))
+model.add(Dense(10000, activation='softmax'))
+
+model.load_weights("NN_model.h5")
+model.compile(optimizer='adam',loss='sparse_categorical_crossentropy',metrics=['accuracy'])
+print(model.predict([np.array([1]), np.array([2.])], batch_size=1, verbose=1).argmax())
+
+print("OK")
+
+
diff --git a/ml/vocab.csv b/ml/vocab.csv
new file mode 100644
index 0000000..d2b8ecd
--- /dev/null
+++ b/ml/vocab.csv
@@ -0,0 +1,759 @@
+,0,1,2,3
+0,-1.0,-1.0,-1.0,-1.0
+1,0.0,-1.0,-1.0,-1.0
+2,0.0,1.0,-1.0,-1.0
+3,0.0,1.0,2.0,-1.0
+4,0.0,1.0,2.0,3.0
+5,0.0,1.0,2.0,4.0
+6,0.0,1.0,2.0,5.0
+7,0.0,1.0,2.0,6.0
+8,0.0,1.0,2.0,7.0
+9,0.0,1.0,2.0,8.0
+10,0.0,1.0,2.0,9.0
+11,0.0,1.0,2.0,10.0
+12,0.0,1.0,2.0,11.0
+13,0.0,1.0,3.0,-1.0
+14,0.0,1.0,3.0,4.0
+15,0.0,1.0,3.0,5.0
+16,0.0,1.0,3.0,6.0
+17,0.0,1.0,3.0,7.0
+18,0.0,1.0,3.0,8.0
+19,0.0,1.0,3.0,9.0
+20,0.0,1.0,3.0,10.0
+21,0.0,1.0,3.0,11.0
+22,0.0,1.0,4.0,-1.0
+23,0.0,1.0,4.0,5.0
+24,0.0,1.0,4.0,6.0
+25,0.0,1.0,4.0,7.0
+26,0.0,1.0,4.0,8.0
+27,0.0,1.0,4.0,9.0
+28,0.0,1.0,4.0,10.0
+29,0.0,1.0,4.0,11.0
+30,0.0,1.0,5.0,-1.0
+31,0.0,1.0,5.0,6.0
+32,0.0,1.0,5.0,8.0
+33,0.0,1.0,5.0,9.0
+34,0.0,1.0,5.0,10.0
+35,0.0,1.0,5.0,11.0
+36,0.0,1.0,6.0,-1.0
+37,0.0,1.0,6.0,7.0
+38,0.0,1.0,6.0,8.0
+39,0.0,1.0,6.0,9.0
+40,0.0,1.0,6.0,10.0
+41,0.0,1.0,6.0,11.0
+42,0.0,1.0,7.0,-1.0
+43,0.0,1.0,7.0,8.0
+44,0.0,1.0,7.0,9.0
+45,0.0,1.0,7.0,10.0
+46,0.0,1.0,7.0,11.0
+47,0.0,1.0,8.0,-1.0
+48,0.0,1.0,8.0,9.0
+49,0.0,1.0,8.0,10.0
+50,0.0,1.0,8.0,11.0
+51,0.0,1.0,9.0,-1.0
+52,0.0,1.0,9.0,10.0
+53,0.0,1.0,9.0,11.0
+54,0.0,1.0,10.0,-1.0
+55,0.0,1.0,10.0,11.0
+56,0.0,1.0,11.0,-1.0
+57,0.0,2.0,-1.0,-1.0
+58,0.0,2.0,3.0,-1.0
+59,0.0,2.0,3.0,4.0
+60,0.0,2.0,3.0,5.0
+61,0.0,2.0,3.0,6.0
+62,0.0,2.0,3.0,7.0
+63,0.0,2.0,3.0,8.0
+64,0.0,2.0,3.0,9.0
+65,0.0,2.0,3.0,10.0
+66,0.0,2.0,3.0,11.0
+67,0.0,2.0,4.0,-1.0
+68,0.0,2.0,4.0,5.0
+69,0.0,2.0,4.0,6.0
+70,0.0,2.0,4.0,7.0
+71,0.0,2.0,4.0,8.0
+72,0.0,2.0,4.0,9.0
+73,0.0,2.0,4.0,10.0
+74,0.0,2.0,4.0,11.0
+75,0.0,2.0,5.0,-1.0
+76,0.0,2.0,5.0,6.0
+77,0.0,2.0,5.0,7.0
+78,0.0,2.0,5.0,8.0
+79,0.0,2.0,5.0,9.0
+80,0.0,2.0,5.0,10.0
+81,0.0,2.0,5.0,11.0
+82,0.0,2.0,6.0,-1.0
+83,0.0,2.0,6.0,7.0
+84,0.0,2.0,6.0,8.0
+85,0.0,2.0,6.0,9.0
+86,0.0,2.0,6.0,10.0
+87,0.0,2.0,6.0,11.0
+88,0.0,2.0,7.0,-1.0
+89,0.0,2.0,7.0,8.0
+90,0.0,2.0,7.0,9.0
+91,0.0,2.0,7.0,10.0
+92,0.0,2.0,7.0,11.0
+93,0.0,2.0,8.0,-1.0
+94,0.0,2.0,8.0,9.0
+95,0.0,2.0,8.0,10.0
+96,0.0,2.0,8.0,11.0
+97,0.0,2.0,9.0,-1.0
+98,0.0,2.0,9.0,10.0
+99,0.0,2.0,9.0,11.0
+100,0.0,2.0,10.0,-1.0
+101,0.0,2.0,10.0,11.0
+102,0.0,2.0,11.0,-1.0
+103,0.0,3.0,-1.0,-1.0
+104,0.0,3.0,4.0,-1.0
+105,0.0,3.0,4.0,5.0
+106,0.0,3.0,4.0,6.0
+107,0.0,3.0,4.0,7.0
+108,0.0,3.0,4.0,8.0
+109,0.0,3.0,4.0,9.0
+110,0.0,3.0,4.0,10.0
+111,0.0,3.0,4.0,11.0
+112,0.0,3.0,5.0,-1.0
+113,0.0,3.0,5.0,6.0
+114,0.0,3.0,5.0,7.0
+115,0.0,3.0,5.0,8.0
+116,0.0,3.0,5.0,9.0
+117,0.0,3.0,5.0,10.0
+118,0.0,3.0,5.0,11.0
+119,0.0,3.0,6.0,-1.0
+120,0.0,3.0,6.0,7.0
+121,0.0,3.0,6.0,8.0
+122,0.0,3.0,6.0,9.0
+123,0.0,3.0,6.0,10.0
+124,0.0,3.0,6.0,11.0
+125,0.0,3.0,7.0,-1.0
+126,0.0,3.0,7.0,8.0
+127,0.0,3.0,7.0,9.0
+128,0.0,3.0,7.0,10.0
+129,0.0,3.0,7.0,11.0
+130,0.0,3.0,8.0,-1.0
+131,0.0,3.0,8.0,9.0
+132,0.0,3.0,8.0,10.0
+133,0.0,3.0,8.0,11.0
+134,0.0,3.0,9.0,-1.0
+135,0.0,3.0,9.0,10.0
+136,0.0,3.0,9.0,11.0
+137,0.0,3.0,10.0,-1.0
+138,0.0,3.0,10.0,11.0
+139,0.0,3.0,11.0,-1.0
+140,0.0,4.0,-1.0,-1.0
+141,0.0,4.0,5.0,-1.0
+142,0.0,4.0,5.0,6.0
+143,0.0,4.0,5.0,7.0
+144,0.0,4.0,5.0,8.0
+145,0.0,4.0,5.0,9.0
+146,0.0,4.0,5.0,10.0
+147,0.0,4.0,5.0,11.0
+148,0.0,4.0,6.0,-1.0
+149,0.0,4.0,6.0,7.0
+150,0.0,4.0,6.0,8.0
+151,0.0,4.0,6.0,9.0
+152,0.0,4.0,6.0,10.0
+153,0.0,4.0,6.0,11.0
+154,0.0,4.0,7.0,-1.0
+155,0.0,4.0,7.0,8.0
+156,0.0,4.0,7.0,9.0
+157,0.0,4.0,7.0,10.0
+158,0.0,4.0,7.0,11.0
+159,0.0,4.0,8.0,-1.0
+160,0.0,4.0,8.0,9.0
+161,0.0,4.0,8.0,10.0
+162,0.0,4.0,8.0,11.0
+163,0.0,4.0,9.0,-1.0
+164,0.0,4.0,9.0,10.0
+165,0.0,4.0,9.0,11.0
+166,0.0,4.0,10.0,-1.0
+167,0.0,4.0,10.0,11.0
+168,0.0,4.0,11.0,-1.0
+169,0.0,5.0,-1.0,-1.0
+170,0.0,5.0,6.0,-1.0
+171,0.0,5.0,6.0,7.0
+172,0.0,5.0,6.0,8.0
+173,0.0,5.0,6.0,9.0
+174,0.0,5.0,6.0,10.0
+175,0.0,5.0,6.0,11.0
+176,0.0,5.0,7.0,-1.0
+177,0.0,5.0,7.0,8.0
+178,0.0,5.0,7.0,9.0
+179,0.0,5.0,7.0,10.0
+180,0.0,5.0,7.0,11.0
+181,0.0,5.0,8.0,-1.0
+182,0.0,5.0,8.0,9.0
+183,0.0,5.0,8.0,10.0
+184,0.0,5.0,8.0,11.0
+185,0.0,5.0,9.0,-1.0
+186,0.0,5.0,9.0,10.0
+187,0.0,5.0,9.0,11.0
+188,0.0,5.0,10.0,-1.0
+189,0.0,5.0,10.0,11.0
+190,0.0,5.0,11.0,-1.0
+191,0.0,6.0,-1.0,-1.0
+192,0.0,6.0,7.0,-1.0
+193,0.0,6.0,7.0,8.0
+194,0.0,6.0,7.0,9.0
+195,0.0,6.0,7.0,10.0
+196,0.0,6.0,7.0,11.0
+197,0.0,6.0,8.0,-1.0
+198,0.0,6.0,8.0,9.0
+199,0.0,6.0,8.0,10.0
+200,0.0,6.0,8.0,11.0
+201,0.0,6.0,9.0,-1.0
+202,0.0,6.0,9.0,10.0
+203,0.0,6.0,9.0,11.0
+204,0.0,6.0,10.0,-1.0
+205,0.0,6.0,10.0,11.0
+206,0.0,6.0,11.0,-1.0
+207,0.0,7.0,-1.0,-1.0
+208,0.0,7.0,8.0,-1.0
+209,0.0,7.0,8.0,9.0
+210,0.0,7.0,8.0,10.0
+211,0.0,7.0,8.0,11.0
+212,0.0,7.0,9.0,-1.0
+213,0.0,7.0,9.0,10.0
+214,0.0,7.0,9.0,11.0
+215,0.0,7.0,10.0,-1.0
+216,0.0,7.0,10.0,11.0
+217,0.0,7.0,11.0,-1.0
+218,0.0,8.0,-1.0,-1.0
+219,0.0,8.0,9.0,-1.0
+220,0.0,8.0,9.0,10.0
+221,0.0,8.0,9.0,11.0
+222,0.0,8.0,10.0,-1.0
+223,0.0,8.0,10.0,11.0
+224,0.0,8.0,11.0,-1.0
+225,0.0,9.0,-1.0,-1.0
+226,0.0,9.0,10.0,-1.0
+227,0.0,9.0,10.0,11.0
+228,0.0,9.0,11.0,-1.0
+229,0.0,10.0,-1.0,-1.0
+230,0.0,10.0,11.0,-1.0
+231,0.0,11.0,-1.0,-1.0
+232,1.0,-1.0,-1.0,-1.0
+233,1.0,2.0,-1.0,-1.0
+234,1.0,2.0,3.0,-1.0
+235,1.0,2.0,3.0,4.0
+236,1.0,2.0,3.0,6.0
+237,1.0,2.0,3.0,8.0
+238,1.0,2.0,3.0,9.0
+239,1.0,2.0,3.0,11.0
+240,1.0,2.0,4.0,-1.0
+241,1.0,2.0,4.0,5.0
+242,1.0,2.0,4.0,6.0
+243,1.0,2.0,4.0,7.0
+244,1.0,2.0,4.0,8.0
+245,1.0,2.0,4.0,9.0
+246,1.0,2.0,4.0,10.0
+247,1.0,2.0,4.0,11.0
+248,1.0,2.0,5.0,-1.0
+249,1.0,2.0,5.0,6.0
+250,1.0,2.0,5.0,8.0
+251,1.0,2.0,5.0,9.0
+252,1.0,2.0,5.0,10.0
+253,1.0,2.0,5.0,11.0
+254,1.0,2.0,6.0,-1.0
+255,1.0,2.0,6.0,7.0
+256,1.0,2.0,6.0,8.0
+257,1.0,2.0,6.0,9.0
+258,1.0,2.0,6.0,10.0
+259,1.0,2.0,6.0,11.0
+260,1.0,2.0,7.0,-1.0
+261,1.0,2.0,7.0,8.0
+262,1.0,2.0,7.0,9.0
+263,1.0,2.0,7.0,10.0
+264,1.0,2.0,7.0,11.0
+265,1.0,2.0,8.0,-1.0
+266,1.0,2.0,8.0,9.0
+267,1.0,2.0,8.0,10.0
+268,1.0,2.0,8.0,11.0
+269,1.0,2.0,9.0,-1.0
+270,1.0,2.0,9.0,10.0
+271,1.0,2.0,9.0,11.0
+272,1.0,2.0,10.0,-1.0
+273,1.0,2.0,10.0,11.0
+274,1.0,2.0,11.0,-1.0
+275,1.0,3.0,-1.0,-1.0
+276,1.0,3.0,4.0,-1.0
+277,1.0,3.0,4.0,6.0
+278,1.0,3.0,4.0,8.0
+279,1.0,3.0,4.0,9.0
+280,1.0,3.0,4.0,10.0
+281,1.0,3.0,4.0,11.0
+282,1.0,3.0,5.0,-1.0
+283,1.0,3.0,5.0,6.0
+284,1.0,3.0,5.0,7.0
+285,1.0,3.0,5.0,8.0
+286,1.0,3.0,5.0,9.0
+287,1.0,3.0,5.0,10.0
+288,1.0,3.0,6.0,-1.0
+289,1.0,3.0,6.0,7.0
+290,1.0,3.0,6.0,8.0
+291,1.0,3.0,6.0,9.0
+292,1.0,3.0,6.0,10.0
+293,1.0,3.0,6.0,11.0
+294,1.0,3.0,7.0,-1.0
+295,1.0,3.0,7.0,8.0
+296,1.0,3.0,7.0,10.0
+297,1.0,3.0,8.0,-1.0
+298,1.0,3.0,8.0,9.0
+299,1.0,3.0,8.0,10.0
+300,1.0,3.0,8.0,11.0
+301,1.0,3.0,9.0,-1.0
+302,1.0,3.0,9.0,10.0
+303,1.0,3.0,9.0,11.0
+304,1.0,3.0,10.0,-1.0
+305,1.0,3.0,10.0,11.0
+306,1.0,3.0,11.0,-1.0
+307,1.0,4.0,-1.0,-1.0
+308,1.0,4.0,5.0,-1.0
+309,1.0,4.0,5.0,8.0
+310,1.0,4.0,5.0,9.0
+311,1.0,4.0,5.0,10.0
+312,1.0,4.0,5.0,11.0
+313,1.0,4.0,6.0,-1.0
+314,1.0,4.0,6.0,7.0
+315,1.0,4.0,6.0,8.0
+316,1.0,4.0,6.0,9.0
+317,1.0,4.0,6.0,10.0
+318,1.0,4.0,6.0,11.0
+319,1.0,4.0,7.0,-1.0
+320,1.0,4.0,7.0,9.0
+321,1.0,4.0,7.0,10.0
+322,1.0,4.0,7.0,11.0
+323,1.0,4.0,8.0,-1.0
+324,1.0,4.0,8.0,9.0
+325,1.0,4.0,8.0,10.0
+326,1.0,4.0,8.0,11.0
+327,1.0,4.0,9.0,-1.0
+328,1.0,4.0,9.0,10.0
+329,1.0,4.0,9.0,11.0
+330,1.0,4.0,10.0,-1.0
+331,1.0,4.0,10.0,11.0
+332,1.0,4.0,11.0,-1.0
+333,1.0,5.0,-1.0,-1.0
+334,1.0,5.0,6.0,-1.0
+335,1.0,5.0,6.0,7.0
+336,1.0,5.0,6.0,8.0
+337,1.0,5.0,6.0,10.0
+338,1.0,5.0,6.0,11.0
+339,1.0,5.0,7.0,-1.0
+340,1.0,5.0,7.0,8.0
+341,1.0,5.0,7.0,9.0
+342,1.0,5.0,7.0,10.0
+343,1.0,5.0,8.0,-1.0
+344,1.0,5.0,8.0,9.0
+345,1.0,5.0,8.0,10.0
+346,1.0,5.0,8.0,11.0
+347,1.0,5.0,9.0,-1.0
+348,1.0,5.0,9.0,10.0
+349,1.0,5.0,9.0,11.0
+350,1.0,5.0,10.0,-1.0
+351,1.0,5.0,10.0,11.0
+352,1.0,5.0,11.0,-1.0
+353,1.0,6.0,-1.0,-1.0
+354,1.0,6.0,7.0,-1.0
+355,1.0,6.0,7.0,8.0
+356,1.0,6.0,7.0,9.0
+357,1.0,6.0,7.0,10.0
+358,1.0,6.0,7.0,11.0
+359,1.0,6.0,8.0,-1.0
+360,1.0,6.0,8.0,9.0
+361,1.0,6.0,8.0,10.0
+362,1.0,6.0,8.0,11.0
+363,1.0,6.0,9.0,-1.0
+364,1.0,6.0,9.0,10.0
+365,1.0,6.0,9.0,11.0
+366,1.0,6.0,10.0,-1.0
+367,1.0,6.0,10.0,11.0
+368,1.0,6.0,11.0,-1.0
+369,1.0,7.0,-1.0,-1.0
+370,1.0,7.0,8.0,-1.0
+371,1.0,7.0,8.0,9.0
+372,1.0,7.0,8.0,11.0
+373,1.0,7.0,9.0,-1.0
+374,1.0,7.0,9.0,10.0
+375,1.0,7.0,9.0,11.0
+376,1.0,7.0,10.0,-1.0
+377,1.0,7.0,10.0,11.0
+378,1.0,7.0,11.0,-1.0
+379,1.0,8.0,-1.0,-1.0
+380,1.0,8.0,9.0,-1.0
+381,1.0,8.0,9.0,10.0
+382,1.0,8.0,9.0,11.0
+383,1.0,8.0,10.0,-1.0
+384,1.0,8.0,10.0,11.0
+385,1.0,8.0,11.0,-1.0
+386,1.0,9.0,-1.0,-1.0
+387,1.0,9.0,10.0,-1.0
+388,1.0,9.0,10.0,11.0
+389,1.0,9.0,11.0,-1.0
+390,1.0,10.0,-1.0,-1.0
+391,1.0,10.0,11.0,-1.0
+392,1.0,11.0,-1.0,-1.0
+393,2.0,-1.0,-1.0,-1.0
+394,2.0,3.0,-1.0,-1.0
+395,2.0,3.0,4.0,-1.0
+396,2.0,3.0,4.0,5.0
+397,2.0,3.0,4.0,6.0
+398,2.0,3.0,4.0,7.0
+399,2.0,3.0,4.0,8.0
+400,2.0,3.0,4.0,9.0
+401,2.0,3.0,4.0,10.0
+402,2.0,3.0,4.0,11.0
+403,2.0,3.0,5.0,-1.0
+404,2.0,3.0,5.0,6.0
+405,2.0,3.0,5.0,7.0
+406,2.0,3.0,5.0,8.0
+407,2.0,3.0,5.0,9.0
+408,2.0,3.0,5.0,10.0
+409,2.0,3.0,5.0,11.0
+410,2.0,3.0,6.0,-1.0
+411,2.0,3.0,6.0,7.0
+412,2.0,3.0,6.0,8.0
+413,2.0,3.0,6.0,9.0
+414,2.0,3.0,6.0,10.0
+415,2.0,3.0,6.0,11.0
+416,2.0,3.0,7.0,-1.0
+417,2.0,3.0,7.0,9.0
+418,2.0,3.0,7.0,10.0
+419,2.0,3.0,7.0,11.0
+420,2.0,3.0,8.0,-1.0
+421,2.0,3.0,8.0,9.0
+422,2.0,3.0,8.0,10.0
+423,2.0,3.0,8.0,11.0
+424,2.0,3.0,9.0,-1.0
+425,2.0,3.0,9.0,10.0
+426,2.0,3.0,9.0,11.0
+427,2.0,3.0,10.0,-1.0
+428,2.0,3.0,10.0,11.0
+429,2.0,3.0,11.0,-1.0
+430,2.0,4.0,-1.0,-1.0
+431,2.0,4.0,5.0,-1.0
+432,2.0,4.0,5.0,6.0
+433,2.0,4.0,5.0,7.0
+434,2.0,4.0,5.0,8.0
+435,2.0,4.0,5.0,9.0
+436,2.0,4.0,5.0,10.0
+437,2.0,4.0,5.0,11.0
+438,2.0,4.0,6.0,-1.0
+439,2.0,4.0,6.0,7.0
+440,2.0,4.0,6.0,8.0
+441,2.0,4.0,6.0,9.0
+442,2.0,4.0,6.0,10.0
+443,2.0,4.0,6.0,11.0
+444,2.0,4.0,7.0,-1.0
+445,2.0,4.0,7.0,8.0
+446,2.0,4.0,7.0,9.0
+447,2.0,4.0,7.0,10.0
+448,2.0,4.0,7.0,11.0
+449,2.0,4.0,8.0,-1.0
+450,2.0,4.0,8.0,9.0
+451,2.0,4.0,8.0,10.0
+452,2.0,4.0,8.0,11.0
+453,2.0,4.0,9.0,-1.0
+454,2.0,4.0,9.0,10.0
+455,2.0,4.0,9.0,11.0
+456,2.0,4.0,10.0,-1.0
+457,2.0,4.0,10.0,11.0
+458,2.0,4.0,11.0,-1.0
+459,2.0,5.0,-1.0,-1.0
+460,2.0,5.0,6.0,-1.0
+461,2.0,5.0,6.0,7.0
+462,2.0,5.0,6.0,8.0
+463,2.0,5.0,6.0,9.0
+464,2.0,5.0,6.0,10.0
+465,2.0,5.0,6.0,11.0
+466,2.0,5.0,7.0,-1.0
+467,2.0,5.0,7.0,8.0
+468,2.0,5.0,7.0,9.0
+469,2.0,5.0,7.0,10.0
+470,2.0,5.0,7.0,11.0
+471,2.0,5.0,8.0,-1.0
+472,2.0,5.0,8.0,9.0
+473,2.0,5.0,8.0,10.0
+474,2.0,5.0,8.0,11.0
+475,2.0,5.0,9.0,-1.0
+476,2.0,5.0,9.0,10.0
+477,2.0,5.0,9.0,11.0
+478,2.0,5.0,10.0,-1.0
+479,2.0,5.0,10.0,11.0
+480,2.0,5.0,11.0,-1.0
+481,2.0,6.0,-1.0,-1.0
+482,2.0,6.0,7.0,-1.0
+483,2.0,6.0,7.0,9.0
+484,2.0,6.0,7.0,10.0
+485,2.0,6.0,7.0,11.0
+486,2.0,6.0,8.0,-1.0
+487,2.0,6.0,8.0,9.0
+488,2.0,6.0,8.0,10.0
+489,2.0,6.0,8.0,11.0
+490,2.0,6.0,9.0,-1.0
+491,2.0,6.0,9.0,10.0
+492,2.0,6.0,9.0,11.0
+493,2.0,6.0,10.0,-1.0
+494,2.0,6.0,10.0,11.0
+495,2.0,6.0,11.0,-1.0
+496,2.0,7.0,-1.0,-1.0
+497,2.0,7.0,8.0,-1.0
+498,2.0,7.0,8.0,9.0
+499,2.0,7.0,8.0,10.0
+500,2.0,7.0,8.0,11.0
+501,2.0,7.0,9.0,-1.0
+502,2.0,7.0,9.0,10.0
+503,2.0,7.0,9.0,11.0
+504,2.0,7.0,10.0,-1.0
+505,2.0,7.0,10.0,11.0
+506,2.0,7.0,11.0,-1.0
+507,2.0,8.0,-1.0,-1.0
+508,2.0,8.0,9.0,-1.0
+509,2.0,8.0,9.0,10.0
+510,2.0,8.0,9.0,11.0
+511,2.0,8.0,10.0,-1.0
+512,2.0,8.0,10.0,11.0
+513,2.0,8.0,11.0,-1.0
+514,2.0,9.0,-1.0,-1.0
+515,2.0,9.0,10.0,-1.0
+516,2.0,9.0,10.0,11.0
+517,2.0,9.0,11.0,-1.0
+518,2.0,10.0,-1.0,-1.0
+519,2.0,10.0,11.0,-1.0
+520,2.0,11.0,-1.0,-1.0
+521,3.0,-1.0,-1.0,-1.0
+522,3.0,4.0,-1.0,-1.0
+523,3.0,4.0,5.0,-1.0
+524,3.0,4.0,5.0,8.0
+525,3.0,4.0,5.0,10.0
+526,3.0,4.0,5.0,11.0
+527,3.0,4.0,6.0,-1.0
+528,3.0,4.0,6.0,7.0
+529,3.0,4.0,6.0,8.0
+530,3.0,4.0,6.0,9.0
+531,3.0,4.0,6.0,10.0
+532,3.0,4.0,6.0,11.0
+533,3.0,4.0,7.0,-1.0
+534,3.0,4.0,7.0,9.0
+535,3.0,4.0,7.0,10.0
+536,3.0,4.0,7.0,11.0
+537,3.0,4.0,8.0,-1.0
+538,3.0,4.0,8.0,9.0
+539,3.0,4.0,8.0,10.0
+540,3.0,4.0,8.0,11.0
+541,3.0,4.0,9.0,-1.0
+542,3.0,4.0,9.0,10.0
+543,3.0,4.0,9.0,11.0
+544,3.0,4.0,10.0,-1.0
+545,3.0,4.0,10.0,11.0
+546,3.0,4.0,11.0,-1.0
+547,3.0,5.0,-1.0,-1.0
+548,3.0,5.0,6.0,-1.0
+549,3.0,5.0,6.0,7.0
+550,3.0,5.0,6.0,8.0
+551,3.0,5.0,6.0,9.0
+552,3.0,5.0,6.0,10.0
+553,3.0,5.0,6.0,11.0
+554,3.0,5.0,7.0,-1.0
+555,3.0,5.0,7.0,8.0
+556,3.0,5.0,7.0,9.0
+557,3.0,5.0,7.0,10.0
+558,3.0,5.0,7.0,11.0
+559,3.0,5.0,8.0,-1.0
+560,3.0,5.0,8.0,9.0
+561,3.0,5.0,8.0,10.0
+562,3.0,5.0,8.0,11.0
+563,3.0,5.0,9.0,-1.0
+564,3.0,5.0,9.0,10.0
+565,3.0,5.0,9.0,11.0
+566,3.0,5.0,10.0,-1.0
+567,3.0,5.0,10.0,11.0
+568,3.0,5.0,11.0,-1.0
+569,3.0,6.0,-1.0,-1.0
+570,3.0,6.0,7.0,-1.0
+571,3.0,6.0,7.0,8.0
+572,3.0,6.0,7.0,10.0
+573,3.0,6.0,7.0,11.0
+574,3.0,6.0,8.0,-1.0
+575,3.0,6.0,8.0,9.0
+576,3.0,6.0,8.0,10.0
+577,3.0,6.0,8.0,11.0
+578,3.0,6.0,9.0,-1.0
+579,3.0,6.0,9.0,10.0
+580,3.0,6.0,9.0,11.0
+581,3.0,6.0,10.0,-1.0
+582,3.0,6.0,10.0,11.0
+583,3.0,6.0,11.0,-1.0
+584,3.0,7.0,-1.0,-1.0
+585,3.0,7.0,8.0,-1.0
+586,3.0,7.0,8.0,10.0
+587,3.0,7.0,8.0,11.0
+588,3.0,7.0,9.0,-1.0
+589,3.0,7.0,10.0,-1.0
+590,3.0,7.0,10.0,11.0
+591,3.0,7.0,11.0,-1.0
+592,3.0,8.0,-1.0,-1.0
+593,3.0,8.0,9.0,-1.0
+594,3.0,8.0,9.0,10.0
+595,3.0,8.0,9.0,11.0
+596,3.0,8.0,10.0,-1.0
+597,3.0,8.0,10.0,11.0
+598,3.0,8.0,11.0,-1.0
+599,3.0,9.0,-1.0,-1.0
+600,3.0,9.0,10.0,-1.0
+601,3.0,9.0,10.0,11.0
+602,3.0,9.0,11.0,-1.0
+603,3.0,10.0,-1.0,-1.0
+604,3.0,10.0,11.0,-1.0
+605,3.0,11.0,-1.0,-1.0
+606,4.0,-1.0,-1.0,-1.0
+607,4.0,5.0,-1.0,-1.0
+608,4.0,5.0,6.0,-1.0
+609,4.0,5.0,6.0,8.0
+610,4.0,5.0,6.0,9.0
+611,4.0,5.0,6.0,10.0
+612,4.0,5.0,6.0,11.0
+613,4.0,5.0,7.0,-1.0
+614,4.0,5.0,7.0,9.0
+615,4.0,5.0,7.0,10.0
+616,4.0,5.0,7.0,11.0
+617,4.0,5.0,8.0,-1.0
+618,4.0,5.0,8.0,9.0
+619,4.0,5.0,8.0,10.0
+620,4.0,5.0,8.0,11.0
+621,4.0,5.0,9.0,-1.0
+622,4.0,5.0,9.0,10.0
+623,4.0,5.0,9.0,11.0
+624,4.0,5.0,10.0,-1.0
+625,4.0,5.0,10.0,11.0
+626,4.0,5.0,11.0,-1.0
+627,4.0,6.0,-1.0,-1.0
+628,4.0,6.0,7.0,-1.0
+629,4.0,6.0,7.0,8.0
+630,4.0,6.0,7.0,9.0
+631,4.0,6.0,7.0,10.0
+632,4.0,6.0,7.0,11.0
+633,4.0,6.0,8.0,-1.0
+634,4.0,6.0,8.0,9.0
+635,4.0,6.0,8.0,10.0
+636,4.0,6.0,8.0,11.0
+637,4.0,6.0,9.0,-1.0
+638,4.0,6.0,9.0,10.0
+639,4.0,6.0,9.0,11.0
+640,4.0,6.0,10.0,-1.0
+641,4.0,6.0,10.0,11.0
+642,4.0,6.0,11.0,-1.0
+643,4.0,7.0,-1.0,-1.0
+644,4.0,7.0,8.0,-1.0
+645,4.0,7.0,8.0,9.0
+646,4.0,7.0,8.0,11.0
+647,4.0,7.0,9.0,-1.0
+648,4.0,7.0,9.0,10.0
+649,4.0,7.0,9.0,11.0
+650,4.0,7.0,10.0,-1.0
+651,4.0,7.0,10.0,11.0
+652,4.0,7.0,11.0,-1.0
+653,4.0,8.0,-1.0,-1.0
+654,4.0,8.0,9.0,-1.0
+655,4.0,8.0,9.0,10.0
+656,4.0,8.0,9.0,11.0
+657,4.0,8.0,10.0,-1.0
+658,4.0,8.0,10.0,11.0
+659,4.0,8.0,11.0,-1.0
+660,4.0,9.0,-1.0,-1.0
+661,4.0,9.0,10.0,-1.0
+662,4.0,9.0,10.0,11.0
+663,4.0,9.0,11.0,-1.0
+664,4.0,10.0,-1.0,-1.0
+665,4.0,10.0,11.0,-1.0
+666,4.0,11.0,-1.0,-1.0
+667,5.0,-1.0,-1.0,-1.0
+668,5.0,6.0,-1.0,-1.0
+669,5.0,6.0,7.0,-1.0
+670,5.0,6.0,7.0,8.0
+671,5.0,6.0,7.0,9.0
+672,5.0,6.0,7.0,10.0
+673,5.0,6.0,7.0,11.0
+674,5.0,6.0,8.0,-1.0
+675,5.0,6.0,8.0,9.0
+676,5.0,6.0,8.0,10.0
+677,5.0,6.0,8.0,11.0
+678,5.0,6.0,9.0,-1.0
+679,5.0,6.0,9.0,10.0
+680,5.0,6.0,9.0,11.0
+681,5.0,6.0,10.0,-1.0
+682,5.0,6.0,10.0,11.0
+683,5.0,6.0,11.0,-1.0
+684,5.0,7.0,-1.0,-1.0
+685,5.0,7.0,8.0,-1.0
+686,5.0,7.0,8.0,9.0
+687,5.0,7.0,8.0,10.0
+688,5.0,7.0,9.0,-1.0
+689,5.0,7.0,9.0,10.0
+690,5.0,7.0,9.0,11.0
+691,5.0,7.0,10.0,-1.0
+692,5.0,7.0,10.0,11.0
+693,5.0,7.0,11.0,-1.0
+694,5.0,8.0,-1.0,-1.0
+695,5.0,8.0,9.0,-1.0
+696,5.0,8.0,9.0,10.0
+697,5.0,8.0,10.0,-1.0
+698,5.0,8.0,10.0,11.0
+699,5.0,8.0,11.0,-1.0
+700,5.0,9.0,-1.0,-1.0
+701,5.0,9.0,10.0,-1.0
+702,5.0,9.0,10.0,11.0
+703,5.0,9.0,11.0,-1.0
+704,5.0,10.0,-1.0,-1.0
+705,5.0,10.0,11.0,-1.0
+706,5.0,11.0,-1.0,-1.0
+707,6.0,-1.0,-1.0,-1.0
+708,6.0,7.0,-1.0,-1.0
+709,6.0,7.0,8.0,-1.0
+710,6.0,7.0,8.0,10.0
+711,6.0,7.0,8.0,11.0
+712,6.0,7.0,9.0,-1.0
+713,6.0,7.0,9.0,10.0
+714,6.0,7.0,9.0,11.0
+715,6.0,7.0,10.0,-1.0
+716,6.0,7.0,10.0,11.0
+717,6.0,7.0,11.0,-1.0
+718,6.0,8.0,-1.0,-1.0
+719,6.0,8.0,9.0,-1.0
+720,6.0,8.0,9.0,11.0
+721,6.0,8.0,10.0,-1.0
+722,6.0,8.0,10.0,11.0
+723,6.0,8.0,11.0,-1.0
+724,6.0,9.0,-1.0,-1.0
+725,6.0,9.0,10.0,-1.0
+726,6.0,9.0,11.0,-1.0
+727,6.0,10.0,-1.0,-1.0
+728,6.0,10.0,11.0,-1.0
+729,6.0,11.0,-1.0,-1.0
+730,7.0,-1.0,-1.0,-1.0
+731,7.0,8.0,-1.0,-1.0
+732,7.0,8.0,9.0,-1.0
+733,7.0,8.0,9.0,11.0
+734,7.0,8.0,10.0,-1.0
+735,7.0,8.0,11.0,-1.0
+736,7.0,9.0,-1.0,-1.0
+737,7.0,9.0,10.0,-1.0
+738,7.0,9.0,10.0,11.0
+739,7.0,9.0,11.0,-1.0
+740,7.0,10.0,-1.0,-1.0
+741,7.0,10.0,11.0,-1.0
+742,7.0,11.0,-1.0,-1.0
+743,8.0,-1.0,-1.0,-1.0
+744,8.0,9.0,-1.0,-1.0
+745,8.0,9.0,10.0,-1.0
+746,8.0,9.0,10.0,11.0
+747,8.0,9.0,11.0,-1.0
+748,8.0,10.0,-1.0,-1.0
+749,8.0,10.0,11.0,-1.0
+750,8.0,11.0,-1.0,-1.0
+751,9.0,-1.0,-1.0,-1.0
+752,9.0,10.0,-1.0,-1.0
+753,9.0,10.0,11.0,-1.0
+754,9.0,11.0,-1.0,-1.0
+755,10.0,-1.0,-1.0,-1.0
+756,10.0,11.0,-1.0,-1.0
+757,11.0,-1.0,-1.0,-1.0
diff --git a/production/NN_model.h5 b/production/NN_model.h5
new file mode 100644
index 0000000..9a1f7cb
Binary files /dev/null and b/production/NN_model.h5 differ
diff --git a/production/accompanist.py b/production/accompanist.py
index 6c4b1c5..1ec459e 100644
--- a/production/accompanist.py
+++ b/production/accompanist.py
@@ -2,9 +2,9 @@
import time
from time import sleep
from multiprocessing import Queue, Value
-from listener import Listener
-from player import Player
-from chord_predictor import ChordPredictor
+from production.listener import Listener
+from production.player import Player
+from production.chord_predictor import ChordPredictor
default_tempo = 124
@@ -17,16 +17,19 @@
class Accompanist:
def __init__(self):
- self.queue_in = Queue()
- self.queue_out = Queue()
- self.predictor_queue = Queue()
+ self.queue_into_listener = None
+ self.queue_from_listener_to_predictor = Queue()
+ self.queue_from_predictor_to_player = Queue()
self.running = Value('i', False)
self.tempo = Value('f', default_tempo)
self.deadline = Value('f', 0)
- self.player = Player(self.queue_out, self.running, self.tempo, self.deadline)
- self.predictor = ChordPredictor(self.queue_in, self.queue_out)
- self.listener = Listener(self.queue_in, self.running, self.tempo, self.deadline)
+ self.websocket = None
+
+ self.player = Player(self.queue_from_predictor_to_player, self.running, self.tempo, self.deadline)
+ self.predictor = ChordPredictor(self.queue_from_listener_to_predictor, self.queue_from_predictor_to_player, self.deadline)
+ self.listener = Listener(self.queue_into_listener, self.queue_from_listener_to_predictor, self.running,
+ self.tempo, self.deadline)
def run(self):
self.running.value = True
@@ -39,8 +42,9 @@ def stop(self):
self.player.stop()
self.listener.stop()
self.predictor.stop()
- self.queue_in = Queue()
- self.queue_out = Queue()
+ self.queue_into_listener = None
+ self.queue_from_listener_to_predictor = Queue()
+ self.queue_from_predictor_to_player = Queue()
def set_tempo(self, tempo=default_tempo):
self.tempo.value = tempo
@@ -48,6 +52,17 @@ def set_tempo(self, tempo=default_tempo):
def set_deadline(self, deadline=0):
self.deadline.value = deadline
+ def set_queue_in(self, queue):
+ self.queue_into_listener = queue
+ self.listener.set_queue_in(queue)
+
+ def set_websocket(self, websocket):
+ self.websocket = websocket
+ self.player.set_websocket(websocket)
+
+ def set_web_delay(self, delay):
+ self.listener.set_web_delay(delay)
+
player = None
listener = None
predictor = None
@@ -63,7 +78,7 @@ def set_deadline(self, deadline=0):
a = Accompanist()
start_time = time.monotonic()
a.run()
- sleep(50)
+ sleep(5)
a.stop()
'''q = a.player
start_time = time.monotonic()
diff --git a/production/chord_predictor.py b/production/chord_predictor.py
index 27086c6..3d19a04 100644
--- a/production/chord_predictor.py
+++ b/production/chord_predictor.py
@@ -1,11 +1,20 @@
-import pickle
+import time
import numpy as np
-from structures import Note, Chord
+import pandas as pd
+from production.structures import Note, Chord
from multiprocessing import Queue, Process, Value
+from keras.models import Sequential
+from keras.layers import Embedding
+from keras.layers import Merge
+from keras.layers import LSTM
+from keras.layers.core import Dense
+
defualt_predicted_len = 128
defualt_velocity = 100
+prediction_time = 0.01
+
def chord_notes(chord):
def interval(start, interval):
@@ -44,21 +53,25 @@ def interval(start, interval):
def run_queue(predictor):
- predictor.load_model("rf_nottingham.pkl")
+ predictor.load_model("production/NN_model.h5")
+ predictor.load_unique_chords("production/vocab.csv")
while predictor.running.value:
- if not predictor.queue_in.empty():
- # print("predictor get")
- chord = predictor.try_predict()
- if chord is not None:
- predictor.queue_out.put(chord, defualt_predicted_len, defualt_velocity)
- # print("predictor put")
+ # print("predictor get")
+ chord = predictor.try_predict()
+ if chord is not None:
+ predictor.queue_out.put(chord, defualt_predicted_len,
+ defualt_velocity)
+ # print("predictor put")
+ time.sleep(0.01)
class ChordPredictor:
model = None
+ unique_chords = None
- def __init__(self, queue_in=Queue(), queue_out=Queue()):
+ def __init__(self, queue_in=Queue(), queue_out=Queue(),
+ deadline=Value('f', 0)):
self.queue_in = queue_in
self.queue_out = queue_out
self.running = Value('i', False)
@@ -67,6 +80,10 @@ def __init__(self, queue_in=Queue(), queue_out=Queue()):
self.chords_len_before_4_4 = 0
self.second_downbeat = False
self.chords_list = []
+ self.deadline = deadline
+ self.prediction_for_beat = False
+ self.currently_playing_chord_num = 0 # TODO
+ self.predicted_chord_num = 0
def run(self):
self.running.value = True
@@ -77,57 +94,78 @@ def stop(self):
self.running.value = False
self.process.join()
- def load_model(self, filename):
- with open(filename, 'rb') as fid:
- self.model = pickle.load(fid)
+ def create_coded(self, X):
+ return np.zeros(len(X))
- def try_predict(self):
- chord = self.queue_in.get()
- # print(chord.downbeat)
- if chord.downbeat is False and self.second_downbeat is False:
- return None
- self.chords_list.append(chord)
- self.chords_len += chord.duration
- if chord.downbeat:
- if not self.second_downbeat:
- self.second_downbeat = True
+ def encode(self, X, coded_X):
+ for j, k in enumerate(X):
+ for i, unique_chord in enumerate(self.unique_chords):
+ if np.array_equal(k, unique_chord):
+ coded_X[j] = i
+
+ def load_model(self, filename):
+ model_chords = Sequential()
+ model_notes = Sequential()
+
+ model_chords.add(Embedding(794, 20, input_length=1,
+ batch_input_shape=(1, 1)))
+ model_notes.add(Embedding(128, 20, input_length=1,
+ batch_input_shape=(1, 1)))
+
+ self.model = Sequential()
+ merged = Merge([model_chords, model_notes])
+ self.model.add(merged)
+ self.model.add(LSTM(20, stateful=True))
+ self.model.add(Dense(794, activation='softmax'))
+
+ self.model.load_weights(filename)
+ self.model.compile(optimizer='adam',
+ loss='sparse_categorical_crossentropy',
+ metrics=['accuracy'])
+
+ def load_unique_chords(self, filename):
+ self.unique_chords = pd.read_csv(filename)
+ self.unique_chords = self.unique_chords.values.T[1:].T
+
+ def make_suitable(self, notes_list):
+ new_list = [el + 60 for el in filter(lambda x: x != -1, notes_list)]
+ print(new_list)
+ # TODO
+ if len(new_list) == 1:
+ note = new_list[0]
+ new_list.extend([note + 4, note + 7, note + 12]) # major triad
+ elif len(new_list) == 2:
+ note1 = new_list[0]
+ note2 = new_list[1]
+ new_list.extend([note1 + 12, note2 + 12])
+ elif len(new_list) == 3:
+ note = new_list[0]
+ if note + 12 < new_list[2]:
+ new_list.append(note + 12)
else:
- self.chords_count_before_4_4 = len(self.chords_list)
- self.chords_len_before_4_4 = 128 # self.chords_len
- if self.chords_len > 128 * 2 * 7 / 8:
- prediction = self.predict(self.chords_list)
- self.chords_list = self.chords_list[self.chords_count_before_4_4:]
- self.chords_len = self.chords_len - self.chords_len_before_4_4
- self.chords_len_before_4_4 = self.chords_len
- self.chords_count_before_4_4 = len(self.chords_list)
- return prediction
+ new_list.append(new_list[2] + 3)
else:
- return None
-
- def predict(self, chords_list):
- # передаётся два такта, кроме последней доли (то есть от двух тактов доступно 7/8 или 14/16 информации)
- numbers = np.array([]) # midi numbers!
- for chord in chords_list:
- num_notes_to_add = round(chord.len() / 8)
- note = chord.notes[0]
- for i in range(num_notes_to_add):
- numbers = np.append(numbers, note.number)
- # shift midi notes to our notes
- numbers = numbers % 12
- # generate beat
- beat = np.hstack([np.ones(4), np.zeros(12), np.ones(4), np.ones(8)])
- if numbers.size != 28:
- # print("Number of notes is wrong: " + str(numbers_size))
- if numbers.size < 28:
- numbers = np.hstack([numbers, np.zeros(28 - len(numbers)) + 12])
- else:
- numbers = numbers[:28]
- # сначала номера, потом биты
- chord = self.model.predict(np.hstack([numbers, beat]).reshape(1, -1))
- # print(chord)
- notes = chord_notes(chord[0])
- list_notes = []
- for note in notes:
- list_notes.append(Note(note + 12 * 4))
- # here you need to set the duration of the chord
- return Chord(list_notes, 128, int(np.mean(list(map(lambda chord: chord.velocity, chords_list)))))
+ new_list = new_list[:4]
+ return list(map(lambda x: Note(int(x)), new_list))
+
+ def try_predict(self):
+ while not self.queue_in.empty():
+ notes = self.queue_in.get()
+ note = notes.notes[0]
+ print(note)
+ self.predicted_chord_num = self.model.predict([
+ np.array([self.currently_playing_chord_num]),
+ np.array([note.number])],
+ batch_size=1,
+ verbose=1).argmax()
+ print("next:", self.predicted_chord_num)
+ if notes.downbeat:
+ self.prediction_for_beat = False
+ if (time.monotonic() > self.deadline.value - prediction_time and
+ self.prediction_for_beat is False):
+ chord_notes = self.unique_chords[self.predicted_chord_num]
+ self.prediction_for_beat = True
+ self.currently_playing_chord_num = self.predicted_chord_num
+ if self.predicted_chord_num > 0:
+ return Chord(self.make_suitable(chord_notes), 128, 128)
+ return None
diff --git a/production/chord_predictor_test.py b/production/chord_predictor_test.py
deleted file mode 100644
index c804611..0000000
--- a/production/chord_predictor_test.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from chord_predictor import *
-
-predictor = ChordPredictor()
-predictor.load_model("rf_nottingham.pkl")
-
-# создаём список аккордов
-
-chords_list = []
-for i in range(28):
- chords_list.append(Chord([Note(60)], 8, 1))
-
-print(predictor.predict(chords_list))
diff --git a/production/listener.py b/production/listener.py
index e13d178..a509845 100644
--- a/production/listener.py
+++ b/production/listener.py
@@ -5,8 +5,8 @@
import pyaudio
from time import sleep
from multiprocessing import Queue, Process, Value
-from aubio import notes, onset
-from structures import Note, Chord
+from aubio import notes, onset, tempo
+from production.structures import Note, Chord
"""
1 beat in bpm is 1/4 of musical beat
@@ -21,88 +21,151 @@
hop_s = win_s // 4
buffer_size = hop_s
+alpha = 0.1
+
def from_ms_to_our_time(time, bpm):
return int(time * (32 * bpm) / (60 * 1000))
+def find_closest_onset_velocity(beat, onsets_velocity):
+ minimum_difference = 100000
+ closest_velocity = -1
+ for local_onset, velocity in onsets_velocity:
+ if beat - local_onset < minimum_difference:
+ closest_velocity = velocity
+ minimum_difference = beat - local_onset
+
+ return closest_velocity
+
+
def run_queue_in(listener):
- p = pyaudio.PyAudio()
- # open stream
- pyaudio_format = pyaudio.paFloat32
- n_channels = 1
- stream = p.open(format=pyaudio_format,
- channels=n_channels,
- rate=sample_rate,
- input=True,
- frames_per_buffer=buffer_size)
+ stream = None
+ if not listener.input_from_queue:
+ p = pyaudio.PyAudio()
+ # open stream
+ pyaudio_format = pyaudio.paFloat32
+ n_channels = 1
+ stream = p.open(format=pyaudio_format,
+ channels=n_channels,
+ rate=sample_rate,
+ input=True,
+ frames_per_buffer=buffer_size)
'''
s = aubio.source('/home/nikolay/pahanini.mp3', sample_rate, buffer_size)
'''
notes_o = notes("default", win_s, hop_s, sample_rate)
onset_o = onset("default", win_s, hop_s, sample_rate)
- temp_o = aubio.tempo("specdiff", win_s, hop_s, sample_rate)
+ temp_o = tempo("specdiff", win_s, hop_s, sample_rate)
last_onset = 0
beats = []
last_beat = 0
count_beat = 0
- last_downbeat = 0
+ # last_downbeat = 0
bar_start = False
# the stream is read until you call stop
prev_time = 0
start_time = time.monotonic()
- while (listener.running.value):
- # read data from audio input
- audiobuffer = stream.read(buffer_size, exception_on_overflow=False)
- samples = np.fromstring(audiobuffer, dtype=np.float32)
- # samples = audiobuffer
- if (onset_o(samples)):
+ # downbeat's structures' initialization
+ latest_onsets_velocities = []
+ i_onset = 0
+ n_onset = 16
+ for i in range(0, n_onset):
+ latest_onsets_velocities.append((0.0, 0))
+
+ beat_groups = np.zeros(4)
+ group_means = np.zeros(4)
+
+ downbeat_group = -1
+
+ while listener.running.value:
+ # read data from audio input
+ if not listener.input_from_queue:
+ audiobuffer = stream.read(buffer_size, exception_on_overflow=False)
+ samples = np.fromstring(audiobuffer, dtype=np.float32)
+ else:
+ samples = listener.queue_in.get()
+ if samples is None:
+ break
+
+ if onset_o(samples):
last_onset = onset_o.get_last_ms()
- if (temp_o(samples)):
+ if temp_o(samples):
tmp = temp_o.get_last_ms()
beats.append(tmp - last_beat)
count_beat = (count_beat + 1) % 4
last_beat = tmp
+
+ beat_groups[count_beat] = last_beat # update last beat of the group
+ beat_velocity = find_closest_onset_velocity(last_beat, latest_onsets_velocities)
+ group_means[count_beat] = alpha * group_means[count_beat] + (1 - alpha) * beat_velocity # update_mean
+
+ downbeat_group = group_means.argmax() # change the current downbeat_group
+ # print(downbeat_group)
+ if count_beat == downbeat_group:
+ bar_start = True
+
+ '''
if (count_beat == 0):
last_downbeat = last_beat
bar_start = True
+ '''
+
new_note = notes_o(samples)
- if (new_note[0] != 0):
- if (len(beats) != 0):
+ if new_note[0] != 0:
+ if len(beats) != 0:
listener.set_tempo(60 * 1000.0 / np.median(beats))
+
+ latest_onsets_velocities[i_onset] = (last_onset, int(new_note[1]))
+ i_onset += 1
+ if i_onset == n_onset:
+ i_onset = 0
+
chord = Chord([Note(int(new_note[0]))], from_ms_to_our_time(last_onset - prev_time, listener.tempo.value),
int(new_note[1]), bar_start)
- # print(bar_start, listener.tempo.value, listener.deadline.value, time.monotonic())
+ print(bar_start, listener.tempo.value, listener.deadline.value, time.monotonic())
bar_start = False
- listener.queue_in.put(chord)
- KOLYA_time = start_time + (last_downbeat + (4 - count_beat) * 60 * 1000.0 / listener.tempo.value) / 1000.0
- print(bar_start, listener.tempo.value, listener.deadline.value, time.monotonic(), KOLYA_time)
+ listener.queue_from_listener_to_predictor.put(chord)
+ KOLYA_time = start_time - listener.web_delay + (beat_groups[downbeat_group] + (4 - count_beat) * 60 * 1000.0
+ / listener.tempo.value) / 1000.0
+ # print(bar_start, listener.tempo.value, listener.deadline.value, time.monotonic(), KOLYA_time)
# print(count_beat, time.monotonic(), KOLYA_time, listener.deadline.value)
- if (count_beat != 0):
+ if count_beat != downbeat_group:
listener.set_deadline(KOLYA_time)
prev_time = last_onset
class Listener:
- def __init__(self, queue=Queue(), running=Value('i', False),
+ def __init__(self, input_queue=None, queue_from_listener_to_predictor=None, running=Value('i', False),
tempo=Value('i', default_tempo),
deadline=Value('f', 0)):
- self.queue_in = queue
+ self.queue_in = input_queue
+ self.queue_from_listener_to_predictor = queue_from_listener_to_predictor
+
+ if input_queue is not None:
+ self.input_from_queue = True
+ else:
+ self.input_from_queue = False
+
+ self.web_delay = 0
+
self.running = running
self.tempo = tempo
self.deadline = deadline
def run(self):
self.running.value = True
- self.process = Process(target=run_queue_in, args=(self, ))
- self.process.start()
+ self.process_queue_in = Process(target=run_queue_in, args=(self, ))
+ self.process_queue_in.start()
def stop(self):
self.running.value = False
- self.process.join()
+ self.queue_in.put_nowait(None)
+ self.process_queue_in.join()
self.queue_in = Queue()
+ print("listener stopped")
def get(self):
if self.queue_in.empty() is False:
@@ -114,11 +177,18 @@ def set_tempo(self, tempo=default_tempo):
def set_deadline(self, deadline=0):
self.deadline.value = deadline
+ def set_queue_in(self, queue):
+ self.queue_in = queue
+ self.input_from_queue = True
+
+ def set_web_delay(self, delay):
+ self.web_delay = delay
+
queue_in = None
running = None
tempo = None
deadline = None
- process = None
+ process_queue_in = None
if __name__ == '__main__':
diff --git a/production/player.py b/production/player.py
index 822296a..b7bb467 100644
--- a/production/player.py
+++ b/production/player.py
@@ -5,8 +5,9 @@
from mido import Message, MidiFile, MidiTrack
from rtmidi import MidiOut
-from structures import Note, Chord
+from production.structures import Note, Chord
from multiprocessing import Queue, Process, Value
+import production.pyfluidsynth as fluidsynth
"""
1 beat in bpm is 1/4 of musical beat
@@ -32,30 +33,57 @@
max_time = sys.float_info.max
empty_chord = Chord([], 0, 0)
+default_soundfont_path = "docker/accompaniator/piano_and_ultrasound.sf2"
+sent_chunk_size_in_secs = 1.5
+small_frame_time_in_secs = 0.1
+output_rate = 44100 # in Hz
+
def len_in_s(duration, bpm):
""" Returns length of chord in s given beats per minute"""
return duration * 60 / (bpm * 32)
+def send_output_queue_to_client(player):
+ """ Goes through the audio output queue and sends chunks from it to the client via tornado WebSocketHandler"""
+ chunk = np.array([])
+ count = 0
+ while player.running.value:
+ small_chunk = player.real_queue_out.get()
+ if small_chunk is None:
+ break
+ if count == 0:
+ chunk = small_chunk
+ else:
+ chunk = np.concatenate([chunk, small_chunk])
+ count += 1
+ if count >= int(sent_chunk_size_in_secs / small_frame_time_in_secs):
+ print(len(chunk))
+ player.websocket.send_audio(chunk)
+ count = 0
+ # sleep(sent_chunk_size_in_secs)
+
+
def run_peak(player):
- while(player.running.value):
+ while player.running.value:
sleep(distance_between_peaks)
player.start_peak.value = time.monotonic()
# player.play_peak()
def run_queue_out(player):
- while (player.running.value):
- if not player.queue_out.empty() and time.monotonic() > player.deadline.value:
- """ track is array of pairs: first is note number in chord, second is note len (duration) in 1/128.
+ while player.running.value:
+ if (not player.queue_out.empty() and
+ time.monotonic() > player.deadline.value):
+ """ track is array of pairs: first is note number in chord,
+ second is note len (duration) in 1/128.
Sum of durations MUST be equal to 128 """
- player.play_chord_arpeggio(np.array([[0, 19], [1, 18], [2, 18], [3, 18], [2, 18], [1, 18], [0, 19]]))
+ player.play_chord_arpeggio(np.array([[0, 19], [1, 18], [2, 18],
+ [3, 18], [2, 18], [1, 18],
+ [0, 19]]))
time.sleep(0.01)
if player.last_note_number is not None:
- note_off = Message('note_off', note=player.last_note_number, velocity=min_velocity,
- channel=default_ultrasound_channel).bytes()
- player.midiout.send_message(note_off)
+ player.note_off(default_channel, player.last_note_number, min_velocity)
class Player:
@@ -66,6 +94,23 @@ def __init__(self, queue=Queue(), running=Value('i', False),
self.midi_for_file = MidiFile()
self.last_chord = empty_chord
+ self.websocket = None
+ self.output_to_websocket = None
+
+ self.fluid_synth = None
+ self.fluid_synth = fluidsynth.Synth()
+ sfid = self.fluid_synth.sfload(default_soundfont_path)
+ self.fluid_synth.program_select(0, sfid, 0, 0)
+ self.real_queue_out = Queue()
+
+ """
+ special ultrasound synth
+ self.ultrasound_fluid_synth = fluidsynth.Synth()
+ sfid = self.ultrasound_fluid_synth.sfload(default_soundfont_path)
+ #TODO: check whether it works
+ self.ultrasound_fluid_synth.program_select(0, sfid, 0, 1) #should be fine??
+ """
+
self.queue_out = queue
self.running = running
self.tempo = tempo
@@ -75,13 +120,9 @@ def __init__(self, queue=Queue(), running=Value('i', False),
def play_peak(self, number=default_peak_number,
velocity=default_peak_velocity):
- note_on = Message('note_on', note=number, velocity=velocity,
- channel=default_ultrasound_channel).bytes()
- self.midiout.send_message(note_on)
+ self.note_on(default_ultrasound_channel, number, velocity)
sleep(default_peak_time)
- note_off = Message('note_off', note=number, velocity=min_velocity,
- channel=default_ultrasound_channel).bytes()
- self.midiout.send_message(note_off)
+ self.note_off(default_ultrasound_channel, number, min_velocity)
def play_chord_same_time(self):
chord = self.queue_out.get()
@@ -97,31 +138,34 @@ def play_chord_same_time(self):
if self.last_chord != empty_chord:
for note in self.last_chord.notes:
- note_off = Message('note_off', note=note.number,
- velocity=min_velocity,
- channel=default_channel).bytes()
- self.midiout.send_message(note_off)
+ self.note_off(default_channel, note.number, min_velocity)
for note in chord.notes:
- note_on = Message('note_on', note=note.number,
- velocity=chord.velocity,
- channel=default_channel).bytes()
- self.midiout.send_message(note_on)
+ self.note_on(default_channel, note.number, chord.velocity)
self.last_chord = chord
- sleep(len_in_s(chord.duration, self.tempo.value))
+ duration_in_secs = len_in_s(chord.duration, self.tempo.value)
+
+ if self.output_to_websocket is True:
+ for i in range((duration_in_secs / sent_chunk_size_in_secs) // 1):
+ # put chunk of full size in the queue
+ self.real_queue_out.put(self.fluid_synth.get_samples(int(output_rate * sent_chunk_size_in_secs)))
+ duration_in_secs -= duration_in_secs
+
+ if duration_in_secs > 0:
+ # put chunk of leftovers in the queue
+ self.real_queue_out.put(self.fluid_synth.get_samples(int(output_rate * duration_in_secs)))
+
+ sleep(duration_in_secs) # not sure if that's needed?
if self.last_chord == chord:
for note in chord.notes:
- note_off = Message('note_off', note=note.number,
- velocity=min_velocity,
- channel=default_channel).bytes()
- self.midiout.send_message(note_off)
+ self.note_off(default_channel, note.number, min_velocity)
def play_chord_arpeggio(self, track=np.array([])):
chord = self.queue_out.get()
- print("player get", chord, "vel", chord.velocity, "queue", self.queue_out.qsize(), "time", time.monotonic())
+ # print("player get", chord, "vel", chord.velocity, "queue", self.queue_out.qsize(), "time", time.monotonic())
if chord.velocity > 127:
chord.velocity = 127
if chord.duration == 0:
@@ -135,37 +179,53 @@ def play_chord_arpeggio(self, track=np.array([])):
chord.notes.append(Note(chord.notes[0].number + 12))
if track == np.array([]):
notes_numbers = np.arange(len(chord.notes))
- notes_durations = np.array([int(128 / len(chord.notes)) for i in range(len(chord.notes))])
+ notes_durations = np.array([int(128 / len(chord.notes))
+ for i in range(len(chord.notes))])
track = np.column_stack((notes_numbers, notes_durations))
notes_sum_durations = np.cumsum(track.transpose(), axis=1)[1]
+
if self.last_note_number is not None:
- note_off = Message('note_off', note=self.last_note_number, velocity=min_velocity,
- channel=default_channel).bytes()
- self.midiout.send_message(note_off)
+ self.note_off(default_channel, self.last_note_number, min_velocity)
+
self.start_chord = time.monotonic()
pair = 0
note_number = track[pair][0]
- note_on = Message('note_on', note=chord.notes[note_number].number, velocity=chord.velocity,
- channel=default_channel).bytes()
- self.midiout.send_message(note_on)
- while (pair < len(track) - 1):
+ self.note_on(default_channel, chord.notes[note_number].number, chord.velocity)
+
+ while pair < len(track) - 1:
# TODO
if time.monotonic() > self.start_chord + max((self.deadline.value - self.start_chord) *
notes_sum_durations[pair] /
notes_sum_durations[-1],
len_in_s(notes_sum_durations[pair],
self.tempo.value)):
- note_off = Message('note_off', note=chord.notes[note_number].number,
- velocity=min_velocity, channel=default_channel).bytes()
- self.midiout.send_message(note_off)
+ self.note_off(default_channel, chord.notes[note_number].number, min_velocity)
pair += 1
note_number = track[pair][0]
- note_on = Message('note_on', note=chord.notes[note_number].number,
- velocity=chord.velocity, channel=default_channel).bytes()
- self.midiout.send_message(note_on)
+ self.note_on(default_channel, chord.notes[note_number].number, chord.velocity)
+
self.last_note_number = chord.notes[note_number].number
- time.sleep(0.01)
+
+ if self.output_to_websocket is True:
+ self.real_queue_out.put(self.fluid_synth.get_samples(int(output_rate * small_frame_time_in_secs)))
+ time.sleep(small_frame_time_in_secs)
+
+ def note_on(self, channel, note, velocity):
+ if self.output_to_websocket is True:
+ self.fluid_synth.noteon(channel, note, velocity)
+
+ else:
+ note_on = Message('note_on', note=note, velocity=velocity, channel=channel).bytes()
+ self.midiout.send_message(note_on)
+
+ def note_off(self, channel, note, velocity):
+ if self.output_to_websocket is True:
+ self.fluid_synth.noteoff(channel, note)
+
+ else:
+ note_off = Message('note_off', note=note, velocity=velocity, channel=channel).bytes()
+ self.midiout.send_message(note_off)
def put(self, chord):
if type(chord) == Chord:
@@ -214,25 +274,39 @@ def save_file(self, filename='my track.mid'):
self.midi_for_file.save(filename)
return filename
+ def set_websocket(self, websocket):
+ self.websocket = websocket
+ self.output_to_websocket = True
+ self.real_queue_out = Queue()
+
def run(self):
self.running.value = True
- self.set_up_ports()
- self.set_up_midi_for_file()
- self.set_up_instrument()
- self.set_up_ultrasound_instrument()
+ if not self.output_to_websocket:
+ self.set_up_ports()
+ self.set_up_midi_for_file()
+ self.set_up_instrument()
+ # self.set_up_ultrasound_instrument()
+
+ self.process_queue_out = Process(target=run_queue_out, args=(self,))
+ self.process_queue_out.start()
- self.queue_process = Process(target=run_queue_out, args=(self,))
- self.queue_process.start()
+ # self.process_run_peak = Process(target=run_peak, args=(self,))
+ # self.process_run_peak.start()
- self.queue_process = Process(target=run_peak, args=(self,))
- self.queue_process.start()
+ self.process_queue_to_client = Process(target=send_output_queue_to_client, args=(self,))
+ self.process_queue_to_client.start()
def stop(self):
""" All chords that already sound will continue to sound """
self.running.value = False
- self.queue_process.join()
- self.queue_process.join()
+ self.real_queue_out.put_nowait(None)
+ self.process_queue_out.join()
+ #self.process_run_peak.join()
+ self.process_queue_to_client.join()
self.queue_out = Queue()
+ self.real_queue_out = Queue()
+ print("player stopped")
+
queue_out = None
running = None
@@ -240,8 +314,9 @@ def stop(self):
deadline = None
start_peak = None
start_chord = None
- queue_process = None
- peak_process = None
+ process_queue_out = None
+ process_run_peak = None
+ process_queue_to_client = None
midiout = None
midi_for_file = None
last_chord = None
diff --git a/production/pyfluidsynth.py b/production/pyfluidsynth.py
new file mode 100644
index 0000000..0214026
--- /dev/null
+++ b/production/pyfluidsynth.py
@@ -0,0 +1,592 @@
+"""
+================================================================================
+
+ pyFluidSynth
+
+ Python bindings for FluidSynth
+
+ Copyright 2008, Nathan Whitehead
+
+
+ Released under the LGPL
+
+ This module contains python bindings for FluidSynth. FluidSynth is a
+ software synthesizer for generating music. It works like a MIDI
+ synthesizer. You load patches, set parameters, then send NOTEON and
+ NOTEOFF events to play notes. Instruments are defined in SoundFonts,
+ generally files with the extension SF2. FluidSynth can either be used
+ to play audio itself, or you can call a function that returns chunks
+ of audio data and output the data to the soundcard yourself.
+ FluidSynth works on all major platforms, so pyFluidSynth should also.
+
+================================================================================
+
+Added lots of bindings and stuff to help with playing live -- Bill Peterson
+"""
+
+from ctypes import *
+from ctypes.util import find_library
+from six import iteritems
+
+# A short circuited or expression to find the FluidSynth library
+# (mostly needed for Windows distributions of libfluidsynth supplied with QSynth)
+
+lib = find_library('fluidsynth') or find_library('libfluidsynth') or find_library('libfluidsynth-1')
+
+
+if lib is None:
+ raise ImportError("Couldn't find the FluidSynth library.")
+
+# Dynamically link the FluidSynth library
+_fl = CDLL(lib)
+
+
+# Helper function for declaring function prototypes
+def cfunc(name, result, *args):
+ """Build and apply a ctypes prototype complete with parameter flags"""
+ atypes = []
+ aflags = []
+ for arg in args:
+ atypes.append(arg[1])
+ aflags.append((arg[2], arg[0]) + arg[3:])
+ return CFUNCTYPE(result, *atypes)((name, _fl), tuple(aflags))
+
+
+# Bump this up when changing the interface for users
+api_version = '1.2.2'
+
+# Function prototypes for C versions of functions
+new_fluid_settings = cfunc('new_fluid_settings', c_void_p)
+
+new_fluid_synth = cfunc('new_fluid_synth', c_void_p,
+ ('settings', c_void_p, 1))
+
+new_fluid_audio_driver = cfunc('new_fluid_audio_driver', c_void_p,
+ ('settings', c_void_p, 1),
+ ('synth', c_void_p, 1))
+
+fluid_settings_setstr = cfunc('fluid_settings_setstr', c_int,
+ ('settings', c_void_p, 1),
+ ('name', c_char_p, 1),
+ ('str', c_char_p, 1))
+
+fluid_settings_setnum = cfunc('fluid_settings_setnum', c_int,
+ ('settings', c_void_p, 1),
+ ('name', c_char_p, 1),
+ ('val', c_double, 1))
+
+fluid_settings_setint = cfunc('fluid_settings_setint', c_int,
+ ('settings', c_void_p, 1),
+ ('name', c_char_p, 1),
+ ('val', c_int, 1))
+
+delete_fluid_audio_driver = cfunc('delete_fluid_audio_driver', None,
+ ('driver', c_void_p, 1))
+
+delete_fluid_synth = cfunc('delete_fluid_synth', None,
+ ('synth', c_void_p, 1))
+
+delete_fluid_settings = cfunc('delete_fluid_settings', None,
+ ('settings', c_void_p, 1))
+
+fluid_synth_sfload = cfunc('fluid_synth_sfload', c_int,
+ ('synth', c_void_p, 1),
+ ('filename', c_char_p, 1),
+ ('update_midi_presets', c_int, 1))
+
+fluid_synth_sfunload = cfunc('fluid_synth_sfunload', c_int,
+ ('synth', c_void_p, 1),
+ ('sfid', c_int, 1),
+ ('update_midi_presets', c_int, 1))
+
+fluid_synth_program_select = cfunc('fluid_synth_program_select', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('sfid', c_int, 1),
+ ('bank', c_int, 1),
+ ('preset', c_int, 1))
+
+fluid_synth_noteon = cfunc('fluid_synth_noteon', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('key', c_int, 1),
+ ('vel', c_int, 1))
+
+fluid_synth_noteoff = cfunc('fluid_synth_noteoff', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('key', c_int, 1))
+
+fluid_synth_pitch_bend = cfunc('fluid_synth_pitch_bend', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('val', c_int, 1))
+
+fluid_synth_cc = cfunc('fluid_synth_cc', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('ctrl', c_int, 1),
+ ('val', c_int, 1))
+
+fluid_synth_get_cc = cfunc('fluid_synth_get_cc', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('num', c_int, 1),
+ ('pval', POINTER(c_int), 1))
+
+fluid_synth_program_change = cfunc('fluid_synth_program_change', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('prg', c_int, 1))
+
+fluid_synth_bank_select = cfunc('fluid_synth_bank_select', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('bank', c_int, 1))
+
+fluid_synth_sfont_select = cfunc('fluid_synth_sfont_select', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('sfid', c_int, 1))
+
+fluid_synth_program_reset = cfunc('fluid_synth_program_reset', c_int,
+ ('synth', c_void_p, 1))
+
+fluid_synth_system_reset = cfunc('fluid_synth_system_reset', c_int,
+ ('synth', c_void_p, 1))
+
+fluid_synth_write_s16 = cfunc('fluid_synth_write_s16', c_void_p,
+ ('synth', c_void_p, 1),
+ ('len', c_int, 1),
+ ('lbuf', c_void_p, 1),
+ ('loff', c_int, 1),
+ ('lincr', c_int, 1),
+ ('rbuf', c_void_p, 1),
+ ('roff', c_int, 1),
+ ('rincr', c_int, 1))
+
+
+class fluid_synth_channel_info_t(Structure):
+ _fields_ = [
+ ('assigned', c_int),
+ ('sfont_id', c_int),
+ ('bank', c_int),
+ ('program', c_int),
+ ('name', c_char * 32),
+ ('reserved', c_char * 32)]
+
+
+fluid_synth_get_channel_info = cfunc('fluid_synth_get_channel_info', c_int,
+ ('synth', c_void_p, 1),
+ ('chan', c_int, 1),
+ ('info', POINTER(fluid_synth_channel_info_t), 1))
+
+fluid_synth_get_reverb_roomsize = cfunc('fluid_synth_get_reverb_roomsize', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_reverb_damp = cfunc('fluid_synth_get_reverb_damp', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_reverb_level = cfunc('fluid_synth_get_reverb_level', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_reverb_width = cfunc('fluid_synth_get_reverb_width', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_chorus_nr = cfunc('fluid_synth_get_chorus_nr', c_int,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_chorus_level = cfunc('fluid_synth_get_chorus_level', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_chorus_speed_Hz = cfunc('fluid_synth_get_chorus_speed_Hz', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_chorus_depth_ms = cfunc('fluid_synth_get_chorus_depth_ms', c_double,
+ ('synth', c_void_p, 1))
+
+fluid_synth_get_chorus_type = cfunc('fluid_synth_get_chorus_type', c_int,
+ ('synth', c_void_p, 1))
+
+new_fluid_midi_driver = cfunc('new_fluid_midi_driver', c_void_p,
+ ('settings', c_void_p, 1),
+ ('handler', CFUNCTYPE(POINTER(c_int), c_void_p, c_void_p), 1),
+ ('event_handler_data', c_void_p, 1))
+
+
+class fluid_midi_router_t(Structure):
+ _fields_ = [
+ ('synth', c_void_p),
+ ('rules_mutex', c_void_p),
+ ('rules', c_void_p * 6),
+ ('free_rules', c_void_p),
+ ('event_handler', c_void_p),
+ ('event_handler_data', c_void_p),
+ ('nr_midi_channels', c_int),
+ ('cmd_rule', c_void_p),
+ ('cmd_rule_type', POINTER(c_int))]
+
+
+new_fluid_midi_router = cfunc('new_fluid_midi_router', POINTER(fluid_midi_router_t),
+ ('settings', c_void_p, 1),
+ ('handler', CFUNCTYPE(POINTER(c_int), c_void_p, c_void_p), 1),
+ ('event_handler_data', c_void_p, 1))
+
+fluid_synth_set_midi_router = cfunc('fluid_synth_set_midi_router', None,
+ ('synth', c_void_p, 1),
+ ('router', c_void_p, 1))
+
+fluid_synth_handle_midi_event = cfunc('fluid_synth_handle_midi_event', POINTER(c_int),
+ ('data', c_void_p, 1),
+ ('event', c_void_p, 1))
+
+fluid_midi_router_handle_midi_event = cfunc('fluid_midi_router_handle_midi_event', POINTER(c_int),
+ ('data', c_void_p, 1),
+ ('event', c_void_p, 1))
+
+fluid_midi_router_clear_rules = cfunc('fluid_midi_router_clear_rules', c_int,
+ ('router', POINTER(fluid_midi_router_t), 1))
+
+fluid_midi_router_set_default_rules = cfunc('fluid_midi_router_set_default_rules', c_int,
+ ('router', POINTER(fluid_midi_router_t), 1))
+
+delete_fluid_midi_router_rule = cfunc('delete_fluid_midi_router_rule', c_int,
+ ('rule', c_void_p, 1))
+
+new_fluid_midi_router_rule = cfunc('new_fluid_midi_router_rule', c_void_p)
+
+fluid_midi_router_add_rule = cfunc('fluid_midi_router_add_rule', c_int,
+ ('router', POINTER(fluid_midi_router_t), 1),
+ ('rule', c_void_p, 1),
+ ('type', c_int, 1))
+
+fluid_midi_router_rule_set_chan = cfunc('fluid_midi_router_rule_set_chan', None,
+ ('rule', c_void_p, 1),
+ ('min', c_int, 1),
+ ('max', c_int, 1),
+ ('mul', c_float, 1),
+ ('add', c_int, 1))
+
+fluid_midi_router_rule_set_param1 = cfunc('fluid_midi_router_rule_set_param1', None,
+ ('rule', c_void_p, 1),
+ ('min', c_int, 1),
+ ('max', c_int, 1),
+ ('mul', c_float, 1),
+ ('add', c_int, 1))
+
+fluid_midi_router_rule_set_param2 = cfunc('fluid_midi_router_rule_set_param2', None,
+ ('rule', c_void_p, 1),
+ ('min', c_int, 1),
+ ('max', c_int, 1),
+ ('mul', c_float, 1),
+ ('add', c_int, 1))
+
+
+def fluid_synth_write_s16_stereo(synth, len):
+ """Return generated samples in stereo 16-bit format
+
+ Return value is a Numpy array of samples.
+
+ """
+ import numpy
+ buf = create_string_buffer(len * 4)
+ fluid_synth_write_s16(synth, len, buf, 0, 2, buf, 1, 2)
+ return numpy.fromstring(buf[:], dtype=numpy.int16)
+
+
+# Object-oriented interface, simplifies access to functions
+
+class Synth:
+ """Synth represents a FluidSynth synthesizer"""
+
+ def __init__(self, gain=0.2, samplerate=44100, channels=256, **kwargs):
+ """Create new synthesizer object to control sound generation
+
+ Optional keyword arguments:
+ gain : scale factor for audio output, default is 0.2
+ lower values are quieter, allow more simultaneous notes
+ samplerate : output samplerate in Hz, default is 44100 Hz
+ added capability for passing arbitrary fluid settings using args
+ """
+ st = new_fluid_settings()
+ fluid_settings_setnum(st, b'synth.gain', gain)
+ fluid_settings_setnum(st, b'synth.sample-rate', samplerate)
+ fluid_settings_setint(st, b'synth.midi-channels', channels)
+ for opt, val in iteritems(kwargs):
+ self.setting(opt, val)
+ self.settings = st
+ self.synth = new_fluid_synth(st)
+ self.audio_driver = None
+ self.midi_driver = None
+ self.router = None
+
+ def setting(self, opt, val):
+ """change an arbitrary synth setting, type-smart"""
+ opt = opt.encode()
+ if isinstance(val, str): # ??
+ fluid_settings_setstr(self.settings, opt, val)
+ elif isinstance(val, int):
+ fluid_settings_setint(self.settings, opt, val)
+ elif isinstance(val, float):
+ fluid_settings_setnum(self.settings, opt, val)
+
+ def start(self, driver=None, device=None, midi_driver=None):
+ """Start audio output driver in separate background thread
+
+ Call this function any time after creating the Synth object.
+ If you don't call this function, use get_samples() to generate
+ samples.
+
+ Optional keyword argument:
+ driver : which audio driver to use for output
+ Possible choices:
+ 'alsa', 'oss', 'jack', 'portaudio'
+ 'sndmgr', 'coreaudio', 'Direct Sound'
+ device: the device to use for audio output
+
+ Not all drivers will be available for every platform, it
+ depends on which drivers were compiled into FluidSynth for
+ your platform.
+
+ """
+ if driver is not None:
+ assert (driver in ['alsa', 'oss', 'jack', 'portaudio', 'sndmgr', 'coreaudio', 'Direct Sound'])
+ fluid_settings_setstr(self.settings, b'audio.driver', driver.encode())
+ if device is not None:
+ fluid_settings_setstr(self.settings, str('audio.%s.device' % (driver)).encode(), device.encode())
+ self.audio_driver = new_fluid_audio_driver(self.settings, self.synth)
+ if midi_driver is not None:
+ assert (midi_driver in ['alsa_seq', 'alsa_raw', 'oss', 'winmidi', 'midishare', 'coremidi'])
+ fluid_settings_setstr(self.settings, b'midi.driver', midi_driver.encode())
+ self.router = new_fluid_midi_router(self.settings, fluid_synth_handle_midi_event, self.synth)
+ fluid_synth_set_midi_router(self.synth, self.router)
+ self.midi_driver = new_fluid_midi_driver(self.settings, fluid_midi_router_handle_midi_event, self.router)
+
+ def delete(self):
+ if self.audio_driver is not None:
+ delete_fluid_audio_driver(self.audio_driver)
+ delete_fluid_synth(self.synth)
+ delete_fluid_settings(self.settings)
+
+ def sfload(self, filename, update_midi_preset=0):
+ """Load SoundFont and return its ID"""
+ return fluid_synth_sfload(self.synth, filename.encode(), update_midi_preset)
+
+ def sfunload(self, sfid, update_midi_preset=0):
+ """Unload a SoundFont and free memory it used"""
+ return fluid_synth_sfunload(self.synth, sfid, update_midi_preset)
+
+ def program_select(self, chan, sfid, bank, preset):
+ """Select a program"""
+ return fluid_synth_program_select(self.synth, chan, sfid, bank, preset)
+
+ def channel_info(self, chan):
+ """get soundfont, bank, prog, preset name of channel"""
+ info = fluid_synth_channel_info_t()
+ fluid_synth_get_channel_info(self.synth, chan, byref(info))
+ return (info.sfont_id, info.bank, info.program, info.name)
+
+ def router_clear(self):
+ if self.router is not None:
+ fluid_midi_router_clear_rules(self.router)
+
+ def router_default(self):
+ if self.router is not None:
+ fluid_midi_router_set_default_rules(self.router)
+
+ def router_begin(self, type):
+ """types are [note|cc|prog|pbend|cpress|kpress]"""
+ if self.router is not None:
+ if type == 'note':
+ self.router.cmd_rule_type = 0
+ elif type == 'cc':
+ self.router.cmd_rule_type = 1
+ elif type == 'prog':
+ self.router.cmd_rule_type = 2
+ elif type == 'pbend':
+ self.router.cmd_rule_type = 3
+ elif type == 'cpress':
+ self.router.cmd_rule_type = 4
+ elif type == 'kpress':
+ self.router.cmd_rule_type = 5
+ if 'self.router.cmd_rule' in globals():
+ delete_fluid_midi_router_rule(self.router.cmd_rule)
+ self.router.cmd_rule = new_fluid_midi_router_rule()
+
+ def router_end(self):
+ if self.router is not None:
+ if self.router.cmd_rule is None:
+ return
+ if fluid_midi_router_add_rule(self.router, self.router.cmd_rule, self.router.cmd_rule_type) < 0:
+ delete_fluid_midi_router_rule(self.router.cmd_rule)
+ self.router.cmd_rule = None
+
+ def router_chan(self, min, max, mul, add):
+ if self.router is not None:
+ fluid_midi_router_rule_set_chan(self.router.cmd_rule, min, max, mul, add)
+
+ def router_par1(self, min, max, mul, add):
+ if self.router is not None:
+ fluid_midi_router_rule_set_param1(self.router.cmd_rule, min, max, mul, add)
+
+ def router_par2(self, min, max, mul, add):
+ if self.router is not None:
+ fluid_midi_router_rule_set_param2(self.router.cmd_rule, min, max, mul, add)
+
+ def set_reverb(self, roomsize=-1.0, damping=-1.0, width=-1.0, level=-1.0):
+ """
+ roomsize Reverb room size value (0.0-1.2)
+ damping Reverb damping value (0.0-1.0)
+ width Reverb width value (0.0-100.0)
+ level Reverb level value (0.0-1.0)
+ """
+ set = 0
+ if roomsize >= 0:
+ set += 0b0001
+ if damping >= 0:
+ set += 0b0010
+ if width >= 0:
+ set += 0b0100
+ if level >= 0:
+ set += 0b1000
+ return fluid_synth_set_reverb_full(self.synth, set, roomsize, damping, width, level)
+
+ def set_chorus(self, nr=-1, level=-1.0, speed=-1.0, depth=-1.0, type=-1):
+ """
+ nr Chorus voice count (0-99, CPU time consumption proportional to this value)
+ level Chorus level (0.0-10.0)
+ speed Chorus speed in Hz (0.29-5.0)
+ depth_ms Chorus depth (max value depends on synth sample rate, 0.0-21.0 is safe for sample rate values up
+ to 96KHz)
+ type Chorus waveform type (0=sine, 1=triangle)
+ """
+ set = 0
+ if nr >= 0:
+ set += 0b00001
+ if level >= 0:
+ set += 0b00010
+ if speed >= 0:
+ set += 0b00100
+ if depth >= 0:
+ set += 0b01000
+ if type >= 0:
+ set += 0b10000
+ return fluid_synth_set_chorus_full(self.synth, set, nr, level, speed, depth, type)
+
+ def get_reverb_roomsize(self):
+ return fluid_synth_get_reverb_roomsize(self.synth)
+
+ def get_reverb_damp(self):
+ return fluid_synth_get_reverb_damp(self.synth)
+
+ def get_reverb_level(self):
+ return fluid_synth_get_reverb_level(self.synth)
+
+ def get_reverb_width(self):
+ return fluid_synth_get_reverb_width(self.synth)
+
+ def get_chorus_nr(self):
+ return fluid_synth_get_chorus_nr(self.synth)
+
+ def get_chorus_level(self):
+ return fluid_synth_get_reverb_level(self.synth)
+
+ def get_chorus_speed(self):
+ return fluid_synth_get_chorus_speed_Hz(self.synth)
+
+ def get_chorus_depth(self):
+ return fluid_synth_get_chorus_depth_ms(self.synth)
+
+ def get_chorus_type(self):
+ return fluid_synth_get_chorus_type(self.synth)
+
+ def noteon(self, chan, key, vel):
+ """Play a note"""
+ if key < 0 or key > 128:
+ return False
+ if chan < 0:
+ return False
+ if vel < 0 or vel > 128:
+ return False
+ return fluid_synth_noteon(self.synth, chan, key, vel)
+
+ def noteoff(self, chan, key):
+ """Stop a note"""
+ if key < 0 or key > 128:
+ return False
+ if chan < 0:
+ return False
+ return fluid_synth_noteoff(self.synth, chan, key)
+
+ def pitch_bend(self, chan, val):
+ """Adjust pitch of a playing channel by small amounts
+
+ A pitch bend value of 0 is no pitch change from default.
+ A value of -2048 is 1 semitone down.
+ A value of 2048 is 1 semitone up.
+ Maximum values are -8192 to +8192 (transposing by 4 semitones).
+
+ """
+ return fluid_synth_pitch_bend(self.synth, chan, val + 8192)
+
+ def cc(self, chan, ctrl, val):
+ """Send control change value
+
+ The controls that are recognized are dependent on the
+ SoundFont. Values are always 0 to 127. Typical controls
+ include:
+ 1 : vibrato
+ 7 : volume
+ 10 : pan (left to right)
+ 11 : expression (soft to loud)
+ 64 : sustain
+ 91 : reverb
+ 93 : chorus
+ """
+ return fluid_synth_cc(self.synth, chan, ctrl, val)
+
+ def get_cc(self, chan, num):
+ i = c_int()
+ fluid_synth_get_cc(self.synth, chan, num, byref(i))
+ return i.value
+
+ def program_change(self, chan, prg):
+ """Change the program"""
+ return fluid_synth_program_change(self.synth, chan, prg)
+
+ def bank_select(self, chan, bank):
+ """Choose a bank"""
+ return fluid_synth_bank_select(self.synth, chan, bank)
+
+ def sfont_select(self, chan, sfid):
+ """Choose a SoundFont"""
+ return fluid_synth_sfont_select(self.synth, chan, sfid)
+
+ def program_reset(self):
+ """Reset the programs on all channels"""
+ return fluid_synth_program_reset(self.synth)
+
+ def system_reset(self):
+ """Stop all notes and reset all programs"""
+ return fluid_synth_system_reset(self.synth)
+
+ def get_samples(self, len=1024):
+ """Generate audio samples
+
+ The return value will be a NumPy array containing the given
+ length of audio samples. If the synth is set to stereo output
+ (the default) the array will be size 2 * len.
+
+ """
+ return fluid_synth_write_s16_stereo(self.synth, len)
+
+
+def raw_audio_string(data):
+ """Return a string of bytes to send to soundcard
+
+ Input is a numpy array of samples. Default output format
+ is 16-bit signed (other formats not currently supported).
+
+ """
+ import numpy
+ return (data.astype(numpy.int16)).tostring()
diff --git a/production/rf_nottingham.pkl b/production/rf_nottingham.pkl
deleted file mode 100644
index d3fbeb1..0000000
Binary files a/production/rf_nottingham.pkl and /dev/null differ
diff --git a/production/structures.py b/production/structures.py
index defd87e..7b7d893 100644
--- a/production/structures.py
+++ b/production/structures.py
@@ -46,7 +46,8 @@ def __str__(self):
return "{%s %s %s}" % (self.duration, self.velocity, str(self.notes))
def __eq__(self, other):
- return self.duration == other.duration and self.velocity == other.velocity and self.notes == other.notes
+ return self.duration == other.duration and \
+ self.velocity == other.velocity and self.notes == other.notes
def add_notes(self, notes_list):
self.notes.extend(notes_list)
diff --git a/production/vocab.csv b/production/vocab.csv
new file mode 100644
index 0000000..d2b8ecd
--- /dev/null
+++ b/production/vocab.csv
@@ -0,0 +1,759 @@
+,0,1,2,3
+0,-1.0,-1.0,-1.0,-1.0
+1,0.0,-1.0,-1.0,-1.0
+2,0.0,1.0,-1.0,-1.0
+3,0.0,1.0,2.0,-1.0
+4,0.0,1.0,2.0,3.0
+5,0.0,1.0,2.0,4.0
+6,0.0,1.0,2.0,5.0
+7,0.0,1.0,2.0,6.0
+8,0.0,1.0,2.0,7.0
+9,0.0,1.0,2.0,8.0
+10,0.0,1.0,2.0,9.0
+11,0.0,1.0,2.0,10.0
+12,0.0,1.0,2.0,11.0
+13,0.0,1.0,3.0,-1.0
+14,0.0,1.0,3.0,4.0
+15,0.0,1.0,3.0,5.0
+16,0.0,1.0,3.0,6.0
+17,0.0,1.0,3.0,7.0
+18,0.0,1.0,3.0,8.0
+19,0.0,1.0,3.0,9.0
+20,0.0,1.0,3.0,10.0
+21,0.0,1.0,3.0,11.0
+22,0.0,1.0,4.0,-1.0
+23,0.0,1.0,4.0,5.0
+24,0.0,1.0,4.0,6.0
+25,0.0,1.0,4.0,7.0
+26,0.0,1.0,4.0,8.0
+27,0.0,1.0,4.0,9.0
+28,0.0,1.0,4.0,10.0
+29,0.0,1.0,4.0,11.0
+30,0.0,1.0,5.0,-1.0
+31,0.0,1.0,5.0,6.0
+32,0.0,1.0,5.0,8.0
+33,0.0,1.0,5.0,9.0
+34,0.0,1.0,5.0,10.0
+35,0.0,1.0,5.0,11.0
+36,0.0,1.0,6.0,-1.0
+37,0.0,1.0,6.0,7.0
+38,0.0,1.0,6.0,8.0
+39,0.0,1.0,6.0,9.0
+40,0.0,1.0,6.0,10.0
+41,0.0,1.0,6.0,11.0
+42,0.0,1.0,7.0,-1.0
+43,0.0,1.0,7.0,8.0
+44,0.0,1.0,7.0,9.0
+45,0.0,1.0,7.0,10.0
+46,0.0,1.0,7.0,11.0
+47,0.0,1.0,8.0,-1.0
+48,0.0,1.0,8.0,9.0
+49,0.0,1.0,8.0,10.0
+50,0.0,1.0,8.0,11.0
+51,0.0,1.0,9.0,-1.0
+52,0.0,1.0,9.0,10.0
+53,0.0,1.0,9.0,11.0
+54,0.0,1.0,10.0,-1.0
+55,0.0,1.0,10.0,11.0
+56,0.0,1.0,11.0,-1.0
+57,0.0,2.0,-1.0,-1.0
+58,0.0,2.0,3.0,-1.0
+59,0.0,2.0,3.0,4.0
+60,0.0,2.0,3.0,5.0
+61,0.0,2.0,3.0,6.0
+62,0.0,2.0,3.0,7.0
+63,0.0,2.0,3.0,8.0
+64,0.0,2.0,3.0,9.0
+65,0.0,2.0,3.0,10.0
+66,0.0,2.0,3.0,11.0
+67,0.0,2.0,4.0,-1.0
+68,0.0,2.0,4.0,5.0
+69,0.0,2.0,4.0,6.0
+70,0.0,2.0,4.0,7.0
+71,0.0,2.0,4.0,8.0
+72,0.0,2.0,4.0,9.0
+73,0.0,2.0,4.0,10.0
+74,0.0,2.0,4.0,11.0
+75,0.0,2.0,5.0,-1.0
+76,0.0,2.0,5.0,6.0
+77,0.0,2.0,5.0,7.0
+78,0.0,2.0,5.0,8.0
+79,0.0,2.0,5.0,9.0
+80,0.0,2.0,5.0,10.0
+81,0.0,2.0,5.0,11.0
+82,0.0,2.0,6.0,-1.0
+83,0.0,2.0,6.0,7.0
+84,0.0,2.0,6.0,8.0
+85,0.0,2.0,6.0,9.0
+86,0.0,2.0,6.0,10.0
+87,0.0,2.0,6.0,11.0
+88,0.0,2.0,7.0,-1.0
+89,0.0,2.0,7.0,8.0
+90,0.0,2.0,7.0,9.0
+91,0.0,2.0,7.0,10.0
+92,0.0,2.0,7.0,11.0
+93,0.0,2.0,8.0,-1.0
+94,0.0,2.0,8.0,9.0
+95,0.0,2.0,8.0,10.0
+96,0.0,2.0,8.0,11.0
+97,0.0,2.0,9.0,-1.0
+98,0.0,2.0,9.0,10.0
+99,0.0,2.0,9.0,11.0
+100,0.0,2.0,10.0,-1.0
+101,0.0,2.0,10.0,11.0
+102,0.0,2.0,11.0,-1.0
+103,0.0,3.0,-1.0,-1.0
+104,0.0,3.0,4.0,-1.0
+105,0.0,3.0,4.0,5.0
+106,0.0,3.0,4.0,6.0
+107,0.0,3.0,4.0,7.0
+108,0.0,3.0,4.0,8.0
+109,0.0,3.0,4.0,9.0
+110,0.0,3.0,4.0,10.0
+111,0.0,3.0,4.0,11.0
+112,0.0,3.0,5.0,-1.0
+113,0.0,3.0,5.0,6.0
+114,0.0,3.0,5.0,7.0
+115,0.0,3.0,5.0,8.0
+116,0.0,3.0,5.0,9.0
+117,0.0,3.0,5.0,10.0
+118,0.0,3.0,5.0,11.0
+119,0.0,3.0,6.0,-1.0
+120,0.0,3.0,6.0,7.0
+121,0.0,3.0,6.0,8.0
+122,0.0,3.0,6.0,9.0
+123,0.0,3.0,6.0,10.0
+124,0.0,3.0,6.0,11.0
+125,0.0,3.0,7.0,-1.0
+126,0.0,3.0,7.0,8.0
+127,0.0,3.0,7.0,9.0
+128,0.0,3.0,7.0,10.0
+129,0.0,3.0,7.0,11.0
+130,0.0,3.0,8.0,-1.0
+131,0.0,3.0,8.0,9.0
+132,0.0,3.0,8.0,10.0
+133,0.0,3.0,8.0,11.0
+134,0.0,3.0,9.0,-1.0
+135,0.0,3.0,9.0,10.0
+136,0.0,3.0,9.0,11.0
+137,0.0,3.0,10.0,-1.0
+138,0.0,3.0,10.0,11.0
+139,0.0,3.0,11.0,-1.0
+140,0.0,4.0,-1.0,-1.0
+141,0.0,4.0,5.0,-1.0
+142,0.0,4.0,5.0,6.0
+143,0.0,4.0,5.0,7.0
+144,0.0,4.0,5.0,8.0
+145,0.0,4.0,5.0,9.0
+146,0.0,4.0,5.0,10.0
+147,0.0,4.0,5.0,11.0
+148,0.0,4.0,6.0,-1.0
+149,0.0,4.0,6.0,7.0
+150,0.0,4.0,6.0,8.0
+151,0.0,4.0,6.0,9.0
+152,0.0,4.0,6.0,10.0
+153,0.0,4.0,6.0,11.0
+154,0.0,4.0,7.0,-1.0
+155,0.0,4.0,7.0,8.0
+156,0.0,4.0,7.0,9.0
+157,0.0,4.0,7.0,10.0
+158,0.0,4.0,7.0,11.0
+159,0.0,4.0,8.0,-1.0
+160,0.0,4.0,8.0,9.0
+161,0.0,4.0,8.0,10.0
+162,0.0,4.0,8.0,11.0
+163,0.0,4.0,9.0,-1.0
+164,0.0,4.0,9.0,10.0
+165,0.0,4.0,9.0,11.0
+166,0.0,4.0,10.0,-1.0
+167,0.0,4.0,10.0,11.0
+168,0.0,4.0,11.0,-1.0
+169,0.0,5.0,-1.0,-1.0
+170,0.0,5.0,6.0,-1.0
+171,0.0,5.0,6.0,7.0
+172,0.0,5.0,6.0,8.0
+173,0.0,5.0,6.0,9.0
+174,0.0,5.0,6.0,10.0
+175,0.0,5.0,6.0,11.0
+176,0.0,5.0,7.0,-1.0
+177,0.0,5.0,7.0,8.0
+178,0.0,5.0,7.0,9.0
+179,0.0,5.0,7.0,10.0
+180,0.0,5.0,7.0,11.0
+181,0.0,5.0,8.0,-1.0
+182,0.0,5.0,8.0,9.0
+183,0.0,5.0,8.0,10.0
+184,0.0,5.0,8.0,11.0
+185,0.0,5.0,9.0,-1.0
+186,0.0,5.0,9.0,10.0
+187,0.0,5.0,9.0,11.0
+188,0.0,5.0,10.0,-1.0
+189,0.0,5.0,10.0,11.0
+190,0.0,5.0,11.0,-1.0
+191,0.0,6.0,-1.0,-1.0
+192,0.0,6.0,7.0,-1.0
+193,0.0,6.0,7.0,8.0
+194,0.0,6.0,7.0,9.0
+195,0.0,6.0,7.0,10.0
+196,0.0,6.0,7.0,11.0
+197,0.0,6.0,8.0,-1.0
+198,0.0,6.0,8.0,9.0
+199,0.0,6.0,8.0,10.0
+200,0.0,6.0,8.0,11.0
+201,0.0,6.0,9.0,-1.0
+202,0.0,6.0,9.0,10.0
+203,0.0,6.0,9.0,11.0
+204,0.0,6.0,10.0,-1.0
+205,0.0,6.0,10.0,11.0
+206,0.0,6.0,11.0,-1.0
+207,0.0,7.0,-1.0,-1.0
+208,0.0,7.0,8.0,-1.0
+209,0.0,7.0,8.0,9.0
+210,0.0,7.0,8.0,10.0
+211,0.0,7.0,8.0,11.0
+212,0.0,7.0,9.0,-1.0
+213,0.0,7.0,9.0,10.0
+214,0.0,7.0,9.0,11.0
+215,0.0,7.0,10.0,-1.0
+216,0.0,7.0,10.0,11.0
+217,0.0,7.0,11.0,-1.0
+218,0.0,8.0,-1.0,-1.0
+219,0.0,8.0,9.0,-1.0
+220,0.0,8.0,9.0,10.0
+221,0.0,8.0,9.0,11.0
+222,0.0,8.0,10.0,-1.0
+223,0.0,8.0,10.0,11.0
+224,0.0,8.0,11.0,-1.0
+225,0.0,9.0,-1.0,-1.0
+226,0.0,9.0,10.0,-1.0
+227,0.0,9.0,10.0,11.0
+228,0.0,9.0,11.0,-1.0
+229,0.0,10.0,-1.0,-1.0
+230,0.0,10.0,11.0,-1.0
+231,0.0,11.0,-1.0,-1.0
+232,1.0,-1.0,-1.0,-1.0
+233,1.0,2.0,-1.0,-1.0
+234,1.0,2.0,3.0,-1.0
+235,1.0,2.0,3.0,4.0
+236,1.0,2.0,3.0,6.0
+237,1.0,2.0,3.0,8.0
+238,1.0,2.0,3.0,9.0
+239,1.0,2.0,3.0,11.0
+240,1.0,2.0,4.0,-1.0
+241,1.0,2.0,4.0,5.0
+242,1.0,2.0,4.0,6.0
+243,1.0,2.0,4.0,7.0
+244,1.0,2.0,4.0,8.0
+245,1.0,2.0,4.0,9.0
+246,1.0,2.0,4.0,10.0
+247,1.0,2.0,4.0,11.0
+248,1.0,2.0,5.0,-1.0
+249,1.0,2.0,5.0,6.0
+250,1.0,2.0,5.0,8.0
+251,1.0,2.0,5.0,9.0
+252,1.0,2.0,5.0,10.0
+253,1.0,2.0,5.0,11.0
+254,1.0,2.0,6.0,-1.0
+255,1.0,2.0,6.0,7.0
+256,1.0,2.0,6.0,8.0
+257,1.0,2.0,6.0,9.0
+258,1.0,2.0,6.0,10.0
+259,1.0,2.0,6.0,11.0
+260,1.0,2.0,7.0,-1.0
+261,1.0,2.0,7.0,8.0
+262,1.0,2.0,7.0,9.0
+263,1.0,2.0,7.0,10.0
+264,1.0,2.0,7.0,11.0
+265,1.0,2.0,8.0,-1.0
+266,1.0,2.0,8.0,9.0
+267,1.0,2.0,8.0,10.0
+268,1.0,2.0,8.0,11.0
+269,1.0,2.0,9.0,-1.0
+270,1.0,2.0,9.0,10.0
+271,1.0,2.0,9.0,11.0
+272,1.0,2.0,10.0,-1.0
+273,1.0,2.0,10.0,11.0
+274,1.0,2.0,11.0,-1.0
+275,1.0,3.0,-1.0,-1.0
+276,1.0,3.0,4.0,-1.0
+277,1.0,3.0,4.0,6.0
+278,1.0,3.0,4.0,8.0
+279,1.0,3.0,4.0,9.0
+280,1.0,3.0,4.0,10.0
+281,1.0,3.0,4.0,11.0
+282,1.0,3.0,5.0,-1.0
+283,1.0,3.0,5.0,6.0
+284,1.0,3.0,5.0,7.0
+285,1.0,3.0,5.0,8.0
+286,1.0,3.0,5.0,9.0
+287,1.0,3.0,5.0,10.0
+288,1.0,3.0,6.0,-1.0
+289,1.0,3.0,6.0,7.0
+290,1.0,3.0,6.0,8.0
+291,1.0,3.0,6.0,9.0
+292,1.0,3.0,6.0,10.0
+293,1.0,3.0,6.0,11.0
+294,1.0,3.0,7.0,-1.0
+295,1.0,3.0,7.0,8.0
+296,1.0,3.0,7.0,10.0
+297,1.0,3.0,8.0,-1.0
+298,1.0,3.0,8.0,9.0
+299,1.0,3.0,8.0,10.0
+300,1.0,3.0,8.0,11.0
+301,1.0,3.0,9.0,-1.0
+302,1.0,3.0,9.0,10.0
+303,1.0,3.0,9.0,11.0
+304,1.0,3.0,10.0,-1.0
+305,1.0,3.0,10.0,11.0
+306,1.0,3.0,11.0,-1.0
+307,1.0,4.0,-1.0,-1.0
+308,1.0,4.0,5.0,-1.0
+309,1.0,4.0,5.0,8.0
+310,1.0,4.0,5.0,9.0
+311,1.0,4.0,5.0,10.0
+312,1.0,4.0,5.0,11.0
+313,1.0,4.0,6.0,-1.0
+314,1.0,4.0,6.0,7.0
+315,1.0,4.0,6.0,8.0
+316,1.0,4.0,6.0,9.0
+317,1.0,4.0,6.0,10.0
+318,1.0,4.0,6.0,11.0
+319,1.0,4.0,7.0,-1.0
+320,1.0,4.0,7.0,9.0
+321,1.0,4.0,7.0,10.0
+322,1.0,4.0,7.0,11.0
+323,1.0,4.0,8.0,-1.0
+324,1.0,4.0,8.0,9.0
+325,1.0,4.0,8.0,10.0
+326,1.0,4.0,8.0,11.0
+327,1.0,4.0,9.0,-1.0
+328,1.0,4.0,9.0,10.0
+329,1.0,4.0,9.0,11.0
+330,1.0,4.0,10.0,-1.0
+331,1.0,4.0,10.0,11.0
+332,1.0,4.0,11.0,-1.0
+333,1.0,5.0,-1.0,-1.0
+334,1.0,5.0,6.0,-1.0
+335,1.0,5.0,6.0,7.0
+336,1.0,5.0,6.0,8.0
+337,1.0,5.0,6.0,10.0
+338,1.0,5.0,6.0,11.0
+339,1.0,5.0,7.0,-1.0
+340,1.0,5.0,7.0,8.0
+341,1.0,5.0,7.0,9.0
+342,1.0,5.0,7.0,10.0
+343,1.0,5.0,8.0,-1.0
+344,1.0,5.0,8.0,9.0
+345,1.0,5.0,8.0,10.0
+346,1.0,5.0,8.0,11.0
+347,1.0,5.0,9.0,-1.0
+348,1.0,5.0,9.0,10.0
+349,1.0,5.0,9.0,11.0
+350,1.0,5.0,10.0,-1.0
+351,1.0,5.0,10.0,11.0
+352,1.0,5.0,11.0,-1.0
+353,1.0,6.0,-1.0,-1.0
+354,1.0,6.0,7.0,-1.0
+355,1.0,6.0,7.0,8.0
+356,1.0,6.0,7.0,9.0
+357,1.0,6.0,7.0,10.0
+358,1.0,6.0,7.0,11.0
+359,1.0,6.0,8.0,-1.0
+360,1.0,6.0,8.0,9.0
+361,1.0,6.0,8.0,10.0
+362,1.0,6.0,8.0,11.0
+363,1.0,6.0,9.0,-1.0
+364,1.0,6.0,9.0,10.0
+365,1.0,6.0,9.0,11.0
+366,1.0,6.0,10.0,-1.0
+367,1.0,6.0,10.0,11.0
+368,1.0,6.0,11.0,-1.0
+369,1.0,7.0,-1.0,-1.0
+370,1.0,7.0,8.0,-1.0
+371,1.0,7.0,8.0,9.0
+372,1.0,7.0,8.0,11.0
+373,1.0,7.0,9.0,-1.0
+374,1.0,7.0,9.0,10.0
+375,1.0,7.0,9.0,11.0
+376,1.0,7.0,10.0,-1.0
+377,1.0,7.0,10.0,11.0
+378,1.0,7.0,11.0,-1.0
+379,1.0,8.0,-1.0,-1.0
+380,1.0,8.0,9.0,-1.0
+381,1.0,8.0,9.0,10.0
+382,1.0,8.0,9.0,11.0
+383,1.0,8.0,10.0,-1.0
+384,1.0,8.0,10.0,11.0
+385,1.0,8.0,11.0,-1.0
+386,1.0,9.0,-1.0,-1.0
+387,1.0,9.0,10.0,-1.0
+388,1.0,9.0,10.0,11.0
+389,1.0,9.0,11.0,-1.0
+390,1.0,10.0,-1.0,-1.0
+391,1.0,10.0,11.0,-1.0
+392,1.0,11.0,-1.0,-1.0
+393,2.0,-1.0,-1.0,-1.0
+394,2.0,3.0,-1.0,-1.0
+395,2.0,3.0,4.0,-1.0
+396,2.0,3.0,4.0,5.0
+397,2.0,3.0,4.0,6.0
+398,2.0,3.0,4.0,7.0
+399,2.0,3.0,4.0,8.0
+400,2.0,3.0,4.0,9.0
+401,2.0,3.0,4.0,10.0
+402,2.0,3.0,4.0,11.0
+403,2.0,3.0,5.0,-1.0
+404,2.0,3.0,5.0,6.0
+405,2.0,3.0,5.0,7.0
+406,2.0,3.0,5.0,8.0
+407,2.0,3.0,5.0,9.0
+408,2.0,3.0,5.0,10.0
+409,2.0,3.0,5.0,11.0
+410,2.0,3.0,6.0,-1.0
+411,2.0,3.0,6.0,7.0
+412,2.0,3.0,6.0,8.0
+413,2.0,3.0,6.0,9.0
+414,2.0,3.0,6.0,10.0
+415,2.0,3.0,6.0,11.0
+416,2.0,3.0,7.0,-1.0
+417,2.0,3.0,7.0,9.0
+418,2.0,3.0,7.0,10.0
+419,2.0,3.0,7.0,11.0
+420,2.0,3.0,8.0,-1.0
+421,2.0,3.0,8.0,9.0
+422,2.0,3.0,8.0,10.0
+423,2.0,3.0,8.0,11.0
+424,2.0,3.0,9.0,-1.0
+425,2.0,3.0,9.0,10.0
+426,2.0,3.0,9.0,11.0
+427,2.0,3.0,10.0,-1.0
+428,2.0,3.0,10.0,11.0
+429,2.0,3.0,11.0,-1.0
+430,2.0,4.0,-1.0,-1.0
+431,2.0,4.0,5.0,-1.0
+432,2.0,4.0,5.0,6.0
+433,2.0,4.0,5.0,7.0
+434,2.0,4.0,5.0,8.0
+435,2.0,4.0,5.0,9.0
+436,2.0,4.0,5.0,10.0
+437,2.0,4.0,5.0,11.0
+438,2.0,4.0,6.0,-1.0
+439,2.0,4.0,6.0,7.0
+440,2.0,4.0,6.0,8.0
+441,2.0,4.0,6.0,9.0
+442,2.0,4.0,6.0,10.0
+443,2.0,4.0,6.0,11.0
+444,2.0,4.0,7.0,-1.0
+445,2.0,4.0,7.0,8.0
+446,2.0,4.0,7.0,9.0
+447,2.0,4.0,7.0,10.0
+448,2.0,4.0,7.0,11.0
+449,2.0,4.0,8.0,-1.0
+450,2.0,4.0,8.0,9.0
+451,2.0,4.0,8.0,10.0
+452,2.0,4.0,8.0,11.0
+453,2.0,4.0,9.0,-1.0
+454,2.0,4.0,9.0,10.0
+455,2.0,4.0,9.0,11.0
+456,2.0,4.0,10.0,-1.0
+457,2.0,4.0,10.0,11.0
+458,2.0,4.0,11.0,-1.0
+459,2.0,5.0,-1.0,-1.0
+460,2.0,5.0,6.0,-1.0
+461,2.0,5.0,6.0,7.0
+462,2.0,5.0,6.0,8.0
+463,2.0,5.0,6.0,9.0
+464,2.0,5.0,6.0,10.0
+465,2.0,5.0,6.0,11.0
+466,2.0,5.0,7.0,-1.0
+467,2.0,5.0,7.0,8.0
+468,2.0,5.0,7.0,9.0
+469,2.0,5.0,7.0,10.0
+470,2.0,5.0,7.0,11.0
+471,2.0,5.0,8.0,-1.0
+472,2.0,5.0,8.0,9.0
+473,2.0,5.0,8.0,10.0
+474,2.0,5.0,8.0,11.0
+475,2.0,5.0,9.0,-1.0
+476,2.0,5.0,9.0,10.0
+477,2.0,5.0,9.0,11.0
+478,2.0,5.0,10.0,-1.0
+479,2.0,5.0,10.0,11.0
+480,2.0,5.0,11.0,-1.0
+481,2.0,6.0,-1.0,-1.0
+482,2.0,6.0,7.0,-1.0
+483,2.0,6.0,7.0,9.0
+484,2.0,6.0,7.0,10.0
+485,2.0,6.0,7.0,11.0
+486,2.0,6.0,8.0,-1.0
+487,2.0,6.0,8.0,9.0
+488,2.0,6.0,8.0,10.0
+489,2.0,6.0,8.0,11.0
+490,2.0,6.0,9.0,-1.0
+491,2.0,6.0,9.0,10.0
+492,2.0,6.0,9.0,11.0
+493,2.0,6.0,10.0,-1.0
+494,2.0,6.0,10.0,11.0
+495,2.0,6.0,11.0,-1.0
+496,2.0,7.0,-1.0,-1.0
+497,2.0,7.0,8.0,-1.0
+498,2.0,7.0,8.0,9.0
+499,2.0,7.0,8.0,10.0
+500,2.0,7.0,8.0,11.0
+501,2.0,7.0,9.0,-1.0
+502,2.0,7.0,9.0,10.0
+503,2.0,7.0,9.0,11.0
+504,2.0,7.0,10.0,-1.0
+505,2.0,7.0,10.0,11.0
+506,2.0,7.0,11.0,-1.0
+507,2.0,8.0,-1.0,-1.0
+508,2.0,8.0,9.0,-1.0
+509,2.0,8.0,9.0,10.0
+510,2.0,8.0,9.0,11.0
+511,2.0,8.0,10.0,-1.0
+512,2.0,8.0,10.0,11.0
+513,2.0,8.0,11.0,-1.0
+514,2.0,9.0,-1.0,-1.0
+515,2.0,9.0,10.0,-1.0
+516,2.0,9.0,10.0,11.0
+517,2.0,9.0,11.0,-1.0
+518,2.0,10.0,-1.0,-1.0
+519,2.0,10.0,11.0,-1.0
+520,2.0,11.0,-1.0,-1.0
+521,3.0,-1.0,-1.0,-1.0
+522,3.0,4.0,-1.0,-1.0
+523,3.0,4.0,5.0,-1.0
+524,3.0,4.0,5.0,8.0
+525,3.0,4.0,5.0,10.0
+526,3.0,4.0,5.0,11.0
+527,3.0,4.0,6.0,-1.0
+528,3.0,4.0,6.0,7.0
+529,3.0,4.0,6.0,8.0
+530,3.0,4.0,6.0,9.0
+531,3.0,4.0,6.0,10.0
+532,3.0,4.0,6.0,11.0
+533,3.0,4.0,7.0,-1.0
+534,3.0,4.0,7.0,9.0
+535,3.0,4.0,7.0,10.0
+536,3.0,4.0,7.0,11.0
+537,3.0,4.0,8.0,-1.0
+538,3.0,4.0,8.0,9.0
+539,3.0,4.0,8.0,10.0
+540,3.0,4.0,8.0,11.0
+541,3.0,4.0,9.0,-1.0
+542,3.0,4.0,9.0,10.0
+543,3.0,4.0,9.0,11.0
+544,3.0,4.0,10.0,-1.0
+545,3.0,4.0,10.0,11.0
+546,3.0,4.0,11.0,-1.0
+547,3.0,5.0,-1.0,-1.0
+548,3.0,5.0,6.0,-1.0
+549,3.0,5.0,6.0,7.0
+550,3.0,5.0,6.0,8.0
+551,3.0,5.0,6.0,9.0
+552,3.0,5.0,6.0,10.0
+553,3.0,5.0,6.0,11.0
+554,3.0,5.0,7.0,-1.0
+555,3.0,5.0,7.0,8.0
+556,3.0,5.0,7.0,9.0
+557,3.0,5.0,7.0,10.0
+558,3.0,5.0,7.0,11.0
+559,3.0,5.0,8.0,-1.0
+560,3.0,5.0,8.0,9.0
+561,3.0,5.0,8.0,10.0
+562,3.0,5.0,8.0,11.0
+563,3.0,5.0,9.0,-1.0
+564,3.0,5.0,9.0,10.0
+565,3.0,5.0,9.0,11.0
+566,3.0,5.0,10.0,-1.0
+567,3.0,5.0,10.0,11.0
+568,3.0,5.0,11.0,-1.0
+569,3.0,6.0,-1.0,-1.0
+570,3.0,6.0,7.0,-1.0
+571,3.0,6.0,7.0,8.0
+572,3.0,6.0,7.0,10.0
+573,3.0,6.0,7.0,11.0
+574,3.0,6.0,8.0,-1.0
+575,3.0,6.0,8.0,9.0
+576,3.0,6.0,8.0,10.0
+577,3.0,6.0,8.0,11.0
+578,3.0,6.0,9.0,-1.0
+579,3.0,6.0,9.0,10.0
+580,3.0,6.0,9.0,11.0
+581,3.0,6.0,10.0,-1.0
+582,3.0,6.0,10.0,11.0
+583,3.0,6.0,11.0,-1.0
+584,3.0,7.0,-1.0,-1.0
+585,3.0,7.0,8.0,-1.0
+586,3.0,7.0,8.0,10.0
+587,3.0,7.0,8.0,11.0
+588,3.0,7.0,9.0,-1.0
+589,3.0,7.0,10.0,-1.0
+590,3.0,7.0,10.0,11.0
+591,3.0,7.0,11.0,-1.0
+592,3.0,8.0,-1.0,-1.0
+593,3.0,8.0,9.0,-1.0
+594,3.0,8.0,9.0,10.0
+595,3.0,8.0,9.0,11.0
+596,3.0,8.0,10.0,-1.0
+597,3.0,8.0,10.0,11.0
+598,3.0,8.0,11.0,-1.0
+599,3.0,9.0,-1.0,-1.0
+600,3.0,9.0,10.0,-1.0
+601,3.0,9.0,10.0,11.0
+602,3.0,9.0,11.0,-1.0
+603,3.0,10.0,-1.0,-1.0
+604,3.0,10.0,11.0,-1.0
+605,3.0,11.0,-1.0,-1.0
+606,4.0,-1.0,-1.0,-1.0
+607,4.0,5.0,-1.0,-1.0
+608,4.0,5.0,6.0,-1.0
+609,4.0,5.0,6.0,8.0
+610,4.0,5.0,6.0,9.0
+611,4.0,5.0,6.0,10.0
+612,4.0,5.0,6.0,11.0
+613,4.0,5.0,7.0,-1.0
+614,4.0,5.0,7.0,9.0
+615,4.0,5.0,7.0,10.0
+616,4.0,5.0,7.0,11.0
+617,4.0,5.0,8.0,-1.0
+618,4.0,5.0,8.0,9.0
+619,4.0,5.0,8.0,10.0
+620,4.0,5.0,8.0,11.0
+621,4.0,5.0,9.0,-1.0
+622,4.0,5.0,9.0,10.0
+623,4.0,5.0,9.0,11.0
+624,4.0,5.0,10.0,-1.0
+625,4.0,5.0,10.0,11.0
+626,4.0,5.0,11.0,-1.0
+627,4.0,6.0,-1.0,-1.0
+628,4.0,6.0,7.0,-1.0
+629,4.0,6.0,7.0,8.0
+630,4.0,6.0,7.0,9.0
+631,4.0,6.0,7.0,10.0
+632,4.0,6.0,7.0,11.0
+633,4.0,6.0,8.0,-1.0
+634,4.0,6.0,8.0,9.0
+635,4.0,6.0,8.0,10.0
+636,4.0,6.0,8.0,11.0
+637,4.0,6.0,9.0,-1.0
+638,4.0,6.0,9.0,10.0
+639,4.0,6.0,9.0,11.0
+640,4.0,6.0,10.0,-1.0
+641,4.0,6.0,10.0,11.0
+642,4.0,6.0,11.0,-1.0
+643,4.0,7.0,-1.0,-1.0
+644,4.0,7.0,8.0,-1.0
+645,4.0,7.0,8.0,9.0
+646,4.0,7.0,8.0,11.0
+647,4.0,7.0,9.0,-1.0
+648,4.0,7.0,9.0,10.0
+649,4.0,7.0,9.0,11.0
+650,4.0,7.0,10.0,-1.0
+651,4.0,7.0,10.0,11.0
+652,4.0,7.0,11.0,-1.0
+653,4.0,8.0,-1.0,-1.0
+654,4.0,8.0,9.0,-1.0
+655,4.0,8.0,9.0,10.0
+656,4.0,8.0,9.0,11.0
+657,4.0,8.0,10.0,-1.0
+658,4.0,8.0,10.0,11.0
+659,4.0,8.0,11.0,-1.0
+660,4.0,9.0,-1.0,-1.0
+661,4.0,9.0,10.0,-1.0
+662,4.0,9.0,10.0,11.0
+663,4.0,9.0,11.0,-1.0
+664,4.0,10.0,-1.0,-1.0
+665,4.0,10.0,11.0,-1.0
+666,4.0,11.0,-1.0,-1.0
+667,5.0,-1.0,-1.0,-1.0
+668,5.0,6.0,-1.0,-1.0
+669,5.0,6.0,7.0,-1.0
+670,5.0,6.0,7.0,8.0
+671,5.0,6.0,7.0,9.0
+672,5.0,6.0,7.0,10.0
+673,5.0,6.0,7.0,11.0
+674,5.0,6.0,8.0,-1.0
+675,5.0,6.0,8.0,9.0
+676,5.0,6.0,8.0,10.0
+677,5.0,6.0,8.0,11.0
+678,5.0,6.0,9.0,-1.0
+679,5.0,6.0,9.0,10.0
+680,5.0,6.0,9.0,11.0
+681,5.0,6.0,10.0,-1.0
+682,5.0,6.0,10.0,11.0
+683,5.0,6.0,11.0,-1.0
+684,5.0,7.0,-1.0,-1.0
+685,5.0,7.0,8.0,-1.0
+686,5.0,7.0,8.0,9.0
+687,5.0,7.0,8.0,10.0
+688,5.0,7.0,9.0,-1.0
+689,5.0,7.0,9.0,10.0
+690,5.0,7.0,9.0,11.0
+691,5.0,7.0,10.0,-1.0
+692,5.0,7.0,10.0,11.0
+693,5.0,7.0,11.0,-1.0
+694,5.0,8.0,-1.0,-1.0
+695,5.0,8.0,9.0,-1.0
+696,5.0,8.0,9.0,10.0
+697,5.0,8.0,10.0,-1.0
+698,5.0,8.0,10.0,11.0
+699,5.0,8.0,11.0,-1.0
+700,5.0,9.0,-1.0,-1.0
+701,5.0,9.0,10.0,-1.0
+702,5.0,9.0,10.0,11.0
+703,5.0,9.0,11.0,-1.0
+704,5.0,10.0,-1.0,-1.0
+705,5.0,10.0,11.0,-1.0
+706,5.0,11.0,-1.0,-1.0
+707,6.0,-1.0,-1.0,-1.0
+708,6.0,7.0,-1.0,-1.0
+709,6.0,7.0,8.0,-1.0
+710,6.0,7.0,8.0,10.0
+711,6.0,7.0,8.0,11.0
+712,6.0,7.0,9.0,-1.0
+713,6.0,7.0,9.0,10.0
+714,6.0,7.0,9.0,11.0
+715,6.0,7.0,10.0,-1.0
+716,6.0,7.0,10.0,11.0
+717,6.0,7.0,11.0,-1.0
+718,6.0,8.0,-1.0,-1.0
+719,6.0,8.0,9.0,-1.0
+720,6.0,8.0,9.0,11.0
+721,6.0,8.0,10.0,-1.0
+722,6.0,8.0,10.0,11.0
+723,6.0,8.0,11.0,-1.0
+724,6.0,9.0,-1.0,-1.0
+725,6.0,9.0,10.0,-1.0
+726,6.0,9.0,11.0,-1.0
+727,6.0,10.0,-1.0,-1.0
+728,6.0,10.0,11.0,-1.0
+729,6.0,11.0,-1.0,-1.0
+730,7.0,-1.0,-1.0,-1.0
+731,7.0,8.0,-1.0,-1.0
+732,7.0,8.0,9.0,-1.0
+733,7.0,8.0,9.0,11.0
+734,7.0,8.0,10.0,-1.0
+735,7.0,8.0,11.0,-1.0
+736,7.0,9.0,-1.0,-1.0
+737,7.0,9.0,10.0,-1.0
+738,7.0,9.0,10.0,11.0
+739,7.0,9.0,11.0,-1.0
+740,7.0,10.0,-1.0,-1.0
+741,7.0,10.0,11.0,-1.0
+742,7.0,11.0,-1.0,-1.0
+743,8.0,-1.0,-1.0,-1.0
+744,8.0,9.0,-1.0,-1.0
+745,8.0,9.0,10.0,-1.0
+746,8.0,9.0,10.0,11.0
+747,8.0,9.0,11.0,-1.0
+748,8.0,10.0,-1.0,-1.0
+749,8.0,10.0,11.0,-1.0
+750,8.0,11.0,-1.0,-1.0
+751,9.0,-1.0,-1.0,-1.0
+752,9.0,10.0,-1.0,-1.0
+753,9.0,10.0,11.0,-1.0
+754,9.0,11.0,-1.0,-1.0
+755,10.0,-1.0,-1.0,-1.0
+756,10.0,11.0,-1.0,-1.0
+757,11.0,-1.0,-1.0,-1.0