Skip to content

Commit

Permalink
WebMidiLink is now supported.
Browse files Browse the repository at this point in the history
  • Loading branch information
severak2 committed Aug 8, 2024
1 parent ec37470 commit 71a125b
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 55 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
# Cyber Music Studio™

*not your usual DAW. Work in progress.*
*Not your usual DAW. Work in progress.*

This will be self-contained browser-based music making program.

It's not finished yet, but you can try [various demos of it](https://severak.github.io/cyber-music-studio/).
It's not finished yet, but you can try [individual instruments](https://severak.github.io/cyber-music-studio/). Some of these instruments now support [WebMidiLink](https://www.g200kg.com/en/docs/webmidilink/index.html).

![](img/preview.png)
![Cyber Music Studio](img/preview.png)

Based on custom library called [heartbeat.js](js/heartbeat.md) which is developed alongside this project (in this repo).

Also it uses custom javascript library and CSS framework (both called `uboot`) and following third party libraries (which are included in this repo):
Also it uses custom javascript library and CSS framework (both called `uboot`) and following third party libraries (all of which are included in this repo):

- [JZZ.js](https://github.com/jazz-soft/JZZ) for MIDI connectivity and keyboard widget
- [Audio Recorder Polyfill](https://github.com/ai/audio-recorder-polyfill) for WAV recording
- [Wavy Jones](https://github.com/stuartmemo/wavy-jones) oscilloscope
- cliparts from [OpenClipArt](https://openclipart.org/) and textures from [3D Jugle](https://3djungle.net/textures/)

Made by [Severák](http://tilde.town/~severak/) in 2021.
Created by [Severák](http://tilde.town/~severak/) since 2021.

Thanks for all the fish.
Thanks for all the fish.
3 changes: 3 additions & 0 deletions beta.html
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,9 @@ <h2>analog nomad</h2>

keyboardMidiWrap('keyboard', 'select_midi_in', traveller);

// this is easter egg for those who can read in code
hb.enableWebMidiLink(traveller);

window.nomad = hb.AnalogNomad();
hb.chnget(nomad, 'vol', 'n_vol');
hb.chnget(nomad, 'saw');
Expand Down
3 changes: 2 additions & 1 deletion fm.html
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ <h1>FM synth</h1>

</div>

<footer>part of <a href="index.html">Cyber Music Studio™</a> by <a href="http://tilde.town/~severak/">Severák</a></footer>
<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>
Expand Down Expand Up @@ -340,6 +340,7 @@ <h1>FM synth</h1>


keyboardMidiWrap('keyboard3', 'select_midi_in3', fm);
hb.enableWebMidiLink(fm);

tape = hb.TapeRecorder();

Expand Down
38 changes: 2 additions & 36 deletions gm.html
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@

</div>

<footer>part of <a href="index.html">Cyber Music Studio™</a> by <a href="http://tilde.town/~severak/">Severák</a></footer>
<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>
Expand Down Expand Up @@ -276,6 +276,7 @@

window.synth = hb.gm.synth();
keyboardMidiWrap('keyboard3', 'select_midi_in3', synth);
hb.enableWebMidiLink(synth);
hb.chnget(synth, 'attack', 'attack');
hb.chnget(synth, 'release', 'release');
hb.chnget(synth, 'eq_low', 'eq_low');
Expand Down Expand Up @@ -353,41 +354,6 @@
tape.loadLocalFile(file);
});


// TODO - put this into heartbeat.js, wrap it and link to https://www.g200kg.com/en/docs/webmidilink/spec.html
function webMidiLinkRecv(event) {
var msg = event.data.split(",");
switch (msg[0]) {
case "midi":
switch (parseInt(msg[1], 16) & 0xf0) {
case 0x80:
console.log('note off', parseInt(msg[2], 16));
synth.noteOff(parseInt(msg[2], 16));
break;
case 0x90:
var velo = parseInt(msg[3], 16);
if (velo > 0) {
console.log('note on', parseInt(msg[2], 16), velo);
synth.noteOn(parseInt(msg[2], 16), velo);
} else {
console.log('note on', parseInt(msg[2], 16), velo);
synth.noteOff(parseInt(msg[2], 16));
}
break;
case 0xb0:
if (parseInt(msg[2], 16) == 0x78) {
console.log('panik!');
synth.panic();
}
break;
}
break;
}
}

window.addEventListener("message", webMidiLinkRecv, false);


});
</script>

Expand Down
11 changes: 6 additions & 5 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@
<h1>Cyber Music Studio</h1>
<p>when finished, <strong>Cyber Music Studio</strong> will be standalone in-browser app for recording and mixing music. But it's not finished yet, so now there are only these instruments:</p>
<ul>
<li><a href="regenschori.html">Regenschori</a> - organ with tape recorder inspired by <a href="https://collection.maas.museum/object/405927">Sankei Stereo Entertainer</a></li>
<li><a href="gm.html">Heartbeat GM</a> - subset of your generic 90s General MIDI keyboard.</li>
<li><a href="kasia.html">KASIA</a> - vowel-consonant synthesizer in style of 80s Casiotones</li>
<li><a href="fm.html">FM synth</a> - basic but usable FM synth (not finished yet)</li>
<li><a href="sk1.html">Sample propulsion laboratory</a> - PCM sampler playground</li>
<li>~ <a href="regenschori.html">Regenschori</a> - organ with tape recorder inspired by <a href="https://collection.maas.museum/object/405927">Sankei Stereo Entertainer</a></li>
<li>~ <a href="gm.html">Heartbeat GM</a> - subset of your generic 90s General MIDI keyboard.</li>
<li>~ <a href="kasia.html">KASIA</a> - vowel-consonant synthesizer in style of 80s Casiotones</li>
<li>~ <a href="fm.html">FM synth</a> - basic but usable FM synth (not finished yet)</li>
<li>~ <a href="sk1.html">Sample propulsion laboratory</a> - PCM sampler playground</li>
<li><a href="lyra8.html">Lyra 8</a> - a parody/homage to <a href="https://somasynths.com/lyra-organismic-synthesizer/" target="_blank">Soma Laboratory Lyra-8</a></li>
<li><a href="beta.html">traveller and analog nomad</a> - port of <a href="http://tilde.town/~severak/studio/">my old VST plugins</a></li>
<li><a href="https://severak.github.io/akwf-playground/">AKWF playground</a> - testing tool for Adventure Kid Waveforms collection</li>
</ul>
<p>Those marked with ~ before name now supports <a href="">WebMidiLink</a> so you can use them in <a href="https://www.g200kg.com/websequencer/">WebSequencer</a> or <a href="http://sketch.txt-nifty.com/blog/chrosspad/chrosspad.html">ChrossPad</a>. Note that only some features are supported (only channel 1 and no pitch bend yet etc).</p>
<p>For your sequencing needs you can try <a href="https://severak.github.io/alda-js/">Alda web workspace</a>.</p>
<p><big>Have a fun!</big></p>
</div>
Expand Down
36 changes: 36 additions & 0 deletions js/heartbeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,42 @@ hb.midi2call = function(midi_data, synth, only_channel) {
}
};

// WebMidiLink support - https://www.g200kg.com/en/docs/webmidilink/index.html
hb.enableWebMidiLink = function(synth) {
// TODO - rewrite to support more channels, pitchBend and other things
var webMidiLinkRecv = function (event) {
var msg = event.data.split(",");
switch (msg[0]) {
case "midi":
switch (parseInt(msg[1], 16) & 0xf0) {
case 0x80:
//console.log('note off', parseInt(msg[2], 16));
synth.noteOff(parseInt(msg[2], 16));
break;
case 0x90:
var velo = parseInt(msg[3], 16);
if (velo > 0) {
//console.log('note on', parseInt(msg[2], 16), velo);
synth.noteOn(parseInt(msg[2], 16), velo);
} else {
//console.log('note on', parseInt(msg[2], 16), velo);
synth.noteOff(parseInt(msg[2], 16));
}
break;
case 0xb0:
if (parseInt(msg[2], 16) == 0x78) {
//console.log('panik!');
synth.panic();
}
break;
}
break;
}
}

window.addEventListener("message", webMidiLinkRecv, false);
};

// components

/*
Expand Down
10 changes: 6 additions & 4 deletions js/heartbeat.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ these function creates various audio nodes and connects them.
- `hb.chain(a, b [, c ...])` - connects `a` and `b` or more nodes, stops on first thing which is not `AudioNode`
- `hb.unchain(a, b [, c ...])` - disconnects `a` and `b` or more nodes, stops on first thing which is not `AudioNode`

### audioParam stuff
## audioParam stuff

these functions are for realtime manipulation of `AudioParam` interface.

Expand All @@ -40,9 +40,9 @@ these functions are for realtime manipulation of `AudioParam` interface.
Default `env` definition is `{attack: 0.05, decay: 0.01, sustain: 0, release: 0.05, max: 1}`. You can supply only
partial definition, but you need to supply at least empty object (`{}`).

### MIDI stuff
## MIDI stuff

- `hb.midi2call(midi_data, synth, only_channel)` - translates `midi_data` to method calls on `synth` instance, if `only_channel` is set then it uses only this channel and ignore others.
Function `hb.midi2call(midi_data, synth, only_channel)` - translates `midi_data` to method calls on `synth` instance, if `only_channel` is set then it uses only this channel and ignore others.

Suported calls are:

Expand All @@ -55,7 +55,9 @@ Suported calls are:
- `synth.start()` - starts playback
- `synth.stop()` - stops playback

### metronome
There is a basic support for [WebMidiLink](https://www.g200kg.com/en/docs/webmidilink/index.html). You can call function `hb.enableWebMidiLink(synth)` and register your `synth` to receive events via WebMidiLink. Only channel 1 is now supported.

## metronome

As `setTimeout` and `setInterval` notoriously bad at timing, there is metronome in this library. It can be used as base for your sequencers, arpegiators and stuff. It has following interface:

Expand Down
3 changes: 2 additions & 1 deletion kasia.html
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ <h2>vowel-consonant synthesis</h2>

</div>

<footer>part of <a href="index.html">Cyber Music Studio™</a> by <a href="http://tilde.town/~severak/">Severák</a></footer>
<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>
Expand Down Expand Up @@ -445,6 +445,7 @@ <h2>vowel-consonant synthesis</h2>
};

keyboardMidiWrap('keyboard3', 'select_midi_in3', synth);
hb.enableWebMidiLink(synth);

tape = hb.TapeRecorder();

Expand Down
3 changes: 2 additions & 1 deletion regenschori.html
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@

</div>

<footer>part of <a href="index.html">Cyber Music Studio™</a> by <a href="http://tilde.town/~severak/">Severák</a></footer>
<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>
Expand Down Expand Up @@ -430,6 +430,7 @@
hb.chnget(sk, 'vibrato_heavy');
hb.chnget(sk, 'a432');
keyboardMidiWrap('keyboard3', 'select_midi_in3', sk);
hb.enableWebMidiLink(sk);

tape = hb.TapeRecorder();

Expand Down
3 changes: 2 additions & 1 deletion sk1.html
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ <h1>Sample propulsion laboratory</h1>

</div>

<p class="footer">part of <a href="index.html">Cyber Music Studio™</a></p>
<p class="footer">Part of <a href="index.html">Cyber Music Studio™</a>. Supports <a href="https://www.g200kg.com/en/docs/webmidilink/index.html" target="_blank">WebMidiLink</a>.</p>

</form>
</div>
Expand Down Expand Up @@ -368,6 +368,7 @@ <h1>Sample propulsion laboratory</h1>
hb.chnget(sk, 'sustain', 'sk1_sustain');
hb.chnget(sk, 'release', 'sk1_release');
keyboardMidiWrap('keyboard3', 'select_midi_in3', sk);
hb.enableWebMidiLink(sk);

var violin = 'UklGRsACAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YZwCAABGAFAAZQBpAGsAYQBdAG4AXQBjABAAbgC4AL4AtgDEALUAsAC4AOQAJgErAf4AJwFWATsDfgThATD9d/gG9aXykPFL8T7xivEH8ofyQvPV8030GPXf9ZL2z/Yb95/3Wfjr+F75z/ks+sX69vpT+8r7Qvy9/CL9Qf2p/SP+hP64/hH/R/93/9n/YAB2AJ4AzQAhAZoBuQHSAS0CQQJnAq0CAwNhA6EDmwPCA+gDGwRKBGAEiAS8BKkE2AQ0BTcFRAVqBUIFkgXbBbcF1gXSBdcFAAYBBukF4QUEBrcEMwLrAxkKTBGlFjoaXRxxHbMdcx0FHXIcXBuqGvAZJhlbGJwX5RYQFmEVsBTfEyMTOxK0ESwRTxDDDzEPnw76DW8N9gw/DFQLNQuyClUK2AlBCb0IQgjSBzgHsQaHBiMGpQVWBf0ErQQbBKoDYwPcAo0CUQIQAq0BhgEtAdcArwAYALr/gP9p/1H/v/52/nf+NP7P/aH9m/1Q/e/82vzu/Jz8XfxA/O/7c/yA/Az73fgz9wT23PQu9LTziPOO87nzs/PX8wD0HvQ69Ev0uvTS9On0/PQ89Uv1ifWQ9cH1BfYg9k72SvaW9rr21vYA9xn3I/dB93/3s/fL99f32/f/9w/4X/iY+Nz49PjT+Pv4FflA+X35fvmt+dL5GvoV+vX5Gfo4+iL6Wfqx+rL6xfrm+vD6JPtJ+3b7lfua+5D7hvuH+5j75PsJ/BL8MvyY/I/8h/zT/Cj9K/3z/Gn9Sf1A/Uz9UP1p/Zj9i/2N/aP9zP0G/mb+H/4e/mX+Vv4X/hT+Kv5B/kz+q/6n/vf+7P7r/vD+Cv/+/jT/Nv8u/zX/cP96/4T/fP+q/8f/4f+u/4j/lv/A/9D/7/9BAG8ANgAFAA==';
hb.ac.decodeAudioData(_base64ToArrayBuffer(violin), function (buffer) {
Expand Down

0 comments on commit 71a125b

Please sign in to comment.