Tasmota/lib/lib_audio/ESP8266Audio/src/AudioGeneratorMIDI.h
2020-10-26 12:51:06 +00:00

182 lines
6.1 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#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