Skip to content

Commit

Permalink
Added scale editor
Browse files Browse the repository at this point in the history
  • Loading branch information
Olivier Gillet committed Aug 11, 2014
1 parent fdda53f commit 0c169d1
Show file tree
Hide file tree
Showing 18 changed files with 397 additions and 2 deletions.
2 changes: 1 addition & 1 deletion avrlib
Submodule avrlib updated 2 files
+0 −3 adc.h
+0 −4 makefile.mk
1 change: 1 addition & 0 deletions yarns/scale_editor/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
AppEngine web app for converting a scala .scl file into a tuning .syx recognized by Yarns.
Empty file added yarns/scale_editor/__init__.py
Empty file.
19 changes: 19 additions & 0 deletions yarns/scale_editor/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
application: scale-editor
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /static
static_dir: static

- url: /favicon.ico
static_files: static/favicon.ico
upload: static/favicon.ico

- url: .*
script: main.application

- url: /syx
script: main.application
11 changes: 11 additions & 0 deletions yarns/scale_editor/index.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
indexes:

# AUTOGENERATED

# This index.yaml is automatically updated whenever the dev_appserver
# detects that a new type of query is run. If you want to manage the
# index.yaml file manually, remove the above marker line (the line
# saying "# AUTOGENERATED"). If you want to manage some indexes
# manually, move them above the marker line. The index.yaml file is
# automatically uploaded to the admin console when you next deploy
# your application using appcfg.py.
89 changes: 89 additions & 0 deletions yarns/scale_editor/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os

import webapp2

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp import template

from music.scala import scala

TEMPLATE_PATH = os.path.join(os.path.dirname(__file__), 'templates')



class SyxHandler(webapp.RequestHandler):
def get(self):
header = '\xf0\x7f\x7f\x08\x09\x03\x7f\x7f'
data = self.request.get('data')
body = ''.join([chr(int(data[x:x+2], 16)) for x in range(0, len(data), 2)])
footer = '\xf7'
self.response.headers['Content-Type'] = 'application/octet-stream'
self.response.headers['Content-disposition'] = \
'attachment; filename=scale.syx'
self.response.out.write(header + body + footer)



class MainHandler(webapp.RequestHandler):
def post(self):
return self.get()

def get(self):
if self.request.get('scl'):
source = self.request.get('scl').decode('iso-8859-1').encode('utf8')
hide_if_error = True
else:
source = self.request.get('source')
hide_if_error = False

semitones = []
try:
error_message = ''
notes = scala.parse(source)
mapping = scala.assign_to_octave(notes)
names = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B']
mappings = []
for i, (shift, note) in enumerate(zip(mapping, names)):
mappings.append(
'%s = %.2f cents' % (note, shift + i * 100.0))
mapping_14bit = [int(round(8192 + 8192 * x / 100.0)) for x in mapping]
mapping_hex = ['%02x%02x' % (m >> 7, m & 0x7f) for m in mapping_14bit]
link = ''.join(mapping_hex)

except scala.ParseError as p:
error_message = p.message
notes = []
mappings = []
link = ''

if not source:
error_message = ''

self.response.headers['Content-Type'] = 'text/html'
template_path = os.path.join(TEMPLATE_PATH, 'index.html')
template_data = {}
template_data['error_message'] = error_message
template_data['notes'] = notes
template_data['mappings'] = mappings
template_data['link'] = link
template_data['source'] = '' if hide_if_error and error_message else source
self.response.out.write(template.render(template_path, template_data))

application = webapp.WSGIApplication([('/', MainHandler), ('/syx', SyxHandler)])
Empty file.
Empty file.
114 changes: 114 additions & 0 deletions yarns/scale_editor/music/scala/scala.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/usr/bin/python2.5
#
# Copyright 2009 Olivier Gillet.
#
# Author: Olivier Gillet ([email protected])
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# -----------------------------------------------------------------------------
#
# Scala reader.

"""Scala reader."""

import math
import re



class ParseError(Exception):

def __init__(self, message):
self.message = message

def __str__(self):
return self.message



def _parse_note(x):
x = re.split('[ !]+', x.strip())[0]
x = x.replace(',', '') # Some files use , as a decimal separator in fractions
if '.' in x:
return float(x)
elif '/' in x:
tokens = x.split('/')
return 1200.0 * math.log(float(tokens[0]) / float(tokens[1])) / math.log(2)
else:
return 1200.0 * math.log(float(x)) / math.log(2)



def parse(lines):
lines = lines.split('\n')
lines = [line.strip('\r ') for line in lines]
lines = [line for line in lines if not line.startswith('!') and line]
if not lines:
raise ParseError('Empty file')

comment = lines[0]
lines = lines[1:]

if not lines:
raise ParseError('Invalid number of notes')

try:
num_notes = int(lines[0])
except Exception as e:
raise ParseError('Incorrect number of notes: %s' % lines[0])

lines = lines[1:]

if len(lines) != num_notes:
raise ParseError('Declared number of notes (%d) does not match number of lines (%d)' % (num_notes, len(lines)))

items = []
for line in lines:
try:
items.append(_parse_note(line))
except Exception as e:
raise ParseError('Cannot parse: %s' % line)

return items



def assign_to_octave(notes):
adjustments = [0.0] * 12
allocated = [False] * 12
for n in [0.0] + notes:
index = int(round(n / 100.0))
shift = None
for candidate_shift in [0, -1, 1, -2, 2, -3, 3, -4, 4]:
position = index + candidate_shift
adjustment = n - position * 100.0
if all([
position >= 0,
position < 12,
adjustment <= 16383/16384.0 * 100.0,
adjustment >= -100.0,
not allocated[position % 12]]):
shift = candidate_shift
break

if shift != None:
position = index + shift
adjustments[position] = n - position * 100.0
allocated[position] = True

return adjustments



if __name__ == '__main__':
pass
Binary file added yarns/scale_editor/static/bg_main.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added yarns/scale_editor/static/db_yarns.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added yarns/scale_editor/static/divider_big.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added yarns/scale_editor/static/favicon.ico
Binary file not shown.
Binary file added yarns/scale_editor/static/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
76 changes: 76 additions & 0 deletions yarns/scale_editor/static/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td { background: transparent; border: 0; margin: 0; padding: 0; vertical-align: baseline; }

body {
font-family: Helvetica, Arial, sans-serif;
font-size: 0.9em;
background: url("bg_main.jpg") top left;
margin: 0;
}

h1, h2, h3, h4, h5, h6 { clear: both; font-weight: normal; }
h1 { margin: 10px 90px 20px 90px; padding-left: 50px; font-size: 36px; font-weight: 100; background: url("db_yarns.png") left 0 no-repeat; }
h2 { margin: 20px 90px 20px 90px; font-size: 24px; color: #b31748; font-weight: 100; }
h3 { margin: 20px 90px 10px 90px; font-size: 18px; font-weight: 100; }
#content p, #content ul, #content ol, #content pre { margin: 0 90px 10px 90px; line-height: 150%; }

a { color: #1a7877; font-weight: bold; }
a:hover { color: #b31748; }


#wrapper {
width: 100%;
margin-left: 20px;
padding: 0;
width: 960px;
}

#content {
width: 720px;
box-shadow: 0 0 20px #706c64;
background-color: #fff;
float: right;
margin: 0;
min-height:100%;
}

#sidebar { width: 240px; padding: 0; float: left; margin: 0; }
#sidebar #logo { background: url("logo.jpg") no-repeat center; display:block; width:100%; height:177px; overflow:hidden; text-indent:-420em; padding:11px 0 30px; }
#credits { margin: 10px 10px; color: #998f85; font-style: oblique; }
.divider_big{ width: 100%; height: 60px; background: url("divider_big.jpg") repeat-x center; }

ul.mappings li {
padding: 8px;
border: 1px;
background-color: #b31748;
border-color: #b31748;
border-style: solid;
color: #fff;
line-height: 2;
}

ul.notes li {
padding: 4px;
border: 1px;
background-color: #1a7877;
border-color: #1a7877;
border-style: solid;
color: #fff;
display: inline;
line-height: 2.2;
}

p.error {
padding: 8px;
background-color: #b31748;
color: #fff;
font-weight: bold;
}

23 changes: 23 additions & 0 deletions yarns/scale_editor/templates/base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<html>
<head>
<title>{{ title }}</title>
<link type="text/css" rel="stylesheet" href="/static/main.css" />
</head>
<body {{ extra_body_tags }} >
<div id='wrapper'>
<div id='content'>
{% block content %}
{% endblock %}

<div class="divider_big"></div>
<div id="credits">
<p>Mutable Instruments, 2014. <a href="https://github.com/pichenettes/eurorack/tree/master/yarns">Source code</a>.</p>
</div>

</div>
<div id='sidebar'>
<a id="logo" href="http://mutable-instruments.net">Mutable Instruments</a>
</div>
</div>
</body>
</html>
Loading

0 comments on commit 0c169d1

Please sign in to comment.