Artifact Content
Artifact 3c5afc75cbce09761b2c0c1470691047fb6e295eeff80441d1e0d43a2f3a4881:
- File source/alternate-implementation.md — part of check-in [4cb0fff742] at 2022-03-27 22:44:18 on branch trunk — Updating JUCE link and build scripts (user: dev size: 4479)
Alternate Implementations
Here's some more demo songs from other systems
The core ideas of bitrhythm are transferable to other languages as well. At most all you need is a music loop and samples to get started.
A basic port to JUCE can be found here.
Here's a demo by SunVox's author ?
An example in C with raylib
brew install raylib
cc ray.c `pkg-config --libs --cflags raylib` -o ray
#include "raylib.h"
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
void setTimeout(int milliseconds)
{
// If milliseconds is less or equal to 0
// will be simple return from function without throw error
if (milliseconds <= 0) {
fprintf(stderr, "Count milliseconds for timeout is less or equal to 0\n");
return;
}
// a current time of milliseconds
int milliseconds_since = clock() * 1000 / CLOCKS_PER_SEC;
// needed count milliseconds of return from this timeout
int end = milliseconds_since + milliseconds;
// wait while until needed time comes
do {
milliseconds_since = clock() * 1000 / CLOCKS_PER_SEC;
} while (milliseconds_since <= end);
}
int main(void)
{
InitAudioDevice(); // Initialize audio device
Sound fxWav = LoadSound("Music/Kick01.wav");
Sound fxWav2 = LoadSound("Music/Clap01.wav");
int bpm;
printf("Enter bpm: ");
scanf("%d", &bpm);
float time_per_beat = (60.0 / bpm) * 4;
int delay = (time_per_beat * 1000) / 16;
printf("%d\n", delay);
while(1) {
int i;
for (i = 1; i <= 16; i++) {
if ((i == 1) || (i == 5) || (i == 9) || (i == 13)) {
PlaySound(fxWav);
}
if ((i == 5) || (i == 13)) {
PlaySound(fxWav2);
}
setTimeout(delay);
}
}
UnloadSound(fxWav); // Unload sound data
UnloadSound(fxWav2); // Unload sound data
CloseAudioDevice(); // Close audio device
return 0;
}
Elementary
Elementary is a javascript runtime - https://github.com/nick-thompson/elementary.
The following is a basic incomplete example that makes use of the sample. Need to update it to the latest Elementary version to simplify tick logic.
const el = require('@nick-thompson/elementary');
const kick02 = './Music/Kick01.wav';
const hh02 = './Music/HH02.wav';
const clap01 = './Music/Clap01.wav';
let voices = {
'60': {gain: 1.0, gate: 0.0, path: kick02, key: 'v1'},
'61': {gain: 1.0, gate: 0.0, path: hh02, key: 'v2'},
'62': {gain: 0.6, gate: 0.0, path: clap01, key: 'v3'},
};
function updateVoiceState(e) {
if (e && e.hasOwnProperty('type') && e.type === 'noteOn') {
if (voices.hasOwnProperty(e.noteNumber)) {
voices[e.noteNumber].gate = 1.0;
}
}
if (e && e.hasOwnProperty('type') && e.type === 'noteOff') {
if (voices.hasOwnProperty(e.noteNumber)) {
voices[e.noteNumber].gate = 0.0;
}
}
}
function samplerVoice(voice) {
let gate = el.const({key: voice.key, value: voice.gate});
return el.mul(voice.gain, el.sample({path: voice.path}, gate));
}
elementary.core.on('load', function() {
let i = 0;
let step = 0;
function function2() {
step = i % 16;
console.log(step)
if (i % 4 == 0) {
updateVoiceState({type: 'noteOn', noteNumber: '60'});
} else {
updateVoiceState({type: 'noteOff', noteNumber: '60'});
}
if ((i !== 0) && (i % 8 == 0)) {
updateVoiceState({type: 'noteOn', noteNumber: '62'});
} else {
updateVoiceState({type: 'noteOff', noteNumber: '62'});
}
let out = el.add(Object.keys(voices).map(function(n) {
return samplerVoice(voices[n]);
}));
let filtered = el.lowpass(1800, 1.414, out);
elementary.core.render(filtered, filtered);
i++;
}
setInterval(function2, 60000 / 120.0 / 2);
});