diff --git a/app.js b/app.js
index 5d831b8..45485ea 100644
--- a/app.js
+++ b/app.js
@@ -56,7 +56,7 @@ var PERIOD = Math.PI * 2;
};
});
- app.directive('dx7ToggleButton', function() {
+ app.directive('toggleButton', function() {
return {
restrict: 'E',
replace: true,
@@ -70,13 +70,11 @@ var PERIOD = Math.PI * 2;
app.directive('knob', function() {
function link(scope, element, attrs) {
var rotationRange = 300; // ±degrees
- var pixelRange = 300; // pixels between max and min
+ var pixelRange = 200; // pixels between max and min
var startY, startModel, down = false;
var fgEl = element.find('div');
var max = element.attr('max');
var min = element.attr('min');
- console.log(fgEl);
-
element.on('mousedown', function(e) {
startY = e.clientY;
startModel = scope.ngModel || 0;
@@ -117,6 +115,55 @@ var PERIOD = Math.PI * 2;
};
});
+ app.directive('slider', function() {
+ function link(scope, element, attrs) {
+ var sliderHandleHeight = 8;
+ var sliderRailHeight = 50;
+ var positionRange = sliderRailHeight - sliderHandleHeight;
+ var pixelRange = 50;
+ var startY, startModel, down = false;
+ var fgEl = element.find('div');
+ var max = element.attr('max');
+ var min = element.attr('min');
+ element.on('mousedown', function(e) {
+ startY = e.clientY;
+ startModel = scope.ngModel || 0;
+ down = true;
+ e.preventDefault();
+ e.stopPropagation();
+ window.addEventListener('mousemove', onMove);
+ window.addEventListener('mouseup', onUp);
+ });
+
+ function onMove(e) {
+ if (down) {
+ var dy = (startY - e.clientY) * (max - min) / pixelRange;
+ scope.ngModel = Math.round(Math.max(min, Math.min(max, dy + startModel)));
+ scope.$apply();
+ }
+ }
+
+ function onUp(e) {
+ down = false;
+ window.removeEventListener('mousemove', onMove);
+ window.removeEventListener('mouseup', onUp);
+ }
+
+ scope.getTop = function() {
+ return positionRange - ((this.ngModel - min) / (max - min) * positionRange);
+ }
+ }
+
+ return {
+ restrict: 'E',
+ replace: true,
+ require: 'ngModel',
+ scope: {'ngModel': '='},
+ template: '
',
+ link: link
+ };
+ });
+
app.controller('MidiCtrl', function($scope) {
var mml = null;
var mmlDemos = [ "t92 l8 o4 $" +
@@ -202,8 +249,12 @@ var PERIOD = Math.PI * 2;
}
};
+ this.onModeClick = function() {
+ console.log("hey ho");
+ };
+
this.onAnalysisChange = function() {
- if (this.showAnalysis) {
+ if (this.analysisMode) {
frequencybox.enable();
wavebox.enable();
} else {
@@ -243,12 +294,19 @@ var PERIOD = Math.PI * 2;
});
app.controller('OperatorCtrl', function($scope) {
- $scope.$watchGroup(['operator.freqCoarse', 'operator.freqFine', 'operator.detune'], function() {
+ $scope.$watchGroup(['operator.oscMode', 'operator.freqCoarse', 'operator.freqFine', 'operator.detune'], function() {
FMVoice.updateFrequency($scope.i);
});
+ $scope.$watch('operator.volume', function() {
+ FMVoice.setOutputLevel($scope.i, $scope.operator.volume);
+ console.log("outputLevel changed", $scope.operator.outputLevel);
+ });
+ $scope.$watch('operator.pan', function() {
+ FMVoice.setPan($scope.i, $scope.operator.pan);
+ });
});
- app.controller('PresetCtrl', ['$localStorage', '$http', function ($localStorage, $http) {
+ app.controller('PresetCtrl', ['$scope', '$localStorage', '$http', function ($scope, $localStorage, $http) {
this.lfoWaveformOptions = [ 'Triangle', 'Saw Down', 'Saw Up', 'Square', 'Sine', 'Sample & Hold' ];
var self = this;
$http.get('roms/ROM1A.SYX')
@@ -274,15 +332,16 @@ var PERIOD = Math.PI * 2;
this.onChange = function() {
console.log("changed preset!", this.selectedIndex);
PARAMS = this.presets[this.selectedIndex];
+ this.params = PARAMS;
// TODO: separate UI parameters from internal synth parameters
// TODO: better initialization of computed parameters
for (var i = 0; i < PARAMS.operators.length; i++) {
var op = PARAMS.operators[i];
- this.onVolumeChange(i, op);
+ FMVoice.setOutputLevel(i, op.volume);
FMVoice.updateFrequency(i);
- this.onPanChange(i, op.pan);
+ FMVoice.setPan(i, op.pan);
}
- this.onFeedbackChange();
+ FMVoice.setFeedback(PARAMS.feedback);
};
this.save = function() {
@@ -299,22 +358,15 @@ var PERIOD = Math.PI * 2;
}
};
- this.onVolumeChange = function(operatorIndex, operator) {
- FMVoice.setOutputLevel(operatorIndex, operator.volume);
- console.log("outputLevel changed", operator.outputLevel);
- };
-
- this.onFeedbackChange = function() {
+ $scope.$watch('feedback', function() {
FMVoice.setFeedback(PARAMS.feedback);
console.log("fbRatio changed", PARAMS.fbRatio);
- };
-
- this.onLFOChange = function() {};
+ });
- this.onPanChange = function(operatorIndex, value) {
- FMVoice.setPan(operatorIndex, value);
- console.log("pan changed", this.getOp(operatorIndex).outputLevelL, this.getOp(operatorIndex).outputLevelR);
- };
+ $scope.$watchGroup(['lfoSpeed', 'lfoDelay', 'lfoAmpModDepth', 'lfoPitchModDepth', 'lfoWaveform'], function() {
+ // TODO: update LFO stuff
+ // FMVoice.updateLFO();
+ });
this.getOp = function(operatorIndex) {
return this.presets[this.selectedIndex].operators[operatorIndex];
diff --git a/fonts/5x8_lcd_hd44780u_a02.eot b/fonts/5x8_lcd_hd44780u_a02.eot
new file mode 100755
index 0000000..03621c6
Binary files /dev/null and b/fonts/5x8_lcd_hd44780u_a02.eot differ
diff --git a/fonts/5x8_lcd_hd44780u_a02.svg b/fonts/5x8_lcd_hd44780u_a02.svg
new file mode 100755
index 0000000..711f627
--- /dev/null
+++ b/fonts/5x8_lcd_hd44780u_a02.svg
@@ -0,0 +1,602 @@
+
+
+
diff --git a/fonts/5x8_lcd_hd44780u_a02.ttf b/fonts/5x8_lcd_hd44780u_a02.ttf
new file mode 100755
index 0000000..23319aa
Binary files /dev/null and b/fonts/5x8_lcd_hd44780u_a02.ttf differ
diff --git a/fonts/5x8_lcd_hd44780u_a02.woff b/fonts/5x8_lcd_hd44780u_a02.woff
new file mode 100755
index 0000000..257f675
Binary files /dev/null and b/fonts/5x8_lcd_hd44780u_a02.woff differ
diff --git a/fonts/digital-7__mono_italic_.eot b/fonts/digital-7__mono_italic_.eot
new file mode 100755
index 0000000..a158e1f
Binary files /dev/null and b/fonts/digital-7__mono_italic_.eot differ
diff --git a/fonts/digital-7__mono_italic_.svg b/fonts/digital-7__mono_italic_.svg
new file mode 100755
index 0000000..a9f0a24
--- /dev/null
+++ b/fonts/digital-7__mono_italic_.svg
@@ -0,0 +1,270 @@
+
+
+
diff --git a/fonts/digital-7__mono_italic_.ttf b/fonts/digital-7__mono_italic_.ttf
new file mode 100755
index 0000000..ddbd3f7
Binary files /dev/null and b/fonts/digital-7__mono_italic_.ttf differ
diff --git a/fonts/digital-7__mono_italic_.woff b/fonts/digital-7__mono_italic_.woff
new file mode 100755
index 0000000..2dcb1f2
Binary files /dev/null and b/fonts/digital-7__mono_italic_.woff differ
diff --git a/images/13460FD783A2087D.png b/images/13460FD783A2087D.png
new file mode 100644
index 0000000..f998279
Binary files /dev/null and b/images/13460FD783A2087D.png differ
diff --git a/images/dx7-logo.png b/images/dx7-logo.png
new file mode 100644
index 0000000..8ad1625
Binary files /dev/null and b/images/dx7-logo.png differ
diff --git a/images/dx7-slider-foreground.svg b/images/dx7-slider-foreground.svg
new file mode 100644
index 0000000..2e01109
--- /dev/null
+++ b/images/dx7-slider-foreground.svg
@@ -0,0 +1,39 @@
+
+
+
diff --git a/images/dx7-slider-rail-dashed.svg b/images/dx7-slider-rail-dashed.svg
new file mode 100644
index 0000000..a3c5471
--- /dev/null
+++ b/images/dx7-slider-rail-dashed.svg
@@ -0,0 +1,47 @@
+
+
+
diff --git a/images/dx7-slider-rail.svg b/images/dx7-slider-rail.svg
new file mode 100644
index 0000000..9522380
--- /dev/null
+++ b/images/dx7-slider-rail.svg
@@ -0,0 +1,10 @@
+
+
+
diff --git a/images/dx7-toggle-off-active.svg b/images/dx7-toggle-off-active.svg
new file mode 100644
index 0000000..a3d8f50
--- /dev/null
+++ b/images/dx7-toggle-off-active.svg
@@ -0,0 +1,40 @@
+
+
+
diff --git a/images/dx7-toggle-off.svg b/images/dx7-toggle-off.svg
new file mode 100644
index 0000000..6801cfb
--- /dev/null
+++ b/images/dx7-toggle-off.svg
@@ -0,0 +1,41 @@
+
+
+
diff --git a/images/dx7-toggle-on-active.svg b/images/dx7-toggle-on-active.svg
new file mode 100644
index 0000000..fa8b0d0
--- /dev/null
+++ b/images/dx7-toggle-on-active.svg
@@ -0,0 +1,43 @@
+
+
+
diff --git a/images/dx7-toggle-on.svg b/images/dx7-toggle-on.svg
new file mode 100644
index 0000000..afb7a4c
--- /dev/null
+++ b/images/dx7-toggle-on.svg
@@ -0,0 +1,44 @@
+
+
+
diff --git a/images/frequency-display.svg b/images/frequency-display.svg
new file mode 100644
index 0000000..2f3dccd
--- /dev/null
+++ b/images/frequency-display.svg
@@ -0,0 +1,42 @@
+
+
+
diff --git a/images/knob-background.svg b/images/knob-background.svg
new file mode 100644
index 0000000..ad8bf3e
--- /dev/null
+++ b/images/knob-background.svg
@@ -0,0 +1,47 @@
+
+
+
diff --git a/images/knob-foreground.svg b/images/knob-foreground.svg
new file mode 100644
index 0000000..a9a8366
--- /dev/null
+++ b/images/knob-foreground.svg
@@ -0,0 +1,39 @@
+
+
+
diff --git a/images/lcd-background.svg b/images/lcd-background.svg
new file mode 100644
index 0000000..6943f43
--- /dev/null
+++ b/images/lcd-background.svg
@@ -0,0 +1,74 @@
+
+
+
diff --git a/index.html b/index.html
index c47b7bc..9e0e99b 100644
--- a/index.html
+++ b/index.html
@@ -7,134 +7,179 @@
MIDI Device:
-
-
+
-
-
-
- Magnitude Spectrum
-
- Waveform
-
-
-
-
- Preset:
-
-
-
-
Algorithm
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].algorithm | number }}
-
-
-
-
Feedback
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].feedback | number : 0 }}
-
-
-
LFO
-
-
Waveform
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].lfoWaveform | number : 0 }}
-
-
-
-
Speed
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].lfoSpeed | number : 0 }}
-
-
-
-
Delay
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].lfoDelay | number : 0 }}
-
-
-
-
Amp. Modulation
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].lfoAmpModDepth | number : 0 }}
-
-
-
-
Pitch Modulation
-
{{ presetCtrl.presets[presetCtrl.selectedIndex].lfoPitchModDepth | number : 0 }}
-
-
-
-
Sync
-
+
+
-
-
OP {{ $index + 1 }}
-
-
-
-
L{{ i + 1 }}
-
{{ operator.levels[i] | number : 0 }}
-
+
+
Operator
+
Frequency
+
EG Rate
+
EG Level
+
Keyboard Scaling Not implemented
+
+
+
+
+
+
+
Level
+
{{ operator.volume | number : 0 }}
+
+
+
+
Pan
+
{{ operator.pan | number : 0 }}
+
+
+
+
Vel Sens
+
{{ operator.velocitySens | number : 0 }}
+
+
+
+
LFO Sens
+
{{ operator.lfoAmpModSens | number : 0 }}
+
+
-
-
R{{ i + 1 }}
-
{{ operator.rates[i] | number : 0 }}
-
+
+
+
+
Coarse
+
{{ operator.freqCoarse | number : 0 }}
+
+
+
+
+ {{ operator.freqRatio | number: 2 }}
+
+
+
+ {{ (operator.freqFixed | number).substr(0,5).replace(',','').replace('/\.$/','') }}
+
+
+
Fine
+
{{ operator.freqFine | number : 0 }}
+
+
+
+
Detune
+
{{ operator.detune | number : 0 }}
+
+
+
+
+
+
-
-
Freq. Coarse
-
{{ operator.freqCoarse | number : 0 }}
-
-
-
-
Freq. Fine
-
{{ operator.freqFine | number : 0 }}
-
-
-
-
Detune
-
{{ operator.detune | number : 0 }}
-
-
-
-
Volume
-
{{ operator.volume | number : 0 }}
-
-
-
-
Velocity Sens.
-
{{ operator.velocitySens | number : 0 }}
-
-
-
-
LFO Sens.
-
{{ operator.lfoAmpModSens | number : 0 }}
-
-
-
-
Pan
-
{{ operator.pan | number : 0 }}
-
-
-
-
Osc. Mode
-
-
+
-

diff --git a/synth.css b/synth.css
index f031ddd..863ea0c 100644
--- a/synth.css
+++ b/synth.css
@@ -5,28 +5,64 @@
font-style:normal;
}
+@font-face {
+ font-family: "Digital-7 MonoItalic";
+ src: url("fonts/digital-7__mono_italic_.eot?") format("eot"), url("fonts/digital-7__mono_italic_.woff") format("woff"), url("fonts/digital-7__mono_italic_.ttf") format("truetype"), url("fonts/digital-7__mono_italic_.svg#Digital-7MonoItalic") format("svg");
+ font-weight: normal;
+ font-style: normal;
+}
+
body {
font-family: Helvetica, Arial, sans-serif;
margin: 20px;
- background-color: #313036;
+ background-color: #4a4950;
color: #ffffff;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.display {
+ position: absolute;
+ width: 406px;
+ height: 56px;
+ top: 39px;
+ left: 254px;
+ background-color: #cfe14a;
+ box-shadow: inset 0 3px 0 3px #c0cf35;
+ background-image: url(images/lcd-background.svg);
+ background-repeat: no-repeat;
+ background-position: 0 0;
+}
+
+.analysis {
+ position: absolute;
+ background-color: #c0cf35;
+ bottom: 8px;
+ right: 9px;
+ top: 13px;
+ width: 168px;
}
-.canvas-title { position: absolute; font-weight: 500; font-size: 12px; padding: 3px; }
-canvas { display: block; }
select.lcd {
font-family: '5x8 LCD HD44780U A02 Regular', serif;
font-size: 16.27px;
- border: 1px solid #cfe14a;
- padding: 1px 4px 3px;
+ line-height: 16px;
+ border: 0;
+ padding: 0;
border-radius: 0;
- background-color: #cfe14a;
+ background-color: transparent;
appearance: none;
-webkit-appearance: none;
- box-shadow: inset 0 2px 0 1px rgba(0,0,0,0.1); //, inset 0 0 6px rgba(192,255,128,1);
outline: 0;
width: 160px;
color: rgba(0,0,0,0.8);
+ position: absolute;
+ bottom: 8px;
+ left: 5px;
}
input[type='range'] {
@@ -53,27 +89,18 @@ input[type='range']::-webkit-slider-thumb {
}
.algo-diagram {
- width: 800px;
+ width: 800px;
}
.param-group {
- float: left;
- background: #232529;
- xmargin-right: 10px;
- padding: 10px;
-}
-
-.param-label {
- display: inline-block;
- width: 80px;
- font-size: 75%;
- margin-right: 10px;
- text-transform: uppercase;
- vertical-align: middle;
+ margin-right: 10px;
+ display: inline-block;
+ vertical-align: top;
+ font-size: 0;
}
.param-value {
- display: inline-block;
+ display: none;
width: 40px;
font-size: 75%;
vertical-align: middle;
@@ -139,9 +166,10 @@ input[type='range']::-webkit-slider-thumb {
}
.dx7-toggle {
+ display: block;
width: 34px;
height: 28px;
- vertical-align: middle;
+ margin: 0 auto;
background-color: #4a4950;
background-image: url('images/dx7-toggle-off.svg');
border: 2px solid #232529;
@@ -164,12 +192,117 @@ input[type='range']::-webkit-slider-thumb {
background-image: url('images/dx7-toggle-on-active.svg');
}
+.dx7-container {
+ background-color: #313036;
+ border: 1px solid #232326;
+ position: relative;
+ width: 1060px;
+}
+
+.dx7-top-panel {
+ display: block;
+ height: 120px;
+ background-color: #232326;
+}
+
+.dx7-headings {
+ font-size: 10px;
+ font-weight: bold;
+ position: relative;
+ display: block;
+ height: 12px;
+ margin: 16px 0 8px;
+ text-transform: uppercase;
+}
+
+/* TODO: position absolute headings inside first row groups */
+.heading-operator { position: absolute; left: 30px; }
+.heading-frequency { position: absolute; left: 260px; }
+.heading-egrate { position: absolute; left: 500px; }
+.heading-eglevel { position: absolute; left: 626px; }
+.heading-keyboardscaling { position: absolute; left: 761px; }
+
+.dx7-logo {
+ position: absolute;
+ top: 40px;
+ left: 30px;
+ width: 153px;
+ height: 47px;
+ background-image: url(images/dx7-logo.png);
+ background-size: cover;
+}
+
+.dx7-operators {
+ display: block;
+ top: 160px;
+}
+
+.dx7-buttons-row-1 {
+
+}
+
+.dx7-buttons-row-2 {
+
+}
+
+.operator {
+ display: block; /* fix for spaces between inline children */
+ padding: 15px 24px;
+ height: 50px;
+ font-size: 0;
+}
+
+.operator:nth-child(odd) {
+ background-color: #36353b;
+}
+
+.param {
+ position: relative;
+ display: inline-block;
+ width: 44px;
+ height: 50px;
+ vertical-align: top;
+}
+
+.param.narrow {
+ width: 29px;
+}
+
+.frequency-display {
+ display: inline-block;
+ width: 60px;
+ height: 43px;
+ box-sizing: border-box;
+ text-align: center;
+ padding: 4px;
+ background-image: url(images/frequency-display.svg);
+ background-position: 0 0;
+ background-repeat: no-repeat;
+ font-family: 'Digital-7 MonoItalic';
+ letter-spacing: 1px;
+ font-size: 20px;
+ color: #fa0606;
+}
+
+.param-label {
+ position: absolute;
+ top: 39px;
+ text-align: center;
+ display: inline-block;
+ width: 100%;
+ font-size: 8px;
+ font-weight: bold;
+ line-height: 9px;
+ text-transform: uppercase;
+ padding: 0 3px;
+ box-sizing: border-box;
+}
+
.knob {
+ margin: 0 auto;
height: 30px;
width: 30px;
background-image: url('images/knob-background.svg');
- display: inline-block;
- vertical-align: middle;
}
.knob-foreground {
@@ -177,4 +310,26 @@ input[type='range']::-webkit-slider-thumb {
width: 30px;
position: absolute;
background-image: url('images/knob-foreground.svg');
+}
+
+.slider {
+ height: 50px;
+ width: 24px;
+ background-image: url('images/dx7-slider-rail-dashed.svg');
+ background-size: 24px;
+ background-position: 0 0;
+ overflow: hidden;
+}
+
+.param:last-child .slider {
+ width: 18px;
+}
+
+.slider-foreground {
+ height: 13px;
+ width: 18px;
+ background-image: url('images/dx7-slider-foreground.svg');
+ background-position: 0 0;
+ position: relative;
+ top: 0px;
}
\ No newline at end of file