You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
5.0 KiB
216 lines
5.0 KiB
/*
|
|
Rosegarden
|
|
A sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <bownie@bownie.com>
|
|
|
|
The moral right of the authors to claim authorship of this work
|
|
has been asserted.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version. See the file
|
|
COPYING included with this distribution for more information.
|
|
*/
|
|
|
|
#include "SF2PatchExtractor.h"
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <map>
|
|
#include <sys/types.h>
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
using std::string;
|
|
using std::cerr;
|
|
using std::endl;
|
|
using std::ifstream;
|
|
using std::ios;
|
|
|
|
|
|
struct Chunk
|
|
{
|
|
char id[4];
|
|
u_int32_t size;
|
|
|
|
Chunk(ifstream *, bool idOnly = false);
|
|
bool isa(std::string s);
|
|
};
|
|
|
|
Chunk::Chunk(ifstream *file, bool idOnly)
|
|
{
|
|
file->read((char *)this->id, 4);
|
|
size = 0;
|
|
|
|
if (idOnly)
|
|
return ;
|
|
|
|
unsigned char sz[4];
|
|
file->read((char *)sz, 4);
|
|
for (int i = 0; i < 4; ++i)
|
|
size += sz[i] << (i * 8);
|
|
}
|
|
|
|
bool
|
|
Chunk::isa(string s)
|
|
{
|
|
return string(id, 4) == s;
|
|
}
|
|
|
|
bool
|
|
SF2PatchExtractor::isSF2File(string fileName)
|
|
{
|
|
ifstream *file = new ifstream(fileName.c_str(), ios::in | ios::binary);
|
|
if (!file)
|
|
throw FileNotFoundException();
|
|
|
|
Chunk riffchunk(file);
|
|
if (!riffchunk.isa("RIFF")) {
|
|
file->close();
|
|
return false;
|
|
}
|
|
|
|
Chunk sfbkchunk(file, true);
|
|
if (!sfbkchunk.isa("sfbk")) {
|
|
file->close();
|
|
return false;
|
|
}
|
|
|
|
file->close();
|
|
return true;
|
|
}
|
|
|
|
SF2PatchExtractor::Device
|
|
SF2PatchExtractor::read(string fileName)
|
|
{
|
|
Device device;
|
|
|
|
ifstream *file = new ifstream(fileName.c_str(), ios::in | ios::binary);
|
|
if (!file)
|
|
throw FileNotFoundException();
|
|
|
|
Chunk riffchunk(file);
|
|
if (!riffchunk.isa("RIFF")) {
|
|
file->close();
|
|
throw WrongFileFormatException();
|
|
}
|
|
|
|
Chunk sfbkchunk(file, true);
|
|
if (!sfbkchunk.isa("sfbk")) {
|
|
file->close();
|
|
throw WrongFileFormatException();
|
|
}
|
|
|
|
while (!file->eof()) {
|
|
|
|
Chunk chunk(file);
|
|
|
|
if (!chunk.isa("LIST")) {
|
|
// cerr << "Skipping " << string(chunk.id, 4) << endl;
|
|
file->seekg(chunk.size, ios::cur);
|
|
continue;
|
|
}
|
|
|
|
Chunk listchunk(file, true);
|
|
if (!listchunk.isa("pdta")) {
|
|
// cerr << "Skipping " << string(id, 4) << endl;
|
|
file->seekg(chunk.size - 4, ios::cur);
|
|
continue;
|
|
}
|
|
|
|
int size = chunk.size - 4;
|
|
while (size > 0) {
|
|
|
|
Chunk pdtachunk(file);
|
|
size -= 8 + pdtachunk.size;
|
|
if (file->eof()) {
|
|
break;
|
|
}
|
|
|
|
if (!pdtachunk.isa("phdr")) { // preset header
|
|
// cerr << "Skipping " << string(pdtachunk.id, 4) << endl;
|
|
file->seekg(pdtachunk.size, ios::cur);
|
|
continue;
|
|
}
|
|
|
|
int presets = pdtachunk.size / 38;
|
|
for (int i = 0; i < presets; ++i) {
|
|
|
|
char name[21];
|
|
u_int16_t bank, program;
|
|
|
|
file->read((char *)name, 20);
|
|
name[20] = '\0';
|
|
file->read((char *)&program, 2);
|
|
file->read((char *)&bank, 2);
|
|
|
|
// cerr << "Read name as " << name << endl;
|
|
|
|
file->seekg(14, ios::cur);
|
|
|
|
if (i == presets - 1 &&
|
|
bank == 255 &&
|
|
program == 255 &&
|
|
string(name) == "EOP")
|
|
continue;
|
|
|
|
device[bank][program] = name;
|
|
}
|
|
}
|
|
}
|
|
|
|
file->close();
|
|
return device;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
#ifdef TEST_SF2_PATCH_EXTRACTOR
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
using SF2PatchExtractor;
|
|
|
|
if (argc != 2) {
|
|
std::cerr << "Usage: " << argv[0] << " sf2filename" << std::endl;
|
|
return 2;
|
|
}
|
|
|
|
try {
|
|
SF2PatchExtractor::Device device =
|
|
SF2PatchExtractor::read(argv[1]);
|
|
|
|
std::cerr << "Done. Presets are:" << std::endl;
|
|
|
|
for (SF2PatchExtractor::Device::iterator di = device.begin();
|
|
di != device.end(); ++di) {
|
|
|
|
std::cerr << "Bank " << di->first << ":" << std::endl;
|
|
|
|
for (SF2PatchExtractor::Bank::iterator bi = di->second.begin();
|
|
bi != di->second.end();
|
|
++bi) {
|
|
|
|
std::cerr << "Program " << bi->first << ": \"" << bi->second
|
|
<< "\"" << std::endl;
|
|
}
|
|
}
|
|
} catch (SF2PatchExtractor::WrongFileFormatException) {
|
|
std::cerr << "Wrong file format" << std::endl;
|
|
} catch (SF2PatchExtractor::FileNotFoundException) {
|
|
std::cerr << "File not found or couldn't be opened" << std::endl;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|