Compare commits
9 Commits
master
...
heinz-nixd
Author | SHA1 | Date | |
---|---|---|---|
|
31098054e6 | ||
|
5391f3f028 | ||
|
cb63c392ae | ||
|
cb21cce287 | ||
|
49324d2dc5 | ||
|
25c26cda56 | ||
|
0afc8f69f6 | ||
|
896d778dee | ||
|
0033ff4a5c |
31
Dockerfile
31
Dockerfile
@ -1,6 +1,4 @@
|
|||||||
FROM ubuntu:18.04
|
FROM ubuntu:14.04
|
||||||
|
|
||||||
ENV DEBIAN_FRONTEND=noninteractive
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
pkg-config \
|
pkg-config \
|
||||||
@ -10,28 +8,19 @@ RUN apt-get update && apt-get install -y \
|
|||||||
libblas-dev \
|
libblas-dev \
|
||||||
liblapack-dev \
|
liblapack-dev \
|
||||||
libatlas-base-dev \
|
libatlas-base-dev \
|
||||||
libsndfile1-dev \
|
|
||||||
libasound2-dev \
|
|
||||||
libjack-dev \
|
|
||||||
gfortran \
|
gfortran \
|
||||||
ffmpeg \
|
|
||||||
llvm-8 \
|
|
||||||
python \
|
python \
|
||||||
python2.7 \
|
python-dev \
|
||||||
python3 \
|
python-pip \
|
||||||
python3-dev \
|
curl && \
|
||||||
python3-pip \
|
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - && \
|
||||||
python3-venv \
|
apt-get install -y nodejs
|
||||||
nvidia-cuda-dev \
|
|
||||||
curl \
|
|
||||||
nodejs \
|
|
||||||
npm && \
|
|
||||||
apt clean
|
|
||||||
|
|
||||||
RUN pip3 install --upgrade pip
|
|
||||||
|
RUN pip install -U https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.12.1-cp27-none-linux_x86_64.whl
|
||||||
|
|
||||||
COPY ./server/requirements.txt /tmp/
|
COPY ./server/requirements.txt /tmp/
|
||||||
RUN pip3 install -r /tmp/requirements.txt
|
RUN pip install -r /tmp/requirements.txt
|
||||||
|
|
||||||
COPY . /src/
|
COPY . /src/
|
||||||
|
|
||||||
@ -41,4 +30,4 @@ RUN npm install && npm run build
|
|||||||
WORKDIR /src/server/
|
WORKDIR /src/server/
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENTRYPOINT python3 server.py
|
ENTRYPOINT python server.py
|
||||||
|
38
README.md
38
README.md
@ -18,6 +18,35 @@ Built by [Yotam Mann](https://github.com/tambien) with friends on the Magenta an
|
|||||||
|
|
||||||
A.I. Duet is composed of two parts, the front-end which is in the `static` folder and the back-end which is in the `server` folder. The front-end client creates short MIDI files using the players's input which is sent to a [Flask](http://flask.pocoo.org/) server. The server takes that MIDI input and "continues" it using [Magenta](https://github.com/tensorflow/magenta) and [TensorFlow](https://www.tensorflow.org/) which is then returned back to the client.
|
A.I. Duet is composed of two parts, the front-end which is in the `static` folder and the back-end which is in the `server` folder. The front-end client creates short MIDI files using the players's input which is sent to a [Flask](http://flask.pocoo.org/) server. The server takes that MIDI input and "continues" it using [Magenta](https://github.com/tensorflow/magenta) and [TensorFlow](https://www.tensorflow.org/) which is then returned back to the client.
|
||||||
|
|
||||||
|
## INSTALLATION
|
||||||
|
|
||||||
|
A.I. Duet only works with [Python 2.7](https://www.python.org/download/releases/2.7/) and it was tested with Node v6. There are two basic ways of installing A.I. Duet: with Docker or without Docker.
|
||||||
|
|
||||||
|
If you already have a Python environment setup, install all of the server dependencies and start the server by typing the following in the terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd server
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
If this does not work, jump down to the [Docker](#docker) installation instructions, which will walk you through installing A.I. Duet within a Docker container.
|
||||||
|
|
||||||
|
If it _did_ install tensorflow and magenta successfully, you can run the server by typing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python server.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Then to build and install the front-end Javascript code, first make sure you have [Node.js](https://nodejs.org) 6 installed. And then install of the dependencies of the project and build the code by typing the following in the terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd static
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now play with A.I. Duet at [localhost:8080](http://localhost:8080).
|
||||||
|
|
||||||
## DOCKER
|
## DOCKER
|
||||||
|
|
||||||
[Docker](https://www.docker.com/) is an open-source containerization software which simplifies installation across various OSes. It is the simplest method to build and install both the front-end and back-end components. Once you have Docker installed, you can just run:
|
[Docker](https://www.docker.com/) is an open-source containerization software which simplifies installation across various OSes. It is the simplest method to build and install both the front-end and back-end components. Once you have Docker installed, you can just run:
|
||||||
@ -29,15 +58,6 @@ $ sudo docker run -t -p 8080:8080 ai-duet
|
|||||||
|
|
||||||
You can now play with A.I. Duet at [localhost:8080](http://localhost:8080).
|
You can now play with A.I. Duet at [localhost:8080](http://localhost:8080).
|
||||||
|
|
||||||
## DOCKER-COMPOSE
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ docker-compose build
|
|
||||||
$ docker-compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now play with A.I. Duet at [localhost:8080](http://localhost:8080).
|
|
||||||
|
|
||||||
## MIDI SUPPORT
|
## MIDI SUPPORT
|
||||||
|
|
||||||
The A.I. Duet supports MIDI keyboard input using [Web Midi API](https://webaudio.github.io/web-midi-api/) and the [WebMIDI](https://github.com/cotejp/webmidi) library.
|
The A.I. Duet supports MIDI keyboard input using [Web Midi API](https://webaudio.github.io/web-midi-api/) and the [WebMIDI](https://github.com/cotejp/webmidi) library.
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
version: '2'
|
|
||||||
|
|
||||||
services:
|
|
||||||
aiduet:
|
|
||||||
build: ./
|
|
||||||
image: aiduet
|
|
||||||
container_name: aiduet
|
|
||||||
hostname: aiduet
|
|
||||||
restart: "no"
|
|
||||||
environment:
|
|
||||||
- NVIDIA_VISIBLE_DEVICES=all
|
|
||||||
devices:
|
|
||||||
- /dev/nvidia0
|
|
||||||
- /dev/nvidiactl
|
|
||||||
- /dev/nvidia-uvm
|
|
||||||
- /dev/nvidia-uvm-tools
|
|
||||||
cap_add:
|
|
||||||
- IPC_LOCK
|
|
||||||
network_mode: host
|
|
@ -1,6 +1,5 @@
|
|||||||
tensorflow-gpu==1.14.0
|
tensorflow==0.12.1
|
||||||
magenta==1.1.8
|
magenta==0.1.8
|
||||||
numba==0.53.1
|
Flask==0.12
|
||||||
Flask
|
gunicorn==19.6.0
|
||||||
gunicorn
|
ipython==5.1.0
|
||||||
ipython
|
|
@ -22,7 +22,7 @@ import sys
|
|||||||
if sys.version_info.major <= 2:
|
if sys.version_info.major <= 2:
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
else:
|
else:
|
||||||
from io import StringIO, BytesIO
|
from io import StringIO
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -34,8 +34,7 @@ app = Flask(__name__, static_url_path='', static_folder=os.path.abspath('../stat
|
|||||||
def predict():
|
def predict():
|
||||||
now = time.time()
|
now = time.time()
|
||||||
values = json.loads(request.data)
|
values = json.loads(request.data)
|
||||||
# midi_data = pretty_midi.PrettyMIDI(StringIO(''.join(chr(v) for v in values)))
|
midi_data = pretty_midi.PrettyMIDI(StringIO(''.join(chr(v) for v in values)))
|
||||||
midi_data = pretty_midi.PrettyMIDI(BytesIO(b''.join((v).to_bytes(1, 'little') for v in values)))
|
|
||||||
duration = float(request.args.get('duration'))
|
duration = float(request.args.get('duration'))
|
||||||
ret_midi = generate_midi(midi_data, duration)
|
ret_midi = generate_midi(midi_data, duration)
|
||||||
return send_file(ret_midi, attachment_filename='return.mid',
|
return send_file(ret_midi, attachment_filename='return.mid',
|
||||||
|
21
static/images/keyboard_icon_CMedit.svg
Normal file
21
static/images/keyboard_icon_CMedit.svg
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 292.7 119.8" style="enable-background:new 0 0 292.7 119.8;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FBBC05;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<polyline class="st0" points="131,92 131,65.9 124.7,65.9 124.7,30.4 110.2,30.4 110.2,92 "/>
|
||||||
|
<polyline class="st0" points="155.9,92 155.9,65.9 149.7,65.9 149.7,30.4 141.4,30.4 141.4,65.9 135.1,65.9 135.1,92 "/>
|
||||||
|
<polyline class="st0" points="255.8,92 255.8,65.9 249.6,65.9 249.6,30.4 241.3,30.4 241.3,65.9 235,65.9 235,92 "/>
|
||||||
|
<polyline class="st0" points="180.9,92 180.9,65.9 180.9,30.4 174.6,30.4 166.3,30.4 166.3,65.9 160.1,65.9 160.1,92 "/>
|
||||||
|
<polyline class="st0" points="205.9,92 205.9,65.9 199.6,65.9 199.6,30.4 185.1,30.4 185.1,92 "/>
|
||||||
|
<polyline class="st0" points="230.8,92 230.8,65.9 224.6,65.9 224.6,30.4 216.3,30.4 216.3,65.9 210,65.9 210,92 "/>
|
||||||
|
<polyline class="st0" points="280.8,92 280.8,65.9 280.8,30.4 274.5,30.4 266.2,30.4 266.2,65.9 260,65.9 260,92 "/>
|
||||||
|
<polyline class="st0" points="81.1,92 81.1,65.9 74.9,65.9 74.9,30.4 66.6,30.4 66.6,65.9 60.3,65.9 60.3,92 "/>
|
||||||
|
<polyline class="st0" points="31.2,92 31.2,65.9 24.9,65.9 24.9,30.4 10.4,30.4 10.4,92 "/>
|
||||||
|
<polyline class="st0" points="56.1,92 56.1,65.9 49.9,65.9 49.9,30.4 41.6,30.4 41.6,65.9 35.3,65.9 35.3,92 "/>
|
||||||
|
<polyline class="st0" points="106.1,92 106.1,65.9 106.1,30.4 99.8,30.4 91.5,30.4 91.5,65.9 85.3,65.9 85.3,92 "/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -23,6 +23,7 @@
|
|||||||
"events": "^1.1.0",
|
"events": "^1.1.0",
|
||||||
"exports-loader": "^0.6.3",
|
"exports-loader": "^0.6.3",
|
||||||
"file-loader": "^0.9.0",
|
"file-loader": "^0.9.0",
|
||||||
|
"idle-timeout": "^1.0.0",
|
||||||
"jsmidgen": "^0.1.5",
|
"jsmidgen": "^0.1.5",
|
||||||
"midi-file-parser": "^1.0.0",
|
"midi-file-parser": "^1.0.0",
|
||||||
"midiconvert": "0.4.1",
|
"midiconvert": "0.4.1",
|
||||||
|
@ -22,33 +22,49 @@ import {Splash} from 'interface/Splash'
|
|||||||
import {About} from 'interface/About'
|
import {About} from 'interface/About'
|
||||||
import {Tutorial} from 'ai/Tutorial'
|
import {Tutorial} from 'ai/Tutorial'
|
||||||
import 'babel-polyfill'
|
import 'babel-polyfill'
|
||||||
|
import IdleTimeout from 'idle-timeout'
|
||||||
|
|
||||||
/////////////// SPLASH ///////////////////
|
/////////////// SPLASH ///////////////////
|
||||||
|
// const about = new About(document.body)
|
||||||
const about = new About(document.body)
|
|
||||||
const splash = new Splash(document.body)
|
const splash = new Splash(document.body)
|
||||||
splash.on('click', () => {
|
splash.on('click', () => {
|
||||||
keyboard.activate()
|
keyboard.activate()
|
||||||
tutorial.start()
|
tutorial.start()
|
||||||
about.showButton()
|
// about.showButton()
|
||||||
})
|
|
||||||
splash.on('about', () => {
|
|
||||||
about.open(true)
|
|
||||||
})
|
|
||||||
about.on('close', () => {
|
|
||||||
if (!splash.loaded || splash.isOpen()){
|
|
||||||
splash.show()
|
|
||||||
} else {
|
|
||||||
keyboard.activate()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
about.on('open', () => {
|
|
||||||
keyboard.deactivate()
|
|
||||||
if (splash.isOpen()){
|
|
||||||
splash.hide()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
// splash.on('about', () => {
|
||||||
|
// about.open(true)
|
||||||
|
// })
|
||||||
|
// about.on('close', () => {
|
||||||
|
// if (!splash.loaded || splash.isOpen()){
|
||||||
|
// splash.show()
|
||||||
|
// keyboard._active = true
|
||||||
|
// } else {
|
||||||
|
//
|
||||||
|
// keyboard.activate()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// about.on('open', () => {
|
||||||
|
// keyboard.deactivate()
|
||||||
|
// if (splash.isOpen()){
|
||||||
|
// splash.hide()
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
//////////////// IDLE TIMER ///////////////
|
||||||
|
const idleTimeout = new IdleTimeout(
|
||||||
|
() => {
|
||||||
|
keyboard.deactivate();
|
||||||
|
// about.close();
|
||||||
|
splash.show();
|
||||||
|
keyboard._active = true;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
element: document,
|
||||||
|
timeout: 60000,
|
||||||
|
loop: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
/////////////// PIANO ///////////////////
|
/////////////// PIANO ///////////////////
|
||||||
|
|
||||||
@ -63,6 +79,7 @@ const sound = new Sound()
|
|||||||
sound.load()
|
sound.load()
|
||||||
|
|
||||||
keyboard.on('keyDown', (note) => {
|
keyboard.on('keyDown', (note) => {
|
||||||
|
idleTimeout.reset()
|
||||||
sound.keyDown(note)
|
sound.keyDown(note)
|
||||||
ai.keyDown(note)
|
ai.keyDown(note)
|
||||||
glow.user()
|
glow.user()
|
||||||
@ -112,4 +129,4 @@ tutorial.on('aiKeyDown', (note, time) => {
|
|||||||
|
|
||||||
tutorial.on('aiKeyUp', (note, time) => {
|
tutorial.on('aiKeyUp', (note, time) => {
|
||||||
ai.keyUp(note, time)
|
ai.keyUp(note, time)
|
||||||
})
|
})
|
||||||
|
@ -23,12 +23,28 @@ const tfLink = 'https://www.tensorflow.org/'
|
|||||||
const toneLink = 'https://github.com/Tonejs/Tone.js'
|
const toneLink = 'https://github.com/Tonejs/Tone.js'
|
||||||
const sourceCode = 'https://github.com/googlecreativelab/aiexperiments-ai-duet'
|
const sourceCode = 'https://github.com/googlecreativelab/aiexperiments-ai-duet'
|
||||||
|
|
||||||
const blurbCopy = `Built by Yotam Mann with friends on the Magenta and Creative Lab teams at Google.
|
const germanAboutCopy = `AI Duet nutzt maschinelles Lernen, um zu entscheiden,
|
||||||
It uses <a target='_blank' href='${tfLink}'>TensorFlow</a>,
|
wie es auf die Noten reagiert, die du spielst. Dafür wurde
|
||||||
<a target='_blank' href='${toneLink}'>Tone.js</a> and tools
|
das neuronale Netz mit einer Vielzahl an Melodien trainiert.
|
||||||
from the <a target='_blank' href='${magentaLink}'>Magenta project</a>.
|
das neuronale Netz mit einer Vielzahl an Melodien trainiert.
|
||||||
The open-source code is <a target='_blank' href='${sourceCode}'>available here</a>.
|
Noten and Takten. Regeln dafür, wie Noten oder Akkorde
|
||||||
Click the keyboard, use your computer keys, or even plug in a MIDI keyboard.`
|
verarbeitet werden sollen, wurden dem System nicht gegeben.`
|
||||||
|
|
||||||
|
const englishAboutCopy = `AI Duet uses machine learning in order to decide on
|
||||||
|
how it responds to the notes you play. To do so,
|
||||||
|
a neural network was trained with lots of melodies.
|
||||||
|
Over time, the system learned about relationships
|
||||||
|
between notes and timing. No rules predefine how
|
||||||
|
notes or chords should be treated.`
|
||||||
|
|
||||||
|
|
||||||
|
const germanMadeByCopy = `Programmierung und Konzept: Yotam Mann und Google’s
|
||||||
|
Creative Lab. Neuronales Netz: Google’s Magenta project.
|
||||||
|
Der gesamte Quellcode ist Open Source: g.co/aiexperiments`
|
||||||
|
|
||||||
|
const englishMadeByCopy = `Programming and Concept: Yotam Mann and Google’s
|
||||||
|
Creative Lab. Neural net: Google’s Magenta project.
|
||||||
|
All the code is open source: g.co/aiexperiments`
|
||||||
|
|
||||||
export class About extends events.EventEmitter{
|
export class About extends events.EventEmitter{
|
||||||
constructor(container){
|
constructor(container){
|
||||||
@ -59,13 +75,13 @@ export class About extends events.EventEmitter{
|
|||||||
const title = document.createElement('div')
|
const title = document.createElement('div')
|
||||||
title.id = 'title'
|
title.id = 'title'
|
||||||
title.textContent = 'A.I. Duet'
|
title.textContent = 'A.I. Duet'
|
||||||
// content.appendChild(title)
|
content.appendChild(title)
|
||||||
|
|
||||||
const video = document.createElement('div')
|
const video = document.createElement('div')
|
||||||
video.id = 'video'
|
video.id = 'video'
|
||||||
//vid YT0k99hCY5I
|
//vid YT0k99hCY5I
|
||||||
video.innerHTML = `<iframe id='youtube-iframe' src="https://www.youtube.com/embed/0ZE1bfPtvZo?modestbranding=0&showinfo=0&enablejsapi=1" frameborder="0" allowfullscreen></iframe>`
|
video.innerHTML = `<iframe id='youtube-iframe' src="https://www.youtube.com/embed/0ZE1bfPtvZo?modestbranding=0&showinfo=0&enablejsapi=1" frameborder="0" allowfullscreen></iframe>`
|
||||||
content.appendChild(video)
|
// content.appendChild(video)
|
||||||
|
|
||||||
this._ytplayer = null
|
this._ytplayer = null
|
||||||
|
|
||||||
@ -84,21 +100,38 @@ export class About extends events.EventEmitter{
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const blurb = document.createElement('div')
|
const germanAbout = document.createElement('div')
|
||||||
blurb.id = 'blurb'
|
germanAbout.id = 'germanAbout'
|
||||||
content.appendChild(blurb)
|
content.appendChild(germanAbout)
|
||||||
blurb.innerHTML = blurbCopy
|
germanAbout.innerHTML = germanAboutCopy
|
||||||
|
|
||||||
|
const englishAbout = document.createElement('div')
|
||||||
|
englishAbout.id = 'englishAbout'
|
||||||
|
content.appendChild(englishAbout)
|
||||||
|
englishAbout.innerHTML = englishAboutCopy
|
||||||
|
|
||||||
|
const germanMadeBy = document.createElement('div')
|
||||||
|
germanMadeBy.id = 'germanMadeBy'
|
||||||
|
content.appendChild(germanMadeBy)
|
||||||
|
germanMadeBy.innerHTML = germanMadeByCopy
|
||||||
|
|
||||||
|
const englishMadeBy = document.createElement('div')
|
||||||
|
englishMadeBy.id = 'englishMadeBy'
|
||||||
|
content.appendChild(englishMadeBy)
|
||||||
|
englishMadeBy.innerHTML = englishMadeByCopy
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
close(){
|
close(){
|
||||||
this._toggleButton.classList.remove('close')
|
this._toggleButton.classList.remove('close')
|
||||||
this._toggleButton.classList.add('open')
|
this._toggleButton.classList.add('open')
|
||||||
|
|
||||||
this._container.classList.remove('visible')
|
this._container.classList.remove('visible')
|
||||||
|
|
||||||
if (this._ytplayer && this._ytplayer.stopVideo){
|
// if (this._ytplayer && this._ytplayer.stopVideo){
|
||||||
this._ytplayer.stopVideo()
|
// this._ytplayer.stopVideo()
|
||||||
}
|
// }
|
||||||
this.emit('close')
|
this.emit('close')
|
||||||
if (window.ga){
|
if (window.ga){
|
||||||
ga('send', 'event', 'AI-Duet', 'Click', 'About - Close')
|
ga('send', 'event', 'AI-Duet', 'Click', 'About - Close')
|
||||||
@ -108,25 +141,25 @@ export class About extends events.EventEmitter{
|
|||||||
this._toggleButton.classList.add('close')
|
this._toggleButton.classList.add('close')
|
||||||
this._toggleButton.classList.remove('open')
|
this._toggleButton.classList.remove('open')
|
||||||
|
|
||||||
this._playButton.classList.add('visible')
|
// this._playButton.classList.add('visible')
|
||||||
this._container.classList.add('visible')
|
this._container.classList.add('visible')
|
||||||
this.emit('open')
|
this.emit('open')
|
||||||
if (window.ga){
|
if (window.ga){
|
||||||
ga('send', 'event', 'AI-Duet', 'Click', 'About - Open')
|
ga('send', 'event', 'AI-Duet', 'Click', 'About - Open')
|
||||||
}
|
}
|
||||||
if (play){
|
// if (play){
|
||||||
this._playVideo()
|
// this._playVideo()
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
// waits until the player is ready to play the video,
|
// waits until the player is ready to play the video,
|
||||||
// otherwise goes back into waiting loop
|
// otherwise goes back into waiting loop
|
||||||
_playVideo(retries=0){
|
// _playVideo(retries=0){
|
||||||
if (this._ytplayer && this._ytplayer.playVideo){
|
// if (this._ytplayer && this._ytplayer.playVideo){
|
||||||
this._ytplayer.playVideo()
|
// this._ytplayer.playVideo()
|
||||||
} else if (retries < 10 && this.isOpen()){
|
// } else if (retries < 10 && this.isOpen()){
|
||||||
setTimeout(() => this._playVideo(retries+1), 200);
|
// setTimeout(() => this._playVideo(retries+1), 200);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
isOpen(){
|
isOpen(){
|
||||||
return this._container.classList.contains('visible')
|
return this._container.classList.contains('visible')
|
||||||
}
|
}
|
||||||
|
@ -47,16 +47,9 @@ export default class Loader extends EventEmitter{
|
|||||||
this.loaded = false
|
this.loaded = false
|
||||||
|
|
||||||
Buffer.on('load', () => {
|
Buffer.on('load', () => {
|
||||||
|
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
|
loader.classList.add("loaded")
|
||||||
fillText.innerHTML = '<div id="piano"></div> <div id="play">PLAY</div>'
|
fillText.innerHTML = '<div id="piano">'
|
||||||
loader.classList.add('clickable')
|
|
||||||
|
|
||||||
loader.addEventListener('click', () => {
|
|
||||||
|
|
||||||
this.emit('click')
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
Buffer.on('progress', (prog) => {
|
Buffer.on('progress', (prog) => {
|
||||||
|
@ -38,9 +38,13 @@ class Splash extends events.EventEmitter{
|
|||||||
titleContainer.appendChild(title)
|
titleContainer.appendChild(title)
|
||||||
|
|
||||||
const subTitle = document.createElement('div')
|
const subTitle = document.createElement('div')
|
||||||
|
const germanSubTitle = document.createElement('div')
|
||||||
subTitle.id = 'subTitle'
|
subTitle.id = 'subTitle'
|
||||||
|
germanSubTitle.id = 'germanSubTitle'
|
||||||
|
titleContainer.appendChild(germanSubTitle)
|
||||||
titleContainer.appendChild(subTitle)
|
titleContainer.appendChild(subTitle)
|
||||||
subTitle.textContent = 'A piano that responds to you.'
|
subTitle.textContent = 'A piano that responds to you.'
|
||||||
|
germanSubTitle.textContent = ' Ein Klavier, das auf deine Eingaben antwortet.'
|
||||||
|
|
||||||
this._clicked = false
|
this._clicked = false
|
||||||
const loader = this._loader = new Loader(titleContainer)
|
const loader = this._loader = new Loader(titleContainer)
|
||||||
@ -51,12 +55,21 @@ class Splash extends events.EventEmitter{
|
|||||||
})
|
})
|
||||||
|
|
||||||
const howItWorks = document.createElement('div')
|
const howItWorks = document.createElement('div')
|
||||||
|
const germanHowItWorks = document.createElement('div')
|
||||||
howItWorks.id = 'howItWorks'
|
howItWorks.id = 'howItWorks'
|
||||||
|
germanHowItWorks.id = 'germanHowItWorks'
|
||||||
|
titleContainer.appendChild(germanHowItWorks)
|
||||||
titleContainer.appendChild(howItWorks)
|
titleContainer.appendChild(howItWorks)
|
||||||
howItWorks.textContent = 'How it works'
|
howItWorks.innerHTML = 'This experiment lets you play a duet with a computer using machine learning.<br>Just play some notes and the computer will respond to your melody.<br>Over time, the system learns about the relationships<br>between notes and timing to play better duets.'
|
||||||
howItWorks.addEventListener('click', () => {
|
germanHowItWorks.innerHTML = 'Mithilfe von maschinellem Lernen kannst du bei diesem Projekt<br>mit dem Computer im Duett spielen. Wenn du eine Melodie anstimmst,<br>wird der Algorithmus auf diese Noten reagieren. Im Laufe der Zeit erlernt<br>das System Bezüge zwischen Noten und Takten und verbessert seine Ergebnisse.<br><br>'
|
||||||
this.emit('about')
|
|
||||||
})
|
// const aboutPageLink = document.createElement('div')
|
||||||
|
// aboutPageLink.id = 'aboutPageLink'
|
||||||
|
// titleContainer.appendChild(aboutPageLink)
|
||||||
|
// aboutPageLink.textContent = 'How it works'
|
||||||
|
// aboutPageLink.addEventListener('click', () => {
|
||||||
|
// this.emit('about')
|
||||||
|
// })
|
||||||
|
|
||||||
const badges = document.createElement('div')
|
const badges = document.createElement('div')
|
||||||
badges.id = 'badges'
|
badges.id = 'badges'
|
||||||
@ -64,7 +77,7 @@ class Splash extends events.EventEmitter{
|
|||||||
|
|
||||||
const aiExperiments = document.createElement('a')
|
const aiExperiments = document.createElement('a')
|
||||||
aiExperiments.id = 'aiExperiments'
|
aiExperiments.id = 'aiExperiments'
|
||||||
aiExperiments.href = 'https://aiexperiments.withgoogle.com'
|
// aiExperiments.href = 'https://aiexperiments.withgoogle.com'
|
||||||
aiExperiments.target = '_blank'
|
aiExperiments.target = '_blank'
|
||||||
aiExperiments.classList.add('badge')
|
aiExperiments.classList.add('badge')
|
||||||
badges.appendChild(aiExperiments)
|
badges.appendChild(aiExperiments)
|
||||||
@ -85,7 +98,7 @@ class Splash extends events.EventEmitter{
|
|||||||
badges.appendChild(break1)
|
badges.appendChild(break1)
|
||||||
|
|
||||||
const magenta = document.createElement('a')
|
const magenta = document.createElement('a')
|
||||||
magenta.href = 'https://magenta.tensorflow.org/'
|
// magenta.href = 'https://magenta.tensorflow.org/'
|
||||||
magenta.target = '_blank'
|
magenta.target = '_blank'
|
||||||
magenta.id = 'magentaLink'
|
magenta.id = 'magentaLink'
|
||||||
magenta.classList.add('badge')
|
magenta.classList.add('badge')
|
||||||
@ -93,10 +106,11 @@ class Splash extends events.EventEmitter{
|
|||||||
magenta.innerHTML = imgHtml + '<div id="text">Built using <span>Magenta</span></div>'
|
magenta.innerHTML = imgHtml + '<div id="text">Built using <span>Magenta</span></div>'
|
||||||
badges.appendChild(magenta)
|
badges.appendChild(magenta)
|
||||||
|
|
||||||
const privacyAndTerms = document.createElement('div')
|
// const privacyAndTerms = document.createElement('div')
|
||||||
privacyAndTerms.id = 'privacyAndTerms'
|
// privacyAndTerms.id = 'privacyAndTerms'
|
||||||
privacyAndTerms.innerHTML = '<a target="_blank" href="https://www.google.com/intl/en/policies/privacy/">Privacy</a><span>&</span><a target="_blank" href="https://www.google.com/intl/en/policies/terms/">Terms</a>'
|
// // privacyAndTerms.innerHTML = '<a target="_blank" href="https://www.google.com/intl/en/policies/privacy/">Privacy</a><span>&</span><a target="_blank" href="https://www.google.com/intl/en/policies/terms/">Terms</a>'
|
||||||
splash.appendChild(privacyAndTerms)
|
// privacyAndTerms.innerHTML = '<a target="_blank">Privacy</a><span>&</span><a target="_blank">Terms</a>'
|
||||||
|
// splash.appendChild(privacyAndTerms)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class Keyboard extends events.EventEmitter{
|
|||||||
|
|
||||||
this._container = container
|
this._container = container
|
||||||
|
|
||||||
this._active = false
|
this._active = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The audio key keyboard
|
* The audio key keyboard
|
||||||
@ -76,9 +76,9 @@ class Keyboard extends events.EventEmitter{
|
|||||||
//the midi input
|
//the midi input
|
||||||
this._midi = new Midi()
|
this._midi = new Midi()
|
||||||
this._midi.on('keyDown', (note) => {
|
this._midi.on('keyDown', (note) => {
|
||||||
this.keyDown(note)
|
this.keyDown(note);
|
||||||
this._emitKeyDown(note)
|
this._emitKeyDown(note);
|
||||||
})
|
});
|
||||||
this._midi.on('keyUp', (note) => {
|
this._midi.on('keyUp', (note) => {
|
||||||
this.keyUp(note)
|
this.keyUp(note)
|
||||||
this._emitKeyUp(note)
|
this._emitKeyUp(note)
|
||||||
@ -111,6 +111,9 @@ class Keyboard extends events.EventEmitter{
|
|||||||
if (!this._active){
|
if (!this._active){
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const splash = document.getElementById("splash");
|
||||||
|
splash.classList.add('disappear');
|
||||||
|
container.classList.add('focus');
|
||||||
if (!this._currentKeys[note]){
|
if (!this._currentKeys[note]){
|
||||||
this._currentKeys[note] = 0
|
this._currentKeys[note] = 0
|
||||||
}
|
}
|
||||||
|
@ -48,14 +48,14 @@ class Midi extends events.EventEmitter{
|
|||||||
})
|
})
|
||||||
inputDevice.addListener('noteon', 'all', (event) => {
|
inputDevice.addListener('noteon', 'all', (event) => {
|
||||||
try {
|
try {
|
||||||
this.emit('keyDown', event.note.number)
|
this.emit('keyDown', event.note.number + 12)
|
||||||
} catch(e){
|
} catch(e){
|
||||||
console.warn(e)
|
console.warn(e)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
inputDevice.addListener('noteoff', 'all', (event) => {
|
inputDevice.addListener('noteoff', 'all', (event) => {
|
||||||
try {
|
try {
|
||||||
this.emit('keyUp', event.note.number)
|
this.emit('keyUp', event.note.number + 12)
|
||||||
} catch(e){
|
} catch(e){
|
||||||
console.warn(e)
|
console.warn(e)
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@
|
|||||||
font-family: $font-family;
|
font-family: $font-family;
|
||||||
font-size: 40px;
|
font-size: 40px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
margin: 20px 0px 30px 0px;
|
margin: 130px 0px 50px 0px;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,17 +143,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#blurb {
|
#germanAbout {
|
||||||
|
|
||||||
position: relative;
|
position: relative;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
font-size: 14px;
|
font-size: 18px;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
#englishAbout {
|
||||||
color: $orange;
|
position: relative;
|
||||||
}
|
text-transform: none;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#germanMadeBy {
|
||||||
|
position: relative;
|
||||||
|
text-transform: none;
|
||||||
|
margin-top: 50px;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#englishMadeBy {
|
||||||
|
position: relative;
|
||||||
|
text-transform: none;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 25px;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ $topMargin: 35px;
|
|||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
#subTitle, #howItWorks {
|
#germanSubTitle, #germanHowItWorks {
|
||||||
margin-top: $topMargin;
|
margin-top: $topMargin;
|
||||||
letter-spacing: 0.8px;
|
letter-spacing: 0.8px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
@ -53,25 +53,66 @@ $topMargin: 35px;
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
#subTitle {
|
||||||
|
margin-top: 0px;
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 15px;
|
||||||
|
width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
#howItWorks {
|
#germanHowItWorks {
|
||||||
$size : 20px;
|
$size : 20px;
|
||||||
$margin: 30px;
|
$margin: 30px;
|
||||||
|
|
||||||
|
color: $orange;
|
||||||
|
width: 100&;
|
||||||
|
display: inline-block;
|
||||||
|
text-align: center;
|
||||||
|
/*margin-left: $margin;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#howItWorks {
|
||||||
|
$size : 20px;
|
||||||
|
$margin: 0px;
|
||||||
|
|
||||||
|
color: $orange;
|
||||||
|
width: 100%;
|
||||||
|
display: inline-block;
|
||||||
|
/*margin-left: $margin;*/
|
||||||
|
letter-spacing: 0.8px;
|
||||||
|
line-height: 30px;
|
||||||
|
font-size: 15px;
|
||||||
|
/*margin-right: auto;*/
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aboutPageLink {
|
||||||
|
$size : 20px;
|
||||||
|
$margin: 30px;
|
||||||
|
|
||||||
|
margin-top: $topMargin;
|
||||||
color: $orange;
|
color: $orange;
|
||||||
width: auto;
|
width: auto;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: $margin;
|
margin-left: $margin;
|
||||||
|
|
||||||
&:before {
|
/*&:before {*/
|
||||||
position: absolute;
|
/*position: absolute;*/
|
||||||
width: $size;
|
/*width: $size;*/
|
||||||
height: $size;
|
/*height: $size;*/
|
||||||
margin-left: -$margin;
|
/*margin-left: -$margin;*/
|
||||||
margin-top: $size / 4;
|
/*margin-top: $size / 4;*/
|
||||||
content : '';
|
/*content : '';*/
|
||||||
background-image: url(../images/yellow_play_triangle.svg);
|
/*background-image: url(../images/yellow_play_triangle.svg);*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@ -82,8 +123,8 @@ $topMargin: 35px;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$loaderWidth: 200px;
|
$loaderWidth: 398px;
|
||||||
$loaderHeight: 60px;
|
$loaderHeight: 118px;
|
||||||
|
|
||||||
#loader {
|
#loader {
|
||||||
position: relative;
|
position: relative;
|
||||||
@ -96,25 +137,6 @@ $topMargin: 35px;
|
|||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
|
|
||||||
&.clickable {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
transition: transform 0.1s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
transform: scale(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#fillText:active {
|
|
||||||
color: black!important;
|
|
||||||
background-color: $orange;
|
|
||||||
|
|
||||||
#piano {
|
|
||||||
filter: brightness(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#loaderText {
|
#loaderText {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -126,7 +148,7 @@ $topMargin: 35px;
|
|||||||
#fill {
|
#fill {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 0%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
|
||||||
@ -135,7 +157,6 @@ $topMargin: 35px;
|
|||||||
width: $loaderWidth;
|
width: $loaderWidth;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
color: $orange;
|
color: $orange;
|
||||||
|
|
||||||
$imgWidth: 40px;
|
$imgWidth: 40px;
|
||||||
$margin : 52px;
|
$margin : 52px;
|
||||||
|
|
||||||
@ -145,17 +166,26 @@ $topMargin: 35px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
#play {
|
#play {
|
||||||
|
margin-left: 150px;
|
||||||
right: $margin;
|
right: $margin;
|
||||||
|
position: relative;
|
||||||
|
/*display: inline-block;*/
|
||||||
|
}
|
||||||
|
#germanPlay {
|
||||||
|
margin-left: 120px;
|
||||||
|
right: $margin;
|
||||||
|
position: absolute;
|
||||||
|
padding-top: 30px;
|
||||||
|
/*display: inline-block;*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#piano {
|
#piano {
|
||||||
left: $margin;
|
width: 100%;
|
||||||
width: $imgWidth;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-image : url(../images/keyboard_icon.svg);
|
background-image : url(../images/keyboard_icon_CMedit.svg);
|
||||||
background-position: center center;
|
background-position: center center;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +195,24 @@ $topMargin: 35px;
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
&.loaded {
|
||||||
|
margin-top: $topMargin;
|
||||||
|
border: none;
|
||||||
|
width: 400px;
|
||||||
|
height: 120px;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
#loaderText {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
#fillText {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
#fill {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,12 +271,12 @@ $topMargin: 35px;
|
|||||||
|
|
||||||
#aiExperiments {
|
#aiExperiments {
|
||||||
background-image: url(../images/badgeAI_master.svg);
|
background-image: url(../images/badgeAI_master.svg);
|
||||||
&:hover {
|
/*&:hover {*/
|
||||||
opacity: 1;
|
/*opacity: 1;*/
|
||||||
&:active {
|
/*&:active {*/
|
||||||
opacity: 0.3;
|
/*opacity: 0.3;*/
|
||||||
}
|
/*}*/
|
||||||
}
|
/*}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
#googleFriends {
|
#googleFriends {
|
||||||
@ -245,9 +293,9 @@ $topMargin: 35px;
|
|||||||
margin-right: 0px;
|
margin-right: 0px;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
&:hover {
|
/*&:hover {*/
|
||||||
opacity: 1;
|
/*opacity: 1;*/
|
||||||
}
|
/*}*/
|
||||||
|
|
||||||
@media (max-width: $smallScreen) {
|
@media (max-width: $smallScreen) {
|
||||||
font-size: 7px;
|
font-size: 7px;
|
||||||
@ -290,10 +338,10 @@ $topMargin: 35px;
|
|||||||
span {
|
span {
|
||||||
color: $orange;
|
color: $orange;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
/*cursor: pointer;*/
|
||||||
&:hover:active {
|
/*&:hover:active {*/
|
||||||
color: white;
|
/*color: white;*/
|
||||||
}
|
/*}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,15 +400,15 @@ $topMargin: 35px;
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: pointer;
|
/*cursor: pointer;*/
|
||||||
|
|
||||||
&:hover {
|
/*&:hover {*/
|
||||||
opacity: 1;
|
/*opacity: 1;*/
|
||||||
|
|
||||||
&:active {
|
/*&:active {*/
|
||||||
opacity: 0.3;
|
/*opacity: 0.3;*/
|
||||||
}
|
/*}*/
|
||||||
}
|
/*}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user