/* AudioGeneratorMIDI Audio output generator that plays MIDI files using a SF2 SoundFont Copyright (C) 2017 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef _AUDIOGENERATORMIDI_H #define _AUDIOGENERATORMIDI_H #include "AudioGenerator.h" #define TSF_NO_STDIO #include "libtinysoundfont/tsf.h" class AudioGeneratorMIDI : public AudioGenerator { public: AudioGeneratorMIDI() { freq=44100; running = false; }; virtual ~AudioGeneratorMIDI() override {}; bool SetSoundfont(AudioFileSource *newsf2) { if (isRunning()) return false; sf2 = newsf2; MakeStreamFromAFS(sf2, &afsSF2); return true; } bool SetSampleRate(int newfreq) { if (isRunning()) return false; freq = newfreq; return true; } virtual bool begin(AudioFileSource *mid, AudioOutput *output) override; virtual bool loop() override; virtual bool stop() override; virtual bool isRunning() override { return running; }; private: int freq; tsf *g_tsf; struct tsf_stream buffer; struct tsf_stream afsMIDI; struct tsf_stream afsSF2; AudioFileSource *sf2; AudioFileSource *midi; protected: struct midi_header { int8_t MThd[4]; uint32_t header_size; uint16_t format_type; uint16_t number_of_tracks; uint16_t time_division; }; struct track_header { int8_t MTrk[4]; uint32_t track_size; }; enum { MAX_TONEGENS = 32, /* max tone generators: tones we can play simultaneously */ MAX_TRACKS = 24 }; /* max number of MIDI tracks we will process */ int hdrptr; unsigned long buflen; int num_tracks; int tracks_done = 0; int num_tonegens = MAX_TONEGENS; int num_tonegens_used = 0; unsigned int ticks_per_beat = 240; unsigned long timenow = 0; unsigned long tempo; /* current tempo in usec/qnote */ // State needed for PlayMID() int notes_skipped = 0; int tracknum = 0; int earliest_tracknum = 0; unsigned long earliest_time = 0; struct tonegen_status { /* current status of a tone generator */ bool playing; /* is it playing? */ char track; /* if so, which track is the note from? */ char note; /* what note is playing? */ char instrument; /* what instrument? */ } tonegen[MAX_TONEGENS]; struct track_status { /* current processing point of a MIDI track */ int trkptr; /* ptr to the next note change */ int trkend; /* ptr past the end of the track */ unsigned long time; /* what time we're at in the score */ unsigned long tempo; /* the tempo last set, in usec per qnote */ unsigned int preferred_tonegen; /* for strategy2, try to use this generator */ unsigned char cmd; /* CMD_xxxx next to do */ unsigned char note; /* for which note */ unsigned char chan; /* from which channel it was */ unsigned char velocity; /* the current volume */ unsigned char last_event; /* the last event, for MIDI's "running status" */ bool tonegens[MAX_TONEGENS]; /* which tone generators our notes are playing on */ } track[MAX_TRACKS]; int midi_chan_instrument[16]; /* which instrument is currently being played on each channel */ /* output bytestream commands, which are also stored in track_status.cmd */ enum { CMD_PLAYNOTE = 0x90, /* play a note: low nibble is generator #, note is next byte */ CMD_STOPNOTE = 0x80, /* stop a note: low nibble is generator # */ CMD_INSTRUMENT = 0xc0, /* change instrument; low nibble is generator #, instrument is next byte */ CMD_RESTART = 0xe0, /* restart the score from the beginning */ CMD_STOP = 0xf0, /* stop playing */ CMD_TEMPO = 0xFE, /* tempo in usec per quarter note ("beat") */ CMD_TRACKDONE = 0xFF }; /* no more data left in this track */ /* portable string length */ int strlength (const char *str) { int i; for (i = 0; str[i] != '\0'; ++i); return i; } /* match a constant character sequence */ int charcmp (const char *buf, const char *match) { int len, i; len = strlength (match); for (i = 0; i < len; ++i) if (buf[i] != match[i]) return 0; return 1; } unsigned char buffer_byte (int offset); unsigned short buffer_short (int offset); unsigned int buffer_int32 (int offset); void midi_error (const char *msg, int curpos); void chk_bufdata (int ptr, unsigned long int len); uint16_t rev_short (uint16_t val); uint32_t rev_long (uint32_t val); void process_header (void); void start_track (int tracknum); unsigned long get_varlen (int *ptr); void find_note (int tracknum); void PrepareMIDI(AudioFileSource *src); int PlayMIDI(); void StopMIDI(); // tsf_stream <-> AudioFileSource static int afs_read(void *data, void *ptr, unsigned int size); static int afs_tell(void *data); static int afs_skip(void *data, unsigned int count); static int afs_seek(void *data, unsigned int pos); static int afs_close(void *data); static int afs_size(void *data); void MakeStreamFromAFS(AudioFileSource *src, tsf_stream *afs); int samplesToPlay; bool sawEOF; int numSamplesRendered; int sentSamplesRendered ; short samplesRendered[256]; }; #endif