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
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
FROM ubuntu:14.04
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
pkg-config \
|
||||
@ -10,28 +8,19 @@ RUN apt-get update && apt-get install -y \
|
||||
libblas-dev \
|
||||
liblapack-dev \
|
||||
libatlas-base-dev \
|
||||
libsndfile1-dev \
|
||||
libasound2-dev \
|
||||
libjack-dev \
|
||||
gfortran \
|
||||
ffmpeg \
|
||||
llvm-8 \
|
||||
python \
|
||||
python2.7 \
|
||||
python3 \
|
||||
python3-dev \
|
||||
python3-pip \
|
||||
python3-venv \
|
||||
nvidia-cuda-dev \
|
||||
curl \
|
||||
nodejs \
|
||||
npm && \
|
||||
apt clean
|
||||
python-dev \
|
||||
python-pip \
|
||||
curl && \
|
||||
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - && \
|
||||
apt-get install -y nodejs
|
||||
|
||||
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/
|
||||
RUN pip3 install -r /tmp/requirements.txt
|
||||
RUN pip install -r /tmp/requirements.txt
|
||||
|
||||
COPY . /src/
|
||||
|
||||
@ -41,4 +30,4 @@ RUN npm install && npm run build
|
||||
WORKDIR /src/server/
|
||||
|
||||
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.
|
||||
|
||||
## 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](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).
|
||||
|
||||
## 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
|
||||
|
||||
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
|
||||
magenta==1.1.8
|
||||
numba==0.53.1
|
||||
Flask
|
||||
gunicorn
|
||||
ipython
|
||||
tensorflow==0.12.1
|
||||
magenta==0.1.8
|
||||
Flask==0.12
|
||||
gunicorn==19.6.0
|
||||
ipython==5.1.0
|
@ -22,7 +22,7 @@ import sys
|
||||
if sys.version_info.major <= 2:
|
||||
from cStringIO import StringIO
|
||||
else:
|
||||
from io import StringIO, BytesIO
|
||||
from io import StringIO
|
||||
import time
|
||||
import json
|
||||
|
||||
@ -34,8 +34,7 @@ app = Flask(__name__, static_url_path='', static_folder=os.path.abspath('../stat
|
||||
def predict():
|
||||
now = time.time()
|
||||
values = json.loads(request.data)
|
||||
# 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)))
|
||||
midi_data = pretty_midi.PrettyMIDI(StringIO(''.join(chr(v) for v in values)))
|
||||
duration = float(request.args.get('duration'))
|
||||
ret_midi = generate_midi(midi_data, duration)
|
||||
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",
|
||||
"exports-loader": "^0.6.3",
|
||||
"file-loader": "^0.9.0",
|
||||
"idle-timeout": "^1.0.0",
|
||||
"jsmidgen": "^0.1.5",
|
||||
"midi-file-parser": "^1.0.0",
|
||||
"midiconvert": "0.4.1",
|
||||
|
@ -22,33 +22,49 @@ import {Splash} from 'interface/Splash'
|
||||
import {About} from 'interface/About'
|
||||
import {Tutorial} from 'ai/Tutorial'
|
||||
import 'babel-polyfill'
|
||||
import IdleTimeout from 'idle-timeout'
|
||||
|
||||
/////////////// SPLASH ///////////////////
|
||||
|
||||
const about = new About(document.body)
|
||||
/////////////// SPLASH ///////////////////
|
||||
// const about = new About(document.body)
|
||||
const splash = new Splash(document.body)
|
||||
splash.on('click', () => {
|
||||
keyboard.activate()
|
||||
tutorial.start()
|
||||
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()
|
||||
}
|
||||
// about.showButton()
|
||||
})
|
||||
// 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 ///////////////////
|
||||
|
||||
@ -63,6 +79,7 @@ const sound = new Sound()
|
||||
sound.load()
|
||||
|
||||
keyboard.on('keyDown', (note) => {
|
||||
idleTimeout.reset()
|
||||
sound.keyDown(note)
|
||||
ai.keyDown(note)
|
||||
glow.user()
|
||||
@ -112,4 +129,4 @@ tutorial.on('aiKeyDown', (note, time) => {
|
||||
|
||||
tutorial.on('aiKeyUp', (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 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.
|
||||
It uses <a target='_blank' href='${tfLink}'>TensorFlow</a>,
|
||||
<a target='_blank' href='${toneLink}'>Tone.js</a> and tools
|
||||
from the <a target='_blank' href='${magentaLink}'>Magenta project</a>.
|
||||
The open-source code is <a target='_blank' href='${sourceCode}'>available here</a>.
|
||||
Click the keyboard, use your computer keys, or even plug in a MIDI keyboard.`
|
||||
const germanAboutCopy = `AI Duet nutzt maschinelles Lernen, um zu entscheiden,
|
||||
wie es auf die Noten reagiert, die du spielst. Dafür wurde
|
||||
das neuronale Netz mit einer Vielzahl an Melodien trainiert.
|
||||
das neuronale Netz mit einer Vielzahl an Melodien trainiert.
|
||||
Noten and Takten. Regeln dafür, wie Noten oder Akkorde
|
||||
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{
|
||||
constructor(container){
|
||||
@ -59,13 +75,13 @@ export class About extends events.EventEmitter{
|
||||
const title = document.createElement('div')
|
||||
title.id = 'title'
|
||||
title.textContent = 'A.I. Duet'
|
||||
// content.appendChild(title)
|
||||
content.appendChild(title)
|
||||
|
||||
const video = document.createElement('div')
|
||||
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>`
|
||||
content.appendChild(video)
|
||||
// content.appendChild(video)
|
||||
|
||||
this._ytplayer = null
|
||||
|
||||
@ -84,21 +100,38 @@ export class About extends events.EventEmitter{
|
||||
})
|
||||
})
|
||||
|
||||
const blurb = document.createElement('div')
|
||||
blurb.id = 'blurb'
|
||||
content.appendChild(blurb)
|
||||
blurb.innerHTML = blurbCopy
|
||||
const germanAbout = document.createElement('div')
|
||||
germanAbout.id = 'germanAbout'
|
||||
content.appendChild(germanAbout)
|
||||
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(){
|
||||
this._toggleButton.classList.remove('close')
|
||||
this._toggleButton.classList.add('open')
|
||||
|
||||
this._container.classList.remove('visible')
|
||||
|
||||
if (this._ytplayer && this._ytplayer.stopVideo){
|
||||
this._ytplayer.stopVideo()
|
||||
}
|
||||
// if (this._ytplayer && this._ytplayer.stopVideo){
|
||||
// this._ytplayer.stopVideo()
|
||||
// }
|
||||
this.emit('close')
|
||||
if (window.ga){
|
||||
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.remove('open')
|
||||
|
||||
this._playButton.classList.add('visible')
|
||||
// this._playButton.classList.add('visible')
|
||||
this._container.classList.add('visible')
|
||||
this.emit('open')
|
||||
if (window.ga){
|
||||
ga('send', 'event', 'AI-Duet', 'Click', 'About - Open')
|
||||
}
|
||||
if (play){
|
||||
this._playVideo()
|
||||
}
|
||||
// if (play){
|
||||
// this._playVideo()
|
||||
// }
|
||||
}
|
||||
// waits until the player is ready to play the video,
|
||||
// otherwise goes back into waiting loop
|
||||
_playVideo(retries=0){
|
||||
if (this._ytplayer && this._ytplayer.playVideo){
|
||||
this._ytplayer.playVideo()
|
||||
} else if (retries < 10 && this.isOpen()){
|
||||
setTimeout(() => this._playVideo(retries+1), 200);
|
||||
}
|
||||
}
|
||||
// _playVideo(retries=0){
|
||||
// if (this._ytplayer && this._ytplayer.playVideo){
|
||||
// this._ytplayer.playVideo()
|
||||
// } else if (retries < 10 && this.isOpen()){
|
||||
// setTimeout(() => this._playVideo(retries+1), 200);
|
||||
// }
|
||||
// }
|
||||
isOpen(){
|
||||
return this._container.classList.contains('visible')
|
||||
}
|
||||
|
@ -47,16 +47,9 @@ export default class Loader extends EventEmitter{
|
||||
this.loaded = false
|
||||
|
||||
Buffer.on('load', () => {
|
||||
|
||||
this.loaded = true
|
||||
|
||||
fillText.innerHTML = '<div id="piano"></div> <div id="play">PLAY</div>'
|
||||
loader.classList.add('clickable')
|
||||
|
||||
loader.addEventListener('click', () => {
|
||||
|
||||
this.emit('click')
|
||||
})
|
||||
loader.classList.add("loaded")
|
||||
fillText.innerHTML = '<div id="piano">'
|
||||
})
|
||||
|
||||
Buffer.on('progress', (prog) => {
|
||||
|
@ -38,9 +38,13 @@ class Splash extends events.EventEmitter{
|
||||
titleContainer.appendChild(title)
|
||||
|
||||
const subTitle = document.createElement('div')
|
||||
const germanSubTitle = document.createElement('div')
|
||||
subTitle.id = 'subTitle'
|
||||
germanSubTitle.id = 'germanSubTitle'
|
||||
titleContainer.appendChild(germanSubTitle)
|
||||
titleContainer.appendChild(subTitle)
|
||||
subTitle.textContent = 'A piano that responds to you.'
|
||||
germanSubTitle.textContent = ' Ein Klavier, das auf deine Eingaben antwortet.'
|
||||
|
||||
this._clicked = false
|
||||
const loader = this._loader = new Loader(titleContainer)
|
||||
@ -51,12 +55,21 @@ class Splash extends events.EventEmitter{
|
||||
})
|
||||
|
||||
const howItWorks = document.createElement('div')
|
||||
const germanHowItWorks = document.createElement('div')
|
||||
howItWorks.id = 'howItWorks'
|
||||
germanHowItWorks.id = 'germanHowItWorks'
|
||||
titleContainer.appendChild(germanHowItWorks)
|
||||
titleContainer.appendChild(howItWorks)
|
||||
howItWorks.textContent = 'How it works'
|
||||
howItWorks.addEventListener('click', () => {
|
||||
this.emit('about')
|
||||
})
|
||||
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.'
|
||||
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>'
|
||||
|
||||
// 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')
|
||||
badges.id = 'badges'
|
||||
@ -64,7 +77,7 @@ class Splash extends events.EventEmitter{
|
||||
|
||||
const aiExperiments = document.createElement('a')
|
||||
aiExperiments.id = 'aiExperiments'
|
||||
aiExperiments.href = 'https://aiexperiments.withgoogle.com'
|
||||
// aiExperiments.href = 'https://aiexperiments.withgoogle.com'
|
||||
aiExperiments.target = '_blank'
|
||||
aiExperiments.classList.add('badge')
|
||||
badges.appendChild(aiExperiments)
|
||||
@ -85,7 +98,7 @@ class Splash extends events.EventEmitter{
|
||||
badges.appendChild(break1)
|
||||
|
||||
const magenta = document.createElement('a')
|
||||
magenta.href = 'https://magenta.tensorflow.org/'
|
||||
// magenta.href = 'https://magenta.tensorflow.org/'
|
||||
magenta.target = '_blank'
|
||||
magenta.id = 'magentaLink'
|
||||
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>'
|
||||
badges.appendChild(magenta)
|
||||
|
||||
const privacyAndTerms = document.createElement('div')
|
||||
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>'
|
||||
splash.appendChild(privacyAndTerms)
|
||||
// const privacyAndTerms = document.createElement('div')
|
||||
// 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">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._active = false
|
||||
this._active = true;
|
||||
|
||||
/**
|
||||
* The audio key keyboard
|
||||
@ -76,9 +76,9 @@ class Keyboard extends events.EventEmitter{
|
||||
//the midi input
|
||||
this._midi = new Midi()
|
||||
this._midi.on('keyDown', (note) => {
|
||||
this.keyDown(note)
|
||||
this._emitKeyDown(note)
|
||||
})
|
||||
this.keyDown(note);
|
||||
this._emitKeyDown(note);
|
||||
});
|
||||
this._midi.on('keyUp', (note) => {
|
||||
this.keyUp(note)
|
||||
this._emitKeyUp(note)
|
||||
@ -111,6 +111,9 @@ class Keyboard extends events.EventEmitter{
|
||||
if (!this._active){
|
||||
return
|
||||
}
|
||||
const splash = document.getElementById("splash");
|
||||
splash.classList.add('disappear');
|
||||
container.classList.add('focus');
|
||||
if (!this._currentKeys[note]){
|
||||
this._currentKeys[note] = 0
|
||||
}
|
||||
|
@ -48,14 +48,14 @@ class Midi extends events.EventEmitter{
|
||||
})
|
||||
inputDevice.addListener('noteon', 'all', (event) => {
|
||||
try {
|
||||
this.emit('keyDown', event.note.number)
|
||||
this.emit('keyDown', event.note.number + 12)
|
||||
} catch(e){
|
||||
console.warn(e)
|
||||
}
|
||||
})
|
||||
inputDevice.addListener('noteoff', 'all', (event) => {
|
||||
try {
|
||||
this.emit('keyUp', event.note.number)
|
||||
this.emit('keyUp', event.note.number + 12)
|
||||
} catch(e){
|
||||
console.warn(e)
|
||||
}
|
||||
|
@ -91,7 +91,7 @@
|
||||
font-family: $font-family;
|
||||
font-size: 40px;
|
||||
line-height: 30px;
|
||||
margin: 20px 0px 30px 0px;
|
||||
margin: 130px 0px 50px 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@ -143,17 +143,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
#blurb {
|
||||
|
||||
#germanAbout {
|
||||
position: relative;
|
||||
text-transform: none;
|
||||
margin-top: 50px;
|
||||
font-size: 14px;
|
||||
font-size: 18px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $orange;
|
||||
}
|
||||
#englishAbout {
|
||||
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;
|
||||
}
|
||||
|
||||
#subTitle, #howItWorks {
|
||||
#germanSubTitle, #germanHowItWorks {
|
||||
margin-top: $topMargin;
|
||||
letter-spacing: 0.8px;
|
||||
line-height: 30px;
|
||||
@ -53,25 +53,66 @@ $topMargin: 35px;
|
||||
text-align: center;
|
||||
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;
|
||||
$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;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
margin-left: $margin;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
width: $size;
|
||||
height: $size;
|
||||
margin-left: -$margin;
|
||||
margin-top: $size / 4;
|
||||
content : '';
|
||||
background-image: url(../images/yellow_play_triangle.svg);
|
||||
}
|
||||
/*&:before {*/
|
||||
/*position: absolute;*/
|
||||
/*width: $size;*/
|
||||
/*height: $size;*/
|
||||
/*margin-left: -$margin;*/
|
||||
/*margin-top: $size / 4;*/
|
||||
/*content : '';*/
|
||||
/*background-image: url(../images/yellow_play_triangle.svg);*/
|
||||
/*}*/
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
@ -82,8 +123,8 @@ $topMargin: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
$loaderWidth: 200px;
|
||||
$loaderHeight: 60px;
|
||||
$loaderWidth: 398px;
|
||||
$loaderHeight: 118px;
|
||||
|
||||
#loader {
|
||||
position: relative;
|
||||
@ -96,25 +137,6 @@ $topMargin: 35px;
|
||||
margin-right: auto;
|
||||
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 {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@ -126,7 +148,7 @@ $topMargin: 35px;
|
||||
#fill {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 0%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: black;
|
||||
|
||||
@ -135,7 +157,6 @@ $topMargin: 35px;
|
||||
width: $loaderWidth;
|
||||
height: 100%;
|
||||
color: $orange;
|
||||
|
||||
$imgWidth: 40px;
|
||||
$margin : 52px;
|
||||
|
||||
@ -145,17 +166,26 @@ $topMargin: 35px;
|
||||
}
|
||||
|
||||
#play {
|
||||
margin-left: 150px;
|
||||
right: $margin;
|
||||
position: relative;
|
||||
/*display: inline-block;*/
|
||||
}
|
||||
#germanPlay {
|
||||
margin-left: 120px;
|
||||
right: $margin;
|
||||
position: absolute;
|
||||
padding-top: 30px;
|
||||
/*display: inline-block;*/
|
||||
}
|
||||
|
||||
#piano {
|
||||
left: $margin;
|
||||
width: $imgWidth;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image : url(../images/keyboard_icon.svg);
|
||||
background-image : url(../images/keyboard_icon_CMedit.svg);
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +195,24 @@ $topMargin: 35px;
|
||||
text-align: center;
|
||||
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 {
|
||||
background-image: url(../images/badgeAI_master.svg);
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
&:active {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
/*&:hover {*/
|
||||
/*opacity: 1;*/
|
||||
/*&:active {*/
|
||||
/*opacity: 0.3;*/
|
||||
/*}*/
|
||||
/*}*/
|
||||
}
|
||||
|
||||
#googleFriends {
|
||||
@ -245,9 +293,9 @@ $topMargin: 35px;
|
||||
margin-right: 0px;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
/*&:hover {*/
|
||||
/*opacity: 1;*/
|
||||
/*}*/
|
||||
|
||||
@media (max-width: $smallScreen) {
|
||||
font-size: 7px;
|
||||
@ -290,10 +338,10 @@ $topMargin: 35px;
|
||||
span {
|
||||
color: $orange;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
&:hover:active {
|
||||
color: white;
|
||||
}
|
||||
/*cursor: pointer;*/
|
||||
/*&:hover:active {*/
|
||||
/*color: white;*/
|
||||
/*}*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -352,15 +400,15 @@ $topMargin: 35px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
/*cursor: pointer;*/
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
/*&:hover {*/
|
||||
/*opacity: 1;*/
|
||||
|
||||
&:active {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
/*&:active {*/
|
||||
/*opacity: 0.3;*/
|
||||
/*}*/
|
||||
/*}*/
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user