-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First attempt at physical modeling. Not usable yet.
- Loading branch information
Showing
1 changed file
with
317 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,317 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Physical modelling test / Cyber Music Studio™</title> | ||
<meta name="viewport" content="width=device-width, initial-scale=1"> | ||
<link rel="stylesheet" href="css/uboot.css"> | ||
<link rel="stylesheet" href="css/gh-fork-ribbon.css"> | ||
<script src="js/bluescreen.js"></script> | ||
<script src="js/uboot.js"></script> | ||
<script src="js/JZZ.js"></script> | ||
<script src="js/JZZ.input.Kbd.js"></script> | ||
<script src="js/JZZ.gui.Select.js"></script> | ||
<script src="js/wavy-jones.js"></script> | ||
<script src="js/wave-encoder-polyfill.js"></script> | ||
<script src="js/heartbeat.js"></script> | ||
<style> | ||
body { | ||
font-family: "Lucida Console", monospace; | ||
background: url("img/1280px-Tsunami_by_hokusai_19th_century.jpg") plum no-repeat fixed top center/cover; | ||
} | ||
|
||
footer { | ||
margin-top: 1em; | ||
background-color: whitesmoke; | ||
} | ||
|
||
.ub-box { | ||
background-color: #FFFFCC; | ||
} | ||
|
||
#box_welcome { width: 30em; margin: auto; } | ||
|
||
a { color: black; text-decoration: underline dashed; text-decoration-thickness: 1px; } | ||
|
||
#power { | ||
font-weight: bold; | ||
font-size: 200%; | ||
} | ||
|
||
.ub-container { | ||
max-width: 60em !important; | ||
} | ||
|
||
.is-blinking { | ||
animation: blinker 1s linear infinite; | ||
} | ||
|
||
@keyframes blinker { | ||
50% { | ||
opacity: 0; | ||
} | ||
} | ||
|
||
.big-buttons button, .big-buttons label { | ||
width: 98%; | ||
font-weight: bold; | ||
font-size: x-large; | ||
} | ||
|
||
#tape_menu { | ||
width: 400px; | ||
height: 250px; | ||
|
||
} | ||
|
||
.ub-form button.red { | ||
background-color: red; | ||
} | ||
|
||
.presets a { | ||
display: inline-block; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
|
||
<a class="github-fork-ribbon" href="https://github.com/severak/cyber-music-studio" data-ribbon="Fork me on GitHub" title="Fork me on GitHub">Fork me on GitHub</a> | ||
|
||
<div class="ub-container"> | ||
<br><br> | ||
|
||
<div class="ub-box" id="box_welcome"> | ||
<img src="img/preview.png" class="ub-fit-img"> | ||
<p>This is part of beta version of Cyber Music Studio™. It's not finished yet and can be broken at any moment.</p> | ||
<p>Feel free to test it and report bugs <a href="https://github.com/severak/cyber-music-studio/issues" target="_blank">here</a>.</p> | ||
<center><button id="power" class="ub-button">OK</button></center> | ||
</div> | ||
|
||
<form class="ub-form ub-hidden" id="form"> | ||
<div class="ub-box"> | ||
<div class="ub-cols"> | ||
<div class="big-buttons"> | ||
<button id="panic_button">PANIC!</button> | ||
</div> | ||
<div> | ||
freq:<br><input type="range" min="80" max="10000" step="1" id="freq" value="10000"> | ||
</div> | ||
<div> | ||
feedback len:<br><input type="range" min="0.001" max="0.5" step="0.001" id="flen" value="0.001"> | ||
</div> | ||
<div> | ||
feedback depth:<br><input type="range" min="0.9" max="0.999" step="0.001" id="fdepth"> | ||
</div> | ||
<div> | ||
freq2:<br><input type="range" min="80" max="20000" step="1" id="freq2" value="20000"> | ||
</div> | ||
</div> | ||
|
||
|
||
|
||
|
||
|
||
|
||
<div> | ||
Use computer keyboard or following widget:<br/> | ||
<div id="keyboard3"></div> | ||
or MIDI input: <select id="select_midi_in3"></select> | ||
</div> | ||
</div> | ||
|
||
<footer>Part of <a href="index.html">Cyber Music Studio™</a> by <a href="http://tilde.town/~severak/">Severák</a>. Supports <a href="https://www.g200kg.com/en/docs/webmidilink/index.html" target="_blank">WebMidiLink</a>.</footer> | ||
|
||
</form> | ||
</div> | ||
|
||
<script> | ||
function keyboardMidiWrap(keyboardElemId, midiSelectorId, synth) { | ||
// set up routing from external MIDI via HTML keyboard to synth itself | ||
var kbd = JZZ.input.Kbd({ at:keyboardElemId, pos:'N', wl: 50, ww:21, bl: 25, bw:12, from:'F4', to:'C7' }); | ||
var midiIn = JZZ.gui.SelectMidiIn({ at: midiSelectorId }); | ||
kbd.connect(function (msg) { | ||
hb.midi2call(msg, synth) | ||
}); | ||
|
||
kbd.getKey('F4').setInnerHTML('Z'); | ||
kbd.getKey('C5').setInnerHTML('B'); | ||
kbd.getKey('F5').setInnerHTML('Q'); | ||
kbd.getKey('C6').setInnerHTML('T'); | ||
kbd.getKey('F6').setInnerHTML('I'); | ||
kbd.getKey('C7').setInnerHTML(']'); | ||
|
||
var ascii = JZZ.input.ASCII({ | ||
// lower row: | ||
Z: 'F4', | ||
S: 'F#4', | ||
X: 'G4', | ||
D: 'G#4', | ||
C: 'A4', | ||
F: 'A#4', | ||
V: 'B4', | ||
B: 'C5', | ||
H: 'C#5', | ||
N: 'D5', | ||
J: 'D#5', | ||
M: 'E5', | ||
// upper row: | ||
Q: 'F5', | ||
2: 'F#5', | ||
W: 'G5', | ||
3: 'G#5', | ||
E: 'A5', | ||
4: 'A#5', | ||
R: 'B5', | ||
T: 'C6', | ||
6: 'C#6', | ||
Y: 'D6', | ||
7: 'D#6', | ||
U: 'E6', | ||
I: 'F6', | ||
9: 'F#6', | ||
O: 'G6', | ||
0: 'G#6', | ||
P: 'A6', | ||
'%': 'A#6', | ||
'[': 'B6', | ||
']': 'C7' | ||
}); | ||
|
||
ascii.connect(kbd); | ||
|
||
// handles all the selection stuff | ||
midiIn.onSelect = function(name) { | ||
JZZ().openMidiIn(name).and(function () { | ||
if (kbd.currentExternal) kbd.currentExternal.disconnect(kbd); | ||
this.connect(kbd); | ||
kbd.currentExternal = this; | ||
}).or(function () { | ||
if (kbd.currentExternal) kbd.currentExternal.disconnect(kbd); | ||
}); | ||
}; | ||
} | ||
|
||
makeNoice = function(startAt) { | ||
if (!startAt) startAt = hb.ac.currentTime; | ||
// adapted from https://noisehack.com/generate-noise-web-audio-api/ | ||
// important - noise must be longer than 2s to be perceived as noise | ||
var bufferSize = 30 * hb.ac.sampleRate, | ||
noiseBuffer = hb.ac.createBuffer(1, bufferSize, hb.ac.sampleRate), | ||
output = noiseBuffer.getChannelData(0); | ||
for (var i = 0; i < bufferSize; i++) { | ||
output[i] = Math.random() * 2 - 1; | ||
} | ||
|
||
var osc = hb.ac.createBufferSource(); | ||
osc.buffer = noiseBuffer; | ||
osc.loop = true; | ||
osc.start(startAt); | ||
|
||
return osc; | ||
}; | ||
|
||
// see https://github.com/web-audio-components/comb/tree/master | ||
|
||
Natural = function() { | ||
var me = {}; | ||
me.commonNoise = hb.makeNoiseOsc(); | ||
me._voices = {}; | ||
me.output = hb.makeGain(0.3); | ||
|
||
me.param = function(name, val) { | ||
me[name] = val; | ||
console.log(name, val); | ||
}; | ||
|
||
me._makeVoice = function(nn) { | ||
var vox = {}; | ||
var noise = makeNoice(); | ||
var env = hb.makeGain(0); | ||
vox.env= env; | ||
hb.adsrStart(env.gain, {attack: 0.001, decay: 0.05}); | ||
|
||
//console.log('flen=' + me.flen + ' fdepth='+me.fdepth + ' freq='+me.freq); | ||
|
||
//var ping = hb.makeOsc('sawtooth', hb.midi2cps(nn)); | ||
|
||
var delay = hb.ac.createDelay(1/hb.midi2cps(nn)); | ||
var feedback = hb.makeGain(me.fdepth); | ||
|
||
var prefilter = hb.makeFilter('lowpass', me.freq); | ||
|
||
//var filter2 = hb.makeFilter('lowpass', me.freq2); | ||
//hb.setNow(filter2.Q, 0.3); | ||
|
||
//hb.chain(ping, env); | ||
|
||
hb.chain(noise, prefilter, env, delay, me.output) | ||
hb.chain(delay, feedback, delay) | ||
|
||
vox.noteOff = function () {}; | ||
me._voices[nn] = vox; | ||
|
||
return vox; | ||
}; | ||
|
||
me._restartVoice = function (nn) { | ||
var env = me._voices[nn].env; | ||
hb.adsrStart(env.gain, {attack: 0.001, decay: 0.004}); | ||
}; | ||
|
||
me.noteOn = function(nn) { | ||
// console.log('on ' + nn); | ||
if (me._voices[nn]) { | ||
me._restartVoice(nn); | ||
return; | ||
} // už je | ||
me._voices[nn] = me._makeVoice(nn); | ||
}; | ||
|
||
me.noteOff = function(nn) { | ||
// console.log('off ' + nn); | ||
if (!me._voices[nn]) return; // už není | ||
me._voices[nn].noteOff(); | ||
delete me._voices[nn]; | ||
}; | ||
|
||
|
||
me.panic = function() { | ||
console.log('PANIC at the disco!'); | ||
for (var nn in me._voices) { | ||
me._voices[nn].noteOff(); | ||
} | ||
// me._vol.gain.setValueAtTime(0, me._ac.currentTime); | ||
}; | ||
|
||
|
||
|
||
return me; | ||
}; | ||
|
||
ub.on("power","click", function(ev){ | ||
ub.stop(ev); | ||
ub.addClass("box_welcome", 'ub-hidden'); | ||
ub.delClass("form", 'ub-hidden'); | ||
|
||
ub.gebi('form').reset(); | ||
hb.start(); | ||
|
||
synth = Natural(); | ||
hb.chnget(synth, 'flen', 'flen'); | ||
hb.chnget(synth, 'fdepth', 'fdepth'); | ||
hb.chnget(synth, 'freq', 'freq'); | ||
hb.chnget(synth, 'freq2', 'freq2'); | ||
var safetyValve = hb.makeGain(1); | ||
hb.chain(synth.output, safetyValve, hb.ac.destination); | ||
|
||
|
||
ub.on('panic_button', 'click', function (ev){ub.stop(ev); hb.setNow(safetyValve.gain, 0); ub.addClass('panic_button', 'red'); }); | ||
|
||
keyboardMidiWrap('keyboard3', 'select_midi_in3', synth); | ||
hb.enableWebMidiLink(synth); | ||
}); | ||
</script> | ||
|
||
<script src="//million.svita.cz/millions_v1.js"></script> | ||
</body> | ||
</html> |