From 9ea109ec980640e902bd94d4967299b97c2197ef Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 12:48:59 +0200 Subject: [PATCH 1/5] 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 From be0877e97c6d7fdf801f9080e74fbcc62fe9a079 Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 14:16:30 +0200 Subject: [PATCH 2/5] Sf3 to Sf2 Now possible to convert back from sf3 to sf2 (option y) Soundfont 2.04 can be converted also (sm24 chunk was rejected) The attenuation before compressing is stored as a comment in the ogg data so that the attenuation can be reverted when uncompressing it Adapted the documentation --- CMakeLists.txt | 32 ++++++- README.md | 32 ++++--- sf3convert.1 | 10 ++- sfconvert.cpp | 29 +++++-- sfont.cpp | 222 ++++++++++++++++++++++++++++++++++++++++--------- sfont.h | 6 +- 6 files changed, 267 insertions(+), 64 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95d5861..5f003da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,8 @@ if (MINGW) target_link_libraries(sf3convert ${QT_LIBRARIES} vorbis - ##vorbisfile + vorbisenc + vorbisfile ogg sndfile-1 ) @@ -52,6 +53,8 @@ if (MINGW) ${CROSS}/lib/libsndfile-1.dll ${CROSS}/lib/libogg.dll ${CROSS}/lib/libvorbis.dll + ${CROSS}/lib/libvorbisenc.dll + ${CROSS}/lib/libvorbisfile.dll ${CROSSQT}/bin/Qt5Core.dll ${CROSSQT}/bin/Qt5Xml.dll ${CROSSQT}/bin/icuin51.dll @@ -82,6 +85,28 @@ else (MINGW) message("libvorbis not found\n") endif (VORBIS_INCDIR) + ## + ## libvorbisenc + ## + + PKGCONFIG1 (vorbisenc 1.3.3 VORBISENC_INCDIR VORBISENC_LIBDIR VORBISENC_LIB VORBISENC_CPP) + if (VORBISENC_INCDIR) + message("libvorbisenc detected ${VORBISENC_INCDIR} ${VORBISENC_LIBDIR} ${VORBISENC_LIB}") + else (VORBISENC_INCDIR) + message("libvorbisenc not found\n") + endif (VORBISENC_INCDIR) + + ## + ## libvorbisfile + ## + + PKGCONFIG1 (vorbisfile 1.3.3 VORBISFILE_INCDIR VORBISFILE_LIBDIR VORBISFILE_LIB VORBISFILE_CPP) + if (VORBISFILE_INCDIR) + message("libvorbisfile detected ${VORBISFILE_INCDIR} ${VORBISFILE_LIBDIR} ${VORBISFILE_LIB}") + else (VORBISFILE_INCDIR) + message("libvorbisfile not found\n") + endif (VORBISFILE_INCDIR) + ## ## libogg @@ -103,13 +128,16 @@ else (MINGW) ${SNDFILE_INCDIR} ${OGG_INCDIR} ${VORBIS_INCDIR} + ${VORBISENC_INCDIR} + ${VORBISFILE_INCDIR} ) target_link_libraries(sf3convert ${QT_LIBRARIES} ${OGG_LIB} ${VORBIS_LIB} - vorbisenc + ${VORBISENC_LIB} + ${VORBISFILE_LIB} ${SNDFILE_LIB} ) diff --git a/README.md b/README.md index 531ddbc..bd2f8f9 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ -### sf3convert +# sf3convert -Utilities for SoundFont files. +Utilities for converting Soundfont files from version 2 to version 3 and back from version 3 to version 2. -* compress sound font files with ogg vorbis for use with [MuseScore](http://musescore.org) -* convert to "C" for embedding +### Sf3 format + +The sf3 format stores compressed data (OGG vorbis files) instead of raw data (WAVE) in a soundfont. There is thus a **loss in quality** but the resulting files are lighter and more prone to be **embedded**. This format has been primarily designed and used by [MuseScore](http://musescore.org) and is now supported by other projects: + +* [Calf-Fluidsynth](http://calf-studio-gear.org/) +* [Carla](http://kxstudio.linuxaudio.org/Applications:Carla) +* [LMMS](https://lmms.io) +* [Polyphone](https://www.polyphone-soundfonts.com) +* [Qsynth](https://qsynth.sourceforge.io) +* [Qtractor](https://qtractor.sourceforge.io) + +**The compressed soundfont has the major version number 3. It is non standard and no specifications have been written yet** ### Compilation @@ -14,6 +24,8 @@ Utilities for SoundFont files. * libsdnfile * libogg * libvorbis +* libvorbisenc +* libvorbisfile ``` $ make release @@ -21,14 +33,10 @@ $ make release ### Usage Example: -This compresses the Fluid sound font from 148 MBytes to 20 MBytes. - - sf3convert -z FluidR3.SF2 mops.sf3 +This compresses the Fluid soundfont from 148 MBytes to 20 MBytes. -**The compressed sound font has the major version number 3. Its non standard -and can be used only (so far) by [MuseScore](http://musescore.org).** + sf3convert -z FluidR3.SF2 compressed_soundfont.sf3 +This uncompresses a soundfont -### TODO: -Stereo samples are compressed as two single streams instead of compressing -them as stereo ogg vorbis streams. This may be less optimal. + sf3convert -y compressed_soundfont.sf3 FluidR3.sf2 \ No newline at end of file diff --git a/sf3convert.1 b/sf3convert.1 index fcd13fe..4e62fda 100644 --- a/sf3convert.1 +++ b/sf3convert.1 @@ -9,7 +9,7 @@ .Nd SoundFont conversion utility .Sh SYNOPSIS .Nm -.Op Fl cdsxz +.Op Fl cdsxyz .Op Fl a Ar ampl .Op Fl p Ar pres .Op Fl S Ar number @@ -20,7 +20,8 @@ The .Nm utility converts an SF2 format SoundFont; it can compress it -into SF3, encode as C for embedding into a binary, or as XML. +into SF3, uncompress from SF3, encode as C for embedding into a binary, +or as XML. .Pp The options are as follows: .Bl -tag -width xxx @@ -49,13 +50,16 @@ as the OGG stream serial number instead of a time-based random one. Create a small soundfont (one instrument/preset), pan to 0. .It Fl x Output XML. +.It Fl y +Uncompress the soundfont. .It Fl z Compress the soundfont. .El .Pp The .Fl c , -.Fl d +.Fl d , +.Fl y and .Fl z options are mutually exclusive. diff --git a/sfconvert.cpp b/sfconvert.cpp index e88c691..ef66181 100644 --- a/sfconvert.cpp +++ b/sfconvert.cpp @@ -26,7 +26,6 @@ #include #include "sfont.h" - //--------------------------------------------------------- // usage //--------------------------------------------------------- @@ -37,6 +36,7 @@ static void usage(const char* pname) fprintf(stderr, " -z compress sf\n"); fprintf(stderr, " -q qq ogg quality\n"); fprintf(stderr, " -a nn amplification in dB before ogg compression\n"); + fprintf(stderr, " -y uncompress sf\n"); fprintf(stderr, " -x xml output\n"); fprintf(stderr, " -c c output\n"); fprintf(stderr, " -p nn preset\n"); @@ -55,6 +55,7 @@ int main(int argc, char* argv[]) bool code = false; bool dump = false; bool compress = false; + bool uncompress = false; bool smallSf = false; double oggQuality = 0.3; double oggAmp = -1.0; @@ -67,7 +68,7 @@ int main(int argc, char* argv[]) fprintf(stderr, "%s: convert sound file\n", argv[0]); int c; - while ((c = getopt(argc, argv, "xcp:dS:szq:a:")) != EOF) { + while ((c = getopt(argc, argv, "xcp:dS:syzq:a:")) != EOF) { switch(c) { case 'x': xml = true; @@ -87,6 +88,9 @@ int main(int argc, char* argv[]) case 's': smallSf = true; break; + case 'y': + uncompress = true; + break; case 'z': compress = true; break; @@ -113,10 +117,14 @@ int main(int argc, char* argv[]) usage(pname); exit(3); } - if (!xml && !code && !dump && !compress) { + if (!xml && !code && !dump && !compress && !uncompress) { usage(pname); exit(4); } + if (compress && uncompress) { + usage(pname) + exit(5) + } SfTools::SoundFont sf(argv[0]); if (smallSf) @@ -144,10 +152,21 @@ int main(int argc, char* argv[]) if (xml) sf.writeXml(&fo); else - sf.write(&fo, oggQuality, oggAmp, oggSerial); + sf.compress(&fo, oggQuality, oggAmp, oggSerial); + fo.close(); + } + else if (uncompress) { + QFile fo(argv[1]); + if (!fo.open(QIODevice::WriteOnly)) { + fprintf(stderr, "cannot open <%s>\n", argv[2]); + exit(2); + } + if (xml) + sf.writeXml(&fo); + else + sf.uncompress(&fo); fo.close(); } qDebug("Soundfont converted in: %d ms", t.elapsed()); return 0; } - diff --git a/sfont.cpp b/sfont.cpp index 0e23b9e..8eb5886 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "sfont.h" #include "time.h" @@ -391,6 +393,9 @@ void SoundFont::readSection(const char* fourcc, int len) sampleLen = len; skip(len); break; + case FOURCC('s','m','2','4'): // audio samples (24-bit part) + skip(len); // Just skip it + break; case FOURCC('p','h','d','r'): // preset headers readPhdr(len); break; @@ -603,8 +608,6 @@ void SoundFont::readShdr(int size) s->sampleLink = readWord(); s->sampletype = readWord(); - s->loopstart -= s->start; - s->loopend -= s->start; // printf("readFontHeader %d %d %d %d\n", s->start, s->end, s->loopstart, s->loopend); samples.append(s); } @@ -615,12 +618,8 @@ void SoundFont::readShdr(int size) // write //--------------------------------------------------------- -bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSerial) +bool SoundFont::write() { - file = f; - _oggQuality = oggQuality; - _oggAmp = oggAmp; - _oggSerial = oggSerial; qint64 riffLenPos; qint64 listLenPos; try { @@ -698,6 +697,24 @@ bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSeri return true; } +bool SoundFont::compress(QFile* f, double oggQuality, double oggAmp, qint64 oggSerial) + { + file = f; + _compress = true; + _oggQuality = oggQuality; + _oggAmp = oggAmp; + _oggSerial = oggSerial; + + return write(); + } + +bool SoundFont::uncompress(QFile* f) + { + file = f; + _compress = false; + return write(); + } + //--------------------------------------------------------- // write //--------------------------------------------------------- @@ -734,8 +751,13 @@ void SoundFont::writeIfil() write("ifil", 4); writeDword(4); unsigned char data[4]; - if (writeCompressed) + if (_compress) { version.major = 3; + version.minor = 0; + } else { + version.major = 2; + version.minor = 1; + } data[0] = version.major; data[1] = version.major >> 8; data[2] = version.minor; @@ -753,35 +775,36 @@ void SoundFont::writeSmpl() qint64 pos = file->pos(); writeDword(0); - int sampleLen = 0; - if (writeCompressed) { + int currentSamplePos = 0; + if (_compress) { + // Compress wave data foreach(Sample* s, samples) { + // Loop start and end are now based on the beginning of each sample + s->loopstart -= s->start; + s->loopend -= s->start; + + // OGG flag added s->sampletype |= 0x10; - int len = writeCompressedSample(s); - s->start = sampleLen; - sampleLen += len; - s->end = sampleLen; + int len = writeCompressedSample(s); // In byte + s->start = currentSamplePos; + currentSamplePos += len; + s->end = currentSamplePos; } } else { - char* buffer = new char[sampleLen]; - QFile f(path); - if (!f.open(QIODevice::ReadOnly)) - throw(QString("cannot open <%1>").arg(f.fileName())); + // Uncompress from OGG data foreach(Sample* s, samples) { - f.seek(samplePos + s->start * sizeof(short)); - - int len = (s->end - s->start) * sizeof(short); - f.read(buffer, len); - write(buffer, len); - s->start = sampleLen / sizeof(short); - sampleLen += len; - s->end = sampleLen / sizeof(short); + // OGG flag is removed + s->sampletype &= ~0x10; + int len = writeUncompressedSample(s) / 2; // In sample data points (16 bits) + s->start = currentSamplePos; + currentSamplePos += len; + s->end = currentSamplePos; + + // Loop start and end are now based on the beginning of the section "smpl" s->loopstart += s->start; s->loopend += s->start; } - f.close(); - delete[] buffer; } qint64 npos = file->pos(); file->seek(pos); @@ -1030,6 +1053,9 @@ int SoundFont::writeCompressedSample(Sample* s) ogg_packet header_comm; ogg_packet header_code; + // Keep a track of the attenuation used before the compression + vorbis_comment_add(&vc, QString("AMP=%0\0").arg(_oggAmp).toStdString().c_str()); + vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code); ogg_stream_packetin(&os, &header); ogg_stream_packetin(&os, &header_comm); @@ -1120,16 +1146,6 @@ int SoundFont::writeCompressedSample(Sample* s) return n; } -//--------------------------------------------------------- -// readCompressedSample -//--------------------------------------------------------- - -char* SoundFont::readCompressedSample(Sample* s) - { - Q_UNUSED(s) - return 0; - } - //--------------------------------------------------------- // writeCSample //--------------------------------------------------------- @@ -1714,7 +1730,7 @@ static const char* generatorNames[] = { "Reserved3", "ScaleTune", "ExclusiveClass", "OverrideRootKey", "Dummy" }; - + //--------------------------------------------------------- // writeXml //--------------------------------------------------------- @@ -1873,4 +1889,130 @@ bool SoundFont::readXml(QFile* f) } return true; } -#endif \ No newline at end of file +#endif + + +struct VorbisData { + int pos; + QByteArray data; +}; + +static VorbisData vorbisData; +static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource); +static int ovSeek(void* datasource, ogg_int64_t offset, int whence); +static long ovTell(void* datasource); +static ov_callbacks ovCallbacks = { ovRead, ovSeek, 0, ovTell }; + +//--------------------------------------------------------- +// ovRead +//--------------------------------------------------------- + +static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource) +{ + VorbisData* vd = (VorbisData*)datasource; + size_t n = size * nmemb; + if (vd->data.size() < int(vd->pos + n)) + n = vd->data.size() - vd->pos; + if (n) { + const char* src = vd->data.data() + vd->pos; + memcpy(ptr, src, n); + vd->pos += n; + } + + return n; +} + +//--------------------------------------------------------- +// ovSeek +//--------------------------------------------------------- + +static int ovSeek(void* datasource, ogg_int64_t offset, int whence) +{ + VorbisData* vd = (VorbisData*)datasource; + switch(whence) { + case SEEK_SET: + vd->pos = offset; + break; + case SEEK_CUR: + vd->pos += offset; + break; + case SEEK_END: + vd->pos = vd->data.size() - offset; + break; + } + return 0; +} + +//--------------------------------------------------------- +// ovTell +//--------------------------------------------------------- + +static long ovTell(void* datasource) +{ + VorbisData* vd = (VorbisData*)datasource; + return vd->pos; +} + +//--------------------------------------------------------- +// writeUncompressedSample +//--------------------------------------------------------- + +int SoundFont::writeUncompressedSample(Sample* s) +{ + // Prepare input data + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) { + fprintf(stderr, "cannot open <%s>\n", qPrintable(f.fileName())); + return 0; + } + f.seek(samplePos + s->start); + int oggSize = s->end - s->start; + short * ibuffer = new short[oggSize]; + f.read((char*)ibuffer, oggSize); + f.close(); + + vorbisData.pos = 0; + vorbisData.data = QByteArray((char *)ibuffer, oggSize); + + // Decode + QByteArray decodedData; + OggVorbis_File vf; + vorbisData.pos = 0; + int length = 0; + if (ov_open_callbacks(&vorbisData, &vf, nullptr, 0, ovCallbacks) == 0) + { + double attenuation = 0; + for (int i = 0; i < vf.vc->comments; i++) + { + QString comment(vf.vc->user_comments[i]); + if (comment.contains("AMP=")) + { + QStringList split = comment.split('='); + if (split.size() == 2) + { + bool ok = false; + attenuation = split[1].toDouble(&ok); + if (!ok) + attenuation = 0; + } + } + } + double linearAmp = pow(10.0, attenuation / 20.0); + + short buffer[2048]; + int numberRead = 0; + int section = 0; + do { + numberRead = ov_read(&vf, (char *)buffer, 2048 * sizeof(short), 0, 2, 1, §ion); + for (unsigned int i = 0; i < numberRead / sizeof(short); i++) + buffer[i] = (double)buffer[i] / linearAmp; + write((char *)buffer, numberRead); + length += numberRead; + } while (numberRead); + + ov_clear(&vf); + } + + delete ibuffer; + return length; +} \ No newline at end of file diff --git a/sfont.h b/sfont.h index 6591344..5dbf406 100644 --- a/sfont.h +++ b/sfont.h @@ -229,14 +229,16 @@ class SoundFont { void writeShdr(); int writeCompressedSample(Sample*); + int writeUncompressedSample(Sample* s); bool writeCSample(Sample*, int); - char* readCompressedSample(Sample*); + bool write(); public: SoundFont(const QString&); ~SoundFont(); bool read(); - bool write(QFile*, double oggQuality, double oggAmp, qint64 oggSerial); + bool compress(QFile* f, double oggQuality, double oggAmp, qint64 oggSerial = rand()); + bool uncompress(QFile* f); bool writeCode(QList); bool writeCode(); void dumpPresets(); From c2ca7e15b280da519e9560e9639481eb0cd81f79 Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 15:14:02 +0200 Subject: [PATCH 3/5] iver and irom chunks Fix a bug: the iver and irom don't stop the conversion --- sfont.cpp | 92 +++++++++++++++++++++++++++++++++++++------------------ sfont.h | 20 +++++++----- 2 files changed, 74 insertions(+), 38 deletions(-) diff --git a/sfont.cpp b/sfont.cpp index 8eb5886..11a83bf 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -66,7 +66,16 @@ static const bool writeCompressed = true; Sample::Sample() { - name = 0; + name = nullptr; + start = 0; + end = 0; + loopstart = 0; + loopend = 0; + samplerate = 0; + origpitch = 0; + pitchadj = 0; + sampleLink = 0; + sampletype = 0; } Sample::~Sample() @@ -80,7 +89,7 @@ Sample::~Sample() Instrument::Instrument() { - name = 0; + name = nullptr; } Instrument::~Instrument() @@ -95,15 +104,20 @@ Instrument::~Instrument() SoundFont::SoundFont(const QString& s) { path = s; - engine = 0; - name = 0; - date = 0; - comment = 0; - tools = 0; - creator = 0; - product = 0; - copyright = 0; - smallSf = false; + engine = nullptr; + name = nullptr; + date = nullptr; + comment = nullptr; + tools = nullptr; + creator = nullptr; + product = nullptr; + copyright = nullptr; + irom = nullptr; + version.major = 0; + version.minor = 0; + iver.major = 0; + iver.minor = 0; + _smallSf = false; } SoundFont::~SoundFont() @@ -116,6 +130,7 @@ SoundFont::~SoundFont() free(creator); free(product); free(copyright); + free(irom); } //--------------------------------------------------------- @@ -329,13 +344,13 @@ int SoundFont::readChar() // readVersion //--------------------------------------------------------- -void SoundFont::readVersion() +void SoundFont::readVersion(sfVersionTag * v) { unsigned char data[4]; if (file->read((char*)data, 4) != 4) throw(QString("unexpected end of file\n")); - version.major = data[0] + (data[1] << 8); - version.minor = data[2] + (data[3] << 8); + v->major = data[0] + (data[1] << 8); + v->minor = data[2] + (data[3] << 8); } //--------------------------------------------------------- @@ -362,7 +377,7 @@ void SoundFont::readSection(const char* fourcc, int len) switch(FOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3])) { case FOURCC('i', 'f', 'i', 'l'): // version - readVersion(); + readVersion(&version); break; case FOURCC('I','N','A','M'): // sound font name name = readString(len); @@ -424,11 +439,14 @@ void SoundFont::readSection(const char* fourcc, int len) readShdr(len); break; case FOURCC('i', 'r', 'o', 'm'): // sample rom + irom = readString(len); + break; case FOURCC('i', 'v', 'e', 'r'): // sample rom version + readVersion(&iver); + break; default: skip(len); throw(QString("unknown fourcc <%1>").arg(fourcc)); - break; } } @@ -650,6 +668,9 @@ bool SoundFont::write() writeStringSection("ICMT", comment); if (copyright) writeStringSection("ICOP", copyright); + if (irom) + writeStringSection("irom", irom); + writeIver(); qint64 pos = file->pos(); file->seek(listLenPos); @@ -765,6 +786,22 @@ void SoundFont::writeIfil() write((char*)data, 4); } +//--------------------------------------------------------- +// writeIVer +//--------------------------------------------------------- + +void SoundFont::writeIver() + { + write("iver", 4); + writeDword(4); + unsigned char data[4]; + data[0] = iver.major; + data[1] = iver.major >> 8; + data[2] = iver.minor; + data[3] = iver.minor >> 8; + write((char*)data, 4); + } + //--------------------------------------------------------- // writeSmpl //--------------------------------------------------------- @@ -985,7 +1022,6 @@ void SoundFont::writeShdr() foreach(const Sample* s, samples) writeSample(s); Sample s; - memset(&s, 0, sizeof(s)); writeSample(&s); } @@ -1046,7 +1082,7 @@ int SoundFont::writeCompressedSample(Sample* s) vorbis_comment_init(&vc); vorbis_analysis_init(&vd, &vi); vorbis_block_init(&vd, &vb); - srand(time(NULL)); + srand(time(nullptr)); ogg_stream_init(&os, _oggSerial == std::numeric_limits::max() ? rand() : (int)_oggSerial); ogg_packet header; @@ -1298,7 +1334,7 @@ bool SoundFont::writeCode() end = (i+1) * n; QList sampleIdx; for (int idx = i * n; idx < end; ++idx) { - if (smallSf && !checkSample(presets, instruments, idx)) + if (_smallSf && !checkSample(presets, instruments, idx)) continue; Sample* s = samples[idx]; writeCSample(s, idx); @@ -1346,7 +1382,7 @@ bool SoundFont::writeCode() int idx2; int idx = 0; foreach(Instrument* instrument, instruments) { - if (smallSf && !checkInstrument(presets, idx)) { + if (_smallSf && !checkInstrument(presets, idx)) { ++idx; continue; } @@ -1361,7 +1397,6 @@ bool SoundFont::writeCode() QList gl; foreach(GeneratorList* g, z->generators) { - const char* name = generatorNames[g->gen]; if (g->gen == Gen_KeyRange) { keyLo = g->amount.lo; keyHi = g->amount.hi; @@ -1374,7 +1409,7 @@ bool SoundFont::writeCode() sampleIdx = g->amount.uword; else gl.append(g); - if (smallSf && g->gen == Gen_Pan) + if (_smallSf && g->gen == Gen_Pan) g->amount.uword = 0; } int idx3 = 0; @@ -1433,7 +1468,7 @@ bool SoundFont::writeCode() foreach(Preset* p, presets) { idx2 = 0; int zones = p->zones.size(); - if (smallSf) + if (_smallSf) zones = 1; foreach(Zone* z, p->zones) { @@ -1444,7 +1479,6 @@ bool SoundFont::writeCode() int instrIdx = -1; foreach(GeneratorList* g, z->generators) { - const char* name = generatorNames[g->gen]; if (g->gen == Gen_KeyRange) { keyLo = g->amount.lo; keyHi = g->amount.hi; @@ -1473,7 +1507,7 @@ bool SoundFont::writeCode() else fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, &instr%d);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi, instrIdx); } - if (smallSf) + if (_smallSf) break; ++idx2; } @@ -1569,7 +1603,6 @@ bool SoundFont::writeCode(QList pnums) QList gl; foreach(GeneratorList* g, z->generators) { - const char* name = generatorNames[g->gen]; if (g->gen == Gen_KeyRange) { keyLo = g->amount.lo; keyHi = g->amount.hi; @@ -1641,7 +1674,6 @@ bool SoundFont::writeCode(QList pnums) int veloHi = 127; int instrIdx = -1; foreach(GeneratorList* g, z->generators) { - const char* name = generatorNames[g->gen]; if (g->gen == Gen_KeyRange) { keyLo = g->amount.lo; keyHi = g->amount.hi; @@ -1901,7 +1933,7 @@ static VorbisData vorbisData; static size_t ovRead(void* ptr, size_t size, size_t nmemb, void* datasource); static int ovSeek(void* datasource, ogg_int64_t offset, int whence); static long ovTell(void* datasource); -static ov_callbacks ovCallbacks = { ovRead, ovSeek, 0, ovTell }; +static ov_callbacks ovCallbacks = { ovRead, ovSeek, nullptr, ovTell }; //--------------------------------------------------------- // ovRead @@ -2013,6 +2045,6 @@ int SoundFont::writeUncompressedSample(Sample* s) ov_clear(&vf); } - delete ibuffer; + delete[] ibuffer; return length; -} \ No newline at end of file +} diff --git a/sfont.h b/sfont.h index 5dbf406..13917fe 100644 --- a/sfont.h +++ b/sfont.h @@ -18,8 +18,8 @@ // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. //============================================================================= -#ifndef __SOUNDFONT_H__ -#define __SOUNDFONT_H__ +#ifndef SFONT_H +#define SFONT_H #include #include @@ -116,7 +116,7 @@ struct Preset { int morphology; QList zones; - Preset():name(0), preset(0), bank(0), presetBagNdx(0), library(0), genre(0), morphology(0) {} + Preset():name(nullptr), preset(0), bank(0), presetBagNdx(0), library(0), genre(0), morphology(0) {} }; //--------------------------------------------------------- @@ -168,6 +168,8 @@ class SoundFont { char* creator; char* product; char* copyright; + char* irom; + sfVersionTag iver; int samplePos; int sampleLen; @@ -182,10 +184,14 @@ class SoundFont { QFile* file; FILE* f; + bool _compress; double _oggQuality; double _oggAmp; qint64 _oggSerial; + // Extra option + bool _smallSf; + unsigned readDword(); int readWord(); int readShort(); @@ -197,7 +203,7 @@ class SoundFont { void readSignature(char* signature); void skip(int); void readSection(const char* fourcc, int len); - void readVersion(); + void readVersion(sfVersionTag* v); char* readString(int); void readPhdr(int); void readBag(int, QList*); @@ -220,6 +226,7 @@ class SoundFont { void writeInstrument(int zoneIdx, const Instrument*); void writeIfil(); + void writeIver(); void writeSmpl(); void writePhdr(); void writeBag(const char* fourcc, QList*); @@ -242,10 +249,6 @@ class SoundFont { bool writeCode(QList); bool writeCode(); void dumpPresets(); - }; - - // Extra option - bool smallSf; #ifndef SFTOOLS_NOXML private: @@ -256,6 +259,7 @@ class SoundFont { bool readXml(QFile*); bool writeXml(QFile*); #endif +}; } #endif From fd7367e1de58eaf00cdcdbf2ecfbd4a7c9e2a56e Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 17:21:18 +0200 Subject: [PATCH 4/5] creation of arrays --- sfont.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sfont.cpp b/sfont.cpp index 11a83bf..e4bbf90 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -1097,7 +1097,7 @@ int SoundFont::writeCompressedSample(Sample* s) ogg_stream_packetin(&os, &header_comm); ogg_stream_packetin(&os, &header_code); - char obuf[1048576]; // 1024 * 1024 + char* obuf = new char[1048576]; // 1024 * 1024 char* p = obuf; for (;;) { @@ -1178,6 +1178,7 @@ int SoundFont::writeCompressedSample(Sample* s) int n = p - obuf; write(obuf, n); + delete [] obuf; delete [] ibuffer; return n; } @@ -1875,7 +1876,7 @@ bool SoundFont::writeSampleFile(Sample* s, QString name) } f.seek(samplePos + s->start * sizeof(short)); int len = s->end - s->start; - short buffer[len]; + short * buffer = new short[len]; f.read((char*)buffer, len * sizeof(short)); f.close(); @@ -1892,10 +1893,12 @@ bool SoundFont::writeSampleFile(Sample* s, QString name) if (sf == 0) { fprintf(stderr, "open soundfile <%s> failed: %s\n", qPrintable(path), sf_strerror(sf)); + delete [] buffer; return false; } sf_write_short(sf, buffer, len); + delete [] buffer; if (sf_close(sf)) { fprintf(stderr, "close soundfile failed\n"); @@ -2048,3 +2051,4 @@ int SoundFont::writeUncompressedSample(Sample* s) delete[] ibuffer; return length; } + From 2e89c14baf849de5c36c9d842f7365ed8ad0261a Mon Sep 17 00:00:00 2001 From: davy7125 Date: Sun, 30 Aug 2020 17:28:13 +0200 Subject: [PATCH 5/5] QString arg --- sfont.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sfont.cpp b/sfont.cpp index e4bbf90..65f2a40 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -1090,7 +1090,7 @@ int SoundFont::writeCompressedSample(Sample* s) ogg_packet header_code; // Keep a track of the attenuation used before the compression - vorbis_comment_add(&vc, QString("AMP=%0\0").arg(_oggAmp).toStdString().c_str()); + vorbis_comment_add(&vc, QString("AMP=%1\0").arg(_oggAmp).toStdString().c_str()); vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code); ogg_stream_packetin(&os, &header);