#include #include #include #include #include #include #include "../include.h" // assumes little endian architechture, sorry // my C port of the 250 SLoC of JS organya player https://github.com/alula/organya-js struct song { uint16_t wait; uint8_t meas[2]; int32_t start; int32_t end; struct instrument { uint16_t freq; uint8_t wave; uint8_t pipi; uint16_t notes; } instruments[16]; struct track { int32_t pos; uint8_t key; uint8_t len; uint8_t vol; uint8_t pan; } *tracks[16]; }; struct organya { struct song *song; float t; int playPos; int samplesPerTick; int samplesThisTick; struct state { float t; // may be float? int key; int frequency; int octave; float pan; float vol; int length; int num_loops; bool playing; bool looping; } state[16]; }; void organya_synth(struct organya *self, float *const buf, size_t bufsize); static void organya_callback(void *userdata, unsigned char *stream, int const length) { struct organya *user = userdata; organya_synth(user, (float *) stream, length / sizeof (float) / 2); } static signed char const waveTable[] = { #embed "organya.bin" }; static struct drums { int filePos; int bits; int channels; int samples; } *drums = NULL; void freesong(struct song *song) { for (int i = 0; i < 16; i++) { free(song->tracks[i]); } free(song); } void freefunc(void *ptr) { struct organya *self = ptr; freesong(self->song); free(self); } struct song *song_constructor(struct blob *file) { char const *p = file->data; size_t bytes = file->size; // header check if (bytes < 6) { return NULL; } bytes -= 6; uint32_t const org1 = *(uint32_t *) p; p += sizeof (uint32_t); uint16_t const orgVersion = *(uint16_t *) p; p += sizeof (uint16_t); if (org1 != 0x2d67724f || orgVersion != 0x3230) { return NULL; } struct song *self = malloc(sizeof (struct song)); if (self == NULL) { return NULL; } if (bytes < 12) { return NULL; } bytes -= 12; self->wait = *(uint16_t *) p; p += sizeof (uint16_t); self->meas[0] = *(uint8_t *) p; p += sizeof (uint8_t); self->meas[1] = *(uint8_t *) p; p += sizeof (uint8_t); self->start = *(int32_t *) p; p += sizeof (int32_t); self->end = *(int32_t *) p; p += sizeof (int32_t); if (bytes < 6 * 16) { return NULL; } bytes -= 6 * 16; for (int i = 0; i < 16; i++) { self->instruments[i].freq = *(uint16_t *) p; p+= sizeof (uint16_t); self->instruments[i].wave = *(uint8_t *) p; p += sizeof (uint8_t); self->instruments[i].pipi = *(uint8_t *) p; p += sizeof (uint8_t); self->instruments[i].notes = *(uint16_t *) p; p += sizeof (uint16_t); } for (int i = 0; i < 16; i++) { self->tracks[i] = NULL; } for (int i = 0; i < 16; i++) { size_t const length = self->instruments[i].notes; struct track *track = malloc(sizeof (track) * length); if (track == NULL && length != 0) { freesong(self); return NULL; } if (bytes < 8 * length) { freesong(self); return NULL; } bytes -= 8 * length; for (unsigned j = 0; j < length; j++) { track[j].pos = *(int32_t *) p; p += sizeof (int32_t); } for (unsigned j = 0; j < length; j++) { track[j].key = *(uint8_t *) p; p += sizeof (uint8_t); } for (unsigned j = 0; j < length; j++) { track[j].len = *(uint8_t *) p; p += sizeof (uint8_t); } for (unsigned j = 0; j < length; j++) { track[j].vol = *(uint8_t *) p; p += sizeof (uint8_t); } for (unsigned j = 0; j < length; j++) { track[j].pan = *(uint8_t *) p; p += sizeof (uint8_t); } self->tracks[i] = track; } //p == ((char *) file->data) + file->size return self; } static int const freqTable[] = {261, 278, 294, 311, 329, 349, 371, 391, 414, 440, 466, 494}; static int const panTable[] = {0, 43, 86, 129, 172, 215, 256, 297, 340, 383, 426, 469, 512}; static int const advTable[] = {1, 1, 2, 2, 4, 8, 16, 32}; static int const octTable[] = {32, 64, 64, 128, 128, 128, 128, 128}; int window_initOrganya(void); int module_organya(struct blob *file, struct userdata *user) { if (drums == NULL) { if (window_initOrganya()) { return 1; } } struct organya *self = malloc(sizeof (struct organya)); if (self == NULL) { return 1; } self->song = song_constructor(file); if (self->song == NULL) { free(self); return 1; } self->t = 0; self->playPos = 0; self->samplesPerTick = (SAMPLE_RATE / 1000.0) * self->song->wait; self->samplesThisTick = 0; for (int i = 0; i < 16; i++) { self->state[i] = (struct state) { .t = 0, .key = 0, .frequency = 0, .octave = 0, .pan = 0.0, .vol = 1.0, .length = 0, .num_loops = 0, .playing = false, .looping = false, }; } user->user = self; user->callback = organya_callback; user->freefunc = freefunc; return 0; } void organya_update(struct organya *self); void organya_synth(struct organya *self, float *const buf, size_t bufsize) { float *const leftBuffer = buf + 0; float *const rightBuffer = buf + 1; for (int sample = 0; sample < bufsize * 2; sample += 2) { if (self->samplesThisTick == 0) { organya_update(self); } leftBuffer[sample] = 0; rightBuffer[sample] = 0; for (int i = 0; i < 16; i++) { if (self->state[i].playing) { int const samples = (i < 8)? 256: drums[i - 8].samples; self->state[i].t += ((float) self->state[i].frequency / (float) SAMPLE_RATE) * advTable[self->state[i].octave]; if (((int) self->state[i].t) >= samples) { if (self->state[i].looping && self->state[i].num_loops != 1) { self->state[i].t = fmod(self->state[i].t, samples); if (self->state[i].num_loops != 1) { self->state[i].num_loops -= 1; } } else { self->state[i].t = 0; self->state[i].playing = false; continue; } } int const t = (int) self->state[i].t & ~(advTable[self->state[i].octave] - 1); int pos = t % samples; int pos2 = !self->state[i].looping && t == samples? pos: ((int) (self->state[i].t + advTable[self->state[i].octave]) & ~(advTable[self->state[i].octave] - 1)) % samples; float const s1 = i < 8? (waveTable[256 * self->song->instruments[i].wave + pos] / 256.0): (((waveTable[drums[i - 8].filePos + pos] & 0xff) - 0x80) / 256.0); float const s2 = i < 8? (waveTable[256 * self->song->instruments[i].wave + pos2] / 256.0): (((waveTable[drums[i - 8].filePos + pos2] & 0xff) - 0x80) / 256.0); float const fract = (float) (self->state[i].t - pos) / (float) advTable[self->state[i].octave]; // lerp float s = s1 + (s2 - s1) * fract; s *= pow(10, (float) ((self->state[i].vol - 255) * 8) / 2000.0); float const pan = (panTable[(int) self->state[i].pan] - 256) * 10; float left = 1, right = 1; if (pan < 0) { right = pow(10, (float) (pan / 2000.0)); } else if (pan > 0) { left = pow(10, (float) (-pan / 2000.0)); } leftBuffer[sample] += s * left; rightBuffer[sample] += s * right; } } if (++self->samplesThisTick == self->samplesPerTick) { self->playPos += 1; self->samplesThisTick = 0; if (self->playPos == self->song->end) { self->playPos = self->song->start; } } } } struct track *find(struct track *array, int value, size_t members) { for (int i = 0; i < members; i++) { if (array[i].pos == value) { return array + i; } } return NULL; } void organya_update(struct organya *self) { for (int track = 0; track < 8; track++) { size_t const length = self->song->instruments[track].notes; struct track *const note = find(self->song->tracks[track], self->playPos, length); if (note) { if (note->key != 255) { int const octave = note->key / 12; int const key = note->key % 12; if (self->state[track].key == 255) { self->state[track].key = note->key; self->state[track].frequency = freqTable[key] * octTable[octave] + (self->song->instruments[track].freq - 1000); if (self->song->instruments[track].pipi != 0 && !self->state[track].playing) { self->state[track].num_loops = ((octave + 1) * 4); } } else if (self->state[track].key != note->key) { self->state[track].key = note->key; self->state[track].frequency = freqTable[key] * octTable[octave] + (self->song->instruments[track].freq - 1000); } if (self->song->instruments[track].pipi != 0 && !self->state[track].playing) { self->state[track].num_loops = ((octave + 1) * 4); } self->state[track].octave = octave; self->state[track].playing = true; self->state[track].looping = true; self->state[track].length = note->len; } if (self->state[track].key != 255) { if (note->vol != 255) { self->state[track].vol = note->vol; } if (note->pan != 255) { self->state[track].pan = note->pan; } } } if (self->state[track].length == 0) { if (self->state[track].key != 255) { if (self->song->instruments[track].pipi == 0) { self->state[track].looping = false; } self->state[track].playing = false; self->state[track].key = 255; } } else { self->state[track].length--; } } for (int track = 8; track < 16; track++) { size_t const length = self->song->instruments[track].notes; struct track *const note = find(self->song->tracks[track], self->playPos, length); if (!note) { continue; } if (note->key != 255) { self->state[track].frequency = note->key * 800 + 100; self->state[track].t = 0; self->state[track].playing = true; } if (note->vol != 255) { self->state[track].vol = note->vol; } if (note->pan != 255) { self->state[track].pan = note->pan; } } } int window_initOrganya(void) { size_t drumsLen = 0; for (size_t i = 256 * 100; i < sizeof (waveTable) - 4; i++) { signed char const *const p = waveTable; if (*(uint32_t *) (p + i) == 0x45564157) { i += sizeof (uint32_t); uint32_t const riffId = *(uint32_t *) (p + i); i += sizeof (uint32_t); uint32_t const riffLen = *(uint32_t *) (p + i); i += sizeof (uint32_t); if (riffId != 0x20746d66) { fputs("invalid RIFF chunk id", stderr); continue; } size_t const startPos = i; int const aFormat = *(uint16_t *) (p + i); i += sizeof (uint16_t); if (aFormat != 1) { fputs("invalid audio format", stderr); i = startPos + riffLen; continue; } int const channels = *(uint16_t *) (p + i); i += sizeof (uint16_t); if (channels != 1) { fputs("only 1 channel files are supported", stderr); i = startPos + riffLen; continue; } int const samples = *(uint32_t *) (p + i); i += sizeof (uint32_t) + 6; // skip rate + padding int const bits = *(uint16_t *) (p + i); i += sizeof (uint16_t); int const wavData = *(uint32_t *) (p + i); i += sizeof (uint32_t); int const wavLen = *(uint32_t *) (p + i); i += sizeof (uint32_t); if (wavData != 0x61746164) { i = startPos + riffLen; continue; } void *newDrums = realloc(drums, sizeof (struct drums) * (drumsLen + 1)); if (newDrums == NULL) { free(drums); drums = NULL; return 1; } drums = newDrums; drums[drumsLen].filePos = i; drums[drumsLen].bits = bits; drums[drumsLen].channels = channels; drums[drumsLen].samples = wavLen; drumsLen++; i += wavLen; } } return 0; }