├── .gitignore ├── README.markdown ├── Makefile ├── convert-gig-file └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | gig2sfz 2 | Makefile.local 3 | *.gig 4 | *.sfz 5 | *.sf2 6 | 7 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | gig2sfz 2 | ======= 3 | 4 | gig2sfz is a program to convert Gigasampler files to SFZ files, using libgig. 5 | It will not convert the sample data itself; use "gigextract" (part of libgig) 6 | for that. Better yet, use the included "convert-gig-file" script. 7 | 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BIN := gig2sfz 2 | SRCS := main.cpp 3 | 4 | -include Makefile.local 5 | 6 | # ======= 7 | 8 | default: $(BIN) 9 | 10 | $(BIN): $(SRCS) 11 | $(CXX) $^ -o $@ $(CFLAGS) -lgig 12 | 13 | .PHONY: test 14 | test: $(BIN) 15 | $(BIN) wurlitzer_ep200.gig 16 | vim "Wurlitzer EP200.sfz" 17 | 18 | -------------------------------------------------------------------------------- /convert-gig-file: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | gig_file="$1" 4 | if [ -z "$gig_file" ]; then 5 | echo "Usage:" 6 | echo "cd to the directory you want to extract everything to" 7 | echo "and do this:" 8 | echo " convert-gig-file " 9 | exit 1 10 | fi 11 | 12 | gigextract "$gig_file" . 13 | 14 | # gigextract stupidly prepends the index to the .wav file names, 15 | # so strip it off. 16 | for file in *.wav; do 17 | echo mv -i "$file" "${file#*_}" 18 | mv -i "$file" "${file#*_}" 19 | done 20 | 21 | gig2sfz "$gig_file" 22 | 23 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "gig.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | 9 | void print_usage() { 10 | cout << "Usage:" << endl; 11 | cout << " gig2sfz " << endl; 12 | cout << endl; 13 | cout << "(Use gigextract to extract the samples.)" << endl; 14 | } 15 | 16 | string out_file_name(string orig_name) { 17 | // Oops, not used, we'll use the instrument names. 18 | string result = orig_name; 19 | return result.replace(result.rfind(".gig"), 4, ".sfz"); 20 | } 21 | 22 | 23 | extern void convert_gig_file(gig::File* gig); 24 | extern void convert_gig_instrument(gig::Instrument* instrument); 25 | extern void convert_gig_region(gig::Region* region, ostream& sfz); 26 | extern int gig_fine_tune_to_cents(uint32_t gig_fine_tune); 27 | 28 | 29 | int main(int argc, char* argv[]) { 30 | if (argc != 2) { 31 | print_usage(); 32 | return 1; 33 | } 34 | 35 | try { 36 | RIFF::File riff(argv[1]); 37 | gig::File gig(&riff); 38 | convert_gig_file(&gig); 39 | } 40 | catch (RIFF::Exception exception) { 41 | exception.PrintMessage(); 42 | return 1; 43 | } 44 | 45 | return 0; 46 | } 47 | 48 | 49 | void convert_gig_file(gig::File* gig) 50 | { 51 | gig::Instrument* instrument = gig->GetFirstInstrument(); 52 | for (; instrument; instrument = gig->GetNextInstrument()) 53 | convert_gig_instrument(instrument); 54 | } 55 | 56 | 57 | void convert_gig_instrument(gig::Instrument* instrument) 58 | { 59 | string name = instrument->pInfo->Name; 60 | if (name == "") 61 | name = "Unnamed Instrument"; 62 | 63 | ofstream sfz((name + ".sfz").c_str()); 64 | 65 | gig::Region* region = instrument->GetFirstRegion(); 66 | for (; region; region = instrument->GetNextRegion()) 67 | convert_gig_region(region, sfz); 68 | } 69 | 70 | 71 | void convert_gig_region(gig::Region* region, ostream& sfz) 72 | { 73 | if (region->Dimensions > 0) 74 | sfz << "" << endl; 75 | else { 76 | sfz << "" << endl; 77 | gig::Sample* sample = region->GetSample(); 78 | sfz << "sample=" << sample->pInfo->Name << endl; 79 | sfz << "pitch_keycenter=" << sample->MIDIUnityNote << endl; 80 | if (sample->FineTune) 81 | sfz << "tune=" << gig_fine_tune_to_cents(sample->FineTune) << endl; 82 | } 83 | sfz << "lokey=" << region->KeyRange.low << endl; 84 | sfz << "hikey=" << region->KeyRange.high << endl; 85 | sfz << endl; 86 | 87 | if (region->Dimensions > 0) { 88 | // Get dimension info. 89 | struct Dimension { 90 | gig::dimension_t type; 91 | int top_bit, bits; 92 | uint8_t mask; 93 | }; 94 | int num_dimensions = region->Dimensions; 95 | Dimension dimensions[num_dimensions]; 96 | int start_bit = 0; 97 | for (int dim = 0; dim < num_dimensions; ++dim) { 98 | gig::dimension_def_t* dim_def = ®ion->pDimensionDefinitions[dim]; 99 | switch (dim_def->dimension) { 100 | case gig::dimension_velocity: 101 | case gig::dimension_releasetrigger: 102 | // These are okay. 103 | dimensions[dim].type = dim_def->dimension; 104 | dimensions[dim].top_bit = start_bit + dim_def->bits - 1; 105 | dimensions[dim].bits = dim_def->bits; 106 | dimensions[dim].mask = 0; 107 | for (int which_bit = 0; which_bit < dim_def->bits; ++which_bit) { 108 | dimensions[dim].mask |= 0x01 << start_bit; 109 | start_bit += 1; 110 | } 111 | break; 112 | 113 | default: 114 | throw RIFF::Exception("Unsupported dimension type."); 115 | break; 116 | } 117 | } 118 | 119 | // Output the dimension regions. 120 | for (int which_dim_rgn = 0; which_dim_rgn < region->DimensionRegions; ++which_dim_rgn) { 121 | gig::DimensionRegion* dim_rgn = region->pDimensionRegions[which_dim_rgn]; 122 | sfz << "" << endl; 123 | sfz << "sample=" << dim_rgn->pSample->pInfo->Name << ".wav" << endl; 124 | sfz << "pitch_keycenter=" << (int) dim_rgn->UnityNote << endl; 125 | if (dim_rgn->FineTune) 126 | sfz << "tune=" << gig_fine_tune_to_cents(dim_rgn->FineTune) << endl; 127 | if (dim_rgn->Gain) 128 | sfz << "volume=" << (-dim_rgn->Gain / 655360.0) << endl; 129 | if (dim_rgn->SampleLoops > 0) { 130 | DLS::sample_loop_t* loop = &dim_rgn->pSampleLoops[0]; 131 | sfz << "loop_mode=loop_continuous "; 132 | sfz << "loop_start=" << loop->LoopStart << " "; 133 | sfz << "loop_end=" << loop->LoopStart + loop->LoopLength - 1 << endl; 134 | } 135 | for (int dim = 0; dim < num_dimensions; ++dim) { 136 | Dimension* dimension = &dimensions[dim]; 137 | switch (dimension->type) { 138 | case gig::dimension_velocity: 139 | { 140 | int loval = 141 | (which_dim_rgn & dimension->mask) << (6 - dimension->top_bit); 142 | sfz << "lovel=" << loval << " "; 143 | int range = (0x80 >> dimension->bits) - 1; 144 | sfz << "hivel=" << loval + range << endl; 145 | } 146 | break; 147 | 148 | case gig::dimension_releasetrigger: 149 | if (which_dim_rgn & dimension->mask) 150 | sfz << "trigger=release" << endl; 151 | break; 152 | } 153 | } 154 | sfz << endl; 155 | } 156 | 157 | sfz << endl; 158 | } 159 | } 160 | 161 | 162 | int gig_fine_tune_to_cents(uint32_t gig_fine_tune) 163 | { 164 | // gig.h says this: 165 | // Specifies the fraction of a semitone up from the specified MIDI unity 166 | // note field. A value of 0x80000000 means 1/2 semitone (50 cents) and a 167 | // value of 0x00000000 means no fine tuning between semitones. 168 | // But it seems to really be integer cents. 169 | 170 | // return round(((double) gig_fine_tune / (double) 0x80000000) * 50); 171 | return (int) gig_fine_tune; 172 | } 173 | 174 | 175 | 176 | --------------------------------------------------------------------------------