mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-12-01 08:31:14 +00:00
fe0a6b00ce
If ZMusic is to act like an external library it may not call delete on external objects because there is no guarantee that they use the same allocator. Deletion must be done as a virtual function to ensure that the correct operator delete gets called, which, unlike the actual destructor is not virtual itself.
728 lines
18 KiB
C++
728 lines
18 KiB
C++
/*
|
|
|
|
TiMidity -- Experimental MIDI to WAVE converter
|
|
Copyright (C) 1995 Tuukka Toivonen <toivonen@clinet.fi>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
instrum.c
|
|
|
|
Code to load and unload GUS-compatible instrument patches.
|
|
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
|
|
#include "timidity.h"
|
|
#include "gf1patch.h"
|
|
#include "t_swap.h"
|
|
|
|
#include "common.h"
|
|
#include "instrum.h"
|
|
#include "playmidi.h"
|
|
|
|
namespace Timidity
|
|
{
|
|
|
|
Instrument *load_instrument_dls(Renderer *song, int drum, int bank, int instrument);
|
|
|
|
Instrument::Instrument()
|
|
: samples(0), sample(NULL)
|
|
{
|
|
}
|
|
|
|
Instrument::~Instrument()
|
|
{
|
|
Sample *sp;
|
|
int i;
|
|
|
|
for (i = samples, sp = &(sample[0]); i != 0; i--, sp++)
|
|
{
|
|
if (sp->type == INST_GUS && sp->data != NULL)
|
|
{
|
|
free(sp->data);
|
|
}
|
|
}
|
|
free(sample);
|
|
}
|
|
|
|
ToneBank::ToneBank()
|
|
{
|
|
tone = new ToneBankElement[128];;
|
|
for (int i = 0; i < MAXPROG; ++i)
|
|
{
|
|
instrument[i] = 0;
|
|
}
|
|
}
|
|
|
|
ToneBank::~ToneBank()
|
|
{
|
|
delete[] tone;
|
|
for (int i = 0; i < MAXPROG; i++)
|
|
{
|
|
if (instrument[i] != NULL && instrument[i] != MAGIC_LOAD_INSTRUMENT)
|
|
{
|
|
delete instrument[i];
|
|
instrument[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
int Renderer::convert_tremolo_sweep(uint8_t sweep)
|
|
{
|
|
if (sweep == 0)
|
|
return 0;
|
|
|
|
return
|
|
int(((control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (rate * sweep));
|
|
}
|
|
|
|
int Renderer::convert_vibrato_sweep(uint8_t sweep, int vib_control_ratio)
|
|
{
|
|
if (sweep == 0)
|
|
return 0;
|
|
|
|
return
|
|
(int)(FSCALE((double)(vib_control_ratio)* SWEEP_TUNING, SWEEP_SHIFT) / (rate * sweep));
|
|
|
|
/* this was overflowing with seashore.pat
|
|
|
|
((vib_control_ratio * SWEEP_TUNING) << SWEEP_SHIFT) / (song->rate * sweep);
|
|
*/
|
|
}
|
|
|
|
int Renderer::convert_tremolo_rate(uint8_t rate)
|
|
{
|
|
return
|
|
int(((control_ratio * rate) << RATE_SHIFT) / (TREMOLO_RATE_TUNING * rate));
|
|
}
|
|
|
|
int Renderer::convert_vibrato_rate(uint8_t rate)
|
|
{
|
|
/* Return a suitable vibrato_control_ratio value */
|
|
return
|
|
int((VIBRATO_RATE_TUNING * rate) / (rate * 2 * VIBRATO_SAMPLE_INCREMENTS));
|
|
}
|
|
|
|
static void reverse_data(sample_t *sp, int ls, int le)
|
|
{
|
|
sample_t s, *ep = sp + le;
|
|
sp += ls;
|
|
le -= ls;
|
|
le /= 2;
|
|
while (le--)
|
|
{
|
|
s = *sp;
|
|
*sp++ = *ep;
|
|
*ep-- = s;
|
|
}
|
|
}
|
|
|
|
/*
|
|
If panning or note_to_use != -1, it will be used for all samples,
|
|
instead of the sample-specific values in the instrument file.
|
|
|
|
For note_to_use, any value <0 or >127 will be forced to 0.
|
|
|
|
For other parameters, 1 means yes, 0 means no, other values are
|
|
undefined.
|
|
|
|
TODO: do reverse loops right */
|
|
Instrument *Renderer::load_instrument(const char *name, int percussion,
|
|
int panning, int note_to_use,
|
|
int strip_loop, int strip_envelope,
|
|
int strip_tail)
|
|
{
|
|
Instrument *ip;
|
|
Sample *sp;
|
|
GF1PatchHeader header;
|
|
GF1InstrumentData idata;
|
|
GF1LayerData layer_data;
|
|
GF1PatchData patch_data;
|
|
int i, j;
|
|
bool noluck = false;
|
|
auto reader = instruments->sfreader;
|
|
|
|
if (!name || reader == nullptr) return nullptr;
|
|
|
|
/* Open patch file */
|
|
auto fp = reader->open_file(name);
|
|
if (!fp)
|
|
{
|
|
/* Try with various extensions */
|
|
std::string tmp = name;
|
|
tmp += ".pat";
|
|
fp = reader->open_file(tmp.c_str());
|
|
if (!fp)
|
|
{
|
|
#ifndef _WIN32 // Windows isn't case-sensitive.
|
|
std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c){ return toupper(c); } );
|
|
fp = reader->open_file(tmp.c_str());
|
|
if (!fp)
|
|
#endif
|
|
{
|
|
noluck = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (noluck)
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_DEBUG, "Instrument `%s' can't be found.\n", name);
|
|
return 0;
|
|
}
|
|
|
|
printMessage(CMSG_INFO, VERB_NOISY, "Loading instrument %s\n", name);
|
|
|
|
/* Read some headers and do cursory sanity checks. */
|
|
|
|
if (sizeof(header) != fp->read(&header, sizeof(header)))
|
|
{
|
|
failread:
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Error reading instrument.\n", name);
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
if (strncmp(header.Header, GF1_HEADER_TEXT, HEADER_SIZE - 4) != 0)
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Not an instrument.\n", name);
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
if (strcmp(header.Header + 8, "110") < 0)
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "%s: Is an old and unsupported patch version.\n", name);
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
if (sizeof(idata) != fp->read(&idata, sizeof(idata)))
|
|
{
|
|
goto failread;
|
|
}
|
|
|
|
header.WaveForms = LittleShort(header.WaveForms);
|
|
header.MasterVolume = LittleShort(header.MasterVolume);
|
|
header.DataSize = LittleLong(header.DataSize);
|
|
idata.Instrument = LittleShort(idata.Instrument);
|
|
|
|
if (header.Instruments != 1 && header.Instruments != 0) /* instruments. To some patch makers, 0 means 1 */
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "Can't handle patches with %d instruments.\n", header.Instruments);
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
|
|
if (idata.Layers != 1 && idata.Layers != 0) /* layers. What's a layer? */
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "Can't handle instruments with %d layers.\n", idata.Layers);
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
|
|
if (sizeof(layer_data) != fp->read(&layer_data, sizeof(layer_data)))
|
|
{
|
|
goto failread;
|
|
}
|
|
|
|
if (layer_data.Samples == 0)
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "Instrument has 0 samples.\n");
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
|
|
ip = new Instrument;
|
|
ip->samples = layer_data.Samples;
|
|
ip->sample = (Sample *)safe_malloc(sizeof(Sample) * layer_data.Samples);
|
|
memset(ip->sample, 0, sizeof(Sample) * layer_data.Samples);
|
|
for (i = 0; i < layer_data.Samples; ++i)
|
|
{
|
|
if (sizeof(patch_data) != fp->read(&patch_data, sizeof(patch_data)))
|
|
{
|
|
fail:
|
|
printMessage(CMSG_ERROR, VERB_NORMAL, "Error reading sample %d.\n", i);
|
|
delete ip;
|
|
fp->close();
|
|
return 0;
|
|
}
|
|
|
|
sp = &(ip->sample[i]);
|
|
|
|
sp->data_length = LittleLong(patch_data.WaveSize);
|
|
sp->loop_start = LittleLong(patch_data.StartLoop);
|
|
sp->loop_end = LittleLong(patch_data.EndLoop);
|
|
sp->sample_rate = LittleShort(patch_data.SampleRate);
|
|
sp->low_freq = float(LittleLong(patch_data.LowFrequency));
|
|
sp->high_freq = float(LittleLong(patch_data.HighFrequency)) + 0.9999f;
|
|
sp->root_freq = float(LittleLong(patch_data.RootFrequency));
|
|
sp->high_vel = 127;
|
|
sp->velocity = -1;
|
|
sp->type = INST_GUS;
|
|
|
|
// Expand to SF2 range.
|
|
if (panning == -1)
|
|
{
|
|
sp->panning = (patch_data.Balance & 0x0F) * 1000 / 15 - 500;
|
|
}
|
|
else
|
|
{
|
|
sp->panning = (panning & 0x7f) * 1000 / 127 - 500;
|
|
}
|
|
compute_pan((sp->panning + 500) / 1000.0, INST_GUS, sp->left_offset, sp->right_offset);
|
|
|
|
/* tremolo */
|
|
if (patch_data.TremoloRate == 0 || patch_data.TremoloDepth == 0)
|
|
{
|
|
sp->tremolo_sweep_increment = 0;
|
|
sp->tremolo_phase_increment = 0;
|
|
sp->tremolo_depth = 0;
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " * no tremolo\n");
|
|
}
|
|
else
|
|
{
|
|
sp->tremolo_sweep_increment = convert_tremolo_sweep(patch_data.TremoloSweep);
|
|
sp->tremolo_phase_increment = convert_tremolo_rate(patch_data.TremoloRate);
|
|
sp->tremolo_depth = patch_data.TremoloDepth;
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " * tremolo: sweep %d, phase %d, depth %d\n",
|
|
sp->tremolo_sweep_increment, sp->tremolo_phase_increment, sp->tremolo_depth);
|
|
}
|
|
|
|
/* vibrato */
|
|
if (patch_data.VibratoRate == 0 || patch_data.VibratoDepth == 0)
|
|
{
|
|
sp->vibrato_sweep_increment = 0;
|
|
sp->vibrato_control_ratio = 0;
|
|
sp->vibrato_depth = 0;
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " * no vibrato\n");
|
|
}
|
|
else
|
|
{
|
|
sp->vibrato_control_ratio = convert_vibrato_rate(patch_data.VibratoRate);
|
|
sp->vibrato_sweep_increment = convert_vibrato_sweep(patch_data.VibratoSweep, sp->vibrato_control_ratio);
|
|
sp->vibrato_depth = patch_data.VibratoDepth;
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " * vibrato: sweep %d, ctl %d, depth %d\n",
|
|
sp->vibrato_sweep_increment, sp->vibrato_control_ratio, sp->vibrato_depth);
|
|
}
|
|
|
|
sp->modes = patch_data.Modes;
|
|
|
|
/* Mark this as a fixed-pitch instrument if such a deed is desired. */
|
|
if (note_to_use != -1)
|
|
{
|
|
sp->scale_note = note_to_use;
|
|
sp->scale_factor = 0;
|
|
}
|
|
else
|
|
{
|
|
sp->scale_note = LittleShort(patch_data.ScaleFrequency);
|
|
sp->scale_factor = LittleShort(patch_data.ScaleFactor);
|
|
if (sp->scale_factor <= 2)
|
|
{
|
|
sp->scale_factor *= 1024;
|
|
}
|
|
else if (sp->scale_factor > 2048)
|
|
{
|
|
sp->scale_factor = 1024;
|
|
}
|
|
if (sp->scale_factor != 1024)
|
|
{
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " * Scale: note %d, factor %d\n",
|
|
sp->scale_note, sp->scale_factor);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
/* seashore.pat in the Midia patch set has no Sustain. I don't
|
|
understand why, and fixing it by adding the Sustain flag to
|
|
all looped patches probably breaks something else. We do it
|
|
anyway. */
|
|
|
|
if (sp->modes & PATCH_LOOPEN)
|
|
{
|
|
sp->modes |= PATCH_SUSTAIN;
|
|
}
|
|
#endif
|
|
/* [RH] Alas, eawpats has percussion instruments with bad envelopes. :(
|
|
* (See cymchina.pat for one example of this sadness.)
|
|
* Do this logic for instruments without a description, only. Hopefully that
|
|
* catches all the patches that need it without including any extra.
|
|
*/
|
|
for (j = 0; j < DESC_SIZE; ++j)
|
|
{
|
|
if (header.Description[j] != 0)
|
|
break;
|
|
}
|
|
/* Strip any loops and envelopes we're permitted to */
|
|
/* [RH] (But PATCH_BACKWARD isn't a loop flag at all!) */
|
|
if ((strip_loop == 1) &&
|
|
(sp->modes & (PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD)))
|
|
{
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " - Removing loop and/or sustain\n");
|
|
if (j == DESC_SIZE)
|
|
{
|
|
sp->modes &= ~(PATCH_SUSTAIN | PATCH_LOOPEN | PATCH_BIDIR | PATCH_BACKWARD);
|
|
}
|
|
}
|
|
|
|
if (strip_envelope == 1)
|
|
{
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " - Removing envelope\n");
|
|
/* [RH] The envelope isn't really removed, but this is the way the standard
|
|
* Gravis patches get that effect: All rates at maximum, and all offsets at
|
|
* a constant level.
|
|
*/
|
|
if (j == DESC_SIZE)
|
|
{
|
|
int k;
|
|
for (k = 1; k < ENVELOPES; ++k)
|
|
{ /* Find highest offset. */
|
|
if (patch_data.EnvelopeOffset[k] > patch_data.EnvelopeOffset[0])
|
|
{
|
|
patch_data.EnvelopeOffset[0] = patch_data.EnvelopeOffset[k];
|
|
}
|
|
}
|
|
for (k = 0; k < ENVELOPES; ++k)
|
|
{
|
|
patch_data.EnvelopeRate[k] = 63;
|
|
patch_data.EnvelopeOffset[k] = patch_data.EnvelopeOffset[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < 6; j++)
|
|
{
|
|
sp->envelope.gf1.rate[j] = patch_data.EnvelopeRate[j];
|
|
/* [RH] GF1NEW clamps the offsets to the range [5,251], so we do too. */
|
|
sp->envelope.gf1.offset[j] = std::max<uint8_t>(5, std::min<uint8_t>(251, patch_data.EnvelopeOffset[j]));
|
|
}
|
|
|
|
/* Then read the sample data */
|
|
if (((sp->modes & PATCH_16) && sp->data_length/2 > MAX_SAMPLE_SIZE) ||
|
|
(!(sp->modes & PATCH_16) && sp->data_length > MAX_SAMPLE_SIZE))
|
|
{
|
|
goto fail;
|
|
}
|
|
sp->data = (sample_t *)safe_malloc(sp->data_length);
|
|
|
|
if (sp->data_length != fp->read(sp->data, sp->data_length))
|
|
goto fail;
|
|
|
|
convert_sample_data(sp, sp->data);
|
|
|
|
/* Reverse reverse loops and pass them off as normal loops */
|
|
if (sp->modes & PATCH_BACKWARD)
|
|
{
|
|
int t;
|
|
/* The GUS apparently plays reverse loops by reversing the
|
|
whole sample. We do the same because the GUS does not SUCK. */
|
|
|
|
printMessage(CMSG_WARNING, VERB_NORMAL, "Reverse loop in %s\n", name);
|
|
reverse_data((sample_t *)sp->data, 0, sp->data_length);
|
|
sp->data[sp->data_length] = sp->data[sp->data_length - 1];
|
|
|
|
t = sp->loop_start;
|
|
sp->loop_start = sp->data_length - sp->loop_end;
|
|
sp->loop_end = sp->data_length - t;
|
|
|
|
sp->modes &= ~PATCH_BACKWARD;
|
|
sp->modes |= PATCH_LOOPEN; /* just in case */
|
|
}
|
|
|
|
/* Then fractional samples */
|
|
sp->data_length <<= FRACTION_BITS;
|
|
sp->loop_start <<= FRACTION_BITS;
|
|
sp->loop_end <<= FRACTION_BITS;
|
|
|
|
/* Adjust for fractional loop points. */
|
|
sp->loop_start |= (patch_data.Fractions & 0x0F) << (FRACTION_BITS-4);
|
|
sp->loop_end |= (patch_data.Fractions & 0xF0) << (FRACTION_BITS-4-4);
|
|
|
|
/* If this instrument will always be played on the same note,
|
|
and it's not looped, we can resample it now. */
|
|
if (sp->scale_factor == 0 && !(sp->modes & PATCH_LOOPEN))
|
|
{
|
|
pre_resample(this, sp);
|
|
}
|
|
|
|
if (strip_tail == 1)
|
|
{
|
|
/* Let's not really, just say we did. */
|
|
printMessage(CMSG_INFO, VERB_DEBUG, " - Stripping tail\n");
|
|
sp->data_length = sp->loop_end;
|
|
}
|
|
}
|
|
fp->close();
|
|
return ip;
|
|
}
|
|
|
|
void convert_sample_data(Sample *sp, const void *data)
|
|
{
|
|
/* convert everything to 32-bit floating point data */
|
|
sample_t *newdata = NULL;
|
|
|
|
switch (sp->modes & (PATCH_16 | PATCH_UNSIGNED))
|
|
{
|
|
case 0:
|
|
{ /* 8-bit, signed */
|
|
int8_t *cp = (int8_t *)data;
|
|
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
|
|
for (int i = 0; i < sp->data_length; ++i)
|
|
{
|
|
if (cp[i] < 0)
|
|
{
|
|
newdata[i] = float(cp[i]) / 128.f;
|
|
}
|
|
else
|
|
{
|
|
newdata[i] = float(cp[i]) / 127.f;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PATCH_UNSIGNED:
|
|
{ /* 8-bit, unsigned */
|
|
uint8_t *cp = (uint8_t *)data;
|
|
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
|
|
for (int i = 0; i < sp->data_length; ++i)
|
|
{
|
|
int c = cp[i] - 128;
|
|
if (c < 0)
|
|
{
|
|
newdata[i] = float(c) / 128.f;
|
|
}
|
|
else
|
|
{
|
|
newdata[i] = float(c) / 127.f;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PATCH_16:
|
|
{ /* 16-bit, signed */
|
|
int16_t *cp = (int16_t *)data;
|
|
/* Convert these to samples */
|
|
sp->data_length >>= 1;
|
|
sp->loop_start >>= 1;
|
|
sp->loop_end >>= 1;
|
|
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
|
|
for (int i = 0; i < sp->data_length; ++i)
|
|
{
|
|
int c = LittleShort(cp[i]);
|
|
if (c < 0)
|
|
{
|
|
newdata[i] = float(c) / 32768.f;
|
|
}
|
|
else
|
|
{
|
|
newdata[i] = float(c) / 32767.f;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case PATCH_16 | PATCH_UNSIGNED:
|
|
{ /* 16-bit, unsigned */
|
|
auto *cp = (uint16_t *)data;
|
|
/* Convert these to samples */
|
|
sp->data_length >>= 1;
|
|
sp->loop_start >>= 1;
|
|
sp->loop_end >>= 1;
|
|
newdata = (sample_t *)safe_malloc((sp->data_length + 1) * sizeof(sample_t));
|
|
for (int i = 0; i < sp->data_length; ++i)
|
|
{
|
|
int c = LittleShort(cp[i]) - 32768;
|
|
if (c < 0)
|
|
{
|
|
newdata[i] = float(c) / 32768.f;
|
|
}
|
|
else
|
|
{
|
|
newdata[i] = float(c) / 32767.f;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* Duplicate the final sample for linear interpolation. */
|
|
newdata[sp->data_length] = newdata[sp->data_length - 1];
|
|
if (sp->data != NULL)
|
|
{
|
|
free(sp->data);
|
|
}
|
|
sp->data = newdata;
|
|
}
|
|
|
|
int Renderer::fill_bank(int dr, int b)
|
|
{
|
|
int i, errors = 0;
|
|
ToneBank *bank = ((dr) ? instruments->drumset[b] : instruments->tonebank[b]);
|
|
if (bank == NULL)
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_NORMAL,
|
|
"Tried to load instruments in non-existent %s %d\n",
|
|
(dr) ? "drumset" : "tone bank", b);
|
|
return 0;
|
|
}
|
|
for (i = 0; i < MAXPROG; i++)
|
|
{
|
|
if (bank->instrument[i] == MAGIC_LOAD_INSTRUMENT)
|
|
{
|
|
bank->instrument[i] = NULL;
|
|
bank->instrument[i] = load_instrument_dls(this, dr, b, i);
|
|
if (bank->instrument[i] != NULL)
|
|
{
|
|
continue;
|
|
}
|
|
Instrument *ip;
|
|
ip = load_instrument_font_order(0, dr, b, i);
|
|
if (ip == NULL)
|
|
{
|
|
if (bank->tone[i].fontbank >= 0)
|
|
{
|
|
ip = load_instrument_font(bank->tone[i].name.c_str(), dr, b, i);
|
|
}
|
|
else
|
|
{
|
|
ip = load_instrument(bank->tone[i].name.c_str(),
|
|
(dr) ? 1 : 0,
|
|
bank->tone[i].pan,
|
|
(bank->tone[i].note != -1) ? bank->tone[i].note : ((dr) ? i : -1),
|
|
(bank->tone[i].strip_loop != -1) ? bank->tone[i].strip_loop : ((dr) ? 1 : -1),
|
|
(bank->tone[i].strip_envelope != -1) ? bank->tone[i].strip_envelope : ((dr) ? 1 : -1),
|
|
bank->tone[i].strip_tail);
|
|
}
|
|
if (ip == NULL)
|
|
{
|
|
ip = load_instrument_font_order(1, dr, b, i);
|
|
}
|
|
}
|
|
bank->instrument[i] = ip;
|
|
if (ip == NULL)
|
|
{
|
|
if (bank->tone[i].name.length() == 0)
|
|
{
|
|
printMessage(CMSG_WARNING, (b != 0) ? VERB_VERBOSE : VERB_DEBUG,
|
|
"No instrument mapped to %s %d, program %d%s\n",
|
|
(dr) ? "drum set" : "tone bank", b, i,
|
|
(b != 0) ? "" : " - this instrument will not be heard");
|
|
}
|
|
else
|
|
{
|
|
printMessage(CMSG_ERROR, VERB_DEBUG,
|
|
"Couldn't load instrument %s (%s %d, program %d)\n",
|
|
bank->tone[i].name.c_str(),
|
|
(dr) ? "drum set" : "tone bank", b, i);
|
|
}
|
|
if (b != 0)
|
|
{
|
|
/* Mark the corresponding instrument in the default
|
|
bank / drumset for loading (if it isn't already) */
|
|
if (((dr) ? instruments->drumset[0] : instruments->tonebank[0])->instrument[i] != NULL)
|
|
{
|
|
((dr) ? instruments->drumset[0] : instruments->tonebank[0])->instrument[i] = MAGIC_LOAD_INSTRUMENT;
|
|
}
|
|
}
|
|
errors++;
|
|
}
|
|
}
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
int Renderer::load_missing_instruments()
|
|
{
|
|
int i = MAXBANK, errors = 0;
|
|
while (i--)
|
|
{
|
|
if (instruments->tonebank[i] != NULL)
|
|
errors += fill_bank(0, i);
|
|
if (instruments->drumset[i] != NULL)
|
|
errors += fill_bank(1, i);
|
|
}
|
|
return errors;
|
|
}
|
|
|
|
void Instruments::free_instruments()
|
|
{
|
|
int i = MAXBANK;
|
|
while (i--)
|
|
{
|
|
if (tonebank[i] != NULL)
|
|
{
|
|
delete tonebank[i];
|
|
tonebank[i] = NULL;
|
|
}
|
|
if (drumset[i] != NULL)
|
|
{
|
|
delete drumset[i];
|
|
drumset[i] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
Instruments::Instruments(MusicIO::SoundFontReaderInterface *reader)
|
|
{
|
|
sfreader = reader;
|
|
tonebank[0] = new ToneBank;
|
|
drumset[0] = new ToneBank;
|
|
}
|
|
|
|
Instruments::~Instruments()
|
|
{
|
|
free_instruments();
|
|
font_freeall();
|
|
for (int i = 0; i < MAXBANK; ++i)
|
|
{
|
|
if (tonebank[i] != NULL)
|
|
{
|
|
delete tonebank[i];
|
|
tonebank[i] = NULL;
|
|
}
|
|
if (drumset[i] != NULL)
|
|
{
|
|
delete drumset[i];
|
|
drumset[i] = NULL;
|
|
}
|
|
}
|
|
if (sfreader != nullptr) sfreader->close();
|
|
sfreader = nullptr;
|
|
}
|
|
|
|
|
|
int Renderer::set_default_instrument(const char *name)
|
|
{
|
|
Instrument *ip;
|
|
if ((ip = load_instrument(name, 0, -1, -1, 0, 0, 0)) == NULL)
|
|
{
|
|
return -1;
|
|
}
|
|
if (default_instrument != NULL)
|
|
{
|
|
delete default_instrument;
|
|
}
|
|
default_instrument = ip;
|
|
default_program = SPECIAL_PROGRAM;
|
|
return 0;
|
|
}
|
|
|
|
|
|
}
|