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 @@

Оцените нас

-
-

-
-
-

-
-
+
+ {% csrf_token %} + {{ feedback_form }} + +
- - - + + +
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