//----------------------------------- //--- 010 Editor v2.0 Binary Template // // File: WAVTemplateAdv.bt // Author: SweetScape Software plus submissions // Revision: 1.1 // Purpose: Defines a template for // parsing WAV audio files. Includes SMPL chunk // parsing (Parsing wav files that contains // Sustain Loops. Useful for Game Developers). // Tested with: CoolEdit and Sound Forge. //----------------------------------- // Typedefs for the wave file typedef char ID[4]; // Record whether we have found a format chunk yet local int haveValidFormat = false; //----------------------------------- // Define structures used in WAV files // Stores the file header information typedef struct { ID groupID; long size; ID riffType; } WAVRIFFHEADER; // Stores the format information for the file typedef struct { ID chunkID; long chunkSize; local int pos = FTell(); short wFormatTag; unsigned short wChannels; unsigned long dwSamplesPerSec; unsigned long dwAvgBytesPerSec; unsigned short wBlockAlign; unsigned short wBitsPerSample; // Mark that we have found a valid format chunk haveValidFormat = true; // Unknown data at the end of the chunk if( chunkSize > (FTell() - pos) ) uchar unknown[ chunkSize - (FTell() - pos) ]; // Padding so the next chunk starts on an even byte if( chunkSize & 1 ) uchar padding; } FORMATCHUNK; // Stores the actual wave data typedef struct { ID chunkID; long chunkSize; // Test if we have a valid format if( !haveValidFormat ) { Warning( "File contains no valid WAVE format chunk." ); return -1; } // Parse the samples of the data if( ((format.wBitsPerSample != 8) && (format.wBitsPerSample != 16) && (format.wBitsPerSample != 32)) || (chunkSize % (int)format.wBlockAlign != 0) ) { // Unsupported storage method used unsigned char waveformData[chunkSize]; } else if( (format.wChannels == 1) && (format.wBitsPerSample == 8) ) { // Define an array of 8-bit samples - common case uchar samples[ chunkSize ]; } else if( (format.wChannels == 1) && (format.wBitsPerSample == 16) ) { // Define an array of 16-bit samples - common case short samples[ chunkSize/2 ]; } else if( (format.wChannels == 1) && (format.wBitsPerSample == 32) ) { // Define an array of 32-bit samples - common case int samples[ chunkSize/4 ]; } else { // Define general case sample struct SAMPLES { if( format.wBitsPerSample == 8 ) uchar channels[ format.wChannels ]; else if( format.wBitsPerSample == 16 ) short channels[ format.wChannels ]; else if( format.wBitsPerSample == 32 ) int channels[ format.wChannels ]; } samples[ chunkSize / (int)format.wBlockAlign ]; } // Padding so the next chunk starts on an even byte if( (chunkSize & 1) && (FTell() < FileSize()) ) uchar padding; } DATACHUNK; // Stores the size of the wave after decompression typedef struct { ID chunkID; long chunkSize; unsigned long uncompressedSize; } FACTCHUNK; // Stores a list of cue points or markers to points in the data typedef struct { long dwIdentifier; long dwPosition; ID fccChunk; long dwChunkStart; long dwBlockStart; long dwSampleOffset; } CUEPOINT; typedef struct { ID chunkID; long chunkSize; local int pos = FTell(); long dwCuePoints; CUEPOINT points[dwCuePoints]; // Unknown data at the end of the chunk if( chunkSize > (FTell() - pos) ) uchar unknown[ chunkSize - (FTell() - pos) ]; } CUECHUNK; // Define a list chunk with a set of subchunks typedef struct { ID chunkID; long chunkSize; char listData[chunkSize]; // Padding so the next chunk starts on an even byte if( (chunkSize & 1) && (FTell() < FileSize()) ) uchar padding; } LISTSUBCHUNK; typedef struct { ID chunkID; long chunkSize; local quad pos = FTell(); ID chunkType; // Read the subchunks while( FTell() - pos < chunkSize ) LISTSUBCHUNK subchunk; // Padding so the next chunk starts on an even byte if( (chunkSize & 1) && (FTell() < FileSize()) ) uchar padding; } LISTCHUNK; // A chunk which could not be identified typedef struct { ID chunkID; long chunkSize; uchar unknownData[chunkSize]; // Padding so the next chunk starts on an even byte if( (chunkSize & 1) && (FTell() < FileSize()) ) uchar padding; } UNKNOWNCHUNK; //--------------------------------------------- // SMPL / SMPL Loop //--------------------------------------------- typedef long SMPLLOOPS_Cue_ID ; string WAV_SMPLLOOPS_Cue_ID( SMPLLOOPS_Cue_ID cid ) { string sret; SPrintf( sret, "Cue Point ID: %u", cid ); return sret; } //--------------------------------------------- typedef long SMPLLOOPS_Play_Count ; string WAV_SMPLLOOPS_Play_Count( SMPLLOOPS_Play_Count pc ) { string sret; if (pc==0) { SPrintf( sret, " Infinite Sustain Loop (%u)", pc ); } else { SPrintf( sret, "Play Count: %u", pc ); } return sret; } //--------------------------------------------- typedef long SMPLLOOPS_Start ; string WAV_SMPLLOOPS_Start( SMPLLOOPS_Start st ) { string sret; SPrintf( sret, "Loop Start at %u byte offset", st ); return sret; } //--------------------------------------------- typedef long SMPLLOOPS_End ; string WAV_SMPLLOOPS_End( SMPLLOOPS_End end ) { string sret; SPrintf( sret, "Loop End at %u byte offset", end ); return sret; } //--------------------------------------------- typedef long SMPLLOOPS_Fraction ; string WAV_SMPLLOOPS_Fraction( SMPLLOOPS_Fraction f ) { string sret; SPrintf( sret, "Fraction: %u", f ); return sret; } //--------------------------------------------- typedef long SMPL_SL ; string WAV_SMPL_SL( SMPL_SL sl ) { string sret; SPrintf( sret, "Number of Samples Loops (Sustain Loops): %u", sl ); return sret; } //--------------------------------------------- typedef long SMPL_SD ; string WAV_SMPL_SD( SMPL_SD sd ) { string sret; SPrintf( sret, "Sample Data (number of bytes): %u", sd ); return sret; } //--------------------------------------------- typedef long SMPL_SMPTE_Offset ; string WAV_SMPL_SMPTE_Offset( SMPL_SMPTE_Offset so ) { string sret; SPrintf( sret, "SMPTE Offset (for synchronization): %u", so ); return sret; } //--------------------------------------------- typedef long SMPL_MIDI_Pitch_Fraction ; string WAV_SMPL_MIDI_Pitch_Fraction( SMPL_MIDI_Pitch_Fraction pf ) { string sret; SPrintf( sret, "MIDI Pitch Fraction: %u", pf ); return sret; } //--------------------------------------------- typedef long SMPL_MIDI_Unity_Note ; string WAV_SMPL_MIDI_Unity_Note( SMPL_MIDI_Unity_Note un ) { string sret; SPrintf( sret, "MIDI unity note value: %u", un ); return sret; } //--------------------------------------------- typedef long SMPL_Product ; string WAV_SMPL_Product( SMPL_Product product ) { string sret; SPrintf( sret, "MIDI model ID (defined by the manufacturer): %u", product ); return sret; } //--------------------------------------------- typedef long SMPL_Sample_Period ; string WAV_SMPL_Sample_Period( SMPL_Sample_Period sp ) { string sret; // The sample period specifies the duration of time that passes during the playback of one sample in nanoseconds (normally equal to 1 / Samplers Per Second, where Samples Per Second is the value found in the format chunk). SPrintf( sret, "Sample Period: %u", sp ); return sret; } //--------------------------------------------- typedef long SMPL_SMPTE ; string WAV_SMPL_SMPTE( SMPL_SMPTE smptef ) { string s; string sret; switch( smptef ) { case 0: SPrintf( s, "No SMPTE offset" ); break; case 24: SPrintf( s, "24 frames per second" ); break; case 25: SPrintf( s, "25 frames per second" ); break; case 29: SPrintf( s, "30 frames per second with frame dropping (30 drop)" ); break; case 30: SPrintf( s, "30 frames per second" ); break; default: SPrintf( s, "unknown (%u)", smptef ); break; } SPrintf( sret, "SMPTE Format: %s", s ); return sret; } //--------------------------------------------- typedef long SMPL_Manufacturer ; string WAV_SMPL_Manufacturer( SMPL_Manufacturer manufacture ) { string s; string sret; switch( manufacture ) { case 0: SPrintf( s, "Unknown" ); break; case 1: SPrintf( s, "Sequential Circuits"); break; case 2: SPrintf( s, "Big Briar"); break; case 3: SPrintf( s, "Octave / Plateau"); break; case 4: SPrintf( s, "Moog"); break; case 5: SPrintf( s, "Passport Designs"); break; case 6: SPrintf( s, "Lexicon"); break; case 7: SPrintf( s, "Kurzweil"); break; case 8: SPrintf( s, "Fender"); break; case 9: SPrintf( s, "Gulbransen"); break; case 10: SPrintf( s, "Delta Labs"); break; case 11: SPrintf( s, "Sound Comp."); break; case 12: SPrintf( s, "General Electro"); break; case 13: SPrintf( s, "Techmar"); break; case 14: SPrintf( s, "Matthews Research"); break; case 16: SPrintf( s, "Oberheim"); break; case 17: SPrintf( s, "PAIA"); break; case 18: SPrintf( s, "Simmons"); break; case 19: SPrintf( s, "DigiDesign"); break; case 20: SPrintf( s, "Fairlight"); break; case 21: SPrintf( s, "JL Cooper"); break; case 22: SPrintf( s, "Lowery"); break; case 23: SPrintf( s, "Lin"); break; case 24: SPrintf( s, "Emu"); break; case 27: SPrintf( s, "Peavey"); break; case 32: SPrintf( s, "Bon Tempi"); break; case 33: SPrintf( s, "S.I.E.L."); break; case 35: SPrintf( s, "SyntheAxe"); break; case 36: SPrintf( s, "Hohner"); break; case 37: SPrintf( s, "Crumar"); break; case 38: SPrintf( s, "Solton"); break; case 39: SPrintf( s, "Jellinghaus Ms"); break; case 40: SPrintf( s, "CTS"); break; case 41: SPrintf( s, "PPG"); break; case 47: SPrintf( s, "Elka"); break; case 54: SPrintf( s, "Cheetah"); break; case 62: SPrintf( s, "Waldorf"); break; case 64: SPrintf( s, "Kawai"); break; case 65: SPrintf( s, "Roland"); break; case 66: SPrintf( s, "Korg"); break; case 67: SPrintf( s, "Yamaha"); break; case 68: SPrintf( s, "Casio"); break; case 70: SPrintf( s, "Kamiya Studio"); break; case 71: SPrintf( s, "Akai"); break; case 72: SPrintf( s, "Victor"); break; case 75: SPrintf( s, "Fujitsu"); break; case 76: SPrintf( s, "Sony"); break; case 78: SPrintf( s, "Teac"); break; case 80: SPrintf( s, "Matsushita"); break; case 81: SPrintf( s, "Fostex"); break; case 82: SPrintf( s, "Zoom"); break; case 84: SPrintf( s, "Matsushita"); break; case 85: SPrintf( s, "Suzuki"); break; case 86: SPrintf( s, "Fuji Sound"); break; case 87: SPrintf( s, "Acoustic Technical Laboratory"); break; default: SPrintf( s, "Unknown"); break; } SPrintf( sret, "Manufacturer: %s", s); return sret; } //---------------------------------------------------------------------- typedef long SMPLLOOPS_Type ; string WAV_SMPL_Loop_Type( SMPLLOOPS_Type loop ) { string s; switch( loop ) { case 0 : SPrintf( s, "Loop: Forward (%u)", loop ); break; case 1 : SPrintf( s, "Loop: Ping Pong (%u)", loop ); break; case 2 : SPrintf( s, "Loop: Reverse (%u)", loop ); break; case 3 : SPrintf( s, "Loop [reserved for future standard types] (%u)", loop ); break; default: s = "Loop: "; } return s; } //---------------------------------------------------------------------- // SMPL Loop typedef struct { SMPLLOOPS_Cue_ID Cue_Point; SMPLLOOPS_Type Type; SMPLLOOPS_Start Start; SMPLLOOPS_End End; SMPLLOOPS_Fraction Fraction; SMPLLOOPS_Play_Count Play_Count; } SMPLLOOPS; //---------------------------------------------------------------------- // Chunk SMPL typedef struct { ID chunkID; long chunkSize; SMPL_Manufacturer Manufacturer; SMPL_Product Product; // Product code (Manufacture) SMPL_Sample_Period Sample_Period; SMPL_MIDI_Unity_Note MIDI_Unity_Note; SMPL_MIDI_Pitch_Fraction MIDI_Pitch_Fraction; SMPL_SMPTE SMPTE; SMPL_SMPTE_Offset SMPTE_Offset; SMPL_SL Num_Sample_Loops; SMPL_SD Sampler_Data; SMPLLOOPS loops[Num_Sample_Loops]; //Padding so the next chunk starts on an even byte if( (chunkSize & 1) && (FTell() < FileSize()) ) uchar padding; } SMPLCHUNK; //--------------------------------------------- // Define the headers LittleEndian(); SetBackColor( cLtPurple ); WAVRIFFHEADER header; // Check for valid header if( header.groupID != "RIFF" || header.riffType != "WAVE" ) { Warning( "File is not a valid wave file. Template stopped." ); return -1; } // Read the file as a set of chunks local char tag[5]; local uint size; while( !FEof() ) { // Read the chunk tag ReadBytes( tag, FTell(), 4 ); tag[4] = 0; // See which chunk this is switch( tag ) { case "fmt ": SetBackColor( cLtGray ); FORMATCHUNK format; break; case "data": SetBackColor( cNone ); DATACHUNK data; break; case "fact": SetBackColor( cLtBlue ); FACTCHUNK fact; break; case "cue ": SetBackColor( cLtGray ); CUECHUNK cue; break; case "smpl": SetBackColor( cLtGray ); SMPLCHUNK smpl; break; case "LIST": SetBackColor( cLtYellow ); LISTCHUNK list; break; default: // Unknown chunk size = ReadUInt( FTell()+4 ); Printf( "Encountered unknown chunk '%s' of size %d at position %Ld.\n", tag, size, FTell() ); SetBackColor( cNone ); UNKNOWNCHUNK unknown; break; } }