--- libgig/trunk/src/gigdump.cpp 2005/05/08 16:19:34 518 +++ libgig/trunk/src/tools/gigdump.cpp 2017/07/20 22:09:54 3323 @@ -1,10 +1,12 @@ /*************************************************************************** * * - * libgig - C++ cross-platform Gigasampler format file loader library * + * libgig - C++ cross-platform Gigasampler format file access library * * * - * Copyright (C) 2003-2005 by Christian Schoenebeck * + * Copyright (C) 2003-2017 by Christian Schoenebeck * * * * * + * This program is part of libgig. * + * * * 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 * @@ -29,43 +31,101 @@ #include #include -#include "gig.h" +#include "../gig.h" using namespace std; string Revision(); void PrintVersion(); +void PrintFileInformations(gig::File* gig); +void PrintGroups(gig::File* gig); void PrintSamples(gig::File* gig); +void PrintScripts(gig::File* gig); void PrintInstruments(gig::File* gig); void PrintRegions(gig::Instrument* instr); void PrintUsage(); void PrintDimensionRegions(gig::Region* rgn); +bool VerifyFile(gig::File* gig); +void RebuildChecksumTable(gig::File* gig); + +class PubSample : public gig::Sample { +public: + using DLS::Sample::pCkData; +}; + +class PubFile : public gig::File { +public: + using gig::File::VerifySampleChecksumTable; + using gig::File::RebuildSampleChecksumTable; +}; + int main(int argc, char *argv[]) { + bool bVerify = false; + bool bRebuildChecksums = false; + if (argc <= 1) { PrintUsage(); return EXIT_FAILURE; } - if (argv[1][0] == '-') { - switch (argv[1][1]) { - case 'v': - PrintVersion(); - return EXIT_SUCCESS; + + int iArg; + for (iArg = 1; iArg < argc; ++iArg) { + const string opt = argv[iArg]; + if (opt == "--") { // common for all command line tools: separator between initial option arguments and i.e. subsequent file arguments + iArg++; + break; + } + if (opt.substr(0, 1) != "-") break; + + if (opt == "-v") { + PrintVersion(); + return EXIT_SUCCESS; + } else if (opt == "--verify") { + bVerify = true; + } else if (opt == "--rebuild-checksums") { + bRebuildChecksums = true; + } else { + cerr << "Unknown option '" << opt << "'" << endl; + cerr << endl; + PrintUsage(); + return EXIT_FAILURE; } } - FILE* hFile = fopen(argv[1], "r"); + if (iArg >= argc) { + cout << "No file name provided!" << endl; + return EXIT_FAILURE; + } + const char* filename = argv[iArg]; + + FILE* hFile = fopen(filename, "r"); if (!hFile) { cout << "Invalid file argument!" << endl; return EXIT_FAILURE; } fclose(hFile); try { - RIFF::File* riff = new RIFF::File(argv[1]); + RIFF::File* riff = new RIFF::File(filename); gig::File* gig = new gig::File(riff); - PrintSamples(gig); - cout << endl; - PrintInstruments(gig); + + if (bRebuildChecksums) { + RebuildChecksumTable(gig); + } else if (bVerify) { + bool OK = VerifyFile(gig); + if (OK) cout << "All checks passed successfully! :-)\n"; + return (OK) ? EXIT_SUCCESS : EXIT_FAILURE; + } else { + PrintFileInformations(gig); + cout << endl; + PrintGroups(gig); + cout << endl; + PrintSamples(gig); + cout << endl; + PrintScripts(gig); + cout << endl; + PrintInstruments(gig); + } delete gig; delete riff; } @@ -81,16 +141,82 @@ return EXIT_SUCCESS; } +void PrintFileInformations(gig::File* gig) { + cout << "Global File Information:" << endl; + cout << " Total instruments: " << gig->Instruments << endl; + if (gig->pVersion) { + cout << " Version: " << gig->pVersion->major << "." + << gig->pVersion->minor << "." + << gig->pVersion->release << "." + << gig->pVersion->build << endl; + } + if (gig->pInfo) { + if (gig->pInfo->Name.size()) + cout << " Name: '" << gig->pInfo->Name << "'\n"; + if (gig->pInfo->ArchivalLocation.size()) + cout << " ArchivalLocation: '" << gig->pInfo->ArchivalLocation << "'\n"; + if (gig->pInfo->CreationDate.size()) + cout << " CreationDate: '" << gig->pInfo->CreationDate << "'\n"; + if (gig->pInfo->Comments.size()) + cout << " Comments: '" << gig->pInfo->Comments << "'\n"; + if (gig->pInfo->Product.size()) + cout << " Product: '" << gig->pInfo->Product << "'\n"; + if (gig->pInfo->Copyright.size()) + cout << " Copyright: '" << gig->pInfo->Copyright << "'\n"; + if (gig->pInfo->Artists.size()) + cout << " Artists: '" << gig->pInfo->Artists << "'\n"; + if (gig->pInfo->Genre.size()) + cout << " Genre: '" << gig->pInfo->Genre << "'\n"; + if (gig->pInfo->Keywords.size()) + cout << " Keywords: '" << gig->pInfo->Keywords << "'\n"; + if (gig->pInfo->Engineer.size()) + cout << " Engineer: '" << gig->pInfo->Engineer << "'\n"; + if (gig->pInfo->Technician.size()) + cout << " Technician: '" << gig->pInfo->Technician << "'\n"; + if (gig->pInfo->Software.size()) + cout << " Software: '" << gig->pInfo->Software << "'\n"; + if (gig->pInfo->Medium.size()) + cout << " Medium: '" << gig->pInfo->Medium << "'\n"; + if (gig->pInfo->Source.size()) + cout << " Source: '" << gig->pInfo->Source << "'\n"; + if (gig->pInfo->SourceForm.size()) + cout << " SourceForm: '" << gig->pInfo->SourceForm << "'\n"; + if (gig->pInfo->Commissioned.size()) + cout << " Commissioned: '" << gig->pInfo->Commissioned << "'\n"; + } +} + +void PrintGroups(gig::File* gig) { + int groups = 0; + cout << "ALL defined Groups:" << endl; + for (gig::Group* pGroup = gig->GetFirstGroup(); pGroup; pGroup = gig->GetNextGroup()) { + groups++; + string name = pGroup->Name; + if (name == "") name = ""; + else name = '\"' + name + '\"'; + cout << " Group " << groups << ")" << endl; + cout << " Name: " << name << endl; + } +} + void PrintSamples(gig::File* gig) { int samples = 0; cout << "ALL Available Samples (as there might be more than referenced by Instruments):" << endl; - gig::Sample* pSample = gig->GetFirstSample(); + PubSample* pSample = (PubSample*) gig->GetFirstSample(); while (pSample) { samples++; + // determine sample's name string name = pSample->pInfo->Name; if (name == "") name = ""; else name = '\"' + name + '\"'; + // determine group this sample belongs to + int iGroup = 1; + for (gig::Group* pGroup = gig->GetFirstGroup(); pGroup; pGroup = gig->GetNextGroup(), iGroup++) { + if (pGroup == pSample->GetGroup()) break; + } + // print sample info cout << " Sample " << samples << ") " << name << ", "; + cout << "Group " << iGroup << ", "; cout << pSample->SamplesPerSecond << "Hz, " << pSample->Channels << " Channels, " << pSample->Loops << " Loops"; if (pSample->Loops) { cout << " (Type: "; @@ -102,8 +228,43 @@ cout << ", LoopFraction=" << pSample->LoopFraction << ", Start=" << pSample->LoopStart << ", End=" << pSample->LoopEnd; cout << ", LoopPlayCount=" << pSample->LoopPlayCount; } - cout << ", Length=" << pSample->SamplesTotal << " Compressed=" << ((pSample->Compressed) ? "true" : "false") << endl; - pSample = gig->GetNextSample(); + cout << flush; + printf(", crc=%x", pSample->GetWaveDataCRC32Checksum()); + fflush(stdout); + cout << ", Length=" << pSample->SamplesTotal << " Compressed=" << ((pSample->Compressed) ? "true" : "false") + << " foffset=" << pSample->pCkData->GetFilePos() + << " fsz=" << pSample->pCkData->GetSize() + << endl; +#if 0 + { + const uint bufSize = 64; + unsigned char buf[bufSize] = {}; + pSample->SetPos(0); + RIFF::file_offset_t n = pSample->pCkData->Read(&buf[0], bufSize, 1); + //RIFF::file_offset_t n = pSample->Read(&buf[0], bufSize / pSample->FrameSize); + cout << " FrameSize=" << pSample->FrameSize << ",Data[" << n << "]" << flush; + for (int x = 0; x < bufSize; ++x) + printf("%02x ", buf[x]); + printf("\n"); + fflush(stdout); + } +#endif + pSample = (PubSample*) gig->GetNextSample(); + } +} + +void PrintScripts(gig::File* gig) { + cout << "ALL Available Real-Time Instrument Scripts (as there might be more than referenced by Instruments):" << endl; + for (int g = 0; gig->GetScriptGroup(g); ++g) { + gig::ScriptGroup* pGroup = gig->GetScriptGroup(g); + cout << " Group " << g+1 << ") '" << pGroup->Name << "'\n"; + for (int s = 0; pGroup->GetScript(s); ++s) { + gig::Script* pScript = pGroup->GetScript(s); + cout << " Script " << s+1 << ") '" << pScript->Name << "':\n"; + cout << "[START OF SCRIPT]\n"; + cout << pScript->GetScriptAsText(); + cout << "[END OF SCRIPT]\n"; + } } } @@ -119,6 +280,14 @@ cout << " Instrument " << instruments << ") " << name << ", "; cout << " MIDIBank=" << pInstrument->MIDIBank << ", MIDIProgram=" << pInstrument->MIDIProgram << endl; + + cout << " ScriptSlots=" << pInstrument->ScriptSlotCount() << endl; + for (int s = 0; s < pInstrument->ScriptSlotCount(); ++s) { + gig::Script* pScript = pInstrument->GetScriptOfSlot(s); + string name = pScript->Name; + cout << " ScriptSlot[" << s << "]='" << name << "'\n"; + } + PrintRegions(pInstrument); pInstrument = gig->GetNextInstrument(); @@ -178,6 +347,12 @@ case gig::dimension_random: // Different samples triggered each time a note is played, random order cout << "RANDOM"; break; + case gig::dimension_smartmidi: // For MIDI tools like legato and repetition mode + cout << "SMARTMIDI"; + break; + case gig::dimension_roundrobinkeyboard: // Different samples triggered each time a note is played, any key advances the counter + cout << "ROUNDROBINKEYBOARD"; + break; case gig::dimension_modwheel: // Modulation Wheel (MIDI Controller 1) cout << "MODWHEEL"; break; @@ -257,9 +432,6 @@ case gig::split_type_normal: cout << "NORMAL" << endl; break; - case gig::split_type_customvelocity: - cout << "CUSTOMVELOCITY" << endl; - break; case gig::split_type_bit: cout << "BIT" << endl; break; @@ -274,10 +446,19 @@ } } +template +static void printIntArray(T_int* array, int size) { + printf("{"); + for (int i = 0; i < size; ++i) + printf("[%d]=%d,", i, array[i]); + printf("}"); + fflush(stdout); +} + void PrintDimensionRegions(gig::Region* rgn) { int dimensionRegions = 0; gig::DimensionRegion* pDimensionRegion; - while (dimensionRegions < 32) { + while (dimensionRegions < rgn->DimensionRegions) { pDimensionRegion = rgn->pDimensionRegions[dimensionRegions]; if (!pDimensionRegion) break; @@ -317,14 +498,87 @@ cout << "UNKNOWN - please report this !"; } cout << ", VelocityResponseDepth=" << (int) pDimensionRegion->VelocityResponseDepth << ", VelocityResponseCurveScaling=" << (int) pDimensionRegion->VelocityResponseCurveScaling << endl; - cout << " Pan=" << (int) pDimensionRegion->Pan << endl; + cout << " VelocityUpperLimit=" << (int) pDimensionRegion->VelocityUpperLimit << " DimensionUpperLimits[]=" << flush; + printIntArray(pDimensionRegion->DimensionUpperLimits, 8); + cout << endl; +#if 0 // requires access to protected member VelocityTable, so commented out for now + if (pDimensionRegion->VelocityTable) { + cout << " VelocityTable[]=" << flush; + printIntArray(pDimensionRegion->VelocityTable, 127); + cout << endl; + } +#endif + gig::eg_opt_t& egopt = pDimensionRegion->EGOptions; + cout << " Pan=" << (int) pDimensionRegion->Pan << ", EGAttackCancel=" << egopt.AttackCancel << ", EGAttackHoldCancel=" << egopt.AttackHoldCancel << ", EGDecayCancel=" << egopt.DecayCancel << ", EGReleaseCancel=" << egopt.ReleaseCancel << endl; dimensionRegions++; } } +struct _FailedSample { + gig::Sample* sample; + uint32_t calculatedCRC; +}; + +bool VerifyFile(gig::File* _gig) { + PubFile* gig = (PubFile*) _gig; + + cout << "Verifying sample checksum table ... " << flush; + if (!gig->VerifySampleChecksumTable()) { + cout << "DAMAGED\n"; + cout << "You may use --rebuild-checksums to repair the sample checksum table.\n"; + return false; + } + cout << "OK\n" << flush; + + cout << "Verifying samples ... " << flush; + std::map failedSamples; + int iTotal = 0; + for (gig::Sample* pSample = gig->GetFirstSample(); pSample; pSample = gig->GetNextSample(), ++iTotal) { + uint32_t crc; // will be set to the actually now calculated checksum + if (!pSample->VerifyWaveData(&crc)) { + _FailedSample failed; + failed.sample = pSample; + failed.calculatedCRC = crc; + failedSamples[iTotal] = failed; + } + } + if (failedSamples.empty()) { + cout << "ALL OK\n"; + return true; + } else { + cout << failedSamples.size() << " of " << iTotal << " Samples DAMAGED:\n"; + for (std::map::iterator it = failedSamples.begin(); it != failedSamples.end(); ++it) { + const int i = it->first; + gig::Sample* pSample = it->second.sample; + + string name = pSample->pInfo->Name; + if (name == "") name = ""; + else name = '\"' + name + '\"'; + + cout << "Damaged Sample " << (i+1) << ") " << name << flush; + printf(" expectedCRC=%x calculatedCRC=%x\n", pSample->GetWaveDataCRC32Checksum(), it->second.calculatedCRC); + } + return false; + } +} + +void RebuildChecksumTable(gig::File* _gig) { + PubFile* gig = (PubFile*) _gig; + + cout << "Recalculating checksums of all samples ... " << flush; + bool bSaveRequired = gig->RebuildSampleChecksumTable(); + cout << "OK\n"; + if (bSaveRequired) { + cout << "WARNING: File structure change required, rebuilding entire file now ..." << endl; + gig->Save(); + cout << "DONE\n"; + cout << "NOTE: Since the entire file was rebuilt, you may need to manually check all samples in this particular case now!\n"; + } +} + string Revision() { - string s = "$Revision: 1.16 $"; + string s = "$Revision$"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } @@ -336,8 +590,12 @@ void PrintUsage() { cout << "gigdump - parses Gigasampler files and prints out the content." << endl; cout << endl; - cout << "Usage: gigdump [-v] FILE" << endl; + cout << "Usage: gigdump [-v | --verify | --rebuild-checksums] FILE" << endl; + cout << endl; + cout << " --rebuild-checksums Rebuild checksum table for all samples." << endl; + cout << endl; + cout << " -v Print version and exit." << endl; cout << endl; - cout << " -v Print version and exit." << endl; + cout << " --verify Checks raw wave data integrity of all samples." << endl; cout << endl; }