update for arduino-esp v2

This commit is contained in:
2021-11-27 11:07:49 +01:00
parent cd47e00a7d
commit dc7c4bbb17
3 changed files with 1 additions and 821 deletions

View File

@@ -1,639 +0,0 @@
/*
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/>.
*/
/*
The MIDI processing engine is a heavily modified version of MIDITONES,
by Len Shustek, https://github.com/LenShustek/miditones .
Whereas MIDITONES original simply parsed a file beforehand to a byte
stream to be played by another program, this does the parsing and
playback in real-time.
Here's his original header/readme w/MIT license, which is subsumed by the
GPL license of the ESP8266Audio project.
*/
/***************************************************************************
MIDITONES: Convert a MIDI file into a simple bytestream of notes
-------------------------------------------------------------------------
The MIT License (MIT)
Copyright (c) 2011,2013,2015,2016, Len Shustek
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
**************************************************************************/
#include "AudioGeneratorMIDI.h"
#pragma GCC optimize ("O3")
#define TSF_NO_STDIO
#define TSF_IMPLEMENTATION
#include "libtinysoundfont/tsf.h"
/**************** utility routines **********************/
/* announce a fatal MIDI file format error */
void AudioGeneratorMIDI::midi_error(const char *msg, int curpos)
{
cb.st(curpos, msg);
#if 0
int ptr;
audioLogger->printf("---> MIDI file error at position %04X (%d): %s\n", (uint16_t) curpos, (uint16_t) curpos, msg);
/* print some bytes surrounding the error */
ptr = curpos - 16;
if (ptr < 0) ptr = 0;
buffer.seek( buffer.data, ptr );
for (int i = 0; i < 32; i++) {
char c;
buffer.read (buffer.data, &c, 1);
audioLogger->printf((ptr + i) == curpos ? " [%02X] " : "%02X ", (int) c & 0xff);
}
audioLogger->printf("\n");
#endif
running = false;
}
/* check that we have a specified number of bytes left in the buffer */
void AudioGeneratorMIDI::chk_bufdata (int ptr, unsigned long int len) {
if ((unsigned) (ptr + len) > buflen)
midi_error ("data missing", ptr);
}
/* fetch big-endian numbers */
uint16_t AudioGeneratorMIDI::rev_short (uint16_t val) {
return ((val & 0xff) << 8) | ((val >> 8) & 0xff);
}
uint32_t AudioGeneratorMIDI::rev_long (uint32_t val) {
return (((rev_short ((uint16_t) val) & 0xffff) << 16) |
(rev_short ((uint16_t) (val >> 16)) & 0xffff));
}
/************** process the MIDI file header *****************/
void AudioGeneratorMIDI::process_header (void) {
struct midi_header hdr;
unsigned int time_division;
chk_bufdata (hdrptr, sizeof (struct midi_header));
buffer.seek (buffer.data, hdrptr);
buffer.read (buffer.data, &hdr, sizeof (hdr));
if (!charcmp ((char *) hdr.MThd, "MThd"))
midi_error ("Missing 'MThd'", hdrptr);
num_tracks = rev_short (hdr.number_of_tracks);
time_division = rev_short (hdr.time_division);
if (time_division < 0x8000)
ticks_per_beat = time_division;
else
ticks_per_beat = ((time_division >> 8) & 0x7f) /* SMTE frames/sec */ *(time_division & 0xff); /* ticks/SMTE frame */
hdrptr += rev_long (hdr.header_size) + 8; /* point past header to track header, presumably. */
return;
}
/**************** Process a MIDI track header *******************/
void AudioGeneratorMIDI::start_track (int tracknum) {
struct track_header hdr;
unsigned long tracklen;
chk_bufdata (hdrptr, sizeof (struct track_header));
buffer.seek (buffer.data, hdrptr);
buffer.read (buffer.data, &hdr, sizeof (hdr));
if (!charcmp ((char *) (hdr.MTrk), "MTrk"))
midi_error ("Missing 'MTrk'", hdrptr);
tracklen = rev_long (hdr.track_size);
hdrptr += sizeof (struct track_header); /* point past header */
chk_bufdata (hdrptr, tracklen);
track[tracknum].trkptr = hdrptr;
hdrptr += tracklen; /* point to the start of the next track */
track[tracknum].trkend = hdrptr; /* the point past the end of the track */
}
unsigned char AudioGeneratorMIDI::buffer_byte (int offset) {
unsigned char c;
buffer.seek (buffer.data, offset);
buffer.read (buffer.data, &c, 1);
return c;
}
unsigned short AudioGeneratorMIDI::buffer_short (int offset) {
unsigned short s;
buffer.seek (buffer.data, offset);
buffer.read (buffer.data, &s, sizeof (short));
return s;
}
unsigned int AudioGeneratorMIDI::buffer_int32 (int offset) {
uint32_t i;
buffer.seek (buffer.data, offset);
buffer.read (buffer.data, &i, sizeof (i));
return i;
}
/* Get a MIDI-style variable-length integer */
unsigned long AudioGeneratorMIDI::get_varlen (int *ptr) {
/* Get a 1-4 byte variable-length value and adjust the pointer past it.
These are a succession of 7-bit values with a MSB bit of zero marking the end */
unsigned long val;
int i, byte;
val = 0;
for (i = 0; i < 4; ++i) {
byte = buffer_byte ((*ptr)++);
val = (val << 7) | (byte & 0x7f);
if (!(byte & 0x80))
return val;
}
return val;
}
/*************** Process the MIDI track data ***************************/
/* Skip in the track for the next "note on", "note off" or "set tempo" command,
then record that information in the track status block and return. */
void AudioGeneratorMIDI::find_note (int tracknum) {
unsigned long int delta_time;
int event, chan;
int note, velocity, controller, pressure, pitchbend, instrument;
int meta_cmd, meta_length;
unsigned long int sysex_length;
struct track_status *t;
const char *tag;
/* process events */
t = &track[tracknum]; /* our track status structure */
while (t->trkptr < t->trkend) {
delta_time = get_varlen (&t->trkptr);
t->time += delta_time;
if (buffer_byte (t->trkptr) < 0x80)
event = t->last_event; /* using "running status": same event as before */
else { /* otherwise get new "status" (event type) */
event = buffer_byte (t->trkptr++);
}
if (event == 0xff) { /* meta-event */
meta_cmd = buffer_byte (t->trkptr++);
meta_length = get_varlen(&t->trkptr);
switch (meta_cmd) {
case 0x00:
break;
case 0x01:
tag = "description";
goto show_text;
case 0x02:
tag = "copyright";
goto show_text;
case 0x03:
tag = "track name";
goto show_text;
case 0x04:
tag = "instrument name";
goto show_text;
case 0x05:
tag = "lyric";
goto show_text;
case 0x06:
tag = "marked point";
goto show_text;
case 0x07:
tag = "cue point";
show_text:
break;
case 0x20:
break;
case 0x2f:
break;
case 0x51: /* tempo: 3 byte big-endian integer! */
t->cmd = CMD_TEMPO;
t->tempo = rev_long (buffer_int32 (t->trkptr - 1)) & 0xffffffL;
t->trkptr += meta_length;
return;
case 0x54:
break;
case 0x58:
break;
case 0x59:
break;
case 0x7f:
tag = "sequencer data";
goto show_hex;
default: /* unknown meta command */
tag = "???";
show_hex:
break;
}
t->trkptr += meta_length;
}
else if (event < 0x80)
midi_error ("Unknown MIDI event type", t->trkptr);
else {
if (event < 0xf0)
t->last_event = event; // remember "running status" if not meta or sysex event
chan = event & 0xf;
t->chan = chan;
switch (event >> 4) {
case 0x8:
t->note = buffer_byte (t->trkptr++);
velocity = buffer_byte (t->trkptr++);
note_off:
t->cmd = CMD_STOPNOTE;
return; /* stop processing and return */
case 0x9:
t->note = buffer_byte (t->trkptr++);
velocity = buffer_byte (t->trkptr++);
if (velocity == 0) /* some scores use note-on with zero velocity for off! */
goto note_off;
t->velocity = velocity;
t->cmd = CMD_PLAYNOTE;
return; /* stop processing and return */
case 0xa:
note = buffer_byte (t->trkptr++);
velocity = buffer_byte (t->trkptr++);
break;
case 0xb:
controller = buffer_byte (t->trkptr++);
velocity = buffer_byte (t->trkptr++);
break;
case 0xc:
instrument = buffer_byte (t->trkptr++);
midi_chan_instrument[chan] = instrument; // record new instrument for this channel
break;
case 0xd:
pressure = buffer_byte (t->trkptr++);
break;
case 0xe:
pitchbend = buffer_byte (t->trkptr) | (buffer_byte (t->trkptr + 1) << 7);
t->trkptr += 2;
break;
case 0xf:
sysex_length = get_varlen (&t->trkptr);
t->trkptr += sysex_length;
break;
default:
midi_error ("Unknown MIDI command", t->trkptr);
}
}
}
t->cmd = CMD_TRACKDONE; /* no more notes to process */
++tracks_done;
// Remove unused warnings..maybe some day we'll look at these
(void)note;
(void)controller;
(void)pressure;
(void)pitchbend;
(void)tag;
}
// Open file, parse headers, get ready tio process MIDI
void AudioGeneratorMIDI::PrepareMIDI(AudioFileSource *src)
{
MakeStreamFromAFS(src, &afsMIDI);
tsf_stream_wrap_cached(&afsMIDI, 32, 64, &buffer);
buflen = buffer.size (buffer.data);
/* process the MIDI file header */
hdrptr = buffer.tell (buffer.data); /* pointer to file and track headers */
process_header ();
printf (" Processing %d tracks.\n", num_tracks);
if (num_tracks > MAX_TRACKS)
midi_error ("Too many tracks", buffer.tell (buffer.data));
/* initialize processing of all the tracks */
for (tracknum = 0; tracknum < num_tracks; ++tracknum) {
start_track (tracknum); /* process the track header */
find_note (tracknum); /* position to the first note on/off */
}
notes_skipped = 0;
tracknum = 0;
earliest_tracknum = 0;
earliest_time = 0;
}
// Parses the note on/offs until we are ready to render some more samples. Then return the
// total number of samples to render before we need to be called again
int AudioGeneratorMIDI::PlayMIDI()
{
/* Continue processing all tracks, in an order based on the simulated time.
This is not unlike multiway merging used for tape sorting algoritms in the 50's! */
do { /* while there are still track notes to process */
static struct track_status *trk;
static struct tonegen_status *tg;
static int tgnum;
static int count_tracks;
static unsigned long delta_time, delta_msec;
/* Find the track with the earliest event time,
and output a delay command if time has advanced.
A potential improvement: If there are multiple tracks with the same time,
first do the ones with STOPNOTE as the next command, if any. That would
help avoid running out of tone generators. In practice, though, most MIDI
files do all the STOPNOTEs first anyway, so it won't have much effect.
*/
earliest_time = 0x7fffffff;
/* Usually we start with the track after the one we did last time (tracknum),
so that if we run out of tone generators, we have been fair to all the tracks.
The alternate "strategy1" says we always start with track 0, which means
that we favor early tracks over later ones when there aren't enough tone generators.
*/
count_tracks = num_tracks;
do {
if (++tracknum >= num_tracks)
tracknum = 0;
trk = &track[tracknum];
if (trk->cmd != CMD_TRACKDONE && trk->time < earliest_time) {
earliest_time = trk->time;
earliest_tracknum = tracknum;
}
} while (--count_tracks);
tracknum = earliest_tracknum; /* the track we picked */
trk = &track[tracknum];
if (earliest_time < timenow)
midi_error ("INTERNAL: time went backwards", trk->trkptr);
/* If time has advanced, output a "delay" command */
delta_time = earliest_time - timenow;
if (delta_time) {
/* Convert ticks to milliseconds based on the current tempo */
unsigned long long temp;
temp = ((unsigned long long) delta_time * tempo) / ticks_per_beat;
delta_msec = temp / 1000; // get around LCC compiler bug
if (delta_msec > 0x7fff)
midi_error ("INTERNAL: time delta too big", trk->trkptr);
int samples = (((int) delta_msec) * freq) / 1000;
timenow = earliest_time;
return samples;
}
timenow = earliest_time;
/* If this track event is "set tempo", just change the global tempo.
That affects how we generate "delay" commands. */
if (trk->cmd == CMD_TEMPO) {
tempo = trk->tempo;
find_note (tracknum);
}
/* If this track event is "stop note", process it and all subsequent "stop notes" for this track
that are happening at the same time. Doing so frees up as many tone generators as possible. */
else if (trk->cmd == CMD_STOPNOTE)
do {
// stop a note
for (tgnum = 0; tgnum < num_tonegens; ++tgnum) { /* find which generator is playing it */
tg = &tonegen[tgnum];
if (tg->playing && tg->track == tracknum && tg->note == trk->note) {
tsf_note_off (g_tsf, tg->instrument, tg->note);
tg->playing = false;
trk->tonegens[tgnum] = false;
}
}
find_note (tracknum); // use up the note
} while (trk->cmd == CMD_STOPNOTE && trk->time == timenow);
/* If this track event is "start note", process only it.
Don't do more than one, so we allow other tracks their chance at grabbing tone generators. */
else if (trk->cmd == CMD_PLAYNOTE) {
bool foundgen = false;
/* if not, then try for any free tone generator */
if (!foundgen)
for (tgnum = 0; tgnum < num_tonegens; ++tgnum) {
tg = &tonegen[tgnum];
if (!tg->playing) {
foundgen = true;
break;
}
}
if (foundgen) {
if (tgnum + 1 > num_tonegens_used)
num_tonegens_used = tgnum + 1;
tg->playing = true;
tg->track = tracknum;
tg->note = trk->note;
trk->tonegens[tgnum] = true;
trk->preferred_tonegen = tgnum;
if (tg->instrument != midi_chan_instrument[trk->chan]) { /* new instrument for this generator */
tg->instrument = midi_chan_instrument[trk->chan];
}
tsf_note_on (g_tsf, tg->instrument, tg->note, trk->velocity / 127.0); // velocity = 0...127
} else {
++notes_skipped;
}
find_note (tracknum); // use up the note
}
}
while (tracks_done < num_tracks);
return -1; // EOF
}
void AudioGeneratorMIDI::StopMIDI()
{
buffer.close(buffer.data);
tsf_close(g_tsf);
printf (" %s %d tone generators were used.\n",
num_tonegens_used < num_tonegens ? "Only" : "All", num_tonegens_used);
if (notes_skipped)
printf
(" %d notes were skipped because there weren't enough tone generators.\n", notes_skipped);
printf (" Done.\n");
}
bool AudioGeneratorMIDI::begin(AudioFileSource *src, AudioOutput *out)
{
// Clear out status variables
for (int i=0; i<MAX_TONEGENS; i++) memset(&tonegen[i], 0, sizeof(struct tonegen_status));
for (int i=0; i<MAX_TRACKS; i++) memset(&track[i], 0, sizeof(struct track_status));
memset(midi_chan_instrument, 0, sizeof(midi_chan_instrument));
g_tsf = tsf_load(&afsSF2);
if (!g_tsf) return false;
tsf_set_output (g_tsf, TSF_MONO, freq, -10 /* dB gain -10 */ );
if (!out->SetRate( freq )) return false;
if (!out->SetBitsPerSample( 16 )) return false;
if (!out->SetChannels( 1 )) return false;
if (!out->begin()) return false;
output = out;
file = src;
running = true;
PrepareMIDI(src);
samplesToPlay = 0;
numSamplesRendered = 0;
sentSamplesRendered = 0;
sawEOF = false;
return running;
}
bool AudioGeneratorMIDI::loop()
{
static int c = 0;
if (!running) goto done; // Nothing to do here!
// First, try and push in the stored sample. If we can't, then punt and try later
if (!output->ConsumeSample(lastSample)) goto done; // Can't send, but no error detected
// Try and stuff the buffer one sample at a time
do {
c++;
if (c%44100 == 0) yield();
play:
if (sentSamplesRendered < numSamplesRendered) {
lastSample[AudioOutput::LEFTCHANNEL] = samplesRendered[sentSamplesRendered];
lastSample[AudioOutput::RIGHTCHANNEL] = samplesRendered[sentSamplesRendered];
sentSamplesRendered++;
} else if (samplesToPlay) {
numSamplesRendered = sizeof(samplesRendered)/sizeof(samplesRendered[0]);
if ((int)samplesToPlay < (int)(sizeof(samplesRendered)/sizeof(samplesRendered[0]))) numSamplesRendered = samplesToPlay;
tsf_render_short_fast(g_tsf, samplesRendered, numSamplesRendered, 0);
lastSample[AudioOutput::LEFTCHANNEL] = samplesRendered[0];
lastSample[AudioOutput::RIGHTCHANNEL] = samplesRendered[0];
sentSamplesRendered = 1;
samplesToPlay -= numSamplesRendered;
} else {
numSamplesRendered = 0;
sentSamplesRendered = 0;
if (sawEOF) {
running = false;
} else {
samplesToPlay = PlayMIDI();
if (samplesToPlay == -1) {
sawEOF = true;
samplesToPlay = freq / 2;
}
goto play;
}
}
} while (running && output->ConsumeSample(lastSample));
done:
file->loop();
output->loop();
return running;
}
bool AudioGeneratorMIDI::stop()
{
StopMIDI();
output->stop();
return true;
}
int AudioGeneratorMIDI::afs_read(void *data, void *ptr, unsigned int size)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->read(ptr, size);
}
int AudioGeneratorMIDI::afs_tell(void *data)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->getPos();
}
int AudioGeneratorMIDI::afs_skip(void *data, unsigned int count)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->seek(count, SEEK_CUR);
}
int AudioGeneratorMIDI::afs_seek(void *data, unsigned int pos)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->seek(pos, SEEK_SET);
}
int AudioGeneratorMIDI::afs_close(void *data)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->close();
}
int AudioGeneratorMIDI::afs_size(void *data)
{
AudioFileSource *s = reinterpret_cast<AudioFileSource *>(data);
return s->getSize();
}
void AudioGeneratorMIDI::MakeStreamFromAFS(AudioFileSource *src, tsf_stream *afs)
{
afs->data = reinterpret_cast<void*>(src);
afs->read = &afs_read;
afs->tell = &afs_tell;
afs->skip = &afs_skip;
afs->seek = &afs_seek;
afs->close = &afs_close;
afs->size = &afs_size;
}

View File

@@ -1,181 +0,0 @@
/*
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

View File

@@ -180,7 +180,7 @@ bool AudioOutputI2S::begin(bool txDAC)
if (output_mode == INTERNAL_DAC)
{
#if SOC_I2S_SUPPORTS_DAC
comm_fmt = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB;
comm_fmt = (i2s_comm_format_t)I2S_COMM_FORMAT_STAND_I2S;
#endif
}