mirror of
https://github.com/birth1mark/orbis8.git
synced 2026-10-29 04:00:30 +01:00
122 lines
4.2 KiB
C++
122 lines
4.2 KiB
C++
//
|
|
// ZEPTO-8 — Fantasy console emulator
|
|
//
|
|
// Copyright © 2017—2020 Sam Hocevar <sam@hocevar.net>
|
|
//
|
|
// This program is free software. It comes without any warranty, to
|
|
// the extent permitted by applicable law. You can redistribute it
|
|
// and/or modify it under the terms of the Do What the Fuck You Want
|
|
// to Public License, Version 2, as published by the WTFPL Task Force.
|
|
// See http://www.wtfpl.net/ for more details.
|
|
//
|
|
|
|
#include "synth.h"
|
|
|
|
#include <cmath> // std::fabs, std::fmod
|
|
#include <cstdlib> // rand
|
|
|
|
namespace z8
|
|
{
|
|
|
|
// Simple random function returning float in range [-1, 1]
|
|
static float rand_float()
|
|
{
|
|
return ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f;
|
|
}
|
|
|
|
float synth::waveform(synth_param ¶ms)
|
|
{
|
|
using std::fabs, std::fmod;
|
|
|
|
float advance = params.phi;
|
|
float t = fmod(advance, 1.f);
|
|
float ret = 0.f;
|
|
|
|
bool noiz = params.filters & 0x2;
|
|
bool buzz = params.filters & 0x4;
|
|
|
|
// Multipliers were measured from PICO-8 WAV exports. Waveforms are
|
|
// inferred from those exports by guessing what the original formulas
|
|
// could be.
|
|
switch (params.instrument)
|
|
{
|
|
case INST_TRIANGLE:
|
|
ret = 1.0f - fabs(4.f * t - 2.0f);
|
|
if (buzz) {
|
|
// seems to be averaged with a tilted saw
|
|
static float const a = 0.875f;
|
|
float bret = t < a ? 2.f * t / a - 1.f
|
|
: 2.f * (1.f - t) / (1.f - a) - 1.f;
|
|
ret = ret * 0.75f + bret * 0.25f;
|
|
}
|
|
return ret * 0.5f;
|
|
case INST_TILTED_SAW:
|
|
{
|
|
float a = buzz ? 0.975f : 0.875f;
|
|
ret = t < a ? 2.f * t / a - 1.f
|
|
: 2.f * (1.f - t) / (1.f - a) - 1.f;
|
|
return ret * 0.5f;
|
|
}
|
|
case INST_SAW:
|
|
ret = (t < 0.5f ? t : t - 1.f);
|
|
// slight offset looping on 2x period
|
|
if (buzz) ret = ret * 0.83f - (fabs(fmod(advance, 2.f) - 1.0f) < 0.5 ? 0.085f : 0.0f);
|
|
return 0.653f * ret;
|
|
case INST_SQUARE:
|
|
return t < (buzz ? 0.4f : 0.5f) ? 0.25f : -0.25f;
|
|
case INST_PULSE:
|
|
return t < (buzz ? 0.255f : 0.316f) ? 0.25f : -0.25f;
|
|
case INST_ORGAN:
|
|
ret = t < 0.5f ? 3.f - fabs(24.f * t - 6.f)
|
|
: 1.f - fabs(16.f * t - 12.f);
|
|
if (buzz)
|
|
{
|
|
// add a cut on the first of the two triangles
|
|
ret = t < 0.5f ? ret * 2.0f + 3.0f : ret;
|
|
ret = (t < 0.5f && ret>-1.875f) ? ret * 0.2f - 1.0f : ret + 0.5f;
|
|
}
|
|
return ret / 9.f;
|
|
case INST_NOISE:
|
|
{
|
|
//const float tscale = 22050 / key_to_freq(63);
|
|
const float tscale = 8.858923f;
|
|
float scale = (advance - params.last_advance) * tscale;
|
|
float new_sample = (params.last_sample + scale * rand_float()) / (1.0f + scale);
|
|
|
|
float factor = 1.0f - params.key / 63.0f;
|
|
ret = new_sample * 1.5f * (1.0f + factor * factor);
|
|
|
|
if (noiz)
|
|
{
|
|
// sound a bit like a saw tooth but not quite
|
|
ret *= 2.0f*(t < 0.5f ? t : t - 1.f);
|
|
}
|
|
|
|
params.last_advance = advance;
|
|
params.last_sample = new_sample;
|
|
|
|
return ret;
|
|
}
|
|
case INST_PHASER:
|
|
{ // sum of two triangle waves with a slightly different frequency
|
|
// the ratio between the frequency seems to be around 110 for c2
|
|
// but it is 97 for c0 and 127 for c5, not sure how to adjust that
|
|
ret = 2.f - fabs(8.f * t - 4.f);
|
|
ret += 1.f - fabs(4.f * fmod(advance * 109.f/110.f, 1.f) - 2.f);
|
|
if (buzz)
|
|
{
|
|
// original triangle has freq 1, 3, 5, 7, 9 ...
|
|
// add waves at 2, 6, 10, 14
|
|
ret += 0.25f - fabs(1.f * fmod(advance * 2.0f + 0.5f, 1.f) - 0.5f);
|
|
// add waves at 4, 12, 20, 28
|
|
ret += 0.125f - fabs(0.5f * fmod(advance * 4.0f, 1.f) - 0.25f);
|
|
}
|
|
return ret / 6.f;
|
|
}
|
|
}
|
|
|
|
return 0.0f;
|
|
}
|
|
|
|
} // namespace z8
|