2012-05-26 14:49:10 +02:00
|
|
|
/* FluidSynth - A Software Synthesizer
|
|
|
|
*
|
|
|
|
* Copyright (C) 2003 Peter Hanappe and others.
|
|
|
|
*
|
|
|
|
* SoundFont file loading code borrowed from Smurf SoundFont Editor
|
|
|
|
* Copyright (C) 1999-2001 Josh Green
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Library General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2 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
|
|
|
|
* Library General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
* License along with this library; if not, write to the Free
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
|
|
|
* 02111-1307, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "sfont.h"
|
|
|
|
#include "fluid.h"
|
|
|
|
#include "voice.h"
|
|
|
|
|
|
|
|
// #define DEBUG_SFONT
|
|
|
|
|
|
|
|
#include "libmscore/xml.h"
|
|
|
|
|
|
|
|
static bool debugMode = false;
|
|
|
|
|
|
|
|
namespace FluidS {
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// SFVersion
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SFVersion::SFVersion()
|
|
|
|
{
|
|
|
|
major = 0;
|
|
|
|
minor = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// SFont
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
SFont::SFont(Fluid* f)
|
|
|
|
{
|
|
|
|
synth = f;
|
|
|
|
samplepos = 0;
|
|
|
|
samplesize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SFont::~SFont()
|
|
|
|
{
|
|
|
|
foreach(Sample* s, sample)
|
|
|
|
delete s;
|
|
|
|
foreach(Preset* p, presets)
|
|
|
|
delete p;
|
|
|
|
foreach(unsigned char* p, infos)
|
|
|
|
delete[] p;
|
|
|
|
foreach(Instrument* i, instruments) {
|
|
|
|
// foreach(Zone* z, i->zones)
|
|
|
|
// delete z;
|
|
|
|
delete i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// read
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SFont::read(const QString& s)
|
|
|
|
{
|
|
|
|
f.setFileName(s);
|
|
|
|
if (!load())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
foreach(Instrument* i, instruments) {
|
|
|
|
if (!i->import_sfont())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(Preset* p, presets) {
|
|
|
|
if (!p->importSfont())
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// get_preset
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Preset* SFont::get_preset(int bank, int num)
|
|
|
|
{
|
|
|
|
foreach(Preset* p, presets) {
|
|
|
|
if ((p->get_banknum() == bank) && (p->get_num() == num))
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Preset
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Preset::Preset(SFont* s)
|
|
|
|
{
|
|
|
|
sfont = s;
|
|
|
|
bank = 0;
|
|
|
|
num = 0;
|
|
|
|
_global_zone = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Preset
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Preset::~Preset()
|
|
|
|
{
|
|
|
|
delete _global_zone;
|
|
|
|
foreach(Zone* z, zones)
|
|
|
|
delete z;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// loadSamples
|
|
|
|
// this is called if the preset is associated with a
|
|
|
|
// channel
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Preset::loadSamples()
|
|
|
|
{
|
|
|
|
// sfont->synth->mutex.lock();
|
|
|
|
if (_global_zone && _global_zone->instrument) {
|
|
|
|
Instrument* i = _global_zone->instrument;
|
|
|
|
if (i->global_zone && i->global_zone->sample)
|
|
|
|
i->global_zone->sample->load();
|
|
|
|
foreach(Zone* iz, i->zones)
|
|
|
|
iz->sample->load();
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(Zone* z, zones) {
|
|
|
|
Instrument* i = z->instrument;
|
|
|
|
if (i->global_zone && i->global_zone->sample)
|
|
|
|
i->global_zone->sample->load();
|
|
|
|
foreach(Zone* iz, i->zones)
|
|
|
|
iz->sample->load();
|
|
|
|
}
|
|
|
|
// sfont->synth->mutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// noteon
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Preset::noteon(Fluid* synth, unsigned id, int chan, int key, int vel, double nt)
|
|
|
|
{
|
|
|
|
Mod* mod;
|
|
|
|
Mod* mod_list[FLUID_NUM_MOD]; /* list for 'sorting' preset modulators */
|
|
|
|
|
|
|
|
Zone* global_preset_zone = global_zone();
|
|
|
|
|
|
|
|
/* run thru all the zones of this preset */
|
|
|
|
foreach (Zone* preset_zone, zones) {
|
|
|
|
/* check if the note falls into the key and velocity range of this
|
|
|
|
preset */
|
|
|
|
if (preset_zone->inside_range(key, vel)) {
|
|
|
|
|
|
|
|
Instrument* inst = preset_zone->get_inst();
|
|
|
|
Zone* global_inst_zone = inst->get_global_zone();
|
|
|
|
|
|
|
|
/* run thru all the zones of this instrument */
|
|
|
|
foreach(Zone* inst_zone, inst->get_zone()) {
|
|
|
|
/* make sure this instrument zone has a valid sample */
|
|
|
|
Sample* sample = inst_zone->get_sample();
|
|
|
|
if (sample == 0 || sample->inRom())
|
|
|
|
continue;
|
|
|
|
/* check if the note falls into the key and velocity range of this
|
|
|
|
instrument */
|
|
|
|
if (inst_zone->inside_range(key, vel) && (sample != 0)) {
|
|
|
|
|
|
|
|
/* this is a good zone. allocate a new synthesis process and
|
|
|
|
initialize it */
|
|
|
|
|
|
|
|
Voice* voice = synth->alloc_voice(id, sample, chan, key, vel, nt);
|
|
|
|
if (voice == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/* Instrumentrument level, generators */
|
|
|
|
|
|
|
|
for (int i = 0; i < GEN_LAST; i++) {
|
|
|
|
/* SF 2.01 section 9.4 'bullet' 4:
|
|
|
|
*
|
|
|
|
* A generator in a local instrument zone supersedes a
|
|
|
|
* global instrument zone generator. Both cases supersede
|
|
|
|
* the default generator -> voice_gen_set */
|
|
|
|
|
|
|
|
if (inst_zone->genlist[i].flags)
|
|
|
|
voice->gen_set(i, inst_zone->genlist[i].val);
|
|
|
|
else if ((global_inst_zone != 0) && (global_inst_zone->genlist[i].flags))
|
|
|
|
voice->gen_set(i, global_inst_zone->genlist[i].val);
|
|
|
|
else {
|
|
|
|
/* The generator has not been defined in this instrument.
|
|
|
|
* Do nothing, leave it at the default.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* for all generators */
|
|
|
|
|
|
|
|
/* global instrument zone, modulators: Put them all into a
|
|
|
|
* list. */
|
|
|
|
|
|
|
|
int mod_list_count = 0;
|
|
|
|
|
|
|
|
if (global_inst_zone){
|
|
|
|
foreach(Mod* mod, global_inst_zone->modlist)
|
|
|
|
mod_list[mod_list_count++] = mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* local instrument zone, modulators.
|
|
|
|
* Replace modulators with the same definition in the list:
|
|
|
|
* SF 2.01 page 69, 'bullet' 8
|
|
|
|
*/
|
|
|
|
foreach(Mod* mod, inst_zone->modlist) {
|
|
|
|
/* 'Identical' modulators will be deleted by setting their
|
|
|
|
* list entry to 0. The list length is known, 0
|
|
|
|
* entries will be ignored later. SF2.01 section 9.5.1
|
|
|
|
* page 69, 'bullet' 3 defines 'identical'. */
|
|
|
|
|
|
|
|
for (int i = 0; i < mod_list_count; i++){
|
|
|
|
if (mod_list[i] && test_identity(mod, mod_list[i])){
|
|
|
|
mod_list[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Finally add the new modulator to to the list. */
|
|
|
|
mod_list[mod_list_count++] = mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add instrument modulators (global / local) to the voice. */
|
|
|
|
for (int i = 0; i < mod_list_count; i++){
|
|
|
|
mod = mod_list[i];
|
|
|
|
if (mod) { // disabled modulators CANNOT be skipped.
|
|
|
|
/* Instrumentrument modulators -supersede- existing (default)
|
|
|
|
* modulators. SF 2.01 page 69, 'bullet' 6 */
|
|
|
|
voice->add_mod(mod, FLUID_VOICE_OVERWRITE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Preset level, generators */
|
|
|
|
|
|
|
|
for (int i = 0; i < GEN_LAST; i++) {
|
|
|
|
/* SF 2.01 section 8.5 page 58: If some generators are
|
|
|
|
* encountered at preset level, they should be ignored */
|
|
|
|
if ((i != GEN_STARTADDROFS)
|
|
|
|
&& (i != GEN_ENDADDROFS)
|
|
|
|
&& (i != GEN_STARTLOOPADDROFS)
|
|
|
|
&& (i != GEN_ENDLOOPADDROFS)
|
|
|
|
&& (i != GEN_STARTADDRCOARSEOFS)
|
|
|
|
&& (i != GEN_ENDADDRCOARSEOFS)
|
|
|
|
&& (i != GEN_STARTLOOPADDRCOARSEOFS)
|
|
|
|
&& (i != GEN_KEYNUM)
|
|
|
|
&& (i != GEN_VELOCITY)
|
|
|
|
&& (i != GEN_ENDLOOPADDRCOARSEOFS)
|
|
|
|
&& (i != GEN_SAMPLEMODE)
|
|
|
|
&& (i != GEN_EXCLUSIVECLASS)
|
|
|
|
&& (i != GEN_OVERRIDEROOTKEY)) {
|
|
|
|
|
|
|
|
/* SF 2.01 section 9.4 'bullet' 9: A generator in a
|
|
|
|
* local preset zone supersedes a global preset zone
|
|
|
|
* generator. The effect is -added- to the destination
|
|
|
|
* summing node -> voice_gen_incr */
|
|
|
|
|
|
|
|
if (preset_zone->genlist[i].flags) {
|
|
|
|
voice->gen_incr(i, preset_zone->genlist[i].val);
|
|
|
|
}
|
|
|
|
else if ((global_preset_zone != 0) && global_preset_zone->genlist[i].flags) {
|
|
|
|
voice->gen_incr(i, global_preset_zone->genlist[i].val);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* The generator has not been defined in this preset
|
|
|
|
* Do nothing, leave it unchanged.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
} /* if available at preset level */
|
|
|
|
} /* for all generators */
|
|
|
|
|
|
|
|
|
|
|
|
/* Global preset zone, modulators: put them all into a
|
|
|
|
* list. */
|
|
|
|
mod_list_count = 0;
|
|
|
|
if (global_preset_zone){
|
|
|
|
foreach(Mod* mod, global_preset_zone->modlist)
|
|
|
|
mod_list[mod_list_count++] = mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process the modulators of the local preset zone. Kick
|
|
|
|
* out all identical modulators from the global preset zone
|
|
|
|
* (SF 2.01 page 69, second-last bullet) */
|
|
|
|
|
|
|
|
foreach(Mod* mod, preset_zone->modlist) {
|
|
|
|
for (int i = 0; i < mod_list_count; i++) {
|
|
|
|
if (mod_list[i] && test_identity(mod,mod_list[i]))
|
|
|
|
mod_list[i] = 0;
|
|
|
|
}
|
|
|
|
/* Finally add the new modulator to the list. */
|
|
|
|
mod_list[mod_list_count++] = mod;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add preset modulators (global / local) to the voice. */
|
|
|
|
for (int i = 0; i < mod_list_count; i++){
|
|
|
|
mod = mod_list[i];
|
|
|
|
if ((mod != 0) && (mod->amount != 0)) { /* disabled modulators can be skipped. */
|
|
|
|
/* Preset modulators -add- to existing instrument /
|
|
|
|
* default modulators. SF2.01 page 70 first bullet on
|
|
|
|
* page */
|
|
|
|
voice->add_mod(mod, FLUID_VOICE_ADD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add the synthesis process to the synthesis loop. */
|
|
|
|
synth->start_voice(voice);
|
|
|
|
|
|
|
|
/* Store the ID of the first voice that was created by this noteon event.
|
|
|
|
* Exclusive class may only terminate older voices.
|
|
|
|
* That avoids killing voices, which have just been created.
|
|
|
|
* (a noteon event can create several voice processes with the same exclusive
|
|
|
|
* class - for example when using stereo samples)
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// importSfont
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Preset::importSfont()
|
|
|
|
{
|
|
|
|
if (name.isEmpty())
|
|
|
|
name = QString("Bank%1,Preset%2").arg(bank).arg(num);
|
|
|
|
|
|
|
|
int idx = 0;
|
|
|
|
foreach(Zone* zone, zones) {
|
|
|
|
// zone->name = QString("%1/%2").arg(name).arg(idx);
|
|
|
|
if (!zone->importZone())
|
|
|
|
return false;
|
|
|
|
if ((idx == 0) && (zone->get_inst() == 0))
|
|
|
|
setGlobalZone(zones.takeAt(0));
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// import_sfont
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Instrument::import_sfont()
|
|
|
|
{
|
|
|
|
int idx = 0;
|
|
|
|
foreach(Zone* zone, zones) {
|
|
|
|
if (!zone->importZone())
|
|
|
|
return false;
|
|
|
|
if ((idx == 0) && (zone->get_sample() == 0))
|
|
|
|
global_zone = zones.takeAt(0);
|
|
|
|
idx++;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Zone
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Zone::Zone()
|
|
|
|
{
|
|
|
|
instrument = 0;
|
|
|
|
sample = 0;
|
|
|
|
sampIdx = 0;
|
|
|
|
instIdx = 0;
|
|
|
|
keylo = 0;
|
|
|
|
keyhi = 128;
|
|
|
|
vello = 0;
|
|
|
|
velhi = 128;
|
|
|
|
|
|
|
|
/* Flag all generators as unused (default, they will be set when they are found
|
|
|
|
* in the sound font).
|
|
|
|
* This also sets the generator values to default, but that is of no concern here.*/
|
|
|
|
|
|
|
|
fluid_gen_set_default_values(&genlist[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
Zone::~Zone()
|
|
|
|
{
|
|
|
|
foreach(Mod* m, modlist)
|
|
|
|
delete m;
|
|
|
|
foreach(SFGen* p, gen)
|
|
|
|
delete p;
|
|
|
|
foreach(SFMod* p, mod)
|
|
|
|
delete p;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// inside_range
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Zone::inside_range(int key, int vel) const
|
|
|
|
{
|
|
|
|
return ((keylo <= key) && (keyhi >= key) && (vello <= vel) && (velhi >= vel));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Instrument
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Instrument::Instrument()
|
|
|
|
{
|
|
|
|
global_zone = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Instrument::~Instrument()
|
|
|
|
{
|
|
|
|
delete global_zone;
|
|
|
|
foreach(Zone* z, zones)
|
|
|
|
delete z;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// importZone
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Zone::importZone()
|
|
|
|
{
|
|
|
|
foreach (SFGen* sfgen, gen) {
|
|
|
|
switch (sfgen->id) {
|
|
|
|
case GEN_KEYRANGE:
|
|
|
|
keylo = sfgen->amount.range.lo;
|
|
|
|
keyhi = sfgen->amount.range.hi;
|
|
|
|
break;
|
|
|
|
case GEN_VELRANGE:
|
|
|
|
vello = sfgen->amount.range.lo;
|
|
|
|
velhi = sfgen->amount.range.hi;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* FIXME: some generators have an unsigned word amount value but i don't know which ones
|
|
|
|
*/
|
|
|
|
genlist[sfgen->id].val = (float) sfgen->amount.sword;
|
|
|
|
genlist[sfgen->id].flags = GEN_SET;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Import the modulators (only SF2.1 and higher)
|
|
|
|
foreach(SFMod* mod_src, mod) {
|
|
|
|
Mod* mod_dest = new Mod;
|
|
|
|
int type;
|
|
|
|
// mod_dest->next = 0; /* pointer to next modulator, this is the end of the list now.*/
|
|
|
|
|
|
|
|
/* *** Amount *** */
|
|
|
|
mod_dest->amount = mod_src->amount;
|
|
|
|
|
|
|
|
/* *** Source *** */
|
|
|
|
mod_dest->src1 = mod_src->src & 127; /* index of source 1, seven-bit value, SF2.01 section 8.2, page 50 */
|
|
|
|
mod_dest->flags1 = 0;
|
|
|
|
|
|
|
|
/* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
|
|
|
|
if (mod_src->src & (1<<7))
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_CC;
|
|
|
|
else
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_GC;
|
|
|
|
|
|
|
|
/* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
|
|
|
|
if (mod_src->src & (1<<8))
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_NEGATIVE;
|
|
|
|
else
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_POSITIVE;
|
|
|
|
|
|
|
|
/* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
|
|
|
|
if (mod_src->src & (1<<9))
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_BIPOLAR;
|
|
|
|
else
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_UNIPOLAR;
|
|
|
|
|
|
|
|
/* modulator source types: SF2.01 section 8.2.1 page 52 */
|
|
|
|
type = (mod_src->src) >> 10;
|
|
|
|
type &= 63; /* type is a 6-bit value */
|
|
|
|
if (type == 0)
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_LINEAR;
|
|
|
|
else if (type == 1)
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_CONCAVE;
|
|
|
|
else if (type == 2)
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_CONVEX;
|
|
|
|
else if (type == 3)
|
|
|
|
mod_dest->flags1 |= FLUID_MOD_SWITCH;
|
|
|
|
else {
|
|
|
|
/* This shouldn't happen - unknown type!
|
|
|
|
* Deactivate the modulator by setting the amount to 0.
|
|
|
|
*/
|
|
|
|
mod_dest->amount=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *** Dest *** */
|
|
|
|
mod_dest->dest = mod_src->dest; /* index of controlled generator */
|
|
|
|
|
|
|
|
/* *** Amount source *** */
|
|
|
|
mod_dest->src2 = mod_src->amtsrc & 127; /* index of source 2, seven-bit value, SF2.01 section 8.2, p.50 */
|
|
|
|
mod_dest->flags2 = 0;
|
|
|
|
|
|
|
|
/* Bit 7: CC flag SF 2.01 section 8.2.1 page 50*/
|
|
|
|
if (mod_src->amtsrc & (1<<7))
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_CC;
|
|
|
|
else
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_GC;
|
|
|
|
|
|
|
|
/* Bit 8: D flag SF 2.01 section 8.2.2 page 51*/
|
|
|
|
if (mod_src->amtsrc & (1<<8))
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_NEGATIVE;
|
|
|
|
else
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_POSITIVE;
|
|
|
|
|
|
|
|
/* Bit 9: P flag SF 2.01 section 8.2.3 page 51*/
|
|
|
|
if (mod_src->amtsrc & (1<<9))
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_BIPOLAR;
|
|
|
|
else
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_UNIPOLAR;
|
|
|
|
|
|
|
|
/* modulator source types: SF2.01 section 8.2.1 page 52 */
|
|
|
|
type = (mod_src->amtsrc) >> 10;
|
|
|
|
type &= 63; /* type is a 6-bit value */
|
|
|
|
if (type == 0)
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_LINEAR;
|
|
|
|
else if (type == 1)
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_CONCAVE;
|
|
|
|
else if (type == 2)
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_CONVEX;
|
|
|
|
else if (type == 3)
|
|
|
|
mod_dest->flags2 |= FLUID_MOD_SWITCH;
|
|
|
|
else {
|
|
|
|
/* This shouldn't happen - unknown type!
|
|
|
|
* Deactivate the modulator by setting the amount to 0. */
|
|
|
|
mod_dest->amount=0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* *** Transform *** */
|
|
|
|
/* SF2.01 only uses the 'linear' transform (0).
|
|
|
|
* Deactivate the modulator by setting the amount to 0 in any other case.
|
|
|
|
*/
|
|
|
|
if (mod_src->trans !=0)
|
|
|
|
mod_dest->amount = 0;
|
|
|
|
|
|
|
|
/* Store the new modulator in the zone The order of modulators
|
|
|
|
* will make a difference, at least in an instrument context: The
|
|
|
|
* second modulator overwrites the first one, if they only differ
|
|
|
|
* in amount.
|
|
|
|
*/
|
|
|
|
modlist.append(mod_dest);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Sample
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Sample::Sample(SFont* s)
|
|
|
|
{
|
|
|
|
sf = s;
|
|
|
|
_valid = false;
|
|
|
|
start = 0;
|
|
|
|
end = 0;
|
|
|
|
loopstart = 0;
|
|
|
|
loopend = 0;
|
|
|
|
samplerate = 0;
|
|
|
|
origpitch = 0;
|
|
|
|
pitchadj = 0;
|
|
|
|
sampletype = 0;
|
|
|
|
data = 0;
|
|
|
|
amplitude_that_reaches_noise_floor_is_valid = false;
|
|
|
|
amplitude_that_reaches_noise_floor = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// Sample
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
Sample::~Sample()
|
|
|
|
{
|
|
|
|
delete[] data;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// load
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void Sample::load()
|
|
|
|
{
|
|
|
|
if (!_valid || data)
|
|
|
|
return;
|
|
|
|
QFile fd(sf->get_name());
|
|
|
|
if (!fd.open(QIODevice::ReadOnly))
|
|
|
|
return;
|
|
|
|
if (sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) {
|
|
|
|
if (!fd.seek(sf->samplePos() + start))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!fd.seek(sf->samplePos() + start * sizeof(short)))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unsigned int size = end - start;
|
|
|
|
|
|
|
|
if (sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) {
|
|
|
|
#ifdef SOUNDFONT3
|
|
|
|
char* p = new char[size];
|
|
|
|
if (fd.read(p, size) != size) {
|
|
|
|
printf(" read %d failed\n", size);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
decompressOggVorbis(p, size);
|
|
|
|
delete[] p;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
data = new short[size];
|
|
|
|
size *= sizeof(short);
|
|
|
|
|
|
|
|
if (fd.read((char*)data, size) != size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
|
|
|
|
unsigned char hi, lo;
|
|
|
|
unsigned int i, j;
|
|
|
|
short s;
|
|
|
|
uchar* cbuf = (uchar*) data;
|
|
|
|
for (i = 0, j = 0; j < size; i++) {
|
|
|
|
lo = cbuf[j++];
|
|
|
|
hi = cbuf[j++];
|
|
|
|
s = (hi << 8) | lo;
|
|
|
|
data[i] = s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end -= (start + 1); // marks last sample, contrary to SF spec.
|
|
|
|
loopstart -= start;
|
|
|
|
loopend -= start;
|
|
|
|
start = 0;
|
|
|
|
}
|
|
|
|
optimize();
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// inRom
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool Sample::inRom() const
|
|
|
|
{
|
|
|
|
return sampletype & FLUID_SAMPLETYPE_ROM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char idlist[] = {
|
|
|
|
"RIFFLISTsfbkINFOsdtapdtaifilisngINAMiromiverICRDIENGIPRD"
|
|
|
|
"ICOPICMTISFTsnamsmplphdrpbagpmodpgeninstibagimodigenshdr"
|
|
|
|
};
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// chunkid
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static int chunkid (unsigned int id)
|
|
|
|
{
|
|
|
|
unsigned int* p = (unsigned int *) & idlist;
|
|
|
|
for (unsigned i = 0; i < sizeof (idlist) / sizeof (int); i++, p++) {
|
|
|
|
if (*p == id)
|
|
|
|
return (i + 1);
|
|
|
|
}
|
|
|
|
return UNKN_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// preset_compare
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static bool preset_compare (Preset* a, Preset* b)
|
|
|
|
{
|
|
|
|
int aval = (a->bank) << 16 | a->num;
|
|
|
|
int bval = (b->bank) << 16 | b->num;
|
|
|
|
return aval < bval;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// readchunk
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::readchunk(SFChunk* var)
|
|
|
|
{
|
|
|
|
safe_fread(var, 8);
|
|
|
|
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
|
|
|
|
var->size = GUINT32_FROM_BE(var->size);
|
|
|
|
else
|
|
|
|
var->size = GUINT32_FROM_LE(var->size);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// load
|
|
|
|
// return true on success
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
bool SFont::load()
|
|
|
|
{
|
|
|
|
if (!f.open(QIODevice::ReadOnly)) {
|
|
|
|
qCritical("Unable to open file \"%s\"", qPrintable(f.fileName()));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
SFChunk chunk;
|
|
|
|
|
|
|
|
try {
|
|
|
|
readchunk(&chunk);
|
|
|
|
if (chunkid(chunk.id) != RIFF_ID)
|
|
|
|
throw(QString("Not a RIFF file"));
|
|
|
|
|
|
|
|
safe_fread(&chunk.id, 4);
|
|
|
|
if (chunkid (chunk.id) != SFBK_ID) /* error if not SFBK_ID */
|
|
|
|
throw(QString("Not a sound font file"));
|
|
|
|
if (chunk.size != f.size() - 8)
|
|
|
|
throw(QString("Sound font file size mismatch %1 %2").arg(chunk.size).arg(f.size() - 8));
|
|
|
|
|
|
|
|
/* Process INFO block */
|
|
|
|
read_listchunk(&chunk);
|
|
|
|
if (chunkid(chunk.id) != INFO_ID)
|
|
|
|
throw(QString("Invalid ID found when expecting INFO chunk"));
|
|
|
|
process_info(chunk.size);
|
|
|
|
|
|
|
|
/* Process sample chunk */
|
|
|
|
read_listchunk(&chunk);
|
|
|
|
if (chunkid (chunk.id) != SDTA_ID)
|
|
|
|
throw(QString("Invalid ID found when expecting SAMPLE chunk"));
|
|
|
|
process_sdta(chunk.size);
|
|
|
|
|
|
|
|
/* process HYDRA chunk */
|
|
|
|
read_listchunk(&chunk);
|
|
|
|
if (chunkid (chunk.id) != PDTA_ID)
|
|
|
|
throw(QString("Invalid ID found when expecting HYDRA chunk"));
|
|
|
|
process_pdta (chunk.size);
|
|
|
|
|
|
|
|
fixup_pgen();
|
|
|
|
fixup_igen();
|
|
|
|
}
|
|
|
|
catch (QString s) {
|
|
|
|
printf("fluid: error loading sound font: %s\n", qPrintable(s));
|
|
|
|
f.close();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
f.close();
|
|
|
|
/* sort preset list by bank, preset # */
|
|
|
|
qSort(presets.begin(), presets.end(), preset_compare);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// READW
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
unsigned short SFont::READW()
|
|
|
|
{
|
|
|
|
unsigned short _temp;
|
|
|
|
safe_fread(&_temp, 2);
|
|
|
|
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
|
|
|
|
return GINT16_FROM_BE(_temp);
|
|
|
|
else
|
|
|
|
return GINT16_FROM_LE(_temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// READD
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::READD(unsigned int& var)
|
|
|
|
{
|
|
|
|
unsigned int _temp;
|
|
|
|
safe_fread(&_temp, 4);
|
|
|
|
if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
|
|
|
|
var = GINT32_FROM_BE(_temp);
|
|
|
|
else
|
|
|
|
var = GINT32_FROM_LE(_temp);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// READB
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
unsigned char SFont::READB()
|
|
|
|
{
|
|
|
|
unsigned char var;
|
|
|
|
safe_fread(&var, 1);
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// READC
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
char SFont::READC()
|
|
|
|
{
|
|
|
|
char var;
|
|
|
|
safe_fread(&var, 1);
|
|
|
|
return var;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// FSKIPW
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::FSKIPW()
|
|
|
|
{
|
|
|
|
safe_fseek(2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SFont::READSTR(char* name)
|
|
|
|
{
|
|
|
|
safe_fread(name, 20);
|
|
|
|
name[20] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// read_listchunk
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::read_listchunk (SFChunk* chunk)
|
|
|
|
{
|
|
|
|
readchunk (chunk);
|
|
|
|
if (chunkid (chunk->id) != LIST_ID)
|
|
|
|
throw(QString("Invalid chunk id in level 0 parse"));
|
|
|
|
safe_fread(&chunk->id, 4);
|
|
|
|
chunk->size -= 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// process_info
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::process_info(int size)
|
|
|
|
{
|
|
|
|
while (size > 0) {
|
|
|
|
SFChunk chunk;
|
|
|
|
readchunk (&chunk);
|
|
|
|
size -= 8;
|
|
|
|
|
|
|
|
unsigned char id = chunkid (chunk.id);
|
|
|
|
|
|
|
|
if (id == IFIL_ID) { // sound font version chunk?
|
|
|
|
if (chunk.size != 4)
|
|
|
|
throw(QString("Sound font version info chunk has invalid size"));
|
|
|
|
|
|
|
|
_version.major = READW();
|
|
|
|
_version.minor = READW();
|
|
|
|
|
|
|
|
if (_version.major < 2 || _version.major > 3)
|
|
|
|
throw(QString("Bad Sound font version %1.%2").arg(_version.major).arg(_version.minor));
|
|
|
|
}
|
|
|
|
else if (id == IVER_ID) { /* ROM version chunk? */
|
|
|
|
if (chunk.size != 4)
|
|
|
|
throw(QString("ROM version info chunk has invalid size"));
|
|
|
|
romver.major = READW();
|
|
|
|
romver.minor = READW();
|
|
|
|
}
|
|
|
|
else if (id != UNKN_ID) {
|
|
|
|
if ((id != ICMT_ID && chunk.size > 256) || (chunk.size > 65536) || (chunk.size % 2))
|
|
|
|
throw(QString("INFO sub chunk has invalid chunk size"));
|
|
|
|
|
|
|
|
/* alloc for chunk id and da chunk */
|
|
|
|
unsigned char* item = new unsigned char[chunk.size + 1];
|
|
|
|
|
|
|
|
*(unsigned char *) item = id;
|
|
|
|
safe_fread (&item[1], chunk.size);
|
|
|
|
|
|
|
|
/* force terminate info item (don't forget uint8 info ID) */
|
|
|
|
*(item + chunk.size) = '\0';
|
|
|
|
infos.append(item);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw(QString("Invalid chunk id in INFO chunk"));
|
|
|
|
size -= chunk.size;
|
|
|
|
}
|
|
|
|
if (size < 0)
|
|
|
|
throw(QString("INFO chunk size mismatch"));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// process_sdta
|
|
|
|
// return true on success
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::process_sdta (int size)
|
|
|
|
{
|
|
|
|
if (size == 0)
|
|
|
|
return; // no sample data?
|
|
|
|
|
|
|
|
/* read sub chunk */
|
|
|
|
SFChunk chunk;
|
|
|
|
readchunk (&chunk);
|
|
|
|
size -= 8;
|
|
|
|
|
|
|
|
if (chunkid (chunk.id) != SMPL_ID)
|
|
|
|
throw(QString("Expected SMPL chunk found invalid id instead"));
|
|
|
|
|
|
|
|
if ((size - chunk.size) != 0)
|
|
|
|
throw(QString("SDTA chunk size mismatch %1 != %2").arg(size).arg(chunk.size));
|
|
|
|
/* sample data follows */
|
|
|
|
setSamplepos(f.pos());
|
|
|
|
setSamplesize(chunk.size);
|
|
|
|
FSKIP(chunk.size);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// pdtahelper
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::pdtahelper (unsigned expid, unsigned reclen, SFChunk* chunk, int* size)
|
|
|
|
{
|
|
|
|
const char* expstr = CHNKIDSTR (expid);
|
|
|
|
|
|
|
|
readchunk (chunk);
|
|
|
|
*size -= 8;
|
|
|
|
|
|
|
|
unsigned id = chunkid(chunk->id);
|
|
|
|
if (id != expid)
|
|
|
|
throw(QString("Expected PDTA sub-chunk %1 found invalid id instead").arg(expstr));
|
|
|
|
|
|
|
|
if (chunk->size % reclen) /* valid chunk size? */
|
|
|
|
throw(QString("chunk size is not a multiple of %1 bytes").arg(reclen));
|
|
|
|
if ((*size -= chunk->size) < 0)
|
|
|
|
throw(QString("%1 chunk size exceeds remaining PDTA chunk size").arg(expstr));
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// process_pdta
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::process_pdta (int size)
|
|
|
|
{
|
|
|
|
static const unsigned id[] = {
|
|
|
|
PHDR_ID, PBAG_ID, PMOD_ID, PGEN_ID, IHDR_ID, IBAG_ID, IMOD_ID, IGEN_ID, SHDR_ID
|
|
|
|
};
|
|
|
|
static const unsigned len[] = {
|
|
|
|
SFPHDRSIZE, SFBAGSIZE, SFMODSIZE, SFGENSIZE, SFIHDRSIZE,
|
|
|
|
SFBAGSIZE, SFMODSIZE, SFGENSIZE, SFSHDRSIZE
|
|
|
|
};
|
|
|
|
typedef void (SFont::*LoadFunc)(int);
|
|
|
|
static const LoadFunc funcArray[] = {
|
|
|
|
&SFont::load_phdr, &SFont::load_pbag, &SFont::load_pmod, &SFont::load_pgen,
|
|
|
|
&SFont::load_ihdr, &SFont::load_ibag, &SFont::load_imod, &SFont::load_igen,
|
|
|
|
&SFont::load_shdr
|
|
|
|
};
|
|
|
|
SFChunk chunk;
|
|
|
|
for (int i = 0; i < 9; ++i) {
|
|
|
|
pdtahelper(id[i], len[i], &chunk, &size);
|
|
|
|
(this->*funcArray[i])(chunk.size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* preset header loader */
|
|
|
|
void SFont::load_phdr (int size)
|
|
|
|
{
|
|
|
|
Preset* pr = 0; /* ptr to current & previous preset */
|
|
|
|
unsigned short zndx, pzndx = 0;
|
|
|
|
|
|
|
|
if (size % SFPHDRSIZE || size == 0)
|
|
|
|
throw(QString("Preset header chunk size is invalid"));
|
|
|
|
|
|
|
|
int i = size / SFPHDRSIZE - 1;
|
|
|
|
if (i == 0) { /* at least one preset + term record */
|
|
|
|
qWarning("File contains no presets");
|
|
|
|
FSKIP (SFPHDRSIZE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (; i > 0; i--) { /* load all preset headers */
|
|
|
|
Preset* p = new Preset(this);
|
|
|
|
presets.append(p);
|
|
|
|
char str[21];
|
|
|
|
READSTR (str);
|
|
|
|
p->name = str;
|
|
|
|
|
|
|
|
p->num = READW();
|
|
|
|
p->bank = READW();
|
|
|
|
zndx = READW();
|
|
|
|
unsigned int tmp;
|
|
|
|
READD (tmp);
|
|
|
|
READD (tmp);
|
|
|
|
READD (tmp);
|
|
|
|
|
|
|
|
if (pr) { /* not first preset? */
|
|
|
|
if (zndx < pzndx)
|
|
|
|
throw(QString("Preset header indices not monotonic"));
|
|
|
|
int i2 = zndx - pzndx;
|
|
|
|
while (i2--)
|
|
|
|
pr->zones.prepend(0);
|
|
|
|
}
|
|
|
|
else if (zndx > 0) /* 1st preset, warn if ofs >0 */
|
|
|
|
qWarning("%d preset zones not referenced, discarding", zndx);
|
|
|
|
pr = p; /* update preset ptr */
|
|
|
|
pzndx = zndx;
|
|
|
|
}
|
|
|
|
FSKIP (24);
|
|
|
|
zndx = READW(); /* Read terminal generator index */
|
|
|
|
FSKIP (12);
|
|
|
|
if (zndx < pzndx)
|
|
|
|
throw(QString("Preset header indices not monotonic"));
|
|
|
|
int i2 = zndx - pzndx;
|
|
|
|
while (i2--)
|
|
|
|
pr->zones.prepend(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* preset bag loader */
|
|
|
|
void SFont::load_pbag (int size)
|
|
|
|
{
|
|
|
|
Zone *z, *pz = 0;
|
|
|
|
unsigned short genndx, modndx;
|
|
|
|
unsigned short pgenndx = 0, pmodndx = 0;
|
|
|
|
|
|
|
|
if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
|
|
|
|
throw(QString("Preset bag chunk size is invalid"));
|
|
|
|
|
|
|
|
foreach(Preset* p, presets) {
|
|
|
|
for (int i = 0; i < p->zones.size(); ++i) {
|
|
|
|
if ((size -= SFBAGSIZE) < 0)
|
|
|
|
throw(QString("1:Preset bag chunk size mismatch"));
|
|
|
|
z = new Zone;
|
|
|
|
genndx = READW(); /* possible read failure ^ */
|
|
|
|
modndx = READW();
|
|
|
|
z->sample = 0;
|
|
|
|
|
|
|
|
if (pz) { /* if not first zone */
|
|
|
|
if (genndx < pgenndx)
|
|
|
|
throw(QString("Preset bag generator indices not monotonic"));
|
|
|
|
if (modndx < pmodndx)
|
|
|
|
throw(QString("Preset bag modulator indices not monotonic"));
|
|
|
|
int ii = genndx - pgenndx;
|
|
|
|
while (ii--)
|
|
|
|
pz->gen.prepend(0);
|
|
|
|
ii = modndx - pmodndx;
|
|
|
|
while (ii--)
|
|
|
|
pz->mod.prepend(0);
|
|
|
|
}
|
|
|
|
pz = z; /* update previous zone ptr */
|
|
|
|
pgenndx = genndx; /* update previous zone gen index */
|
|
|
|
pmodndx = modndx; /* update previous zone mod index */
|
|
|
|
p->zones[i] = z;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size -= SFBAGSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("2:Preset bag chunk size mismatch"));
|
|
|
|
|
|
|
|
genndx = READW();
|
|
|
|
modndx = READW();
|
|
|
|
|
|
|
|
if (!pz) {
|
|
|
|
if (genndx > 0)
|
|
|
|
qWarning("No preset generators and terminal index not 0");
|
|
|
|
if (modndx > 0)
|
|
|
|
qWarning("No preset modulators and terminal index not 0");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (genndx < pgenndx)
|
|
|
|
throw(QString("Preset bag generator indices not monotonic"));
|
|
|
|
if (modndx < pmodndx)
|
|
|
|
throw(QString("Preset bag modulator indices not monotonic"));
|
|
|
|
int i = genndx - pgenndx;
|
|
|
|
while (i--)
|
|
|
|
pz->gen.prepend(0);
|
|
|
|
i = modndx - pmodndx;
|
|
|
|
while (i--)
|
|
|
|
pz->mod.prepend(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// load_pmod
|
|
|
|
// preset modulator loader
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::load_pmod (int size)
|
|
|
|
{
|
|
|
|
foreach (Preset* p, presets) {
|
|
|
|
foreach(Zone* p2, p->zones) {
|
|
|
|
for (int i = 0; i < p2->mod.size(); ++i) {
|
|
|
|
if ((size -= SFMODSIZE) < 0)
|
|
|
|
throw(QString("Preset modulator chunk size mismatch"));
|
|
|
|
SFMod* m = new SFMod;
|
|
|
|
m->src = READW();
|
|
|
|
m->dest = READW();
|
|
|
|
m->amount = READW();
|
|
|
|
m->amtsrc = READW();
|
|
|
|
m->trans = READW();
|
|
|
|
p2->mod[i] = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
If there isn't even a terminal record
|
|
|
|
Hmmm, the specs say there should be one, but..
|
|
|
|
*/
|
|
|
|
if (size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
size -= SFMODSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("Preset modulator chunk size mismatch"));
|
|
|
|
FSKIP (SFMODSIZE); /* terminal mod */
|
|
|
|
}
|
|
|
|
|
|
|
|
static const unsigned short badgen[] = {
|
|
|
|
Gen_Unused1, Gen_Unused2, Gen_Unused3, Gen_Unused4,
|
|
|
|
Gen_Reserved1, Gen_Reserved2, Gen_Reserved3, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned short badpgen[] = {
|
|
|
|
Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs,
|
|
|
|
Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_EndAddrCoarseOfs,
|
|
|
|
Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity,
|
|
|
|
Gen_EndLoopAddrCoarseOfs, Gen_SampleModes, Gen_ExclusiveClass,
|
|
|
|
Gen_OverrideRootKey, 0
|
|
|
|
};
|
|
|
|
|
|
|
|
/* check validity of instrument generator */
|
|
|
|
static int gen_valid (int gen)
|
|
|
|
{ /* is generator id valid? */
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (gen > Gen_MaxValid)
|
|
|
|
return false;
|
|
|
|
while (badgen[i] && badgen[i] != gen)
|
|
|
|
i++;
|
|
|
|
return (badgen[i] == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check validity of preset generator */
|
|
|
|
static int gen_validp (int gen)
|
|
|
|
{ /* is preset generator valid? */
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
if (!gen_valid (gen))
|
|
|
|
return false;
|
|
|
|
while (badpgen[i] && badpgen[i] != (unsigned short) gen)
|
|
|
|
i++;
|
|
|
|
return (badpgen[i] == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// gen_inlist
|
|
|
|
// Find generator in gen list
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
static SFGen* gen_inlist (int gen, const QList<SFGen*>& genlist)
|
|
|
|
{
|
|
|
|
foreach(SFGen* p, genlist) {
|
|
|
|
if (p == 0)
|
|
|
|
break;
|
|
|
|
if (gen == p->id)
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* delete zone from zone list */
|
|
|
|
static void sfont_zone_delete (QList<Zone*>* l, Zone * zone)
|
|
|
|
{
|
|
|
|
l->removeOne(zone);
|
|
|
|
delete zone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------
|
|
|
|
* preset generator loader
|
|
|
|
* generator (per preset) loading rules:
|
|
|
|
* Zones with no generators or modulators shall be annihilated
|
|
|
|
* Global zone must be 1st zone, discard additional ones (instrumentless zones)
|
|
|
|
*
|
|
|
|
* generator (per zone) loading rules (in order of decreasing precedence):
|
|
|
|
* KeyRange is 1st in list (if exists), else discard
|
|
|
|
* if a VelRange exists only preceded by a KeyRange, else discard
|
|
|
|
* if a generator follows an instrument discard it
|
|
|
|
* if a duplicate generator exists replace previous one
|
|
|
|
* ------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
void SFont::load_pgen (int size)
|
|
|
|
{
|
|
|
|
foreach(Preset* p, presets) {
|
|
|
|
bool gzone = false;
|
|
|
|
bool discarded = false;
|
|
|
|
|
|
|
|
QList<Zone*>& zl = p->zones;
|
|
|
|
for (int k = 0; k < zl.size(); ++k) {
|
|
|
|
int level = 0;
|
|
|
|
Zone* z = zl[k];
|
|
|
|
int i = 0;
|
|
|
|
for (; i < z->gen.size();) {
|
|
|
|
SFGen* dup = 0;
|
|
|
|
bool skip = false;
|
|
|
|
bool drop = false;
|
|
|
|
if ((size -= SFGENSIZE) < 0)
|
|
|
|
throw(QString("1:Preset generator chunk size mismatch"));
|
|
|
|
|
|
|
|
unsigned short genid = READW();
|
|
|
|
SFGenAmount genval;
|
|
|
|
if (genid == Gen_KeyRange) { /* nothing precedes */
|
|
|
|
if (level == 0) {
|
|
|
|
level = 1;
|
|
|
|
genval.range.lo = READB();
|
|
|
|
genval.range.hi = READB();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
else if (genid == Gen_VelRange) { // only KeyRange precedes
|
|
|
|
if (level <= 1) {
|
|
|
|
level = 2;
|
|
|
|
genval.range.lo = READB();
|
|
|
|
genval.range.hi = READB();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
else if (genid == Gen_Instrument) { /* inst is last gen */
|
|
|
|
level = 3;
|
|
|
|
genval.uword = READW();
|
|
|
|
z->instIdx = genval.uword + 1;
|
|
|
|
break; /* break out of generator loop */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
level = 2;
|
|
|
|
if (gen_validp (genid)) { /* generator valid? */
|
|
|
|
genval.sword = READW();
|
|
|
|
dup = gen_inlist (genid, z->gen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
if (!skip) {
|
|
|
|
SFGen *g;
|
|
|
|
if (!dup) { /* if gen ! dup alloc new */
|
|
|
|
g = new SFGen;
|
|
|
|
g->id = genid;
|
|
|
|
z->gen[i] = g;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
g = dup; // ptr to orig gen
|
|
|
|
drop = true;
|
|
|
|
}
|
|
|
|
g->amount = genval;
|
|
|
|
}
|
|
|
|
else { /* Skip this generator */
|
|
|
|
discarded = true;
|
|
|
|
drop = true;
|
|
|
|
FSKIPW ();
|
|
|
|
}
|
|
|
|
if (!drop)
|
|
|
|
++i;
|
|
|
|
else {
|
|
|
|
z->gen.removeAt(i); // drop place holder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (level == 3)
|
|
|
|
z->gen.removeAt(i); // drop place holder
|
|
|
|
else { // congratulations its a global zone
|
|
|
|
if (!gzone) { // Prior global zones?
|
|
|
|
gzone = true;
|
|
|
|
|
|
|
|
// if global zone is not 1st zone, relocate
|
|
|
|
if (k != 0) {
|
|
|
|
Zone* save = zl.takeAt(k);
|
2013-06-03 12:54:11 +02:00
|
|
|
qDebug("Preset \"%s\": Global zone is not first zone", qPrintable(p->name));
|
2012-05-26 14:49:10 +02:00
|
|
|
zl.prepend(save);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { // previous global zone exists, discard
|
2013-06-03 12:54:11 +02:00
|
|
|
qDebug("Preset \"%s\": Discarding invalid global zone", qPrintable(p->name));
|
2012-05-26 14:49:10 +02:00
|
|
|
sfont_zone_delete(&zl, z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (; i < z->gen.size();) {
|
|
|
|
discarded = true;
|
|
|
|
if ((size -= SFGENSIZE) < 0)
|
|
|
|
throw(QString("2:Preset generator chunk size mismatch"));
|
|
|
|
FSKIP (SFGENSIZE);
|
|
|
|
z->gen.removeAt(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (discarded)
|
2013-06-03 12:54:11 +02:00
|
|
|
qDebug("Preset \"%s\": Some invalid generators were discarded", qPrintable(p->name));
|
2012-05-26 14:49:10 +02:00
|
|
|
}
|
|
|
|
/* in case there isn't a terminal record */
|
|
|
|
if (size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
size -= SFGENSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("3:Preset generator chunk size mismatch"));
|
|
|
|
FSKIP (SFGENSIZE); /* terminal gen */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* instrument header loader */
|
|
|
|
void SFont::load_ihdr(int size)
|
|
|
|
{
|
|
|
|
Instrument *p, *pr = 0; /* ptr to current & previous instrument */
|
|
|
|
unsigned short zndx, pzndx = 0;
|
|
|
|
|
|
|
|
if (size % SFIHDRSIZE || size == 0) /* chunk size is valid? */
|
|
|
|
throw(QString("Instrumentrument header has invalid size"));
|
|
|
|
|
|
|
|
size = size / SFIHDRSIZE - 1;
|
|
|
|
if (size == 0) { /* at least one preset + term record */
|
|
|
|
qWarning("File contains no instruments");
|
|
|
|
FSKIP (SFIHDRSIZE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < size; i++) { /* load all instrument headers */
|
|
|
|
p = new Instrument;
|
|
|
|
instruments.append(p);
|
|
|
|
char buffer[21];
|
|
|
|
READSTR (buffer); /* Possible read failure ^ */
|
|
|
|
zndx = READW();
|
|
|
|
|
|
|
|
if (pr) { /* not first instrument? */
|
|
|
|
if (zndx < pzndx)
|
|
|
|
throw(QString("Instrument header indices not monotonic"));
|
|
|
|
int i2 = zndx - pzndx;
|
|
|
|
while (i2--)
|
|
|
|
pr->zones.prepend(0);
|
|
|
|
}
|
|
|
|
else if (zndx > 0) { /* 1st inst, warn if ofs >0 */
|
|
|
|
qWarning("%d instrument zones not referenced, discarding", zndx);
|
|
|
|
}
|
|
|
|
pzndx = zndx;
|
|
|
|
pr = p; /* update instrument ptr */
|
|
|
|
}
|
|
|
|
|
|
|
|
FSKIP (20);
|
|
|
|
zndx = READW();
|
|
|
|
|
|
|
|
if (zndx < pzndx)
|
|
|
|
throw(QString("Instrumentrument header indices not monotonic"));
|
|
|
|
int i2 = zndx - pzndx;
|
|
|
|
while (i2--)
|
|
|
|
pr->zones.prepend(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* instrument bag loader */
|
|
|
|
void SFont::load_ibag(int size)
|
|
|
|
{
|
|
|
|
Zone *z, *pz = 0;
|
|
|
|
unsigned short genndx, modndx, pgenndx = 0, pmodndx = 0;
|
|
|
|
|
|
|
|
if (size % SFBAGSIZE || size == 0) /* size is multiple of SFBAGSIZE? */
|
|
|
|
throw(QString("Instrumentrument bag chunk size is invalid"));
|
|
|
|
|
|
|
|
foreach(Instrument* in, instruments) {
|
|
|
|
int n = in->zones.size();
|
|
|
|
for (int i = 0; i < n; ++i) {
|
|
|
|
if ((size -= SFBAGSIZE) < 0) {
|
|
|
|
throw(QString("Instrument bag chunk size mismatch"));
|
|
|
|
}
|
|
|
|
z = new Zone;
|
|
|
|
in->zones[i] = z;
|
|
|
|
genndx = READW();
|
|
|
|
modndx = READW();
|
|
|
|
z->sample = 0;
|
|
|
|
|
|
|
|
if (pz) { /* if not first zone */
|
|
|
|
if (genndx < pgenndx)
|
|
|
|
throw(QString("Instrumentrument generator indices not monotonic"));
|
|
|
|
if (modndx < pmodndx)
|
|
|
|
throw(QString("Instrumentrument modulator indices not monotonic"));
|
|
|
|
int i2 = genndx - pgenndx;
|
|
|
|
while (i2--)
|
|
|
|
pz->gen.prepend(0);
|
|
|
|
i2 = modndx - pmodndx;
|
|
|
|
while (i2--)
|
|
|
|
pz->mod.prepend(0);
|
|
|
|
}
|
|
|
|
pz = z; /* update previous zone ptr */
|
|
|
|
pgenndx = genndx;
|
|
|
|
pmodndx = modndx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
size -= SFBAGSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("Instrumentrument chunk size mismatch"));
|
|
|
|
|
|
|
|
genndx = READW();
|
|
|
|
modndx = READW();
|
|
|
|
|
|
|
|
if (!pz) { /* in case that all are no zoners */
|
|
|
|
if (genndx > 0)
|
|
|
|
qWarning("No instrument generators and terminal index not 0");
|
|
|
|
if (modndx > 0)
|
|
|
|
qWarning("No instrument modulators and terminal index not 0");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (genndx < pgenndx)
|
|
|
|
throw(QString("Instrumentrument generator indices not monotonic"));
|
|
|
|
if (modndx < pmodndx)
|
|
|
|
throw(QString("Instrumentrument modulator indices not monotonic"));
|
|
|
|
int i = genndx - pgenndx;
|
|
|
|
while (i--)
|
|
|
|
pz->gen.prepend(0);
|
|
|
|
i = modndx - pmodndx;
|
|
|
|
while (i--)
|
|
|
|
pz->mod.prepend(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* instrument modulator loader */
|
|
|
|
void SFont::load_imod(int size)
|
|
|
|
{
|
|
|
|
foreach(Instrument* i, instruments) {
|
|
|
|
foreach(Zone* p2, i->zones) {
|
|
|
|
for (int k = 0; k < p2->mod.size(); ++k) {
|
|
|
|
if ((size -= SFMODSIZE) < 0)
|
|
|
|
throw(QString("Instrumentrument modulator chunk size mismatch"));
|
|
|
|
SFMod* m = new SFMod;
|
|
|
|
m->src = READW();
|
|
|
|
m->dest = READW();
|
|
|
|
m->amount = READW();
|
|
|
|
m->amtsrc = READW();
|
|
|
|
m->trans = READW();
|
|
|
|
p2->mod[k] = m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
If there isn't even a terminal record
|
|
|
|
Hmmm, the specs say there should be one, but..
|
|
|
|
*/
|
|
|
|
if (size == 0)
|
|
|
|
return;
|
|
|
|
size -= SFMODSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("Instrumentrument modulator chunk size mismatch"));
|
|
|
|
FSKIP (SFMODSIZE); /* terminal mod */
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// load_igen
|
|
|
|
// load instrument generators
|
|
|
|
// (see load_pgen for loading rules)
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::load_igen (int size)
|
|
|
|
{
|
|
|
|
foreach(Instrument* instr, instruments) {
|
|
|
|
bool gzone = false;
|
|
|
|
bool discarded = false;
|
|
|
|
|
|
|
|
QList<Zone*> zl = instr->zones;
|
|
|
|
|
|
|
|
for (int k = 0; k < zl.size(); ++k) {
|
|
|
|
int level = 0;
|
|
|
|
Zone* z = zl[k];
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
for (; i < z->gen.size();) {
|
|
|
|
SFGenAmount genval;
|
|
|
|
SFGen* dup = 0;
|
|
|
|
bool skip = false;
|
|
|
|
bool drop = false;
|
|
|
|
if ((size -= SFGENSIZE) < 0)
|
|
|
|
throw(QString("IGEN chunk size mismatch"));
|
|
|
|
|
|
|
|
unsigned short genid = READW();
|
|
|
|
|
|
|
|
if (genid == Gen_KeyRange) { /* nothing precedes */
|
|
|
|
if (level == 0) {
|
|
|
|
level = 1;
|
|
|
|
genval.range.lo = READB();
|
|
|
|
genval.range.hi = READB();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
else if (genid == Gen_VelRange) { // only KeyRange precedes
|
|
|
|
if (level <= 1) {
|
|
|
|
level = 2;
|
|
|
|
genval.range.lo = READB();
|
|
|
|
genval.range.hi = READB();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
else if (genid == Gen_SampleId) { // sample is last gen
|
|
|
|
level = 3;
|
|
|
|
genval.uword = READW();
|
|
|
|
z->sampIdx = genval.uword + 1;
|
|
|
|
break; /* break out of generator loop */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
level = 2;
|
|
|
|
if (gen_valid (genid)) { // gen valid?
|
|
|
|
genval.sword = READW();
|
|
|
|
dup = gen_inlist (genid, z->gen);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
skip = true;
|
|
|
|
}
|
|
|
|
if (!skip) {
|
|
|
|
SFGen* g;
|
|
|
|
if (!dup) { /* if gen ! dup alloc new */
|
|
|
|
g = new SFGen;
|
|
|
|
g->id = genid;
|
|
|
|
z->gen[i] = g;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
g = dup;
|
|
|
|
drop = true;
|
|
|
|
}
|
|
|
|
g->amount = genval;
|
|
|
|
}
|
|
|
|
else { /* skip this generator */
|
|
|
|
discarded = true;
|
|
|
|
drop = true;
|
|
|
|
FSKIPW ();
|
|
|
|
}
|
|
|
|
if (!drop)
|
|
|
|
++i;
|
|
|
|
else
|
|
|
|
z->gen.removeAt(i);
|
|
|
|
}
|
|
|
|
if (level == 3)
|
|
|
|
z->gen.removeAt(i);
|
|
|
|
else { /* its a global zone */
|
|
|
|
if (!gzone) {
|
|
|
|
gzone = true;
|
|
|
|
|
|
|
|
/* if global zone is not 1st zone, relocate */
|
|
|
|
if (k != 0) {
|
|
|
|
Zone* save = zl.takeAt(k);
|
|
|
|
zl.prepend(save);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else { /* previous global zone exists, discard */
|
|
|
|
sfont_zone_delete (&zl, z);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (; i < z->gen.size();) {
|
|
|
|
discarded = true;
|
|
|
|
if ((size -= SFGENSIZE) < 0)
|
|
|
|
throw(QString("Instrumentrument generator chunk size mismatch"));
|
|
|
|
FSKIP (SFGENSIZE);
|
|
|
|
z->gen.removeAt(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (discarded && debugMode)
|
|
|
|
qWarning("Instrument: Some invalid generators were discarded");
|
|
|
|
}
|
|
|
|
/* for those non-terminal record cases, grr! */
|
|
|
|
if (size == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
size -= SFGENSIZE;
|
|
|
|
if (size != 0)
|
|
|
|
throw(QString("IGEN chunk size mismatch"));
|
|
|
|
FSKIP (SFGENSIZE); /* terminal gen */
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// load_shdr
|
|
|
|
// sample header loader
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::load_shdr (int size)
|
|
|
|
{
|
|
|
|
if (size % SFSHDRSIZE || size == 0) /* size is multiple of SHDR size? */
|
|
|
|
throw(QString("Sample header has invalid size"));
|
|
|
|
|
|
|
|
size = size / SFSHDRSIZE - 1;
|
|
|
|
if (size == 0) { // at least one sample + term record?
|
|
|
|
qWarning("File contains no samples");
|
|
|
|
FSKIP (SFSHDRSIZE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* load all sample headers */
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
Sample* p = new Sample(this);
|
|
|
|
sample.append(p);
|
|
|
|
char buffer[21];
|
|
|
|
READSTR (buffer);
|
|
|
|
// READSTR (p->name);
|
|
|
|
READD (p->start);
|
|
|
|
READD (p->end); /* - end, loopstart and loopend */
|
|
|
|
READD (p->loopstart); /* - will be checked and turned into */
|
|
|
|
READD (p->loopend);
|
|
|
|
READD (p->samplerate);
|
|
|
|
p->origpitch = READB();
|
|
|
|
p->pitchadj = READC();
|
|
|
|
FSKIPW (); // skip sample link
|
|
|
|
p->sampletype = READW();
|
|
|
|
if (p->sampletype & FLUID_SAMPLETYPE_ROM) {
|
|
|
|
p->setValid(false);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if ((p->end > getSamplesize()) || (p->start > (p->end - 4))) {
|
|
|
|
qWarning("Sample start/end file positions are invalid, disabling");
|
|
|
|
p->setValid(false);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
p->setValid(true);
|
|
|
|
if (p->sampletype & FLUID_SAMPLETYPE_OGG_VORBIS) {
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// loop is fowled?? (cluck cluck :)
|
|
|
|
if (p->loopend > p->end || p->loopstart >= p->loopend || p->loopstart <= p->start) {
|
|
|
|
/* can pad loop by 8 samples and ensure at least 4 for loop (2*8+4) */
|
|
|
|
if ((p->end - p->start) >= 20) {
|
|
|
|
p->loopstart = p->start + 8;
|
|
|
|
p->loopend = p->end - 8;
|
|
|
|
}
|
|
|
|
else { // loop is fowled, sample is tiny (can't pad 8 samples)
|
|
|
|
p->loopstart = p->start + 1;
|
|
|
|
p->loopend = p->end - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((p->end - p->start) < 8)
|
|
|
|
p->setValid(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FSKIP (SFSHDRSIZE); /* skip terminal shdr */
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// fixup_pgen
|
|
|
|
// "fixup" (inst # -> inst ptr) instrument references
|
|
|
|
// in preset list
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::fixup_pgen()
|
|
|
|
{
|
|
|
|
foreach(Preset* p, presets) {
|
|
|
|
foreach(Zone* z, p->zones) {
|
|
|
|
if (z->instIdx) { // load instrument #
|
|
|
|
z->instrument = instruments[z->instIdx-1];
|
|
|
|
if (!z->instrument)
|
|
|
|
throw(QString("Preset %1 %2: Invalid instrument reference").arg(p->bank).arg(p->num));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* "fixup" (sample # -> sample ptr) sample references in instrument list */
|
|
|
|
void SFont::fixup_igen()
|
|
|
|
{
|
|
|
|
foreach(Instrument* p, instruments) {
|
|
|
|
foreach(Zone* z, p->zones) {
|
|
|
|
if (z->sampIdx) {
|
|
|
|
z->sample = sample[z->sampIdx - 1];
|
|
|
|
if (!z->sample)
|
|
|
|
throw(QString("Instrument: Invalid sample reference"));
|
|
|
|
// throw(QString("Instrument <%1>: Invalid sample reference").arg(p->name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// safe_fread
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::safe_fread(void* buf, int count)
|
|
|
|
{
|
|
|
|
if (f.read((char*)buf, count) != count) {
|
|
|
|
if (f.atEnd())
|
|
|
|
throw(QString("EOF while attemping to read %1 bytes").arg(count));
|
|
|
|
else
|
|
|
|
throw(QString("File read failed"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------
|
|
|
|
// safe_fseek
|
|
|
|
//---------------------------------------------------------
|
|
|
|
|
|
|
|
void SFont::safe_fseek(long ofs)
|
|
|
|
{
|
|
|
|
qint64 newpos = ofs + f.pos();
|
|
|
|
if (f.seek(newpos) == -1)
|
|
|
|
throw(QString("File seek failed with offset = %1").arg(ofs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|