From ed724fecb15d90d8075ed1edb963f455cb91b0a1 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Sun, 4 Aug 2019 16:58:20 -0400 Subject: Midiplay plugin ehancements - Improved robustness - Improved sound quality - Use mixer and DSP Patch by Igor Poretsky Change-Id: I6fa617158cbaa53ae842295cdbdbe3a478e49ded --- apps/plugins/midi/guspat.c | 60 ++++++++++++++- apps/plugins/midi/midifile.c | 23 +++++- apps/plugins/midi/midiplay.c | 174 ++++++++++++++++++++++++++++--------------- apps/plugins/midi/midiutil.c | 38 +++++----- apps/plugins/midi/midiutil.h | 5 +- apps/plugins/midi/synth.c | 41 ++++------ apps/plugins/midi/synth.h | 2 +- 7 files changed, 230 insertions(+), 113 deletions(-) (limited to 'apps/plugins') diff --git a/apps/plugins/midi/guspat.c b/apps/plugins/midi/guspat.c index f650555520..d94fbce021 100644 --- a/apps/plugins/midi/guspat.c +++ b/apps/plugins/midi/guspat.c @@ -66,6 +66,8 @@ static struct GWaveform * loadWaveform(int file) rb->memset(wav, 0, sizeof(struct GWaveform)); wav->name=readData(file, 7); + if (!wav->name) + return NULL; /* printf("\nWAVE NAME = [%s]", wav->name); */ wav->fractions=readChar(file); wav->wavSize=readDWord(file); @@ -81,7 +83,11 @@ static struct GWaveform * loadWaveform(int file) wav->balance=readChar(file); wav->envRate=readData(file, 6); + if (!wav->envRate) + return NULL; wav->envOffset=readData(file, 6); + if (!wav->envOffset) + return NULL; wav->tremSweep=readChar(file); wav->tremRate=readChar(file); @@ -95,7 +101,11 @@ static struct GWaveform * loadWaveform(int file) wav->scaleFactor=readWord(file); /* printf("\nScaleFreq = %d ScaleFactor = %d RootFreq = %d", wav->scaleFreq, wav->scaleFactor, wav->rootFreq); */ wav->res=readData(file, 36); + if (!wav->res) + return NULL; wav->data=readData(file, wav->wavSize); + if (!wav->data) + return NULL; wav->numSamples = wav->wavSize / 2; wav->startLoop = wav->startLoop >> 1; @@ -166,7 +176,10 @@ static int selectWaveform(struct GPatch * pat, int midiNote) struct GPatch * gusload(char * filename) { struct GPatch * gp = (struct GPatch *)malloc(sizeof(struct GPatch)); - rb->memset(gp, 0, sizeof(struct GPatch)); + + if (gp) + rb->memset(gp, 0, sizeof(struct GPatch)); + else return NULL; int file = rb->open(filename, O_RDONLY); @@ -179,8 +192,23 @@ struct GPatch * gusload(char * filename) } gp->header=readData(file, 12); + if (!gp->header) + { + rb->close(file); + return NULL; + } gp->gravisid=readData(file, 10); + if (!gp->gravisid) + { + rb->close(file); + return NULL; + } gp->desc=readData(file, 60); + if (!gp->desc) + { + rb->close(file); + return NULL; + } gp->inst=readChar(file); gp->voc=readChar(file); gp->chan=readChar(file); @@ -188,28 +216,52 @@ struct GPatch * gusload(char * filename) gp->vol=readWord(file); gp->datSize=readDWord(file); gp->res=readData(file, 36); + if (!gp->res) + { + rb->close(file); + return NULL; + } gp->instrID=readWord(file); gp->instrName=readData(file,16); + if (!gp->instrName) + { + rb->close(file); + return NULL; + } gp->instrSize=readDWord(file); gp->layers=readChar(file); gp->instrRes=readData(file,40); - + if (!gp->instrRes) + { + rb->close(file); + return NULL; + } gp->layerDup=readChar(file); gp->layerID=readChar(file); gp->layerSize=readDWord(file); gp->numWaves=readChar(file); gp->layerRes=readData(file,40); - + if (!gp->layerRes) + { + rb->close(file); + return NULL; + } /* printf("\nFILE: %s", filename); */ /* printf("\nlayerSamples=%d", gp->numWaves); */ int a=0; for(a=0; anumWaves; a++) + { gp->waveforms[a] = loadWaveform(file); - + if (!gp->waveforms[a]) + { + rb->close(file); + return NULL; + } + } /* printf("\nPrecomputing note table"); */ diff --git a/apps/plugins/midi/midifile.c b/apps/plugins/midi/midifile.c index 86544fd944..2340c6d14f 100644 --- a/apps/plugins/midi/midifile.c +++ b/apps/plugins/midi/midifile.c @@ -106,6 +106,11 @@ struct MIDIfile * loadFile(const char * filename) if(id == ID_MTRK) { mfload->tracks[track] = readTrack(file); + if (!mfload->tracks[track]) + { + rb->close(file); + return NULL; + } track++; } else { @@ -125,7 +130,7 @@ struct MIDIfile * loadFile(const char * filename) * and then track 2 starts loading */ int rStatus = 0; -/* Returns 0 if done, 1 if keep going */ +/* Returns 0 if done, 1 if keep going and -1 in case of error */ static int readEvent(int file, void * dest) { struct Event dummy; @@ -152,6 +157,8 @@ static int readEvent(int file, void * dest) { /* Null-terminate for text events */ ev->evData = malloc(ev->len+1); /* Extra byte for the null termination */ + if (!ev->evData) + return -1; rb->read(file, ev->evData, ev->len); ev->evData[ev->len] = 0; @@ -272,27 +279,35 @@ struct Track * readTrack(int file) int pos = rb->lseek(file, 0, SEEK_CUR); - while(readEvent(file, NULL)) /* Memory saving technique */ + int evstat; + + while ((evstat = readEvent(file, NULL)) > 0) /* Memory saving technique */ numEvents++; /* Attempt to read in events, count how many */ /* THEN allocate memory and read them in */ + if (evstat < 0) + return NULL; rb->lseek(file, pos, SEEK_SET); int trackSize = (numEvents+1) * sizeof(struct Event); void * dataPtr = malloc(trackSize); + if (!dataPtr) + return NULL; trk->dataBlock = dataPtr; numEvents=0; - while(readEvent(file, dataPtr)) + while ((evstat = readEvent(file, dataPtr)) > 0) { if(trackSize < dataPtr-trk->dataBlock) { midi_debug("Track parser memory out of bounds"); - exit(1); + return NULL; } dataPtr+=sizeof(struct Event); numEvents++; } + if (evstat < 0) + return NULL; trk->numEvents = numEvents; return trk; diff --git a/apps/plugins/midi/midiplay.c b/apps/plugins/midi/midiplay.c index bef13b649d..8e867e5173 100644 --- a/apps/plugins/midi/midiplay.c +++ b/apps/plugins/midi/midiplay.c @@ -348,6 +348,10 @@ #define SYNC #endif +#ifndef ALIGNED_ATTR +#define ALIGNED_ATTR(x) __attribute__((aligned(x))) +#endif + struct MIDIfile * mf IBSS_ATTR; int number_of_samples IBSS_ATTR; /* the number of samples in the current tick */ @@ -355,52 +359,79 @@ int playing_time IBSS_ATTR; /* How many seconds into the file have we been play int samples_this_second IBSS_ATTR; /* How many samples produced during this second so far? */ long bpm IBSS_ATTR; -int32_t gmbuf[BUF_SIZE*NBUF]; -static unsigned int samples_in_buf; +#ifndef SYNC +/* Small silence clip. ~5.80ms @ 44.1kHz */ +static int32_t silence[256] ALIGNED_ATTR(4) = { 0 }; + +static int32_t gmbuf[BUF_SIZE * NBUF] ALIGNED_ATTR(4); + +static volatile bool swap = false; +static volatile bool lastswap = true; +#else +static int32_t gmbuf[BUF_SIZE] ALIGNED_ATTR(4); +#endif + +static volatile size_t samples_in_buf; -bool midi_end = false; -bool quit = false; -bool swap = false; -bool lastswap = true; +static volatile bool midi_end = false; +static volatile bool quit = false; + +static int32_t samp_buf[MAX_SAMPLES * 2] IBSS_ATTR; + +static struct dsp_config *dsp; +static struct dsp_buffer src; +static struct dsp_buffer dst; static inline void synthbuf(void) { int32_t *outptr; - int i = BUF_SIZE; + int available = BUF_SIZE; -#if defined(HAVE_ADJUSTABLE_CPU_FREQ) - rb->cpu_boost(true); -#endif #ifndef SYNC if (lastswap == swap) return; - lastswap = swap; outptr = (swap ? gmbuf : gmbuf+BUF_SIZE); #else outptr = gmbuf; #endif - if (midi_end) { - samples_in_buf = 0; - return; - } +#if defined(HAVE_ADJUSTABLE_CPU_FREQ) + rb->cpu_boost(true); +#endif /* synth samples for as many whole ticks as we can fit in the buffer */ - for (; i >= number_of_samples; i -= number_of_samples) + while (available > 0) { - synthSamples((int32_t*)outptr, number_of_samples); - outptr += number_of_samples; -#ifndef SYNC - /* synthbuf is called in interrupt context is SYNC is defined so it cannot yield - that bug causing the sim to crach when not using SYNC should really be fixed */ - rb->yield(); -#endif - if (tick() == 0) - midi_end = true; /* no more midi data to play */ + if ((dst.remcount <= 0) && !midi_end) + { + int nsamples = synthSamples(samp_buf, number_of_samples); + if (nsamples < number_of_samples) + number_of_samples -= nsamples; + else if (!tick()) + midi_end = true; /* no more midi data to play */ + src.remcount = nsamples; + src.pin[0] = &samp_buf[0]; + src.pin[1] = &samp_buf[1]; + src.proc_mask = 0; + } + dst.remcount = 0; + dst.bufcount = available; + dst.p16out = (int16_t *)outptr; + rb->dsp_process(dsp, &src, &dst); + if (dst.remcount > 0) + { + outptr += dst.remcount; + available -= dst.remcount; + } + else if (midi_end) + break; } /* how many samples did we write to the buffer? */ - samples_in_buf = BUF_SIZE-i; + samples_in_buf = BUF_SIZE - available; +#ifndef SYNC + lastswap = swap; +#endif #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif @@ -409,26 +440,27 @@ static inline void synthbuf(void) static void get_more(const void** start, size_t* size) { #ifndef SYNC - if(lastswap != swap) + swap = !swap; + if(lastswap == swap) { - midi_debug("Buffer miss!"); /* Comment out the midi_debug to make missses less noticable. */ + *start = silence; + *size = sizeof(silence); + swap = !swap; + return; } - + else if (samples_in_buf) + *start = swap ? (gmbuf + BUF_SIZE) : gmbuf; #else synthbuf(); /* For some reason midiplayer crashes when an update is forced */ + if (samples_in_buf) + *start = gmbuf; #endif - - *size = samples_in_buf*sizeof(int32_t); -#ifndef SYNC - *start = swap ? gmbuf : gmbuf + BUF_SIZE; - swap = !swap; -#else - *start = gmbuf; -#endif - if (samples_in_buf==0) { + else + { *start = NULL; quit = true; /* this was the last buffer to play */ } + *size = samples_in_buf*sizeof(int32_t); } static int midimain(const void * filename) @@ -460,13 +492,26 @@ static int midimain(const void * filename) return -1; } + rb->talk_force_shutup(); rb->pcm_play_stop(); #if INPUT_SRC_CAPS != 0 /* Select playback */ rb->audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); #endif - rb->pcm_set_frequency(SAMPLE_RATE); /* 44100 22050 11025 */ + + dst.remcount = 0; + dsp = rb->dsp_get_config(CODEC_IDX_AUDIO); + rb->dsp_configure(dsp, DSP_RESET, 0); + rb->dsp_configure(dsp, DSP_FLUSH, 0); + rb->dsp_configure(dsp, DSP_SET_OUT_FREQUENCY, rb->mixer_get_frequency()); +#ifdef HAVE_PITCHCONTROL + rb->sound_set_pitch(PITCH_SPEED_100); + rb->dsp_set_timestretch(PITCH_SPEED_100); +#endif + rb->dsp_configure(dsp, DSP_SET_SAMPLE_DEPTH, 22); + rb->dsp_configure(dsp, DSP_SET_FREQUENCY, SAMPLE_RATE); /* 44100 22050 11025 */ + rb->dsp_configure(dsp, DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); /* * tick() will do one MIDI clock tick. Then, there's a loop here that @@ -501,8 +546,12 @@ static int midimain(const void * filename) playing_time = 0; samples_this_second = 0; +#ifndef SYNC synthbuf(); - rb->pcm_play_data(&get_more, NULL, NULL, 0); +#endif + + rb->pcmbuf_fade(false, true); + rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0); while (!quit) { @@ -547,7 +596,9 @@ static int midimain(const void * filename) { /* Rewinding is tricky. Basically start the file over */ /* but run through the tracks without the synth running */ - rb->pcm_play_stop(); + rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); + rb->dsp_configure(dsp, DSP_FLUSH, 0); + dst.remcount = 0; #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(true); #endif @@ -555,39 +606,45 @@ static int midimain(const void * filename) #if defined(HAVE_ADJUSTABLE_CPU_FREQ) rb->cpu_boost(false); #endif +#ifndef SYNC lastswap = !swap; synthbuf(); +#endif midi_debug("Rewind to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) - rb->pcm_play_data(&get_more, NULL, NULL, 0); + rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0); break; } case MIDI_FFWD: { - rb->pcm_play_stop(); + rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); + rb->dsp_configure(dsp, DSP_FLUSH, 0); + dst.remcount = 0; +#if defined(HAVE_ADJUSTABLE_CPU_FREQ) + rb->cpu_boost(true); +#endif seekForward(5); +#if defined(HAVE_ADJUSTABLE_CPU_FREQ) + rb->cpu_boost(false); +#endif +#ifndef SYNC lastswap = !swap; synthbuf(); +#endif midi_debug("Skip to %d:%02d\n", playing_time/60, playing_time%60); if (is_playing) - rb->pcm_play_data(&get_more, NULL, NULL, 0); + rb->mixer_channel_play_data(PCM_MIXER_CHAN_PLAYBACK, get_more, NULL, 0); break; } case MIDI_PLAYPAUSE: { - if (is_playing) - { - midi_debug("Paused at %d:%02d\n", playing_time/60, playing_time%60); - is_playing = false; - rb->pcm_play_stop(); - } else - { - midi_debug("Playing from %d:%02d\n", playing_time/60, playing_time%60); - is_playing = true; - rb->pcm_play_data(&get_more, NULL, NULL, 0); - } + is_playing = !is_playing; + midi_debug("%s %d:%02d\n", + is_playing ? "Playing from" : "Paused at", + playing_time/60, playing_time%60); + rb->mixer_channel_play_pause(PCM_MIXER_CHAN_PLAYBACK, is_playing); break; } @@ -598,6 +655,10 @@ static int midimain(const void * filename) quit = true; } } + + rb->pcmbuf_fade(false, false); + rb->mixer_channel_stop(PCM_MIXER_CHAN_PLAYBACK); + return 0; } @@ -625,9 +686,6 @@ enum plugin_status plugin_start(const void* parameter) rb->profstop(); #endif - rb->pcm_play_stop(); - rb->pcm_set_frequency(HW_SAMPR_DEFAULT); - rb->splash(HZ, "FINISHED PLAYING"); if (retval == -1) diff --git a/apps/plugins/midi/midiutil.c b/apps/plugins/midi/midiutil.c index bfac1d5244..b9e57a405b 100644 --- a/apps/plugins/midi/midiutil.c +++ b/apps/plugins/midi/midiutil.c @@ -39,37 +39,40 @@ struct SynthObject voices[MAX_VOICES] IBSS_ATTR; static void *alloc(int size) { - static char *offset = NULL; - static size_t totalSize = 0; + static char *offset[2] = {NULL}; + static size_t totalSize[2] = {0}; char *ret; + int n; int remainder = size % 4; size = size + 4-remainder; - if (offset == NULL) + if (offset[0] == NULL) { - offset = rb->plugin_get_audio_buffer(&totalSize); + offset[0] = rb->plugin_get_buffer(&totalSize[0]); } - if (size + 4 > (int)totalSize) + if (offset[1] == NULL) { - midi_debug("Out of Memory"); - midi_debug("MALLOC BARF"); - midi_debug("MALLOC BARF"); - midi_debug("MALLOC BARF"); - midi_debug("MALLOC BARF"); - midi_debug("MALLOC BARF"); - /* We've made our point. */ + offset[1] = rb->plugin_get_audio_buffer(&totalSize[1]); + } + + n = (totalSize[0] > totalSize[1]) ? 1 : 0; + if (size + 4 > (int)totalSize[n]) + n ^= 1; + if (size + 4 > (int)totalSize[n]) + { + midi_debug("Out of Memory"); return NULL; } - ret = offset + 4; - *((unsigned int *)offset) = size; + ret = offset[n] + 4; + *((unsigned int *)offset[n]) = size; - offset += size + 4; - totalSize -= size + 4; + offset[n] += size + 4; + totalSize[n] -= size + 4; return ret; } @@ -117,7 +120,8 @@ unsigned char readChar(int file) void * readData(int file, int len) { void * dat = malloc(len); - rb->read(file, dat, len); + if (dat) + rb->read(file, dat, len); return dat; } diff --git a/apps/plugins/midi/midiutil.h b/apps/plugins/midi/midiutil.h index cf3666c97f..72bff11b3f 100644 --- a/apps/plugins/midi/midiutil.h +++ b/apps/plugins/midi/midiutil.h @@ -25,14 +25,11 @@ #define BUF_SIZE 16384 /* 64 kB output buffers */ #define NBUF 2 +#define MAX_SAMPLES 512 #ifndef SIMULATOR -#if (HW_SAMPR_CAPS & SAMPR_CAP_22) /* use 22050Hz if we can */ -#define SAMPLE_RATE SAMPR_22 /* 22050 */ -#else #define SAMPLE_RATE SAMPR_44 /* 44100 */ -#endif /* Some of the pp based targets can't handle too many voices mainly because they have to use 44100Hz sample rate, this could be diff --git a/apps/plugins/midi/synth.c b/apps/plugins/midi/synth.c index e95dd7cec9..f199d544e5 100644 --- a/apps/plugins/midi/synth.c +++ b/apps/plugins/midi/synth.c @@ -300,7 +300,8 @@ static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned i s1 = so->decay; s2 = s1 * pan; s1 = (s1 << 7) -s2; - *(out++) += ((s1 << 9) & 0xFFFF0000) | ((s2 >> 7) &0xFFFF); + *(out++) += s1; + *(out++) += s2; continue; } } else /* OK to advance voice */ @@ -431,7 +432,8 @@ static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned i s2 = s1 * pan; s1 = (s1 << 7) - s2; - *(out++) += ((s1 << 9) & 0xFFFF0000) | ((s2 >> 7) &0xFFFF); + *(out++) += s1; + *(out++) += s2; } /* store these again */ @@ -441,40 +443,29 @@ static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned i return; } -/* buffer to hold all the samples for the current tick, this is a hack - neccesary for coldfire targets as pcm_play_data uses the dma which cannot - access iram */ -int32_t samp_buf[512] IBSS_ATTR; - /* synth num_samples samples and write them to the */ /* buffer pointed to by buf_ptr */ -void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR; -void synthSamples(int32_t *buf_ptr, unsigned int num_samples) +size_t synthSamples(int32_t *buf_ptr, size_t num_samples) ICODE_ATTR; +size_t synthSamples(int32_t *buf_ptr, size_t num_samples) { - if (UNLIKELY(num_samples > 512)) - DEBUGF("num_samples is too big!\n"); - else - { - int i; - struct SynthObject *voicept; + unsigned int i; + struct SynthObject *voicept; + size_t nsamples = MIN(num_samples, MAX_SAMPLES); - rb->memset(samp_buf, 0, num_samples*4); + rb->memset(buf_ptr, 0, nsamples * 2 * sizeof(int32_t)); - for(i=0; i < MAX_VOICES; i++) + for(i=0; i < MAX_VOICES; i++) + { + voicept=&voices[i]; + if(voicept->isUsed) { - voicept=&voices[i]; - if(voicept->isUsed) - { - synthVoice(voicept, samp_buf, num_samples); - } + synthVoice(voicept, buf_ptr, nsamples); } - - rb->memcpy(buf_ptr, samp_buf, num_samples*4); } /* TODO: Automatic Gain Control, anyone? */ /* Or, should this be implemented on the DSP's output volume instead? */ - return; /* No more ghetto lowpass filter. Linear interpolation works well. */ + return nsamples; /* No more ghetto lowpass filter. Linear interpolation works well. */ } diff --git a/apps/plugins/midi/synth.h b/apps/plugins/midi/synth.h index 4cc6a545f0..b44da21fd2 100644 --- a/apps/plugins/midi/synth.h +++ b/apps/plugins/midi/synth.h @@ -20,7 +20,7 @@ ****************************************************************************/ int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig); void setPoint(struct SynthObject * so, int pt); -void synthSamples(int32_t *buf_ptr, unsigned int num_samples); +size_t synthSamples(int32_t *buf_ptr, size_t num_samples); void resetControllers(void); -- cgit v1.2.3