-
Notifications
You must be signed in to change notification settings - Fork 471
Expand file tree
/
Copy pathAudioGeneratorMIDI.h
More file actions
197 lines (166 loc) · 6.39 KB
/
AudioGeneratorMIDI.h
File metadata and controls
197 lines (166 loc) · 6.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
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
#if defined(xxxESP32) && (__GNUC__ >= 8) && (__XTENSA__)
// Do not build, Espressif's GCC8+ has a compiler bug
#else // __GNUC__ == 8
#include "AudioGenerator.h"
#define TSF_NO_STDIO
#define TSF_CONST_FILE
#define TSF_SAMPLES_SHORT
#include "libtinysoundfont/tsf.h"
class AudioGeneratorMIDI : public AudioGenerator {
public:
AudioGeneratorMIDI() {
freq = 22050;
running = false;
};
virtual ~AudioGeneratorMIDI() override {};
#if 0
bool SetSoundfont(AudioFileSource *newsf2) {
if (isRunning()) {
return false;
}
sf2 = newsf2;
MakeStreamFromAFS(sf2, &afsSF2);
return true;
}
#endif
bool SetSoundFont(tsf *t) {
if (isRunning()) {
return false;
}
_tsf = t;
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;
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? */
int playIndex; /* is index provided?
Unique identifier generated when note starts playing.
This help us to turn the note off faster */
} 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();
int samplesToPlay;
bool sawEOF;
int numSamplesRendered;
int sentSamplesRendered ;
short samplesRendered[256 * 2 * 2];
tsf *_tsf = nullptr;
};
#endif //__GNUC__ == 8
#endif