From e57bf668c2e2cbc9f092c1e73769a3dddb8ddb6a Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 10 Jul 2022 16:32:21 +0000 Subject: [PATCH] MIDI music player --- source/engine/iir.c | 210 +++++++++++++----------------------------- source/engine/music.c | 67 ++++++++++++++ source/engine/music.h | 17 ++++ source/engine/sound.c | 4 +- 4 files changed, 148 insertions(+), 150 deletions(-) create mode 100644 source/engine/music.c create mode 100644 source/engine/music.h diff --git a/source/engine/iir.c b/source/engine/iir.c index f49e7a8..22adf0c 100644 --- a/source/engine/iir.c +++ b/source/engine/iir.c @@ -43,35 +43,6 @@ #include "limits.h" #include "iir.h" #include "dsp.h" -#include "log.h" - -struct dsp_iir make_iir(int cofs, int order) -{ - struct dsp_iir new; - new.dcof = calloc(sizeof(float), cofs * order); - new.ccof = calloc(sizeof(float), cofs * order); - new.dx = calloc(sizeof(float), cofs * order); - new.dy = calloc(sizeof(float), cofs * order); - new.order = order; - new.n = cofs; - - return new; -} - - -struct dsp_iir biquad_iir() -{ - struct dsp_iir new = make_iir(3, 1); - return new; -} - -struct dsp_iir p2_iir_order(int order) -{ - struct dsp_iir new = make_iir(3, order); - new.order = order; - - return new; -} /********************************************************************** binomial_mult - multiplies a series of binomials together and returns @@ -670,105 +641,25 @@ float *fir_bpf(int n, double fcf1, double fcf2) -struct dsp_iir sp_lp(double fcf) -{ - struct dsp_iir new = make_iir(2, 1); - double x = exp(-2*M_PI*fcf); - new.ccof[0] = 1 - x; - new.ccof[1] = 0.f; - new.dcof[0] = 0.f; - new.dcof[1] = x; - - return new; -} - -struct dsp_iir sp_hp(double fcf) -{ - struct dsp_iir new = make_iir(2, 1); - double x = exp(-2*M_PI*fcf); - - new.ccof[0] = (1 + x)/2; - new.ccof[1] = -new.ccof[0]; - - new.dcof[0] = 0.f; - new.dcof[1] = x; - - return new; -} - -/* http://www.dspguide.com/ch19/3.htm */ -struct dsp_iir sp_bp(double fcf, double bw) -{ - double R = 1 - 3*bw; - double K = (1 - 2*R*cos(2*M_PI*fcf)+pow(R, 2)) / (2 - 2*cos(2*M_PI*fcf)); - - struct dsp_iir new = make_iir(3, 1); - - new.ccof[0] = 1-K; - new.ccof[1] = 2*(K-R)*cos(2*M_PI*fcf); - new.ccof[2] = pow(R, 2) - K; - - new.dcof[0] = 0.f; - new.dcof[1] = 2*R*cos(2*M_PI*fcf); - new.dcof[2] = -1 * pow(R, 2); - - return new; -} - -struct dsp_iir sp_notch(double fcf, double bw) -{ - double R = 1 - 3*bw; - double K = (1 - 2*R*cos(2*M_PI*fcf)+pow(R, 2)) / (2 - 2*cos(2*M_PI*fcf)); - - struct dsp_iir new = make_iir(3, 1); - - new.ccof[0] = K; - new.ccof[1] = -2*K*cos(2*M_PI*fcf); - new.ccof[2] = K; - - new.dcof[0] = 0.f; - new.dcof[1] = 2*R*cos(2*M_PI*fcf); - new.dcof[2] = -1 * pow(R, 2); - - return new; -} - -double chevy_pct_to_e(double pct) -{ - if (pct > 0.292 || pct < 0) { - YughWarn("Gave a percentage out of range. Should be between 0.0 and 0.292 to get a proper value. Gave %f.", pct); - pct = pct > 0.292 ? 0.292 : 0.f; - } - - double e = pow(1/(1-pct), 2) - 1; - - return e; -} - -/* Four stage single pole low pass, similar to gauss */ -/* http://www.dspguide.com/ch19/2.htm */ -struct dsp_iir sp_lp_gauss(double fcf) -{ - struct dsp_iir new = make_iir(5, 1); - double x = exp(-14.445*fcf); - - new.ccof[0] = pow((1 - x), 4); - - new.dcof[0] = 0.f; - new.dcof[1] = 4*x; - new.dcof[2] = -6 * pow(x, 2); - new.dcof[3] = 4 * pow(x, 3); - new.dcof[4] = -1 * pow(x,4); - - return new; -} /* Biquad filters */ + +struct dsp_iir biquad_iir() +{ + struct dsp_iir new; + new.dcof = malloc(sizeof(float) * 3); + new.ccof = malloc(sizeof(float) * 3); + new.dx = malloc(sizeof(float) * 3); + new.dy = malloc(sizeof(float) * 3); + new.n = 3; + return new; +} + void biquad_iir_fill(struct dsp_iir bq, double *a, double *b) { bq.ccof[0] = (b[0] / a[0]); @@ -951,7 +842,7 @@ void p2_ccalc(double fcf, double p, double g, double *a, double *b) double w0 = tan(M_PI * fcf); double k[2]; k[0] = p * w0; - k[1] = g * pow(w0, 2); + k[1] = g * pow2(w0); a[0] = k[1] / (1 + k[0] + k[1]); a[1] = 2 * a[0]; @@ -1021,7 +912,19 @@ struct dsp_iir p2_beshp(double fcf) return new; } +struct dsp_iir p2_iir_order(int order) +{ + struct dsp_iir new; + new.n = 3; + new.order = order; + new.ccof = calloc(sizeof(float), 3 * order); + new.dcof = calloc(sizeof(float), 3 * order); + new.dx = calloc(sizeof(float), 3 * order); + new.dy = calloc(sizeof(float), 3 * order); + + return new; +} short p2_filter(struct dsp_iir iir, short val) { @@ -1059,8 +962,8 @@ struct dsp_iir che_lp(int order, double fcf, double e) double a = tan(M_PI * fcf); - double a2 = pow(a, 2); - double u = log((1.f + sqrt(1.f + pow(e, 2)))/e); + double a2 = pow2(a); + double u = log((1.f + sqrt(1.f + pow2(e)))/e); double su = sinh(u/new.order); double cu = cosh(u/new.order); double b, c, s; @@ -1070,7 +973,7 @@ struct dsp_iir che_lp(int order, double fcf, double e) { b = sin(M_PI * (2.f*i + 1.f)/(2.f*new.order)) * su; c = cos(M_PI * (2.f*i + 1.f)/(2.f*new.order)) * cu; - c = pow(b, 2) + pow(c, 2); + c = pow2(b) + pow2(c); s = a2*c + 2.f*a*b + 1.f; double A = a2/(4.f); @@ -1091,8 +994,8 @@ struct dsp_iir che_hp(int order, double fcf, double e) struct dsp_iir new = che_lp(order, fcf, e); double a = tan(M_PI * fcf); - double a2 = pow(a, 2); - double u = log((1.f + sqrt(1.f + pow(e, 2)))/e); + double a2 = pow2(a); + double u = log((1.f + sqrt(1.f + pow2(e)))/e); double su = sinh(u/new.order); double cu = cosh(u/new.order); double b, c, s; @@ -1102,7 +1005,7 @@ struct dsp_iir che_hp(int order, double fcf, double e) { b = sin(M_PI * (2.f*i + 1.f)/(2.f*new.order)) * su; c = cos(M_PI * (2.f*i + 1.f)/(2.f*new.order)) * cu; - c = pow(b, 2) + pow(c, 2); + c = pow2(b) + pow2(c); s = a2*c + 2.f*a*b + 1.f; double A = 1.f/(4.f); @@ -1118,23 +1021,29 @@ struct dsp_iir che_hp(int order, double fcf, double e) struct dsp_iir che_bp(int order, double s, double fcf1, double fcf2, double e) { - struct dsp_iir new = make_iir(5, order); + if (order %4 != 0) { + YughWarn("Tried to make a filter with wrong order. Given order was %d, but order should be 4, 8, 12, ...", order); + } - double a = cos(M_PI*(fcf1+fcf2)/s) / cos(M_PI*(fcf2-fcf1)/s); - double a2 = pow(a, 2); + double ep = 2.f/e; + + int n = order / 4; + struct dsp_iir new = p2_iir_order(order); + + double a = cos(M_PI*(fcf1+fcf2)/2) / cos(M_PI*(fcf2-fcf1)/s); + double a2 = pow2(a); double b = tan(M_PI*(fcf2-fcf1)/s); - double b2 = pow(b, 2); - double u = log((1.f+sqrt(1.f+pow(e, 2)))/e); + double b2 = pow2(b); + double u = log((1.f+sqrt(1.f+pow2(e)))/e); double su = sinh(2.f*u/new.order); double cu = cosh(2.f*u/new.order); double A = b2/(4.f); double r, c; - double ep = 2.f/e; for (int i = 0; i < new.order; ++i) { r = sin(M_PI*(2.f*i+1.f)/new.order)*su; c = cos(M_PI*(2.f*i+1.f)/new.order)*su; - c = pow(r, 2) + pow(c, 2); + c = pow2(r) + pow2(c); s = b2*c + 2.f*b*r + 1.f; new.ccof[0*i] = ep * 1.f/A; @@ -1151,32 +1060,37 @@ struct dsp_iir che_bp(int order, double s, double fcf1, double fcf2, double e) return new; } -struct dsp_iir che_notch(int order, double s, double fcf1, double fcf2, double e) +struct dsp_iir che_notch(int order, double fcf1, double fcf2, double e) { - struct dsp_iir new = make_iir(5, order); + if (order %4 != 0) { + YughWarn("Tried to make a filter with wrong order. Given order was %d, but order should be 4, 8, 12, ...", order); + } - double a = cos(M_PI*(fcf1+fcf2)/s) / cos(M_PI*(fcf2-fcf1)/s); - double a2 = pow(a, 2); + int n = order / 4; + + struct dsp_iir new = p2_iir_order(order); + + double a = cos(M_PI*(fcf1+fcf2)/2) / cos(M_PI*(fcf2-fcf1)/s); + double a2 = pow2(a); double b = tan(M_PI*(fcf2-fcf1)/s); - double b2 = pow(b, 2); - double u = log((1.f+sqrt(1.f+pow(e, 2)))/e); - double su = sinh(2.f*u/new.n); - double cu = cosh(2.f*u/new.n); + double b2 = pow2(b); + double u = log((1.f+sqrt(1.f+pow2(e)))/e); + double su = sinh(2.f*u/n); + double cu = cosh(2.f*u/n); double A = b2/(4.f*s); - double r, c; - double ep = 2.f/e; + double r, c, s; for (int i = 0; i < new.order; ++i) { r = sin(M_PI*(2.f*i+1.f)/new.order)*su; - c = cos(M_PI*(2.f*i+1.f)/new.order)*su; - c = pow(r, 2) + pow(c, 2); + c = cos(M_PI*(2.f*i+1.f)/ew.order)*su; + c = pow2(r) + pow2(c); s = b2*c + 2.f*b*r + 1.f; new.ccof[0*i] = ep * 1.f/A; new.ccof[1*i] = ep * -2.f/A; new.ccof[2*i] = ep * 1.f/A; - new.dcof[0*i] = 0.f; + new.ddof[0*i] = 0.f; new.dcof[1*i] = ep * 4.f*a*(c+b*r)/s; new.dcof[2*i] = ep * 2.f*(b2-2.f*a2*c-c)/s; new.dcof[3*i] = ep * 4.f*a*(c-b*r)/s; diff --git a/source/engine/music.c b/source/engine/music.c new file mode 100644 index 0000000..20e2458 --- /dev/null +++ b/source/engine/music.c @@ -0,0 +1,67 @@ +#include "music.h" + +#include "dsp.h" +#include "tsf.h" +#include "tml.h" +#include "mix.h" +#include "sound.h" + +#define TSF_BLOCK 64 + +struct dsp_filter cursong; +struct dsp_midi_song gsong; + +void dsp_midi_fillbuf(struct dsp_midi_song *song, void *out, int n) +{ + short *o = (short*)out; + tml_message *midi = song->midi; + + for (int i = TSF_BLOCK; n; n -= i, o += n*CHANNELS) { + if (i > n) i = n; + + for (song->time += i * (1000.f / SAMPLERATE); midi && song->time >= midi->time; midi = midi->next) { + switch (midi->type) + { + case TML_PROGRAM_CHANGE: + tsf_channel_set_presetnumber(song->sf, midi->channel, midi->program, (midi->channel == 9)); + break; + + case TML_NOTE_ON: + tsf_channel_note_on(song->sf, midi->channel, midi->key, midi->velocity / 127.f); + break; + + case TML_NOTE_OFF: + tsf_channel_note_off(song->sf, midi->channel, midi->key); + break; + + case TML_PITCH_BEND: + tsf_channel_set_pitchwheel(song->sf, midi->channel, midi->pitch_bend); + break; + + case TML_CONTROL_CHANGE: + tsf_channel_midi_control(song->sf, midi->channel, midi->control, midi->control_value); + break; + } + } + + tsf_render_short(song->sf, o, i, 0); + } + + song->midi = midi; +} + +void play_song(const char *midi, const char *sf) +{ + gsong.midi = tml_load_filename("sounds/one-winged-angel.mid"); + gsong.sf = tsf_load_filename("sounds/mario.sf2"); + gsong.time = 0.f; + + tsf_set_output(gsong.sf, TSF_STEREO_INTERLEAVED, SAMPLERATE, 0.f); + + // Preset on 10th MIDI channel to use percussion sound bank if possible + tsf_channel_set_bank_preset(gsong.sf, 9, 128, 0); + + cursong.data = &gsong; + cursong.filter = dsp_midi_fillbuf; + first_free_bus(cursong); +} diff --git a/source/engine/music.h b/source/engine/music.h new file mode 100644 index 0000000..409db42 --- /dev/null +++ b/source/engine/music.h @@ -0,0 +1,17 @@ +#ifndef MUSIC_H +#define MUSIC_H + +#include "tsf.h" +#include "tml.h" + +struct dsp_midi_song { + float bpm; + double time; + tsf *sf; + tml_message *midi; +}; + +void play_song(const char *midi, const char *sf); +void dsp_midi_fillbuf(struct dsp_midi_song *song, void *out, int n); + +#endif \ No newline at end of file diff --git a/source/engine/sound.c b/source/engine/sound.c index 5e87bd0..e24157f 100755 --- a/source/engine/sound.c +++ b/source/engine/sound.c @@ -6,6 +6,7 @@ #include "math.h" #include "limits.h" #include "time.h" +#include "music.h" #include "SDL2/SDL.h" @@ -226,8 +227,7 @@ void sound_init() - tsf *tsf_load_filename("sounds/ff7.sf2"); - tml_message *tml_load_filename("sounds/one_winged_angel.mid"); + play_song("", ""); } void audio_open(const char *device)