RISC-V/midi/midiplayer.cpp
2024-03-10 17:40:00 +01:00

114 lines
3.7 KiB
C++

#include "midiplayer.h"
#include "tone.h"
#include "audio.h"
#include "pcmdma.h"
#include "pwmconfig.h"
/**
* @file
* @brief Jednoduchý přehrávač midi souborů.
*
* Kompletní midi obsahuje zvukové fonty, které jsou obrovské. Tohle je velice zjednodušené,
* takže docela dobře přehrává skladby typu ragtime, orchestrální midi jsou skoro nepoužitelné.
* Přesto se to pro jednoduché zvuky může hodit, protože je to poměrně nenáročné na systémové
* prostředky. Může to fungovat dokonce i na 8-bitovém uP.
* */
// static Filtr iir;
/// Generátory tónů
static Tone gens[maxGens];
/// Generuj vzorek pro všechny tóny @return Vzorek
static inline short genSample (void) {
int res = 0;
for (unsigned int i=0; i<maxGens; i++) res += gens[i].step();
// Pro jistotu omezíme - předejdeme chrastění
if (res > maxValue) res = maxValue;
if (res < minValue) res = minValue;
return (res);
}
/// Počítá další vzorek
short MidiPlayer::nextSample (void) {
if (pause) pause -= 1; // Časování tónu
else ToneChange(); // Nový tón - MidiPlayer::ToneChange
return genSample ();
}
static constexpr unsigned AudioMidiDelay = 24;
static constexpr int INPUT_BIT_RANGE = 16;
static constexpr unsigned SIGMA_MASK = (1u << (INPUT_BIT_RANGE + 0)) - 1u;
static constexpr unsigned SIGNED_OFFEST = (1u << (INPUT_BIT_RANGE - 1));
// Předpokládá se na vstupu signed int o šířce INPUT_BIT_RANGE
// přičemž 0 na vstupu odpovídá MAXPWM / 2 na výstupu. Vypadá to divně, ale funguje.
static unsigned pwm_sd (const int input) {
static unsigned sigma = 0; // podstatné je, že proměnná je statická
const unsigned sample = (input + SIGNED_OFFEST) * MAXPWM;
sigma &= SIGMA_MASK; // v podstatě se odečte hodnota PWM
sigma += sample; // integrace prostým součtem
return sigma >> INPUT_BIT_RANGE;
}
/******************************************************************/
/// Konstruktor
MidiPlayer::MidiPlayer() noexcept : OneWay(), led (LED_CFG), passcnt (0u) {
index = 0;
pause = 0;
melody = scores[index++];
running = true;
}
void MidiPlayer::pass() {
const bool b = passcnt & 0x10000;
led << b;
passcnt += 1u;
}
unsigned MidiPlayer::Send (uint16_t * const ptr, const unsigned len) {
//if (!but.get()) running = true; // případně spouštět tlačítkem, není implementováno
if (!running) {
for (unsigned n=0; n<len; n++) ptr [n] = MAXPWM >> 1;
return len;
}
for (unsigned n=0; n<len; n++) {
const short s = nextSample();
ptr [n] = pwm_sd (s);
}
return len;
}
void MidiPlayer::stop (void) {
//running = false; // na konci každé melodie stop
melody = scores[index++];
if (!melody) {
index = 0;
melody = scores[index++];
running = false; // test bez tlačítka
}
}
void MidiPlayer::ToneChange (void) {
unsigned char midt;
for (;;) { // Pro všechny tóny před pauzou
unsigned char cmd = * melody++;
if (cmd & 0x80) { // event
const unsigned geno = cmd & 0x0F;
cmd >>= 4;
switch (cmd) {
case 0x8: // off
gens[geno].setMidiOff();
break;
case 0x9: // on
midt = * melody++;
gens[geno].setMidiOn (midt);
break;
default:
stop();
return; // melodie končí eventem 0xf0
}
} else { // pause
midt = * melody++;
// Když to trochu uteče, zase se z toho nestřílí, tak to nechme být.
pause = ((unsigned int) cmd << 8) + midt; // v ms
pause *= AudioMidiDelay; // ale máme vzorkování cca 24 kHz
return;
}
}
}