From 9ea109ec980640e902bd94d4967299b97c2197ef Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 12:48:59 +0200 Subject: [PATCH] Integration and sample links Integration improvement (fix musescore#11): * preprocessor variable to loose the xml and libsnd dependencies * namespace SfTools * smallSf now a class variable * C++11 not needed anymore (struct initialization) * dynamic declaration of array now ok with visual C++ (fix musescore#10) * warning and unused variables removed Bug fix: * sample links now kept in the soundfont (fix musescore#9) --- sfconvert.cpp | 6 +- sfont.cpp | 390 +++++++++++++++++++++++++------------------------- sfont.h | 37 +++-- 3 files changed, 227 insertions(+), 206 deletions(-) diff --git a/sfconvert.cpp b/sfconvert.cpp index 99f88ce..e88c691 100644 --- a/sfconvert.cpp +++ b/sfconvert.cpp @@ -26,7 +26,6 @@ #include #include "sfont.h" -bool smallSf = false; //--------------------------------------------------------- // usage @@ -56,6 +55,7 @@ int main(int argc, char* argv[]) bool code = false; bool dump = false; bool compress = false; + bool smallSf = false; double oggQuality = 0.3; double oggAmp = -1.0; qint64 oggSerial = std::numeric_limits::max(); @@ -118,7 +118,9 @@ int main(int argc, char* argv[]) exit(4); } - SoundFont sf(argv[0]); + SfTools::SoundFont sf(argv[0]); + if (smallSf) + sf.smallSf = true; if (!sf.read()) { fprintf(stderr, "sf read error\n"); diff --git a/sfont.cpp b/sfont.cpp index 5061101..0e23b9e 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -23,16 +23,19 @@ #include #include #include -#include #include #include "sfont.h" -#include "xml.h" #include "time.h" +#ifndef SFTOOLS_NOXML +#include "xml.h" +#include +#endif + #include -extern bool smallSf; // create small sf +using namespace SfTools; // #define DEBUG @@ -98,6 +101,7 @@ SoundFont::SoundFont(const QString& s) creator = 0; product = 0; copyright = 0; + smallSf = false; } SoundFont::~SoundFont() @@ -596,7 +600,7 @@ void SoundFont::readShdr(int size) s->samplerate = readDword(); s->origpitch = readByte(); s->pitchadj = readChar(); - readWord(); // sampleLink + s->sampleLink = readWord(); s->sampletype = readWord(); s->loopstart -= s->start; @@ -607,124 +611,6 @@ void SoundFont::readShdr(int size) skip(46); // trailing record } -//--------------------------------------------------------- -// writeXml -//--------------------------------------------------------- - -bool SoundFont::writeXml(QFile* f) - { - Xml xml(f); - - xml.header(); - xml.stag("Sfont"); - xml.tag("version", QString("%1.%2").arg(version.major).arg(version.minor)); - if (name) - xml.tag("name", Xml::xmlString(name)); - if (engine) - xml.tag("engine", Xml::xmlString(engine)); - if (date) - xml.tag("date", Xml::xmlString(date)); - if (comment) - xml.tag("comment", Xml::xmlString(comment)); - if (tools) - xml.tag("tools", Xml::xmlString(tools)); - if (creator) - xml.tag("creator", Xml::xmlString(creator)); - if (product) - xml.tag("product", Xml::xmlString(product)); - if (copyright) - xml.tag("copyright", Xml::xmlString(copyright)); - - foreach(Preset* p, presets) { - xml.stag(QString("Preset name=\"%1\" preset=\"%2\" bank=\"%3\"") - .arg(p->name).arg(p->preset).arg(p->bank)); - foreach(Zone* z, p->zones) - write(xml, z); - xml.etag(); - } - foreach(Instrument* instrument, instruments) { - xml.stag(QString("Instrument name=\"%1\"").arg(instrument->name)); - foreach(Zone* z, instrument->zones) - write(xml, z); - xml.etag(); - } - int idx = 0; - foreach(Sample* s, samples) { - xml.stag(QString("Sample name=\"%1\"").arg(s->name)); - xml.tag("start", s->start); - xml.tag("end", s->end); - xml.tag("loopstart", s->loopstart); - xml.tag("loopend", s->loopend); - xml.tag("samplerate", s->samplerate); - xml.tag("origpitch", s->origpitch); - if (s->pitchadj) - xml.tag("pitchadj", s->pitchadj); - xml.tag("sampletype", s->sampletype); - xml.etag(); - writeSampleFile(s, QString("%1").arg(idx)); - ++idx; - } - xml.etag(); - return true; - } - -static const char* generatorNames[] = { - "StartAddrOfs", "EndAddrOfs", "StartLoopAddrOfs", - "EndLoopAddrOfs", "StartAddrCoarseOfs", "ModLFO2Pitch", - "VibLFO2Pitch", "ModEnv2Pitch", "FilterFc", "FilterQ", - "ModLFO2FilterFc", "ModEnv2FilterFc", "EndAddrCoarseOfs", - "ModLFO2Vol", "Unused1", "ChorusSend", "ReverbSend", "Pan", - "Unused2", "Unused3", "Unused4", - "ModLFODelay", "ModLFOFreq", "VibLFODelay", "VibLFOFreq", - "ModEnvDelay", "ModEnvAttack", "ModEnvHold", "ModEnvDecay", - "ModEnvSustain", "ModEnvRelease", "Key2ModEnvHold", - "Key2ModEnvDecay", "VolEnvDelay", "VolEnvAttack", - "VolEnvHold", "VolEnvDecay", "VolEnvSustain", "VolEnvRelease", - "Key2VolEnvHold", "Key2VolEnvDecay", "Instrument", - "Reserved1", "KeyRange", "VelRange", - "StartLoopAddrCoarseOfs", "Keynum", "Velocity", - "Attenuation", "Reserved2", "EndLoopAddrCoarseOfs", - "CoarseTune", "FineTune", "SampleId", "SampleModes", - "Reserved3", "ScaleTune", "ExclusiveClass", "OverrideRootKey", - "Dummy" - }; - -//--------------------------------------------------------- -// write -//--------------------------------------------------------- - -void SoundFont::write(Xml& xml, Zone* z) - { - xml.stag("Zone"); - foreach(GeneratorList* g, z->generators) { - const char* name = generatorNames[g->gen]; - if (g->gen == Gen_KeyRange || g->gen == Gen_VelRange) - xml.tagE(QString("Generator name=\"%1\" lo=\"%2\" hi=\"%3\"") - .arg(name).arg(g->amount.lo).arg(g->amount.hi)); - else if (g->gen == Gen_Instrument) { - int idx = g->amount.uword; - xml.tag("Instrument", instruments[idx]->name); - } - else if (g->gen == Gen_SampleId) { - int idx = g->amount.uword; - xml.tag("SampleId", samples[idx]->name); - } - else - xml.tagE(QString("Generator name=\"%1\" val=\"%2\"") - .arg(name).arg(g->amount.sword)); - } - foreach(ModulatorList* m, z->modulators) { - xml.stag("Modulator"); - xml.tag("src", m->src); - xml.tag("dst", m->dst); - xml.tag("amount", m->amount); - xml.tag("amtSrc", m->amtSrc); - xml.tag("transform", m->transform); - xml.etag(); - } - xml.etag(); - } - //--------------------------------------------------------- // write //--------------------------------------------------------- @@ -812,24 +698,6 @@ bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSeri return true; } -//--------------------------------------------------------- -// readXml -//--------------------------------------------------------- - -bool SoundFont::readXml(QFile* f) - { - QDomDocument doc; - int line, column; - QString err; - if (!doc.setContent(f, false, &err, &line, &column)) { - QString s; - printf("error reading file %s at line %d column %d: %s\n", - qPrintable(f->fileName()), line, column, qPrintable(err)); - return false; - } - return true; - } - //--------------------------------------------------------- // write //--------------------------------------------------------- @@ -936,7 +804,6 @@ void SoundFont::writePhdr() zoneIdx += p->zones.size(); } Preset p; - memset(&p, sizeof(p), 0); writePreset(zoneIdx, &p); } @@ -1067,7 +934,6 @@ void SoundFont::writeInst() zoneIdx += p->zones.size(); } Instrument p; - memset(&p, sizeof(p), 0); writeInstrument(zoneIdx, &p); } @@ -1118,54 +984,10 @@ void SoundFont::writeSample(const Sample* s) writeDword(s->samplerate); writeByte(s->origpitch); writeChar(s->pitchadj); - writeWord(0); + writeWord(s->sampleLink); writeWord(s->sampletype); } -//--------------------------------------------------------- -// writeSampleFile -//--------------------------------------------------------- - -bool SoundFont::writeSampleFile(Sample* s, QString name) - { - QString path = "waves/" + name + ".ogg"; - - QFile f(path); - if (!f.open(QIODevice::ReadOnly)) { - fprintf(stderr, "cannot open <%s>\n", qPrintable(f.fileName())); - return false; - } - f.seek(samplePos + s->start * sizeof(short)); - int len = s->end - s->start; - short buffer[len]; - f.read((char*)buffer, len * sizeof(short)); - f.close(); - - // int format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; - int format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; - - SF_INFO info; - memset(&info, 0, sizeof(info)); - info.channels = 1; - info.samplerate = s->samplerate; - info.format = format; - - SNDFILE* sf = sf_open(qPrintable(path), SFM_WRITE, &info); - if (sf == 0) { - fprintf(stderr, "open soundfile <%s> failed: %s\n", - qPrintable(path), sf_strerror(sf)); - return false; - } - - sf_write_short(sf, buffer, len); - - if (sf_close(sf)) { - fprintf(stderr, "close soundfile failed\n"); - return false; - } - return true; - } - //--------------------------------------------------------- // writeCompressedSample //--------------------------------------------------------- @@ -1179,7 +1001,7 @@ int SoundFont::writeCompressedSample(Sample* s) } f.seek(samplePos + s->start * sizeof(short)); int samples = s->end - s->start; - short ibuffer[samples]; + short * ibuffer = new short[samples]; f.read((char*)ibuffer, samples * sizeof(short)); f.close(); @@ -1195,6 +1017,7 @@ int SoundFont::writeCompressedSample(Sample* s) int ret = vorbis_encode_init_vbr(&vi, 1, s->samplerate, _oggQuality); if (ret) { printf("vorbis init failed\n"); + delete [] ibuffer; return false; } vorbis_comment_init(&vc); @@ -1212,7 +1035,7 @@ int SoundFont::writeCompressedSample(Sample* s) ogg_stream_packetin(&os, &header_comm); ogg_stream_packetin(&os, &header_code); - char obuf[1024 * 1024]; + char obuf[1048576]; // 1024 * 1024 char* p = obuf; for (;;) { @@ -1293,6 +1116,7 @@ int SoundFont::writeCompressedSample(Sample* s) int n = p - obuf; write(obuf, n); + delete [] ibuffer; return n; } @@ -1302,6 +1126,7 @@ int SoundFont::writeCompressedSample(Sample* s) char* SoundFont::readCompressedSample(Sample* s) { + Q_UNUSED(s) return 0; } @@ -1318,7 +1143,7 @@ bool SoundFont::writeCSample(Sample* s, int idx) } fi.seek(samplePos + s->start * sizeof(short)); int samples = s->end - s->start; - short ibuffer[samples]; + short * ibuffer = new short[samples]; fi.read((char*)ibuffer, samples * sizeof(short)); fi.close(); @@ -1338,6 +1163,7 @@ bool SoundFont::writeCSample(Sample* s, int idx) fprintf(f, ",\n "); } fprintf(f, "\n };\n"); + delete [] ibuffer; return true; } @@ -1362,7 +1188,6 @@ static bool checkInstrument(QList pnums, QList presets, int instrI } static bool checkInstrument(QList presets, int instrIdx) { - bool result = false; for(int i = 0; i < presets.size(); i++) { Preset* p = presets[i]; Zone* z = p->zones[0]; @@ -1389,7 +1214,6 @@ static bool checkSample(QList pnums, QList presets, QListzones.size(); foreach(Zone* z, instrument->zones) { QList gl; foreach(GeneratorList* g, z->generators) { @@ -1417,7 +1241,6 @@ static bool checkSample(QList presets, QList instruments, ++idx; continue; } - int zones = instrument->zones.size(); foreach(Zone* z, instrument->zones) { QList gl; foreach(GeneratorList* g, z->generators) { @@ -1870,3 +1693,184 @@ void SoundFont::dumpPresets() } +#ifndef SFTOOLS_NOXML +static const char* generatorNames[] = { + "StartAddrOfs", "EndAddrOfs", "StartLoopAddrOfs", + "EndLoopAddrOfs", "StartAddrCoarseOfs", "ModLFO2Pitch", + "VibLFO2Pitch", "ModEnv2Pitch", "FilterFc", "FilterQ", + "ModLFO2FilterFc", "ModEnv2FilterFc", "EndAddrCoarseOfs", + "ModLFO2Vol", "Unused1", "ChorusSend", "ReverbSend", "Pan", + "Unused2", "Unused3", "Unused4", + "ModLFODelay", "ModLFOFreq", "VibLFODelay", "VibLFOFreq", + "ModEnvDelay", "ModEnvAttack", "ModEnvHold", "ModEnvDecay", + "ModEnvSustain", "ModEnvRelease", "Key2ModEnvHold", + "Key2ModEnvDecay", "VolEnvDelay", "VolEnvAttack", + "VolEnvHold", "VolEnvDecay", "VolEnvSustain", "VolEnvRelease", + "Key2VolEnvHold", "Key2VolEnvDecay", "Instrument", + "Reserved1", "KeyRange", "VelRange", + "StartLoopAddrCoarseOfs", "Keynum", "Velocity", + "Attenuation", "Reserved2", "EndLoopAddrCoarseOfs", + "CoarseTune", "FineTune", "SampleId", "SampleModes", + "Reserved3", "ScaleTune", "ExclusiveClass", "OverrideRootKey", + "Dummy" + }; + +//--------------------------------------------------------- +// writeXml +//--------------------------------------------------------- + +bool SoundFont::writeXml(QFile* f) + { + Xml xml(f); + + xml.header(); + xml.stag("Sfont"); + xml.tag("version", QString("%1.%2").arg(version.major).arg(version.minor)); + if (name) + xml.tag("name", Xml::xmlString(name)); + if (engine) + xml.tag("engine", Xml::xmlString(engine)); + if (date) + xml.tag("date", Xml::xmlString(date)); + if (comment) + xml.tag("comment", Xml::xmlString(comment)); + if (tools) + xml.tag("tools", Xml::xmlString(tools)); + if (creator) + xml.tag("creator", Xml::xmlString(creator)); + if (product) + xml.tag("product", Xml::xmlString(product)); + if (copyright) + xml.tag("copyright", Xml::xmlString(copyright)); + + foreach(Preset* p, presets) { + xml.stag(QString("Preset name=\"%1\" preset=\"%2\" bank=\"%3\"") + .arg(p->name).arg(p->preset).arg(p->bank)); + foreach(Zone* z, p->zones) + write(xml, z); + xml.etag(); + } + foreach(Instrument* instrument, instruments) { + xml.stag(QString("Instrument name=\"%1\"").arg(instrument->name)); + foreach(Zone* z, instrument->zones) + write(xml, z); + xml.etag(); + } + int idx = 0; + foreach(Sample* s, samples) { + xml.stag(QString("Sample name=\"%1\"").arg(s->name)); + xml.tag("start", s->start); + xml.tag("end", s->end); + xml.tag("loopstart", s->loopstart); + xml.tag("loopend", s->loopend); + xml.tag("samplerate", s->samplerate); + xml.tag("origpitch", s->origpitch); + if (s->pitchadj) + xml.tag("pitchadj", s->pitchadj); + xml.tag("sampletype", s->sampletype); + xml.etag(); + writeSampleFile(s, QString("%1").arg(idx)); + ++idx; + } + xml.etag(); + return true; + } + +//--------------------------------------------------------- +// write +//--------------------------------------------------------- + +void SoundFont::write(Xml& xml, Zone* z) + { + xml.stag("Zone"); + foreach(GeneratorList* g, z->generators) { + const char* name = generatorNames[g->gen]; + if (g->gen == Gen_KeyRange || g->gen == Gen_VelRange) + xml.tagE(QString("Generator name=\"%1\" lo=\"%2\" hi=\"%3\"") + .arg(name).arg(g->amount.lo).arg(g->amount.hi)); + else if (g->gen == Gen_Instrument) { + int idx = g->amount.uword; + xml.tag("Instrument", instruments[idx]->name); + } + else if (g->gen == Gen_SampleId) { + int idx = g->amount.uword; + xml.tag("SampleId", samples[idx]->name); + } + else + xml.tagE(QString("Generator name=\"%1\" val=\"%2\"") + .arg(name).arg(g->amount.sword)); + } + foreach(ModulatorList* m, z->modulators) { + xml.stag("Modulator"); + xml.tag("src", m->src); + xml.tag("dst", m->dst); + xml.tag("amount", m->amount); + xml.tag("amtSrc", m->amtSrc); + xml.tag("transform", m->transform); + xml.etag(); + } + xml.etag(); + } + +//--------------------------------------------------------- +// writeSampleFile +//--------------------------------------------------------- + +bool SoundFont::writeSampleFile(Sample* s, QString name) + { + QString path = "waves/" + name + ".ogg"; + + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) { + fprintf(stderr, "cannot open <%s>\n", qPrintable(f.fileName())); + return false; + } + f.seek(samplePos + s->start * sizeof(short)); + int len = s->end - s->start; + short buffer[len]; + f.read((char*)buffer, len * sizeof(short)); + f.close(); + + // int format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; + int format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; + + SF_INFO info; + memset(&info, 0, sizeof(info)); + info.channels = 1; + info.samplerate = s->samplerate; + info.format = format; + + SNDFILE* sf = sf_open(qPrintable(path), SFM_WRITE, &info); + if (sf == 0) { + fprintf(stderr, "open soundfile <%s> failed: %s\n", + qPrintable(path), sf_strerror(sf)); + return false; + } + + sf_write_short(sf, buffer, len); + + if (sf_close(sf)) { + fprintf(stderr, "close soundfile failed\n"); + return false; + } + return true; + } + +//--------------------------------------------------------- +// readXml +//--------------------------------------------------------- + +bool SoundFont::readXml(QFile* f) + { + QDomDocument doc; + int line, column; + QString err; + if (!doc.setContent(f, false, &err, &line, &column)) { + QString s; + printf("error reading file %s at line %d column %d: %s\n", + qPrintable(f->fileName()), line, column, qPrintable(err)); + return false; + } + return true; + } +#endif \ No newline at end of file diff --git a/sfont.h b/sfont.h index ed204ef..6591344 100644 --- a/sfont.h +++ b/sfont.h @@ -27,6 +27,8 @@ class Xml; class QFile; +namespace SfTools { + //--------------------------------------------------------- // sfVersionTag //--------------------------------------------------------- @@ -105,14 +107,16 @@ struct Zone { //--------------------------------------------------------- struct Preset { - char* name {0}; - int preset {0}; - int bank {0}; - int presetBagNdx {0}; // used only for read - int library {0}; - int genre {0}; - int morphology {0}; + char* name; + int preset; + int bank; + int presetBagNdx; // used only for read + int library; + int genre; + int morphology; QList zones; + + Preset():name(0), preset(0), bank(0), presetBagNdx(0), library(0), genre(0), morphology(0) {} }; //--------------------------------------------------------- @@ -142,6 +146,7 @@ struct Sample { int origpitch; int pitchadj; + int sampleLink; int sampletype; Sample(); @@ -207,8 +212,6 @@ class SoundFont { void writeChar(char); void writeShort(short); void write(const char* p, int n); - void write(Xml&, Zone*); - bool writeSampleFile(Sample*, QString); void writeSample(const Sample*); void writeStringSection(const char* fourcc, char* s); void writePreset(int zoneIdx, const Preset*); @@ -234,11 +237,23 @@ class SoundFont { ~SoundFont(); bool read(); bool write(QFile*, double oggQuality, double oggAmp, qint64 oggSerial); - bool readXml(QFile*); - bool writeXml(QFile*); bool writeCode(QList); bool writeCode(); void dumpPresets(); }; + + // Extra option + bool smallSf; + +#ifndef SFTOOLS_NOXML + private: + void write(Xml&, Zone*); + bool writeSampleFile(Sample*, QString); + + public: + bool readXml(QFile*); + bool writeXml(QFile*); +#endif +} #endif