108 lines
3.5 KiB
C++
108 lines
3.5 KiB
C++
#include "midiplayer.h"
|
|
#include "tone.h"
|
|
#include "audio.h"
|
|
#include "pcmdma.h"
|
|
//#include "filtr.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() /*, but(GpioPortA, 2, GPIO_Mode_IN)*/ {
|
|
//but.setPuPd(GPIO_PuPd_UP);
|
|
index = 0;
|
|
pause = 0;
|
|
melody = scores[index++];
|
|
running = true;
|
|
}
|
|
unsigned MidiPlayer::Send (uint16_t * const ptr, const unsigned len) {
|
|
// if (!but.get()) running = true;
|
|
|
|
if (!running) {
|
|
for (unsigned n=0; n<len; n++) ptr [n] = 1000;
|
|
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;
|
|
melody = scores[index++];
|
|
if (!melody) {
|
|
index = 0;
|
|
melody = scores[index++];
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|