From 8182734c135bf12c8742c2ac512d703f1683f277 Mon Sep 17 00:00:00 2001 From: davy7125 Date: Mon, 5 Nov 2018 11:49:52 +0100 Subject: [PATCH 1/3] Integration and sample links Integration improvement (fix #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 #10) * warning and unused variables removed Bug fix: * sample links now kept in the soundfont (fix #9) --- sfconvert.cpp | 7 +- sfont.cpp | 2937 ++++++++++++++++++++++++------------------------- sfont.h | 334 +++--- 3 files changed, 1645 insertions(+), 1633 deletions(-) diff --git a/sfconvert.cpp b/sfconvert.cpp index 99f88ce..56122fa 100644 --- a/sfconvert.cpp +++ b/sfconvert.cpp @@ -26,8 +26,6 @@ #include #include "sfont.h" -bool smallSf = false; - //--------------------------------------------------------- // usage //--------------------------------------------------------- @@ -56,6 +54,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 +117,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..3a3baf8 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -23,30 +23,33 @@ #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 #define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) #ifdef __i486__ #define BE_LONG(x) \ - ({ int __value; \ - asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \ - __value; }) + ({ int __value; \ + asm ("bswap %1; movl %1,%0" : "=g" (__value) : "r" (x)); \ + __value; }) #else #define BE_LONG(x) ((((x)&0xFF)<<24) | \ - (((x)&0xFF00)<<8) | \ - (((x)&0xFF0000)>>8) | \ - (((x)>>24)&0xFF)) + (((x)&0xFF00)<<8) | \ + (((x)&0xFF0000)>>8) | \ + (((x)>>24)&0xFF)) #endif #define FOURCC(a, b, c, d) a << 24 | b << 16 | c << 8 | d @@ -60,1377 +63,1197 @@ static const bool writeCompressed = true; //--------------------------------------------------------- Sample::Sample() - { - name = 0; - } +{ + name = 0; +} Sample::~Sample() - { - free(name); - } +{ + free(name); +} //--------------------------------------------------------- // Instrument //--------------------------------------------------------- Instrument::Instrument() - { - name = 0; - } +{ + name = 0; +} Instrument::~Instrument() - { - free(name); - } +{ + free(name); +} //--------------------------------------------------------- // SoundFont //--------------------------------------------------------- SoundFont::SoundFont(const QString& s) - { - path = s; - engine = 0; - name = 0; - date = 0; - comment = 0; - tools = 0; - creator = 0; - product = 0; - copyright = 0; - } +{ + path = s; + engine = 0; + name = 0; + date = 0; + comment = 0; + tools = 0; + creator = 0; + product = 0; + copyright = 0; + smallSf = false; +} SoundFont::~SoundFont() - { - free(engine); - free(name); - free(date); - free(comment); - free(tools); - free(creator); - free(product); - free(copyright); - } +{ + free(engine); + free(name); + free(date); + free(comment); + free(tools); + free(creator); + free(product); + free(copyright); +} //--------------------------------------------------------- // read //--------------------------------------------------------- bool SoundFont::read() - { - file = new QFile(path); - if (!file->open(QIODevice::ReadOnly)) { - fprintf(stderr, "cannot open <%s>\n", qPrintable(path)); - delete file; - return false; - } - try { - int len = readFourcc("RIFF"); - readSignature("sfbk"); - len -= 4; - while (len) { - int len2 = readFourcc("LIST"); - len -= (len2 + 8); - char fourcc[5]; - fourcc[0] = 0; - readSignature(fourcc); - fourcc[4] = 0; - len2 -= 4; - while (len2) { - fourcc[0] = 0; - int len3 = readFourcc(fourcc); - fourcc[4] = 0; - len2 -= (len3 + 8); - readSection(fourcc, len3); - } - } - } - catch (QString s) { - printf("read sf file failed: %s\n", qPrintable(s)); - delete file; - return false; +{ + file = new QFile(path); + if (!file->open(QIODevice::ReadOnly)) { + fprintf(stderr, "cannot open <%s>\n", qPrintable(path)); + delete file; + return false; + } + try { + int len = readFourcc("RIFF"); + readSignature("sfbk"); + len -= 4; + while (len) { + int len2 = readFourcc("LIST"); + len -= (len2 + 8); + char fourcc[5]; + fourcc[0] = 0; + readSignature(fourcc); + fourcc[4] = 0; + len2 -= 4; + while (len2) { + fourcc[0] = 0; + int len3 = readFourcc(fourcc); + fourcc[4] = 0; + len2 -= (len3 + 8); + readSection(fourcc, len3); } - delete file; - return true; - } + } + } + catch (QString s) { + printf("read sf file failed: %s\n", qPrintable(s)); + delete file; + return false; + } + delete file; + return true; +} //--------------------------------------------------------- // skip //--------------------------------------------------------- void SoundFont::skip(int n) - { - qint64 pos = file->pos(); - if (!file->seek(pos + n)) - throw(QString("unexpected end of file\n")); - } +{ + qint64 pos = file->pos(); + if (!file->seek(pos + n)) + throw(QString("unexpected end of file\n")); +} //--------------------------------------------------------- // readFourcc //--------------------------------------------------------- int SoundFont::readFourcc(char* signature) - { - readSignature(signature); - return readDword(); - } +{ + readSignature(signature); + return readDword(); +} int SoundFont::readFourcc(const char* signature) - { - readSignature(signature); - return readDword(); - } +{ + readSignature(signature); + return readDword(); +} //--------------------------------------------------------- // readSignature //--------------------------------------------------------- void SoundFont::readSignature(const char* signature) - { - char fourcc[4]; - readSignature(fourcc); - if (memcmp(fourcc, signature, 4) != 0) - throw(QString("fourcc <%1> expected").arg(signature)); - } +{ + char fourcc[4]; + readSignature(fourcc); + if (memcmp(fourcc, signature, 4) != 0) + throw(QString("fourcc <%1> expected").arg(signature)); +} void SoundFont::readSignature(char* signature) - { - if (file->read(signature, 4) != 4) - throw(QString("unexpected end of file\n")); - } +{ + if (file->read(signature, 4) != 4) + throw(QString("unexpected end of file\n")); +} //--------------------------------------------------------- // readDword //--------------------------------------------------------- unsigned SoundFont::readDword() - { - unsigned format; - if (file->read((char*)&format, 4) != 4) - throw(QString("unexpected end of file\n")); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return BE_LONG(format); - else - return format; - } +{ + unsigned format; + if (file->read((char*)&format, 4) != 4) + throw(QString("unexpected end of file\n")); + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return BE_LONG(format); + else + return format; +} //--------------------------------------------------------- // writeDword //--------------------------------------------------------- void SoundFont::writeDword(int val) - { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - val = BE_LONG(val); - write((char*)&val, 4); - } +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + val = BE_LONG(val); + write((char*)&val, 4); +} //--------------------------------------------------------- // writeWord //--------------------------------------------------------- void SoundFont::writeWord(unsigned short int val) - { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - val = BE_SHORT(val); - write((char*)&val, 2); - } +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + val = BE_SHORT(val); + write((char*)&val, 2); +} //--------------------------------------------------------- // writeByte //--------------------------------------------------------- void SoundFont::writeByte(unsigned char val) - { - write((char*)&val, 1); - } +{ + write((char*)&val, 1); +} //--------------------------------------------------------- // writeChar //--------------------------------------------------------- void SoundFont::writeChar(char val) - { - write((char*)&val, 1); - } +{ + write((char*)&val, 1); +} //--------------------------------------------------------- // writeShort //--------------------------------------------------------- void SoundFont::writeShort(short val) - { - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - val = BE_SHORT(val); - write((char*)&val, 2); - } +{ + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + val = BE_SHORT(val); + write((char*)&val, 2); +} //--------------------------------------------------------- // readWord //--------------------------------------------------------- int SoundFont::readWord() - { - unsigned short format; - if (file->read((char*)&format, 2) != 2) - throw(QString("unexpected end of file\n")); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return BE_SHORT(format); - else - return format; - } +{ + unsigned short format; + if (file->read((char*)&format, 2) != 2) + throw(QString("unexpected end of file\n")); + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return BE_SHORT(format); + else + return format; +} //--------------------------------------------------------- // readShort //--------------------------------------------------------- int SoundFont::readShort() - { - short format; - if (file->read((char*)&format, 2) != 2) - throw(QString("unexpected end of file\n")); - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - return BE_SHORT(format); - else - return format; - } +{ + short format; + if (file->read((char*)&format, 2) != 2) + throw(QString("unexpected end of file\n")); + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) + return BE_SHORT(format); + else + return format; +} //--------------------------------------------------------- // readByte //--------------------------------------------------------- int SoundFont::readByte() - { - uchar val; - if (file->read((char*)&val, 1) != 1) - throw(QString("unexpected end of file\n")); - return val; - } +{ + uchar val; + if (file->read((char*)&val, 1) != 1) + throw(QString("unexpected end of file\n")); + return val; +} //--------------------------------------------------------- // readChar //--------------------------------------------------------- int SoundFont::readChar() - { - char val; - if (file->read(&val, 1) != 1) - throw(QString("unexpected end of file\n")); - return val; - } +{ + char val; + if (file->read(&val, 1) != 1) + throw(QString("unexpected end of file\n")); + return val; +} //--------------------------------------------------------- // readVersion //--------------------------------------------------------- void SoundFont::readVersion() - { - 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); - } +{ + 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); +} //--------------------------------------------------------- // readString //--------------------------------------------------------- char* SoundFont::readString(int n) - { - char data[2500]; - if (file->read((char*)data, n) != n) - throw(QString("unexpected end of file\n")); - if (data[n-1] != 0) - data[n] = 0; - return strdup(data); - } +{ + char data[2500]; + if (file->read((char*)data, n) != n) + throw(QString("unexpected end of file\n")); + if (data[n-1] != 0) + data[n] = 0; + return strdup(data); +} //--------------------------------------------------------- // readSection //--------------------------------------------------------- void SoundFont::readSection(const char* fourcc, int len) - { - printf("readSection <%s> len %d\n", fourcc, len); - - switch(FOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3])) { - case FOURCC('i', 'f', 'i', 'l'): // version - readVersion(); - break; - case FOURCC('I','N','A','M'): // sound font name - name = readString(len); - break; - case FOURCC('i','s','n','g'): // target render engine - engine = readString(len); - break; - case FOURCC('I','P','R','D'): // product for which the bank was intended - product = readString(len); - break; - case FOURCC('I','E','N','G'): // sound designers and engineers for the bank - creator = readString(len); - break; - case FOURCC('I','S','F','T'): // SoundFont tools used to create and alter the bank - tools = readString(len); - break; - case FOURCC('I','C','R','D'): // date of creation of the bank - date = readString(len); - break; - case FOURCC('I','C','M','T'): // comments on the bank - comment = readString(len); - break; - case FOURCC('I','C','O','P'): // copyright message - copyright = readString(len); - break; - case FOURCC('s','m','p','l'): // the digital audio samples - samplePos = file->pos(); - sampleLen = len; - skip(len); - break; - case FOURCC('p','h','d','r'): // preset headers - readPhdr(len); - break; - case FOURCC('p','b','a','g'): // preset index list - readBag(len, &pZones); - break; - case FOURCC('p','m','o','d'): // preset modulator list - readMod(len, &pZones); - break; - case FOURCC('p','g','e','n'): // preset generator list - readGen(len, &pZones); - break; - case FOURCC('i','n','s','t'): // instrument names and indices - readInst(len); - break; - case FOURCC('i','b','a','g'): // instrument index list - readBag(len, &iZones); - break; - case FOURCC('i','m','o','d'): // instrument modulator list - readMod(len, &iZones); - break; - case FOURCC('i','g','e','n'): // instrument generator list - readGen(len, &iZones); - break; - case FOURCC('s','h','d','r'): // sample headers - readShdr(len); - break; - case FOURCC('i', 'r', 'o', 'm'): // sample rom - case FOURCC('i', 'v', 'e', 'r'): // sample rom version - default: - skip(len); - throw(QString("unknown fourcc <%1>").arg(fourcc)); - break; - } - } +{ + printf("readSection <%s> len %d\n", fourcc, len); + + switch(FOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3])) { + case FOURCC('i', 'f', 'i', 'l'): // version + readVersion(); + break; + case FOURCC('I','N','A','M'): // sound font name + name = readString(len); + break; + case FOURCC('i','s','n','g'): // target render engine + engine = readString(len); + break; + case FOURCC('I','P','R','D'): // product for which the bank was intended + product = readString(len); + break; + case FOURCC('I','E','N','G'): // sound designers and engineers for the bank + creator = readString(len); + break; + case FOURCC('I','S','F','T'): // SoundFont tools used to create and alter the bank + tools = readString(len); + break; + case FOURCC('I','C','R','D'): // date of creation of the bank + date = readString(len); + break; + case FOURCC('I','C','M','T'): // comments on the bank + comment = readString(len); + break; + case FOURCC('I','C','O','P'): // copyright message + copyright = readString(len); + break; + case FOURCC('s','m','p','l'): // the digital audio samples + samplePos = file->pos(); + sampleLen = len; + skip(len); + break; + case FOURCC('p','h','d','r'): // preset headers + readPhdr(len); + break; + case FOURCC('p','b','a','g'): // preset index list + readBag(len, &pZones); + break; + case FOURCC('p','m','o','d'): // preset modulator list + readMod(len, &pZones); + break; + case FOURCC('p','g','e','n'): // preset generator list + readGen(len, &pZones); + break; + case FOURCC('i','n','s','t'): // instrument names and indices + readInst(len); + break; + case FOURCC('i','b','a','g'): // instrument index list + readBag(len, &iZones); + break; + case FOURCC('i','m','o','d'): // instrument modulator list + readMod(len, &iZones); + break; + case FOURCC('i','g','e','n'): // instrument generator list + readGen(len, &iZones); + break; + case FOURCC('s','h','d','r'): // sample headers + readShdr(len); + break; + case FOURCC('i', 'r', 'o', 'm'): // sample rom + case FOURCC('i', 'v', 'e', 'r'): // sample rom version + default: + skip(len); + throw(QString("unknown fourcc <%1>").arg(fourcc)); + break; + } +} //--------------------------------------------------------- // readPhdr //--------------------------------------------------------- void SoundFont::readPhdr(int len) - { - if (len < (38 * 2)) - throw(QString("phdr too short")); - if (len % 38) - throw(QString("phdr not a multiple of 38")); - int n = len / 38; - if (n <= 1) { - printf("no presets\n"); - skip(len); - return; - } - int index1 = 0, index2; - for (int i = 0; i < n; ++i) { - Preset* preset = new Preset; - preset->name = readString(20); - preset->preset = readWord(); - preset->bank = readWord(); - index2 = readWord(); - preset->library = readDword(); - preset->genre = readDword(); - preset->morphology = readDword(); - if (index2 < index1) - throw("preset header indices not monotonic"); - if (i > 0) { - int n = index2 - index1; - while (n--) { - Zone* z = new Zone; - presets.back()->zones.append(z); - pZones.append(z); - } - } - index1 = index2; - presets.append(preset); +{ + if (len < (38 * 2)) + throw(QString("phdr too short")); + if (len % 38) + throw(QString("phdr not a multiple of 38")); + int n = len / 38; + if (n <= 1) { + printf("no presets\n"); + skip(len); + return; + } + int index1 = 0, index2; + for (int i = 0; i < n; ++i) { + Preset* preset = new Preset; + preset->name = readString(20); + preset->preset = readWord(); + preset->bank = readWord(); + index2 = readWord(); + preset->library = readDword(); + preset->genre = readDword(); + preset->morphology = readDword(); + if (index2 < index1) + throw("preset header indices not monotonic"); + if (i > 0) { + int n = index2 - index1; + while (n--) { + Zone* z = new Zone; + presets.back()->zones.append(z); + pZones.append(z); } - presets.takeLast(); - } + } + index1 = index2; + presets.append(preset); + } + presets.takeLast(); +} //--------------------------------------------------------- // readBag //--------------------------------------------------------- void SoundFont::readBag(int len, QList* zones) - { - if (len % 4) - throw(QString("bag size not a multiple of 4")); - int gIndex2, mIndex2; - int gIndex1 = readWord(); - int mIndex1 = readWord(); - len -= 4; - foreach(Zone* zone, *zones) { - gIndex2 = readWord(); - mIndex2 = readWord(); - len -= 4; - if (len < 0) - throw(QString("bag size too small")); - if (gIndex2 < gIndex1) - throw("generator indices not monotonic"); - if (mIndex2 < mIndex1) - throw("modulator indices not monotonic"); - int n = mIndex2 - mIndex1; - while (n--) - zone->modulators.append(new ModulatorList); - n = gIndex2 - gIndex1; - while (n--) - zone->generators.append(new GeneratorList); - gIndex1 = gIndex2; - mIndex1 = mIndex2; - } - } +{ + if (len % 4) + throw(QString("bag size not a multiple of 4")); + int gIndex2, mIndex2; + int gIndex1 = readWord(); + int mIndex1 = readWord(); + len -= 4; + foreach(Zone* zone, *zones) { + gIndex2 = readWord(); + mIndex2 = readWord(); + len -= 4; + if (len < 0) + throw(QString("bag size too small")); + if (gIndex2 < gIndex1) + throw("generator indices not monotonic"); + if (mIndex2 < mIndex1) + throw("modulator indices not monotonic"); + int n = mIndex2 - mIndex1; + while (n--) + zone->modulators.append(new ModulatorList); + n = gIndex2 - gIndex1; + while (n--) + zone->generators.append(new GeneratorList); + gIndex1 = gIndex2; + mIndex1 = mIndex2; + } +} //--------------------------------------------------------- // readMod //--------------------------------------------------------- void SoundFont::readMod(int size, QList* zones) - { - foreach(Zone* zone, *zones) { - foreach(ModulatorList* m, zone->modulators) { - size -= 10; - if (size < 0) - throw(QString("pmod size mismatch")); - m->src = static_cast(readWord()); - m->dst = static_cast(readWord()); - m->amount = readShort(); - m->amtSrc = static_cast(readWord()); - m->transform = static_cast(readWord()); - } - } - if (size != 10) - throw(QString("modulator list size mismatch")); - skip(10); - } +{ + foreach(Zone* zone, *zones) { + foreach(ModulatorList* m, zone->modulators) { + size -= 10; + if (size < 0) + throw(QString("pmod size mismatch")); + m->src = static_cast(readWord()); + m->dst = static_cast(readWord()); + m->amount = readShort(); + m->amtSrc = static_cast(readWord()); + m->transform = static_cast(readWord()); + } + } + if (size != 10) + throw(QString("modulator list size mismatch")); + skip(10); +} //--------------------------------------------------------- // readGen //--------------------------------------------------------- void SoundFont::readGen(int size, QList* zones) - { - if (size % 4) - throw(QString("bad generator list size")); - foreach (Zone* zone, *zones) { - size -= (zone->generators.size() * 4); - if (size < 0) - break; - - foreach (GeneratorList* gen, zone->generators) { - gen->gen = static_cast(readWord()); - if (gen->gen == Gen_KeyRange || gen->gen == Gen_VelRange) { - gen->amount.lo = readByte(); - gen->amount.hi = readByte(); - } - else if (gen->gen == Gen_Instrument) - gen->amount.uword = readWord(); - else - gen->amount.sword = readWord(); - } +{ + if (size % 4) + throw(QString("bad generator list size")); + foreach (Zone* zone, *zones) { + size -= (zone->generators.size() * 4); + if (size < 0) + break; + + foreach (GeneratorList* gen, zone->generators) { + gen->gen = static_cast(readWord()); + if (gen->gen == Gen_KeyRange || gen->gen == Gen_VelRange) { + gen->amount.lo = readByte(); + gen->amount.hi = readByte(); } - if (size != 4) - throw(QString("generator list size mismatch %1 != 4").arg(size)); - skip(size); - } + else if (gen->gen == Gen_Instrument) + gen->amount.uword = readWord(); + else + gen->amount.sword = readWord(); + } + } + if (size != 4) + throw(QString("generator list size mismatch %1 != 4").arg(size)); + skip(size); +} //--------------------------------------------------------- // readInst //--------------------------------------------------------- void SoundFont::readInst(int size) - { - int n = size / 22; - int index1 = 0, index2; - for (int i = 0; i < n; ++i) { - Instrument* instrument = new Instrument; - instrument->name = readString(20); - index2 = readWord(); - if (index2 < index1) - throw("instrument header indices not monotonic"); - if (i > 0) { - int n = index2 - index1; - while (n--) { - Zone* z = new Zone; - instruments.back()->zones.append(z); - iZones.append(z); - } - } - index1 = index2; - instruments.append(instrument); +{ + int n = size / 22; + int index1 = 0, index2; + for (int i = 0; i < n; ++i) { + Instrument* instrument = new Instrument; + instrument->name = readString(20); + index2 = readWord(); + if (index2 < index1) + throw("instrument header indices not monotonic"); + if (i > 0) { + int n = index2 - index1; + while (n--) { + Zone* z = new Zone; + instruments.back()->zones.append(z); + iZones.append(z); } - instruments.takeLast(); - } + } + index1 = index2; + instruments.append(instrument); + } + instruments.takeLast(); +} //--------------------------------------------------------- // readShdr //--------------------------------------------------------- void SoundFont::readShdr(int size) - { - int n = size / 46; - for (int i = 0; i < n-1; ++i) { - Sample* s = new Sample; - s->name = readString(20); - s->start = readDword(); - s->end = readDword(); - s->loopstart = readDword(); - s->loopend = readDword(); - s->samplerate = readDword(); - s->origpitch = readByte(); - s->pitchadj = readChar(); - readWord(); // sampleLink - 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); - } - 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(); - } +{ + int n = size / 46; + for (int i = 0; i < n-1; ++i) { + Sample* s = new Sample; + s->name = readString(20); + s->start = readDword(); + s->end = readDword(); + s->loopstart = readDword(); + s->loopend = readDword(); + s->samplerate = readDword(); + s->origpitch = readByte(); + s->pitchadj = readChar(); + 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); + } + skip(46); // trailing record +} //--------------------------------------------------------- // write //--------------------------------------------------------- bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSerial) - { - file = f; - _oggQuality = oggQuality; - _oggAmp = oggAmp; - _oggSerial = oggSerial; - qint64 riffLenPos; - qint64 listLenPos; - try { - file->write("RIFF", 4); - riffLenPos = file->pos(); - writeDword(0); - file->write("sfbk", 4); - - file->write("LIST", 4); - listLenPos = file->pos(); - writeDword(0); - file->write("INFO", 4); - - writeIfil(); - if (name) - writeStringSection("INAM", name); - if (engine) - writeStringSection("isng", engine); - if (product) - writeStringSection("IPRD", product); - if (creator) - writeStringSection("IENG", creator); - if (tools) - writeStringSection("ISFT", tools); - if (date) - writeStringSection("ICRD", date); - if (comment) - writeStringSection("ICMT", comment); - if (copyright) - writeStringSection("ICOP", copyright); - - qint64 pos = file->pos(); - file->seek(listLenPos); - writeDword(pos - listLenPos - 4); - file->seek(pos); - - file->write("LIST", 4); - listLenPos = file->pos(); - writeDword(0); - file->write("sdta", 4); - writeSmpl(); - pos = file->pos(); - file->seek(listLenPos); - writeDword(pos - listLenPos - 4); - file->seek(pos); - - file->write("LIST", 4); - listLenPos = file->pos(); - writeDword(0); - file->write("pdta", 4); - - writePhdr(); - writeBag("pbag", &pZones); - writeMod("pmod", &pZones); - writeGen("pgen", &pZones); - writeInst(); - writeBag("ibag", &iZones); - writeMod("imod", &iZones); - writeGen("igen", &iZones); - writeShdr(); - - pos = file->pos(); - file->seek(listLenPos); - writeDword(pos - listLenPos - 4); - file->seek(pos); - - qint64 endPos = file->pos(); - file->seek(riffLenPos); - writeDword(endPos - riffLenPos - 4); - } - catch (QString s) { - printf("write sf file failed: %s\n", qPrintable(s)); - 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; - } +{ + file = f; + _oggQuality = oggQuality; + _oggAmp = oggAmp; + _oggSerial = oggSerial; + qint64 riffLenPos; + qint64 listLenPos; + try { + file->write("RIFF", 4); + riffLenPos = file->pos(); + writeDword(0); + file->write("sfbk", 4); + + file->write("LIST", 4); + listLenPos = file->pos(); + writeDword(0); + file->write("INFO", 4); + + writeIfil(); + if (name) + writeStringSection("INAM", name); + if (engine) + writeStringSection("isng", engine); + if (product) + writeStringSection("IPRD", product); + if (creator) + writeStringSection("IENG", creator); + if (tools) + writeStringSection("ISFT", tools); + if (date) + writeStringSection("ICRD", date); + if (comment) + writeStringSection("ICMT", comment); + if (copyright) + writeStringSection("ICOP", copyright); + + qint64 pos = file->pos(); + file->seek(listLenPos); + writeDword(pos - listLenPos - 4); + file->seek(pos); + + file->write("LIST", 4); + listLenPos = file->pos(); + writeDword(0); + file->write("sdta", 4); + writeSmpl(); + pos = file->pos(); + file->seek(listLenPos); + writeDword(pos - listLenPos - 4); + file->seek(pos); + + file->write("LIST", 4); + listLenPos = file->pos(); + writeDword(0); + file->write("pdta", 4); + + writePhdr(); + writeBag("pbag", &pZones); + writeMod("pmod", &pZones); + writeGen("pgen", &pZones); + writeInst(); + writeBag("ibag", &iZones); + writeMod("imod", &iZones); + writeGen("igen", &iZones); + writeShdr(); + + pos = file->pos(); + file->seek(listLenPos); + writeDword(pos - listLenPos - 4); + file->seek(pos); + + qint64 endPos = file->pos(); + file->seek(riffLenPos); + writeDword(endPos - riffLenPos - 4); + } + catch (QString s) { + printf("write sf file failed: %s\n", qPrintable(s)); + return false; + } + return true; +} //--------------------------------------------------------- // write //--------------------------------------------------------- void SoundFont::write(const char* p, int n) - { - if (file->write(p, n) != n) - throw(QString("write error")); - } +{ + if (file->write(p, n) != n) + throw(QString("write error")); +} //--------------------------------------------------------- // writeStringSection //--------------------------------------------------------- void SoundFont::writeStringSection(const char* fourcc, char* s) - { - write(fourcc, 4); - int nn = strlen(s) + 1; - int n = ((nn + 1) / 2) * 2; - writeDword(n); - write(s, nn); - if (n - nn) { - char c = 0; - write(&c, 1); - } - } +{ + write(fourcc, 4); + int nn = strlen(s) + 1; + int n = ((nn + 1) / 2) * 2; + writeDword(n); + write(s, nn); + if (n - nn) { + char c = 0; + write(&c, 1); + } +} //--------------------------------------------------------- // writeIfil //--------------------------------------------------------- void SoundFont::writeIfil() - { - write("ifil", 4); - writeDword(4); - unsigned char data[4]; - if (writeCompressed) - version.major = 3; - data[0] = version.major; - data[1] = version.major >> 8; - data[2] = version.minor; - data[3] = version.minor >> 8; - write((char*)data, 4); - } +{ + write("ifil", 4); + writeDword(4); + unsigned char data[4]; + if (writeCompressed) + version.major = 3; + data[0] = version.major; + data[1] = version.major >> 8; + data[2] = version.minor; + data[3] = version.minor >> 8; + write((char*)data, 4); +} //--------------------------------------------------------- // writeSmpl //--------------------------------------------------------- void SoundFont::writeSmpl() - { - write("smpl", 4); - - qint64 pos = file->pos(); - writeDword(0); - int sampleLen = 0; - if (writeCompressed) { - foreach(Sample* s, samples) { - s->sampletype |= 0x10; - int len = writeCompressedSample(s); - s->start = sampleLen; - sampleLen += len; - s->end = sampleLen; - } - } - else { - char* buffer = new char[sampleLen]; - QFile f(path); - if (!f.open(QIODevice::ReadOnly)) - throw(QString("cannot open <%1>").arg(f.fileName())); - 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); - s->loopstart += s->start; - s->loopend += s->start; - } - f.close(); - delete[] buffer; - } - qint64 npos = file->pos(); - file->seek(pos); - writeDword(npos - pos - 4); - file->seek(npos); - } +{ + write("smpl", 4); + + qint64 pos = file->pos(); + writeDword(0); + int sampleLen = 0; + if (writeCompressed) { + foreach(Sample* s, samples) { + s->sampletype |= 0x10; + int len = writeCompressedSample(s); + s->start = sampleLen; + sampleLen += len; + s->end = sampleLen; + } + } + else { + char* buffer = new char[sampleLen]; + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) + throw(QString("cannot open <%1>").arg(f.fileName())); + 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); + s->loopstart += s->start; + s->loopend += s->start; + } + f.close(); + delete[] buffer; + } + qint64 npos = file->pos(); + file->seek(pos); + writeDword(npos - pos - 4); + file->seek(npos); +} //--------------------------------------------------------- // writePhdr //--------------------------------------------------------- void SoundFont::writePhdr() - { - write("phdr", 4); - int n = presets.size(); - writeDword((n + 1) * 38); - int zoneIdx = 0; - foreach(const Preset* p, presets) { - writePreset(zoneIdx, p); - zoneIdx += p->zones.size(); - } - Preset p; - memset(&p, sizeof(p), 0); - writePreset(zoneIdx, &p); - } +{ + write("phdr", 4); + int n = presets.size(); + writeDword((n + 1) * 38); + int zoneIdx = 0; + foreach(const Preset* p, presets) { + writePreset(zoneIdx, p); + zoneIdx += p->zones.size(); + } + Preset p; + writePreset(zoneIdx, &p); +} //--------------------------------------------------------- // writePreset //--------------------------------------------------------- void SoundFont::writePreset(int zoneIdx, const Preset* preset) - { - char name[20]; - memset(name, 0, 20); - if (preset->name) - memcpy(name, preset->name, strlen(preset->name)); - write(name, 20); - writeWord(preset->preset); - writeWord(preset->bank); - writeWord(zoneIdx); - writeDword(preset->library); - writeDword(preset->genre); - writeDword(preset->morphology); - } +{ + char name[20]; + memset(name, 0, 20); + if (preset->name) + memcpy(name, preset->name, strlen(preset->name)); + write(name, 20); + writeWord(preset->preset); + writeWord(preset->bank); + writeWord(zoneIdx); + writeDword(preset->library); + writeDword(preset->genre); + writeDword(preset->morphology); +} //--------------------------------------------------------- // writeBag //--------------------------------------------------------- void SoundFont::writeBag(const char* fourcc, QList* zones) - { - write(fourcc, 4); - int n = zones->size(); - writeDword((n + 1) * 4); - int gIndex = 0; - int pIndex = 0; - foreach(const Zone* z, *zones) { - writeWord(gIndex); - writeWord(pIndex); - gIndex += z->generators.size(); - pIndex += z->modulators.size(); - } - writeWord(gIndex); - writeWord(pIndex); - } +{ + write(fourcc, 4); + int n = zones->size(); + writeDword((n + 1) * 4); + int gIndex = 0; + int pIndex = 0; + foreach(const Zone* z, *zones) { + writeWord(gIndex); + writeWord(pIndex); + gIndex += z->generators.size(); + pIndex += z->modulators.size(); + } + writeWord(gIndex); + writeWord(pIndex); +} //--------------------------------------------------------- // writeMod //--------------------------------------------------------- void SoundFont::writeMod(const char* fourcc, const QList* zones) - { - write(fourcc, 4); - int n = 0; - foreach(const Zone* z, *zones) - n += z->modulators.size(); - writeDword((n + 1) * 10); - - foreach(const Zone* zone, *zones) { - foreach(const ModulatorList* m, zone->modulators) - writeModulator(m); - } - ModulatorList mod; - memset(&mod, 0, sizeof(mod)); - writeModulator(&mod); - } +{ + write(fourcc, 4); + int n = 0; + foreach(const Zone* z, *zones) + n += z->modulators.size(); + writeDword((n + 1) * 10); + + foreach(const Zone* zone, *zones) { + foreach(const ModulatorList* m, zone->modulators) + writeModulator(m); + } + ModulatorList mod; + memset(&mod, 0, sizeof(mod)); + writeModulator(&mod); +} //--------------------------------------------------------- // writeModulator //--------------------------------------------------------- void SoundFont::writeModulator(const ModulatorList* m) - { - writeWord(m->src); - writeWord(m->dst); - writeShort(m->amount); - writeWord(m->amtSrc); - writeWord(m->transform); - } +{ + writeWord(m->src); + writeWord(m->dst); + writeShort(m->amount); + writeWord(m->amtSrc); + writeWord(m->transform); +} //--------------------------------------------------------- // writeGen //--------------------------------------------------------- void SoundFont::writeGen(const char* fourcc, QList* zones) - { - write(fourcc, 4); - int n = 0; - foreach(const Zone* z, *zones) - n += z->generators.size(); - writeDword((n + 1) * 4); - - foreach(const Zone* zone, *zones) { - foreach(const GeneratorList* g, zone->generators) - writeGenerator(g); - } - GeneratorList gen; - memset(&gen, 0, sizeof(gen)); - writeGenerator(&gen); - } +{ + write(fourcc, 4); + int n = 0; + foreach(const Zone* z, *zones) + n += z->generators.size(); + writeDword((n + 1) * 4); + + foreach(const Zone* zone, *zones) { + foreach(const GeneratorList* g, zone->generators) + writeGenerator(g); + } + GeneratorList gen; + memset(&gen, 0, sizeof(gen)); + writeGenerator(&gen); +} //--------------------------------------------------------- // writeGenerator //--------------------------------------------------------- void SoundFont::writeGenerator(const GeneratorList* g) - { - writeWord(g->gen); - if (g->gen == Gen_KeyRange || g->gen == Gen_VelRange) { - writeByte(g->amount.lo); - writeByte(g->amount.hi); - } - else if (g->gen == Gen_Instrument) - writeWord(g->amount.uword); - else - writeWord(g->amount.sword); - } +{ + writeWord(g->gen); + if (g->gen == Gen_KeyRange || g->gen == Gen_VelRange) { + writeByte(g->amount.lo); + writeByte(g->amount.hi); + } + else if (g->gen == Gen_Instrument) + writeWord(g->amount.uword); + else + writeWord(g->amount.sword); +} //--------------------------------------------------------- // writeInst //--------------------------------------------------------- void SoundFont::writeInst() - { - write("inst", 4); - int n = instruments.size(); - writeDword((n + 1) * 22); - int zoneIdx = 0; - foreach(const Instrument* p, instruments) { - writeInstrument(zoneIdx, p); - zoneIdx += p->zones.size(); - } - Instrument p; - memset(&p, sizeof(p), 0); - writeInstrument(zoneIdx, &p); - } +{ + write("inst", 4); + int n = instruments.size(); + writeDword((n + 1) * 22); + int zoneIdx = 0; + foreach(const Instrument* p, instruments) { + writeInstrument(zoneIdx, p); + zoneIdx += p->zones.size(); + } + Instrument p; + writeInstrument(zoneIdx, &p); +} //--------------------------------------------------------- // writeInstrument //--------------------------------------------------------- void SoundFont::writeInstrument(int zoneIdx, const Instrument* instrument) - { - char name[20]; - memset(name, 0, 20); - if (instrument->name) - memcpy(name, instrument->name, strlen(instrument->name)); - write(name, 20); - writeWord(zoneIdx); - } +{ + char name[20]; + memset(name, 0, 20); + if (instrument->name) + memcpy(name, instrument->name, strlen(instrument->name)); + write(name, 20); + writeWord(zoneIdx); +} //--------------------------------------------------------- // writeShdr //--------------------------------------------------------- void SoundFont::writeShdr() - { - write("shdr", 4); - writeDword(46 * (samples.size() + 1)); - foreach(const Sample* s, samples) - writeSample(s); - Sample s; - memset(&s, 0, sizeof(s)); - writeSample(&s); - } +{ + write("shdr", 4); + writeDword(46 * (samples.size() + 1)); + foreach(const Sample* s, samples) + writeSample(s); + Sample s; + memset(&s, 0, sizeof(s)); + writeSample(&s); +} //--------------------------------------------------------- // writeSample //--------------------------------------------------------- void SoundFont::writeSample(const Sample* s) - { - char name[20]; - memset(name, 0, 20); - if (s->name) - memcpy(name, s->name, strlen(s->name)); - write(name, 20); - writeDword(s->start); - writeDword(s->end); - writeDword(s->loopstart); - writeDword(s->loopend); - writeDword(s->samplerate); - writeByte(s->origpitch); - writeChar(s->pitchadj); - writeWord(0); - 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; - } +{ + char name[20]; + memset(name, 0, 20); + if (s->name) + memcpy(name, s->name, strlen(s->name)); + write(name, 20); + writeDword(s->start); + writeDword(s->end); + writeDword(s->loopstart); + writeDword(s->loopend); + writeDword(s->samplerate); + writeByte(s->origpitch); + writeChar(s->pitchadj); + writeWord(s->sampleLink); + writeWord(s->sampletype); +} //--------------------------------------------------------- // writeCompressedSample //--------------------------------------------------------- int SoundFont::writeCompressedSample(Sample* s) - { - QFile f(path); - if (!f.open(QIODevice::ReadOnly)) { - fprintf(stderr, "cannot open <%s>\n", qPrintable(f.fileName())); - return 0; - } - f.seek(samplePos + s->start * sizeof(short)); - int samples = s->end - s->start; - short ibuffer[samples]; - f.read((char*)ibuffer, samples * sizeof(short)); - f.close(); - - ogg_stream_state os; - ogg_page og; - ogg_packet op; - vorbis_info vi; - vorbis_dsp_state vd; - vorbis_block vb; - vorbis_comment vc; - - vorbis_info_init(&vi); - int ret = vorbis_encode_init_vbr(&vi, 1, s->samplerate, _oggQuality); - if (ret) { - printf("vorbis init failed\n"); - return false; - } - vorbis_comment_init(&vc); - vorbis_analysis_init(&vd, &vi); - vorbis_block_init(&vd, &vb); - srand(time(NULL)); - ogg_stream_init(&os, _oggSerial == std::numeric_limits::max() ? rand() : (int)_oggSerial); - - ogg_packet header; - ogg_packet header_comm; - ogg_packet header_code; - - vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code); - ogg_stream_packetin(&os, &header); - ogg_stream_packetin(&os, &header_comm); - ogg_stream_packetin(&os, &header_code); - - char obuf[1024 * 1024]; - char* p = obuf; - - for (;;) { - int result = ogg_stream_flush(&os, &og); - if (result == 0) - break; - memcpy(p, og.header, og.header_len); - p += og.header_len; - memcpy(p, og.body, og.body_len); - p += og.body_len; - } - - long i; - int page = 0; - double linearAmp = pow(10.0, _oggAmp / 20.0); - for(;;) { - int bufflength = qMin(BLOCK_SIZE, samples-page*BLOCK_SIZE); - float **buffer = vorbis_analysis_buffer(&vd, bufflength); - int j = 0; - int max = qMin((page+1)*BLOCK_SIZE, samples); - for (i = page * BLOCK_SIZE; i < max ; i++) { - buffer[0][j] = (ibuffer[i] / 32768.f) * linearAmp; - j++; - } - - vorbis_analysis_wrote(&vd, bufflength); - - while (vorbis_analysis_blockout(&vd, &vb) == 1) { - vorbis_analysis(&vb, 0); - vorbis_bitrate_addblock(&vb); - - while (vorbis_bitrate_flushpacket(&vd, &op)) { - ogg_stream_packetin(&os, &op); - - for(;;) { - int result = ogg_stream_pageout(&os, &og); - if (result == 0) - break; - memcpy(p, og.header, og.header_len); - p += og.header_len; - memcpy(p, og.body, og.body_len); - p += og.body_len; - } - } - } - page++; - if ((max == samples) || !((samples-page*BLOCK_SIZE)>0)) - break; - } - - vorbis_analysis_wrote(&vd, 0); - - while (vorbis_analysis_blockout(&vd, &vb) == 1) { +{ + QFile f(path); + if (!f.open(QIODevice::ReadOnly)) { + fprintf(stderr, "cannot open <%s>\n", qPrintable(f.fileName())); + return 0; + } + f.seek(samplePos + s->start * sizeof(short)); + int samples = s->end - s->start; + short * ibuffer = new short[samples]; + f.read((char*)ibuffer, samples * sizeof(short)); + f.close(); + + ogg_stream_state os; + ogg_page og; + ogg_packet op; + vorbis_info vi; + vorbis_dsp_state vd; + vorbis_block vb; + vorbis_comment vc; + + vorbis_info_init(&vi); + 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); + vorbis_analysis_init(&vd, &vi); + vorbis_block_init(&vd, &vb); + srand(time(NULL)); + ogg_stream_init(&os, _oggSerial == std::numeric_limits::max() ? rand() : (int)_oggSerial); + + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code); + ogg_stream_packetin(&os, &header); + ogg_stream_packetin(&os, &header_comm); + ogg_stream_packetin(&os, &header_code); + + char obuf[1048576]; // 1024 * 1024 + char* p = obuf; + + for (;;) { + int result = ogg_stream_flush(&os, &og); + if (result == 0) + break; + memcpy(p, og.header, og.header_len); + p += og.header_len; + memcpy(p, og.body, og.body_len); + p += og.body_len; + } + + long i; + int page = 0; + double linearAmp = pow(10.0, _oggAmp / 20.0); + for(;;) { + int bufflength = qMin(BLOCK_SIZE, samples-page*BLOCK_SIZE); + float **buffer = vorbis_analysis_buffer(&vd, bufflength); + int j = 0; + int max = qMin((page+1)*BLOCK_SIZE, samples); + for (i = page * BLOCK_SIZE; i < max ; i++) { + buffer[0][j] = (ibuffer[i] / 32768.f) * linearAmp; + j++; + } + + vorbis_analysis_wrote(&vd, bufflength); + + while (vorbis_analysis_blockout(&vd, &vb) == 1) { vorbis_analysis(&vb, 0); vorbis_bitrate_addblock(&vb); while (vorbis_bitrate_flushpacket(&vd, &op)) { - ogg_stream_packetin(&os, &op); - - for(;;) { - int result = ogg_stream_pageout(&os, &og); - if (result == 0) - break; - memcpy(p, og.header, og.header_len); - p += og.header_len; - memcpy(p, og.body, og.body_len); - p += og.body_len; - } - } + ogg_stream_packetin(&os, &op); + + for(;;) { + int result = ogg_stream_pageout(&os, &og); + if (result == 0) + break; + memcpy(p, og.header, og.header_len); + p += og.header_len; + memcpy(p, og.body, og.body_len); + p += og.body_len; + } } + } + page++; + if ((max == samples) || !((samples-page*BLOCK_SIZE)>0)) + break; + } + + vorbis_analysis_wrote(&vd, 0); + + while (vorbis_analysis_blockout(&vd, &vb) == 1) { + vorbis_analysis(&vb, 0); + vorbis_bitrate_addblock(&vb); + + while (vorbis_bitrate_flushpacket(&vd, &op)) { + ogg_stream_packetin(&os, &op); + + for(;;) { + int result = ogg_stream_pageout(&os, &og); + if (result == 0) + break; + memcpy(p, og.header, og.header_len); + p += og.header_len; + memcpy(p, og.body, og.body_len); + p += og.body_len; + } + } + } - ogg_stream_clear(&os); - vorbis_block_clear(&vb); - vorbis_dsp_clear(&vd); - vorbis_comment_clear(&vc); - vorbis_info_clear(&vi); + ogg_stream_clear(&os); + vorbis_block_clear(&vb); + vorbis_dsp_clear(&vd); + vorbis_comment_clear(&vc); + vorbis_info_clear(&vi); - int n = p - obuf; - write(obuf, n); + int n = p - obuf; + write(obuf, n); - return n; - } + delete [] ibuffer; + return n; +} //--------------------------------------------------------- // readCompressedSample //--------------------------------------------------------- char* SoundFont::readCompressedSample(Sample* s) - { - return 0; - } +{ + Q_UNUSED(s) + return 0; +} //--------------------------------------------------------- // writeCSample //--------------------------------------------------------- bool SoundFont::writeCSample(Sample* s, int idx) - { - QFile fi(path); - if (!fi.open(QIODevice::ReadOnly)) { - fprintf(stderr, "cannot open <%s>\n", qPrintable(fi.fileName())); - return false; - } - fi.seek(samplePos + s->start * sizeof(short)); - int samples = s->end - s->start; - short ibuffer[samples]; - fi.read((char*)ibuffer, samples * sizeof(short)); - fi.close(); - - fprintf(f, "static const short wave%d[] = {\n ", idx); - int n = 0; - for (int row = 0;; ++row) { - for (int col = 0; col < 16; ++col) { - if (n >= samples) - break; - if (col != 0) - fprintf(f, ", "); - fprintf(f, "%d", ibuffer[n]); - ++n; - } +{ + QFile fi(path); + if (!fi.open(QIODevice::ReadOnly)) { + fprintf(stderr, "cannot open <%s>\n", qPrintable(fi.fileName())); + return false; + } + fi.seek(samplePos + s->start * sizeof(short)); + int samples = s->end - s->start; + short * ibuffer = new short[samples]; + fi.read((char*)ibuffer, samples * sizeof(short)); + fi.close(); + + fprintf(f, "static const short wave%d[] = {\n ", idx); + int n = 0; + for (int row = 0;; ++row) { + for (int col = 0; col < 16; ++col) { if (n >= samples) - break; - fprintf(f, ",\n "); - } - fprintf(f, "\n };\n"); - return true; - } + break; + if (col != 0) + fprintf(f, ", "); + fprintf(f, "%d", ibuffer[n]); + ++n; + } + if (n >= samples) + break; + fprintf(f, ",\n "); + } + fprintf(f, "\n };\n"); + delete [] ibuffer; + return true; +} //--------------------------------------------------------- // checkInstrument //--------------------------------------------------------- static bool checkInstrument(QList pnums, QList presets, int instrIdx) - { - foreach(int idx, pnums) { - Preset* p = presets[idx]; - foreach(Zone* z, p->zones) { - foreach(GeneratorList* g, z->generators) { - if (g->gen == Gen_Instrument) { - if (instrIdx == g->amount.uword) - return true; - } - } - } +{ + foreach(int idx, pnums) { + Preset* p = presets[idx]; + foreach(Zone* z, p->zones) { + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_Instrument) { + if (instrIdx == g->amount.uword) + return true; + } } - return false; - } + } + } + return false; +} 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]; - foreach(GeneratorList* g, z->generators) { - if (g->gen == Gen_Instrument) { - if (instrIdx == g->amount.uword) - return true; - } - } + for(int i = 0; i < presets.size(); i++) { + Preset* p = presets[i]; + Zone* z = p->zones[0]; + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_Instrument) { + if (instrIdx == g->amount.uword) + return true; } - return false; - } + } + } + return false; +} //--------------------------------------------------------- // checkSample //--------------------------------------------------------- static bool checkSample(QList pnums, QList presets, QList instruments, - int sampleIdx) - { - int idx = 0; - foreach(Instrument* instrument, instruments) { - if (!checkInstrument(pnums, presets, idx)) { - ++idx; - continue; - } - int zones = instrument->zones.size(); - foreach(Zone* z, instrument->zones) { - QList gl; - foreach(GeneratorList* g, z->generators) { - if (g->gen == Gen_SampleId) { - if (sampleIdx == g->amount.uword) - return true; - } - } - } + int sampleIdx) +{ + int idx = 0; + foreach(Instrument* instrument, instruments) { + if (!checkInstrument(pnums, presets, idx)) { ++idx; + continue; + } + foreach(Zone* z, instrument->zones) { + QList gl; + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_SampleId) { + if (sampleIdx == g->amount.uword) + return true; + } } - return false; - } + } + ++idx; + } + return false; +} //--------------------------------------------------------- // checkSample //--------------------------------------------------------- static bool checkSample(QList presets, QList instruments, - int sampleIdx) - { - int idx = 0; - foreach(Instrument* instrument, instruments) { - if (!checkInstrument(presets, idx)) { - ++idx; - continue; - } - int zones = instrument->zones.size(); - foreach(Zone* z, instrument->zones) { - QList gl; - foreach(GeneratorList* g, z->generators) { - if (g->gen == Gen_SampleId) { - if (sampleIdx == g->amount.uword) - return true; - } - } - } + int sampleIdx) +{ + int idx = 0; + foreach(Instrument* instrument, instruments) { + if (!checkInstrument(presets, idx)) { ++idx; + continue; + } + foreach(Zone* z, instrument->zones) { + QList gl; + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_SampleId) { + if (sampleIdx == g->amount.uword) + return true; + } } - return false; - } + } + ++idx; + } + return false; +} //--------------------------------------------------------- @@ -1438,231 +1261,229 @@ static bool checkSample(QList presets, QList instruments, //--------------------------------------------------------- bool SoundFont::writeCode() - { - int segments = 8; - int n = samples.size() / segments; - for (int i = 0; i < segments; ++i) { - char buffer[16]; - sprintf(buffer, "sf%d.cpp", i + 1); - f = fopen(buffer, "w+"); - fprintf(f, "//\n// this is generated code, do not change\n//\n"); - fprintf(f, "#include \"sfont.h\"\n\n"); - fprintf(f, "namespace FluidS {\n\n"); - - // - // dump wave samples - // - int end; - if (i+1 == segments) - end = samples.size(); +{ + int segments = 8; + int n = samples.size() / segments; + for (int i = 0; i < segments; ++i) { + char buffer[16]; + sprintf(buffer, "sf%d.cpp", i + 1); + f = fopen(buffer, "w+"); + fprintf(f, "//\n// this is generated code, do not change\n//\n"); + fprintf(f, "#include \"sfont.h\"\n\n"); + fprintf(f, "namespace FluidS {\n\n"); + + // + // dump wave samples + // + int end; + if (i+1 == segments) + end = samples.size(); + else + end = (i+1) * n; + QList sampleIdx; + for (int idx = i * n; idx < end; ++idx) { + if (smallSf && !checkSample(presets, instruments, idx)) + continue; + Sample* s = samples[idx]; + writeCSample(s, idx); + sampleIdx.append(idx); + } + + // + // dump Sample[] + // + foreach(int idx, sampleIdx) { + Sample* s = samples[idx]; + fprintf(f, "Sample sample%d(%d, %d, %d, %d, %d, %d, %d, %d, wave%d);\n", + idx, + 0, + s->end - (s->start + 1), + s->loopstart, // - s->start, + s->loopend, // - s->start, + s->samplerate, s->origpitch, s->pitchadj, s->sampletype, + idx); + } + fprintf(f, "} // end namespace\n"); + fclose(f); + } + + f = fopen("sf.cpp", "w+"); + fprintf(f, "//\n// this is generated code, do not change\n//\n"); + fprintf(f, "#include \"sfont.h\"\n\n"); + fprintf(f, "namespace FluidS {\n\n"); + + fprintf(f, "extern Sample "); + for (int i = 0; i < samples.size(); ++i) { + if (i) { + if ((i % 8) == 0) + fprintf(f, ";\nextern Sample "); else - end = (i+1) * n; - QList sampleIdx; - for (int idx = i * n; idx < end; ++idx) { - if (smallSf && !checkSample(presets, instruments, idx)) - continue; - Sample* s = samples[idx]; - writeCSample(s, idx); - sampleIdx.append(idx); - } - - // - // dump Sample[] - // - foreach(int idx, sampleIdx) { - Sample* s = samples[idx]; - fprintf(f, "Sample sample%d(%d, %d, %d, %d, %d, %d, %d, %d, wave%d);\n", - idx, - 0, - s->end - (s->start + 1), - s->loopstart, // - s->start, - s->loopend, // - s->start, - s->samplerate, s->origpitch, s->pitchadj, s->sampletype, - idx); - } - fprintf(f, "} // end namespace\n"); - fclose(f); + fprintf(f, ", "); + } + fprintf(f, "sample%d", i); + } + fprintf(f, ";\n"); + + // + // dump Instrument[] + // + int idx2; + int idx = 0; + foreach(Instrument* instrument, instruments) { + if (smallSf && !checkInstrument(presets, idx)) { + ++idx; + continue; + } + int zones = instrument->zones.size(); + idx2 = 0; + foreach(Zone* z, instrument->zones) { + int keyLo = 0; + int keyHi = 127; + int veloLo = 0; + int veloHi = 127; + int sampleIdx = -1; + + QList gl; + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_KeyRange) { + keyLo = g->amount.lo; + keyHi = g->amount.hi; + } + else if (g->gen == Gen_VelRange) { + veloLo = g->amount.lo; + veloHi = g->amount.hi; + } + else if (g->gen == Gen_SampleId) + sampleIdx = g->amount.uword; + else + gl.append(g); + if (smallSf && g->gen == Gen_Pan) + g->amount.uword = 0; } - - f = fopen("sf.cpp", "w+"); - fprintf(f, "//\n// this is generated code, do not change\n//\n"); - fprintf(f, "#include \"sfont.h\"\n\n"); - fprintf(f, "namespace FluidS {\n\n"); - - fprintf(f, "extern Sample "); - for (int i = 0; i < samples.size(); ++i) { - if (i) { - if ((i % 8) == 0) - fprintf(f, ";\nextern Sample "); - else + int idx3 = 0; + foreach(GeneratorList* g, gl) { + if ((idx3 % 8) == 0) { + if (idx3) + fprintf(f, ";\n"); + fprintf(f, "static Generator "); + } + else + fprintf(f, ", "); + fprintf(f, "ge_%d_%d_%d(%d, %d)", + idx, idx2, idx3, int(g->gen), g->amount.sword); + ++idx3; + } + fprintf(f, ";\n"); + int n = gl.size(); + if (n) { + fprintf(f, "static Generator* geList_%d_%d[%d] = {\n ", idx, idx2, n); + for (int i = 0; i < n; ++i) { + if (i) { fprintf(f, ", "); - } - fprintf(f, "sample%d", i); + if ((i % 8) == 0) + fprintf(f, "\n "); + } + fprintf(f, "&ge_%d_%d_%d", idx, idx2, i); + } + fprintf(f, "\n };\n"); } - fprintf(f, ";\n"); - - // - // dump Instrument[] - // - int idx2; - int idx = 0; - foreach(Instrument* instrument, instruments) { - if (smallSf && !checkInstrument(presets, idx)) { - ++idx; - continue; - } - int zones = instrument->zones.size(); - idx2 = 0; - foreach(Zone* z, instrument->zones) { - int keyLo = 0; - int keyHi = 127; - int veloLo = 0; - int veloHi = 127; - int sampleIdx = -1; - - 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; - } - else if (g->gen == Gen_VelRange) { - veloLo = g->amount.lo; - veloHi = g->amount.hi; - } - else if (g->gen == Gen_SampleId) - sampleIdx = g->amount.uword; - else - gl.append(g); - if (smallSf && g->gen == Gen_Pan) - g->amount.uword = 0; - } - int idx3 = 0; - foreach(GeneratorList* g, gl) { - if ((idx3 % 8) == 0) { - if (idx3) - fprintf(f, ";\n"); - fprintf(f, "static Generator "); - } - else - fprintf(f, ", "); - fprintf(f, "ge_%d_%d_%d(%d, %d)", - idx, idx2, idx3, int(g->gen), g->amount.sword); - ++idx3; - } - fprintf(f, ";\n"); - int n = gl.size(); - if (n) { - fprintf(f, "static Generator* geList_%d_%d[%d] = {\n ", idx, idx2, n); - for (int i = 0; i < n; ++i) { - if (i) { - fprintf(f, ", "); - if ((i % 8) == 0) - fprintf(f, "\n "); - } - fprintf(f, "&ge_%d_%d_%d", idx, idx2, i); - } - fprintf(f, "\n };\n"); - } - if (sampleIdx == -1) - fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, 0, 0, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); - else if (n) - fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, geList_%d_%d); // %s\n", - idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, idx, idx2, - samples[sampleIdx]->name); - else - fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, 0); // %s\n", - idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, - samples[sampleIdx]->name); - - ++idx2; - } - - fprintf(f, "static IZone* izones%d[%d] = {\n", idx, zones); - idx2 = 0; - for (int i = 0; i < zones; ++i) - fprintf(f, " &iz%d_%d,\n", idx, idx2++); - fprintf(f, " };\n"); - fprintf(f, "static Instrument instr%d(0, %d, izones%d);\n\n", idx, zones, idx); - ++idx; + if (sampleIdx == -1) + fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, 0, 0, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); + else if (n) + fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, geList_%d_%d); // %s\n", + idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, idx, idx2, + samples[sampleIdx]->name); + else + fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, 0); // %s\n", + idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, + samples[sampleIdx]->name); + + ++idx2; + } + + fprintf(f, "static IZone* izones%d[%d] = {\n", idx, zones); + idx2 = 0; + for (int i = 0; i < zones; ++i) + fprintf(f, " &iz%d_%d,\n", idx, idx2++); + fprintf(f, " };\n"); + fprintf(f, "static Instrument instr%d(0, %d, izones%d);\n\n", idx, zones, idx); + ++idx; + } + // + // dump Preset[] + // + idx = 0; + foreach(Preset* p, presets) { + idx2 = 0; + int zones = p->zones.size(); + if (smallSf) + zones = 1; + + foreach(Zone* z, p->zones) { + int keyLo = 0; + int keyHi = 127; + int veloLo = 0; + int veloHi = 127; + int instrIdx = -1; + + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_KeyRange) { + keyLo = g->amount.lo; + keyHi = g->amount.hi; + } + else if (g->gen == Gen_VelRange) { + veloLo = g->amount.lo; + veloHi = g->amount.hi; + } + else if (g->gen == Gen_Instrument) + instrIdx = g->amount.uword; } - // - // dump Preset[] - // - idx = 0; - foreach(Preset* p, presets) { - idx2 = 0; - int zones = p->zones.size(); - if (smallSf) - zones = 1; - - foreach(Zone* z, p->zones) { - int keyLo = 0; - int keyHi = 127; - int veloLo = 0; - 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; - } - else if (g->gen == Gen_VelRange) { - veloLo = g->amount.lo; - veloHi = g->amount.hi; - } - else if (g->gen == Gen_Instrument) - instrIdx = g->amount.uword; - } - if (keyLo == 0 - && keyHi == 127 - && veloLo == 0 - && veloHi == 127) { - if (instrIdx == -1) - fprintf(f, "static PZone pz%d_%d(0);\n", idx, idx2); - else - fprintf(f, "static PZone pz%d_%d(&instr%d);\n", idx, idx2, instrIdx); - } - else { - //abort(); - - if (instrIdx == -1) - fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); - else - fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, &instr%d);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi, instrIdx); - } - if (smallSf) - break; - ++idx2; - } - fprintf(f, "static PZone* pzones%d[%d] = {\n", idx, zones); - idx2 = 0; - for (int i = 0; i < zones; ++i) { - fprintf(f, " &pz%d_%d,\n", idx, idx2); - ++idx2; - } - fprintf(f, " };\n"); - - fprintf(f, "static Preset preset%d(%d, %d, 0, %d, pzones%d); // %s\n\n", - idx, p->preset, p->bank, zones, idx, p->name); - ++idx; + if (keyLo == 0 + && keyHi == 127 + && veloLo == 0 + && veloHi == 127) { + if (instrIdx == -1) + fprintf(f, "static PZone pz%d_%d(0);\n", idx, idx2); + else + fprintf(f, "static PZone pz%d_%d(&instr%d);\n", idx, idx2, instrIdx); } + else { + //abort(); + + if (instrIdx == -1) + fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); + else + fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, &instr%d);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi, instrIdx); + } + if (smallSf) + break; + ++idx2; + } + fprintf(f, "static PZone* pzones%d[%d] = {\n", idx, zones); + idx2 = 0; + for (int i = 0; i < zones; ++i) { + fprintf(f, " &pz%d_%d,\n", idx, idx2); + ++idx2; + } + fprintf(f, " };\n"); + + fprintf(f, "static Preset preset%d(%d, %d, 0, %d, pzones%d); // %s\n\n", + idx, p->preset, p->bank, zones, idx, p->name); + ++idx; + } - fprintf(f, "static Preset* sfPresets[%d] = {\n", presets.size()); - for(int idx = 0; idx < presets.size(); ++idx) - fprintf(f, " &preset%d, // %s\n", idx, presets[idx]->name); - fprintf(f, " };\n"); + fprintf(f, "static Preset* sfPresets[%d] = {\n", presets.size()); + for(int idx = 0; idx < presets.size(); ++idx) + fprintf(f, " &preset%d, // %s\n", idx, presets[idx]->name); + fprintf(f, " };\n"); - fprintf(f, "static SFont _buildinSf(%d, sfPresets);\n", presets.size()); - fprintf(f, "SFont* createSf() { return &_buildinSf; }\n"); - fprintf(f, "} // end namespace\n"); - fclose(f); - return true; - } + fprintf(f, "static SFont _buildinSf(%d, sfPresets);\n", presets.size()); + fprintf(f, "SFont* createSf() { return &_buildinSf; }\n"); + fprintf(f, "} // end namespace\n"); + fclose(f); + return true; +} @@ -1671,202 +1492,378 @@ bool SoundFont::writeCode() //--------------------------------------------------------- bool SoundFont::writeCode(QList pnums) - { - printf("write code\n"); - - int n = samples.size(); - f = fopen("sf.cpp", "w+"); - fprintf(f, "//\n// this is generated code, do not change\n//\n"); - fprintf(f, "#include \"sfont.h\"\n\n"); - fprintf(f, "namespace FluidS {\n\n"); - - // - // dump wave samples - // - QList sampleIdx; - for (int idx = 0; idx < n; ++idx) { - if (!checkSample(pnums, presets, instruments, idx)) - continue; - Sample* s = samples[idx]; - writeCSample(s, idx); - sampleIdx.append(idx); - } +{ + printf("write code\n"); + + int n = samples.size(); + f = fopen("sf.cpp", "w+"); + fprintf(f, "//\n// this is generated code, do not change\n//\n"); + fprintf(f, "#include \"sfont.h\"\n\n"); + fprintf(f, "namespace FluidS {\n\n"); + + // + // dump wave samples + // + QList sampleIdx; + for (int idx = 0; idx < n; ++idx) { + if (!checkSample(pnums, presets, instruments, idx)) + continue; + Sample* s = samples[idx]; + writeCSample(s, idx); + sampleIdx.append(idx); + } + + // + // dump Sample[] + // + foreach(int idx, sampleIdx) { + Sample* s = samples[idx]; + fprintf(f, "Sample sample%d(%d, %d, %d, %d, %d, %d, %d, %d, wave%d);\n", + idx, + 0, // sample start + s->end - s->start, // samples + s->loopstart - s->start, + s->loopend - s->start, + s->samplerate, + s->origpitch, s->pitchadj, s->sampletype, + idx); + } + + // + // dump Instrument[] + // + int idx2; + int idx = 0; + foreach(Instrument* instrument, instruments) { + if (!checkInstrument(pnums, presets, idx)) { + ++idx; + continue; + } + int zones = instrument->zones.size(); + idx2 = 0; + foreach(Zone* z, instrument->zones) { + int keyLo = 0; + int keyHi = 127; + int veloLo = 0; + int veloHi = 127; + int sampleIdx = -1; + + QList gl; - // - // dump Sample[] - // - foreach(int idx, sampleIdx) { - Sample* s = samples[idx]; - fprintf(f, "Sample sample%d(%d, %d, %d, %d, %d, %d, %d, %d, wave%d);\n", - idx, - 0, // sample start - s->end - s->start, // samples - s->loopstart - s->start, - s->loopend - s->start, - s->samplerate, - s->origpitch, s->pitchadj, s->sampletype, - idx); + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_KeyRange) { + keyLo = g->amount.lo; + keyHi = g->amount.hi; + } + else if (g->gen == Gen_VelRange) { + veloLo = g->amount.lo; + veloHi = g->amount.hi; + } + else if (g->gen == Gen_SampleId) + sampleIdx = g->amount.uword; + else + gl.append(g); } - - // - // dump Instrument[] - // - int idx2; - int idx = 0; - foreach(Instrument* instrument, instruments) { - if (!checkInstrument(pnums, presets, idx)) { - ++idx; - continue; - } - int zones = instrument->zones.size(); - idx2 = 0; - foreach(Zone* z, instrument->zones) { - int keyLo = 0; - int keyHi = 127; - int veloLo = 0; - int veloHi = 127; - int sampleIdx = -1; - - 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; - } - else if (g->gen == Gen_VelRange) { - veloLo = g->amount.lo; - veloHi = g->amount.hi; - } - else if (g->gen == Gen_SampleId) - sampleIdx = g->amount.uword; - else - gl.append(g); - } - int idx3 = 0; - foreach(GeneratorList* g, gl) { - if ((idx3 % 8) == 0) { - if (idx3) - fprintf(f, ";\n"); - fprintf(f, "static Generator "); - } - else - fprintf(f, ", "); - fprintf(f, "ge_%d_%d_%d(%d, %d)", - idx, idx2, idx3, int(g->gen), g->amount.sword); - ++idx3; - } - fprintf(f, ";\n"); - int n = gl.size(); - if (n) { - fprintf(f, "static Generator* geList_%d_%d[%d] = {\n ", idx, idx2, n); - for (int i = 0; i < n; ++i) { - if (i) { - fprintf(f, ", "); - if ((i % 8) == 0) - fprintf(f, "\n "); - } - fprintf(f, "&ge_%d_%d_%d", idx, idx2, i); - } - fprintf(f, "\n };\n"); - } - if (sampleIdx == -1) - fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, 0, 0, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); - else - fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, geList_%d_%d);\n", - idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, idx, idx2); - ++idx2; - } - - fprintf(f, "static IZone* izones%d[%d] = {\n", idx, zones); - idx2 = 0; - for (int i = 0; i < zones; ++i) - fprintf(f, " &iz%d_%d,\n", idx, idx2++); - fprintf(f, " };\n"); - fprintf(f, "static Instrument instr%d(0, %d, izones%d);\n\n", idx, zones, idx); - ++idx; + int idx3 = 0; + foreach(GeneratorList* g, gl) { + if ((idx3 % 8) == 0) { + if (idx3) + fprintf(f, ";\n"); + fprintf(f, "static Generator "); + } + else + fprintf(f, ", "); + fprintf(f, "ge_%d_%d_%d(%d, %d)", + idx, idx2, idx3, int(g->gen), g->amount.sword); + ++idx3; } - // - // dump Preset[] - // - - foreach(int idx, pnums) { - Preset* p = presets[idx]; - idx2 = 0; - int zones = p->zones.size(); - foreach(Zone* z, p->zones) { - int keyLo = 0; - int keyHi = 127; - int veloLo = 0; - 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; - } - else if (g->gen == Gen_VelRange) { - veloLo = g->amount.lo; - veloHi = g->amount.hi; - } - else if (g->gen == Gen_Instrument) - instrIdx = g->amount.uword; - } - if (keyLo == 0 - && keyHi == 127 - && veloLo == 0 - && veloHi == 127) { - if (instrIdx == -1) - fprintf(f, "static PZone pz%d_%d(0);\n", idx, idx2); - else - fprintf(f, "static PZone pz%d_%d(&instr%d);\n", idx, idx2, instrIdx); - } - else { - if (instrIdx == -1) - fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); - else - fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, &instr%d);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi, instrIdx); - } - ++idx2; - } - fprintf(f, "static PZone* pzones%d[%d] = {\n", idx, zones); - idx2 = 0; - for (int i = 0; i < zones; ++i) { - fprintf(f, " &pz%d_%d,\n", idx, idx2); - ++idx2; - } - fprintf(f, " };\n"); - - fprintf(f, "static Preset preset%d(%d, %d, 0, %d, pzones%d); // %s\n\n", - idx, p->preset, p->bank, p->zones.size(), idx, p->name); - ++idx; + fprintf(f, ";\n"); + int n = gl.size(); + if (n) { + fprintf(f, "static Generator* geList_%d_%d[%d] = {\n ", idx, idx2, n); + for (int i = 0; i < n; ++i) { + if (i) { + fprintf(f, ", "); + if ((i % 8) == 0) + fprintf(f, "\n "); + } + fprintf(f, "&ge_%d_%d_%d", idx, idx2, i); + } + fprintf(f, "\n };\n"); } - - fprintf(f, "static Preset* sfPresets[%d] = {\n", pnums.size()); - foreach(int idx, pnums) - fprintf(f, " &preset%d, // %s\n", idx, presets[idx]->name); - fprintf(f, " };\n"); - - fprintf(f, "static SFont _buildinSf(%d, sfPresets);\n", pnums.size()); - fprintf(f, "SFont* createSf() { return &_buildinSf; }\n"); - fprintf(f, "} // end namespace\n"); - fclose(f); - return true; - } + if (sampleIdx == -1) + fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, 0, 0, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); + else + fprintf(f, "static IZone iz%d_%d(%d, %d, %d, %d, &sample%d, %d, geList_%d_%d);\n", + idx, idx2, keyLo, keyHi, veloLo, veloHi, sampleIdx, n, idx, idx2); + ++idx2; + } + + fprintf(f, "static IZone* izones%d[%d] = {\n", idx, zones); + idx2 = 0; + for (int i = 0; i < zones; ++i) + fprintf(f, " &iz%d_%d,\n", idx, idx2++); + fprintf(f, " };\n"); + fprintf(f, "static Instrument instr%d(0, %d, izones%d);\n\n", idx, zones, idx); + ++idx; + } + // + // dump Preset[] + // + + foreach(int idx, pnums) { + Preset* p = presets[idx]; + idx2 = 0; + int zones = p->zones.size(); + foreach(Zone* z, p->zones) { + int keyLo = 0; + int keyHi = 127; + int veloLo = 0; + int veloHi = 127; + int instrIdx = -1; + foreach(GeneratorList* g, z->generators) { + if (g->gen == Gen_KeyRange) { + keyLo = g->amount.lo; + keyHi = g->amount.hi; + } + else if (g->gen == Gen_VelRange) { + veloLo = g->amount.lo; + veloHi = g->amount.hi; + } + else if (g->gen == Gen_Instrument) + instrIdx = g->amount.uword; + } + if (keyLo == 0 + && keyHi == 127 + && veloLo == 0 + && veloHi == 127) { + if (instrIdx == -1) + fprintf(f, "static PZone pz%d_%d(0);\n", idx, idx2); + else + fprintf(f, "static PZone pz%d_%d(&instr%d);\n", idx, idx2, instrIdx); + } + else { + if (instrIdx == -1) + fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, 0);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi); + else + fprintf(f, "static PZone pz%d_%d(%d, %d, %d, %d, &instr%d);\n", idx, idx2, keyLo, keyHi, veloLo, veloHi, instrIdx); + } + ++idx2; + } + fprintf(f, "static PZone* pzones%d[%d] = {\n", idx, zones); + idx2 = 0; + for (int i = 0; i < zones; ++i) { + fprintf(f, " &pz%d_%d,\n", idx, idx2); + ++idx2; + } + fprintf(f, " };\n"); + + fprintf(f, "static Preset preset%d(%d, %d, 0, %d, pzones%d); // %s\n\n", + idx, p->preset, p->bank, p->zones.size(), idx, p->name); + ++idx; + } + + fprintf(f, "static Preset* sfPresets[%d] = {\n", pnums.size()); + foreach(int idx, pnums) + fprintf(f, " &preset%d, // %s\n", idx, presets[idx]->name); + fprintf(f, " };\n"); + + fprintf(f, "static SFont _buildinSf(%d, sfPresets);\n", pnums.size()); + fprintf(f, "SFont* createSf() { return &_buildinSf; }\n"); + fprintf(f, "} // end namespace\n"); + fclose(f); + return true; +} //--------------------------------------------------------- // dumpPresets //--------------------------------------------------------- void SoundFont::dumpPresets() - { - int idx = 0; - foreach(const Preset* p, presets) { - printf("%03d %04x-%02x %s\n", idx, p->bank, p->preset, p->name); - ++idx; - } - } +{ + int idx = 0; + foreach(const Preset* p, presets) { + printf("%03d %04x-%02x %s\n", idx, p->bank, p->preset, p->name); + ++idx; + } +} + +#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; +} + +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 = new short[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)); + delete [] buffer; + return false; + } + + sf_write_short(sf, buffer, len); + delete [] buffer; + + 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 diff --git a/sfont.h b/sfont.h index ed204ef..fe8faa4 100644 --- a/sfont.h +++ b/sfont.h @@ -23,41 +23,42 @@ #include #include - -class Xml; class QFile; +class Xml; + +namespace SfTools { //--------------------------------------------------------- // sfVersionTag //--------------------------------------------------------- struct sfVersionTag { - int major; - int minor; - }; + int major; + int minor; +}; enum Modulator { - }; +}; enum Generator { - Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, - Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch, - Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ, - Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs, - Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan, - Gen_Unused2, Gen_Unused3, Gen_Unused4, - Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq, - Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay, - Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold, - Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack, - Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease, - Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument, - Gen_Reserved1, Gen_KeyRange, Gen_VelRange, - Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, - Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs, - Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes, - Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey, - Gen_Dummy - }; + Gen_StartAddrOfs, Gen_EndAddrOfs, Gen_StartLoopAddrOfs, + Gen_EndLoopAddrOfs, Gen_StartAddrCoarseOfs, Gen_ModLFO2Pitch, + Gen_VibLFO2Pitch, Gen_ModEnv2Pitch, Gen_FilterFc, Gen_FilterQ, + Gen_ModLFO2FilterFc, Gen_ModEnv2FilterFc, Gen_EndAddrCoarseOfs, + Gen_ModLFO2Vol, Gen_Unused1, Gen_ChorusSend, Gen_ReverbSend, Gen_Pan, + Gen_Unused2, Gen_Unused3, Gen_Unused4, + Gen_ModLFODelay, Gen_ModLFOFreq, Gen_VibLFODelay, Gen_VibLFOFreq, + Gen_ModEnvDelay, Gen_ModEnvAttack, Gen_ModEnvHold, Gen_ModEnvDecay, + Gen_ModEnvSustain, Gen_ModEnvRelease, Gen_Key2ModEnvHold, + Gen_Key2ModEnvDecay, Gen_VolEnvDelay, Gen_VolEnvAttack, + Gen_VolEnvHold, Gen_VolEnvDecay, Gen_VolEnvSustain, Gen_VolEnvRelease, + Gen_Key2VolEnvHold, Gen_Key2VolEnvDecay, Gen_Instrument, + Gen_Reserved1, Gen_KeyRange, Gen_VelRange, + Gen_StartLoopAddrCoarseOfs, Gen_Keynum, Gen_Velocity, + Gen_Attenuation, Gen_Reserved2, Gen_EndLoopAddrCoarseOfs, + Gen_CoarseTune, Gen_FineTune, Gen_SampleId, Gen_SampleModes, + Gen_Reserved3, Gen_ScaleTune, Gen_ExclusiveClass, Gen_OverrideRootKey, + Gen_Dummy +}; enum Transform { Linear }; @@ -66,179 +67,192 @@ enum Transform { Linear }; //--------------------------------------------------------- struct ModulatorList { - Modulator src; - Generator dst; - int amount; - Modulator amtSrc; - Transform transform; - }; + Modulator src; + Generator dst; + int amount; + Modulator amtSrc; + Transform transform; +}; //--------------------------------------------------------- // GeneratorList //--------------------------------------------------------- union GeneratorAmount { - short sword; - ushort uword; - struct { - uchar lo, hi; - }; - }; + short sword; + ushort uword; + struct { + uchar lo, hi; + }; +}; struct GeneratorList { - Generator gen; - GeneratorAmount amount; - }; + Generator gen; + GeneratorAmount amount; +}; //--------------------------------------------------------- // Zone //--------------------------------------------------------- struct Zone { - QList generators; - QList modulators; - int instrumentIndex; - }; + QList generators; + QList modulators; + int instrumentIndex; +}; //--------------------------------------------------------- // Preset //--------------------------------------------------------- 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}; - QList zones; - }; + 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) {} +}; //--------------------------------------------------------- // Instrument //--------------------------------------------------------- struct Instrument { - char* name; - int index; // used only for read - QList zones; + char* name; + int index; // used only for read + QList zones; - Instrument(); - ~Instrument(); - }; + Instrument(); + ~Instrument(); +}; //--------------------------------------------------------- // Sample //--------------------------------------------------------- struct Sample { - char* name; - uint start; - uint end; - uint loopstart; - uint loopend; - uint samplerate; - - int origpitch; - int pitchadj; - int sampletype; - - Sample(); - ~Sample(); - }; + char* name; + uint start; + uint end; + uint loopstart; + uint loopend; + uint samplerate; + + int origpitch; + int pitchadj; + int sampleLink; + int sampletype; + + Sample(); + ~Sample(); +}; //--------------------------------------------------------- // SoundFont //--------------------------------------------------------- class SoundFont { - QString path; - sfVersionTag version; - char* engine; - char* name; - char* date; - char* comment; - char* tools; - char* creator; - char* product; - char* copyright; - - int samplePos; - int sampleLen; - - QList presets; - QList instruments; - - QList pZones; - QList iZones; - QList samples; - - QFile* file; - FILE* f; - - double _oggQuality; - double _oggAmp; - qint64 _oggSerial; - - unsigned readDword(); - int readWord(); - int readShort(); - int readByte(); - int readChar(); - int readFourcc(const char*); - int readFourcc(char*); - void readSignature(const char* signature); - void readSignature(char* signature); - void skip(int); - void readSection(const char* fourcc, int len); - void readVersion(); - char* readString(int); - void readPhdr(int); - void readBag(int, QList*); - void readMod(int, QList*); - void readGen(int, QList*); - void readInst(int); - void readShdr(int); - - void writeDword(int); - void writeWord(unsigned short int); - void writeByte(unsigned char); - 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*); - void writeModulator(const ModulatorList*); - void writeGenerator(const GeneratorList*); - void writeInstrument(int zoneIdx, const Instrument*); - - void writeIfil(); - void writeSmpl(); - void writePhdr(); - void writeBag(const char* fourcc, QList*); - void writeMod(const char* fourcc, const QList*); - void writeGen(const char* fourcc, QList*); - void writeInst(); - void writeShdr(); - - int writeCompressedSample(Sample*); - bool writeCSample(Sample*, int); - char* readCompressedSample(Sample*); - - public: - SoundFont(const QString&); - ~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(); - }; + QString path; + sfVersionTag version; + char* engine; + char* name; + char* date; + char* comment; + char* tools; + char* creator; + char* product; + char* copyright; + + int samplePos; + int sampleLen; + + QList presets; + QList instruments; + + QList pZones; + QList iZones; + QList samples; + + QFile* file; + FILE* f; + + double _oggQuality; + double _oggAmp; + qint64 _oggSerial; + + unsigned readDword(); + int readWord(); + int readShort(); + int readByte(); + int readChar(); + int readFourcc(const char*); + int readFourcc(char*); + void readSignature(const char* signature); + void readSignature(char* signature); + void skip(int); + void readSection(const char* fourcc, int len); + void readVersion(); + char* readString(int); + void readPhdr(int); + void readBag(int, QList*); + void readMod(int, QList*); + void readGen(int, QList*); + void readInst(int); + void readShdr(int); + + void writeDword(int); + void writeWord(unsigned short int); + void writeByte(unsigned char); + void writeChar(char); + void writeShort(short); + void write(const char* p, int n); + void writeSample(const Sample*); + void writeStringSection(const char* fourcc, char* s); + void writePreset(int zoneIdx, const Preset*); + void writeModulator(const ModulatorList*); + void writeGenerator(const GeneratorList*); + void writeInstrument(int zoneIdx, const Instrument*); + + void writeIfil(); + void writeSmpl(); + void writePhdr(); + void writeBag(const char* fourcc, QList*); + void writeMod(const char* fourcc, const QList*); + void writeGen(const char* fourcc, QList*); + void writeInst(); + void writeShdr(); + + int writeCompressedSample(Sample*); + bool writeCSample(Sample*, int); + char* readCompressedSample(Sample*); + +public: + SoundFont(const QString&); + ~SoundFont(); + bool read(); + bool write(QFile*, double oggQuality, double oggAmp, qint64 oggSerial = rand()); + 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 2856228cbd0aff5639ef03af1f6b0d2d365338c9 Mon Sep 17 00:00:00 2001 From: davy7125 Date: Mon, 5 Nov 2018 17:44:09 +0100 Subject: [PATCH 2/3] 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 | 34 +++-- sf3convert.1 | 10 +- sfconvert.cpp | 35 ++++-- sfont.cpp | 331 +++++++++++++++++++++++++++++++++++-------------- sfont.h | 10 +- 6 files changed, 332 insertions(+), 120 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 95d5861..ead54ee 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..b3a60d9 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,21 +24,19 @@ Utilities for SoundFont files. * libsdnfile * libogg * libvorbis +* libvorbisenc +* libvorbisfile ``` $ make release ``` -### Usage Example: - -This compresses the Fluid sound font from 148 MBytes to 20 MBytes. +### Usage example: - sf3convert -z FluidR3.SF2 mops.sf3 +This compresses a 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 diff --git a/sf3convert.1 b/sf3convert.1 index fcd13fe..52299e3 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 it 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 56122fa..9b01d1a 100644 --- a/sfconvert.cpp +++ b/sfconvert.cpp @@ -31,29 +31,31 @@ //--------------------------------------------------------- static void usage(const char* pname) - { +{ fprintf(stderr, "usage: %s [-flags] soundfont [outfile]\n", 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"); fprintf(stderr, " -d dump presets\n"); fprintf(stderr, " -s create small sf (one instrument/preset), pan to 0\n"); fprintf(stderr, " -S nn ogg serial number\n"); - } +} //--------------------------------------------------------- // main //--------------------------------------------------------- int main(int argc, char* argv[]) - { +{ bool xml = false; bool code = false; bool dump = false; bool compress = false; + bool uncompress = false; bool smallSf = false; double oggQuality = 0.3; double oggAmp = -1.0; @@ -66,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; @@ -86,6 +88,9 @@ int main(int argc, char* argv[]) case 's': smallSf = true; break; + case 'y': + uncompress = true; + break; case 'z': compress = true; break; @@ -112,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) @@ -143,10 +152,22 @@ 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 3a3baf8..2d7872d 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "sfont.h" #include "time.h" @@ -56,8 +58,6 @@ using namespace SfTools; #define BLOCK_SIZE 1024 -static const bool writeCompressed = true; - //--------------------------------------------------------- // Sample //--------------------------------------------------------- @@ -122,10 +122,10 @@ SoundFont::~SoundFont() bool SoundFont::read() { - file = new QFile(path); - if (!file->open(QIODevice::ReadOnly)) { + _file = new QFile(path); + if (!_file->open(QIODevice::ReadOnly)) { fprintf(stderr, "cannot open <%s>\n", qPrintable(path)); - delete file; + delete _file; return false; } try { @@ -151,10 +151,10 @@ bool SoundFont::read() } catch (QString s) { printf("read sf file failed: %s\n", qPrintable(s)); - delete file; + delete _file; return false; } - delete file; + delete _file; return true; } @@ -164,8 +164,8 @@ bool SoundFont::read() void SoundFont::skip(int n) { - qint64 pos = file->pos(); - if (!file->seek(pos + n)) + qint64 pos = _file->pos(); + if (!_file->seek(pos + n)) throw(QString("unexpected end of file\n")); } @@ -199,7 +199,7 @@ void SoundFont::readSignature(const char* signature) void SoundFont::readSignature(char* signature) { - if (file->read(signature, 4) != 4) + if (_file->read(signature, 4) != 4) throw(QString("unexpected end of file\n")); } @@ -210,7 +210,7 @@ void SoundFont::readSignature(char* signature) unsigned SoundFont::readDword() { unsigned format; - if (file->read((char*)&format, 4) != 4) + if (_file->read((char*)&format, 4) != 4) throw(QString("unexpected end of file\n")); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) return BE_LONG(format); @@ -276,7 +276,7 @@ void SoundFont::writeShort(short val) int SoundFont::readWord() { unsigned short format; - if (file->read((char*)&format, 2) != 2) + if (_file->read((char*)&format, 2) != 2) throw(QString("unexpected end of file\n")); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) return BE_SHORT(format); @@ -291,7 +291,7 @@ int SoundFont::readWord() int SoundFont::readShort() { short format; - if (file->read((char*)&format, 2) != 2) + if (_file->read((char*)&format, 2) != 2) throw(QString("unexpected end of file\n")); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) return BE_SHORT(format); @@ -306,7 +306,7 @@ int SoundFont::readShort() int SoundFont::readByte() { uchar val; - if (file->read((char*)&val, 1) != 1) + if (_file->read((char*)&val, 1) != 1) throw(QString("unexpected end of file\n")); return val; } @@ -318,7 +318,7 @@ int SoundFont::readByte() int SoundFont::readChar() { char val; - if (file->read(&val, 1) != 1) + if (_file->read(&val, 1) != 1) throw(QString("unexpected end of file\n")); return val; } @@ -330,7 +330,7 @@ int SoundFont::readChar() void SoundFont::readVersion() { unsigned char data[4]; - if (file->read((char*)data, 4) != 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); @@ -343,7 +343,7 @@ void SoundFont::readVersion() char* SoundFont::readString(int n) { char data[2500]; - if (file->read((char*)data, n) != n) + if (_file->read((char*)data, n) != n) throw(QString("unexpected end of file\n")); if (data[n-1] != 0) data[n] = 0; @@ -387,10 +387,13 @@ void SoundFont::readSection(const char* fourcc, int len) copyright = readString(len); break; case FOURCC('s','m','p','l'): // the digital audio samples - samplePos = file->pos(); + samplePos = _file->pos(); 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; @@ -590,7 +593,8 @@ void SoundFont::readInst(int size) void SoundFont::readShdr(int size) { int n = size / 46; - for (int i = 0; i < n-1; ++i) { + for (int i = 0; i < n-1; ++i) + { Sample* s = new Sample; s->name = readString(20); s->start = readDword(); @@ -603,8 +607,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); } @@ -614,25 +616,20 @@ 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 { - file->write("RIFF", 4); - riffLenPos = file->pos(); + _file->write("RIFF", 4); + riffLenPos = _file->pos(); writeDword(0); - file->write("sfbk", 4); + _file->write("sfbk", 4); - file->write("LIST", 4); - listLenPos = file->pos(); + _file->write("LIST", 4); + listLenPos = _file->pos(); writeDword(0); - file->write("INFO", 4); + _file->write("INFO", 4); writeIfil(); if (name) @@ -652,25 +649,25 @@ bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSeri if (copyright) writeStringSection("ICOP", copyright); - qint64 pos = file->pos(); - file->seek(listLenPos); + qint64 pos = _file->pos(); + _file->seek(listLenPos); writeDword(pos - listLenPos - 4); - file->seek(pos); + _file->seek(pos); - file->write("LIST", 4); - listLenPos = file->pos(); + _file->write("LIST", 4); + listLenPos = _file->pos(); writeDword(0); - file->write("sdta", 4); + _file->write("sdta", 4); writeSmpl(); - pos = file->pos(); - file->seek(listLenPos); + pos = _file->pos(); + _file->seek(listLenPos); writeDword(pos - listLenPos - 4); - file->seek(pos); + _file->seek(pos); - file->write("LIST", 4); - listLenPos = file->pos(); + _file->write("LIST", 4); + listLenPos = _file->pos(); writeDword(0); - file->write("pdta", 4); + _file->write("pdta", 4); writePhdr(); writeBag("pbag", &pZones); @@ -682,13 +679,13 @@ bool SoundFont::write(QFile* f, double oggQuality, double oggAmp, qint64 oggSeri writeGen("igen", &iZones); writeShdr(); - pos = file->pos(); - file->seek(listLenPos); + pos = _file->pos(); + _file->seek(listLenPos); writeDword(pos - listLenPos - 4); - file->seek(pos); + _file->seek(pos); - qint64 endPos = file->pos(); - file->seek(riffLenPos); + qint64 endPos = _file->pos(); + _file->seek(riffLenPos); writeDword(endPos - riffLenPos - 4); } catch (QString s) { @@ -698,13 +695,31 @@ 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 //--------------------------------------------------------- void SoundFont::write(const char* p, int n) { - if (file->write(p, n) != n) + if (_file->write(p, n) != n) throw(QString("write error")); } @@ -734,8 +749,16 @@ 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; @@ -750,43 +773,48 @@ void SoundFont::writeIfil() void SoundFont::writeSmpl() { write("smpl", 4); - - qint64 pos = file->pos(); + qint64 pos = _file->pos(); writeDword(0); - int sampleLen = 0; - if (writeCompressed) { - foreach(Sample* s, samples) { + 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())); - 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); + else + { + // Uncompress from OGG data + foreach(Sample* s, samples) + { + // 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); + + qint64 npos = _file->pos(); + _file->seek(pos); writeDword(npos - pos - 4); - file->seek(npos); + _file->seek(npos); } //--------------------------------------------------------- @@ -1030,6 +1058,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); @@ -1052,10 +1083,10 @@ int SoundFont::writeCompressedSample(Sample* s) int page = 0; double linearAmp = pow(10.0, _oggAmp / 20.0); for(;;) { - int bufflength = qMin(BLOCK_SIZE, samples-page*BLOCK_SIZE); + int bufflength = qMin(BLOCK_SIZE, samples - page * BLOCK_SIZE); float **buffer = vorbis_analysis_buffer(&vd, bufflength); int j = 0; - int max = qMin((page+1)*BLOCK_SIZE, samples); + int max = qMin((page+1) * BLOCK_SIZE, samples); for (i = page * BLOCK_SIZE; i < max ; i++) { buffer[0][j] = (ibuffer[i] / 32768.f) * linearAmp; j++; @@ -1120,16 +1151,6 @@ int SoundFont::writeCompressedSample(Sample* s) return n; } -//--------------------------------------------------------- -// readCompressedSample -//--------------------------------------------------------- - -char* SoundFont::readCompressedSample(Sample* s) -{ - Q_UNUSED(s) - return 0; -} - //--------------------------------------------------------- // writeCSample //--------------------------------------------------------- @@ -1867,3 +1888,129 @@ bool SoundFont::readXml(QFile* f) return true; } #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; +} diff --git a/sfont.h b/sfont.h index fe8faa4..66db8d9 100644 --- a/sfont.h +++ b/sfont.h @@ -178,9 +178,10 @@ class SoundFont { QList iZones; QList samples; - QFile* file; + QFile* _file; FILE* f; + bool _compress; double _oggQuality; double _oggAmp; qint64 _oggSerial; @@ -228,14 +229,17 @@ 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 = rand()); + bool compress(QFile* f, double oggQuality, double oggAmp, qint64 oggSerial = rand()); + bool uncompress(QFile* f); bool writeCode(QList); bool writeCode(); void dumpPresets(); From 0b1d8523ca7adbe16a2a5e7af2a541ae93e4b51b Mon Sep 17 00:00:00 2001 From: davy7125 Date: Tue, 6 Nov 2018 17:09:59 +0100 Subject: [PATCH 3/3] iver and irom chunks Fix a bug: the iver and irom don't stop the conversion --- sfont.cpp | 37 +++++++++++++++++++++++++++++++++---- sfont.h | 5 ++++- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/sfont.cpp b/sfont.cpp index 2d7872d..1ab135a 100644 --- a/sfont.cpp +++ b/sfont.cpp @@ -101,6 +101,11 @@ SoundFont::SoundFont(const QString& s) creator = 0; product = 0; copyright = 0; + irom = 0; + version.major = 0; + version.minor = 0; + iver.major = 0; + iver.minor = 0; smallSf = false; } @@ -114,6 +119,7 @@ SoundFont::~SoundFont() free(creator); free(product); free(copyright); + free(irom); } //--------------------------------------------------------- @@ -327,13 +333,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); } //--------------------------------------------------------- @@ -360,7 +366,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); @@ -422,7 +428,11 @@ 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)); @@ -648,6 +658,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); @@ -766,6 +779,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 //--------------------------------------------------------- diff --git a/sfont.h b/sfont.h index 66db8d9..530bda7 100644 --- a/sfont.h +++ b/sfont.h @@ -167,6 +167,8 @@ class SoundFont { char* creator; char* product; char* copyright; + char* irom; + sfVersionTag iver; int samplePos; int sampleLen; @@ -197,7 +199,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 +222,7 @@ class SoundFont { void writeInstrument(int zoneIdx, const Instrument*); void writeIfil(); + void writeIver(); void writeSmpl(); void writePhdr(); void writeBag(const char* fourcc, QList*);