/* * JAIV1 BANK structure * 0x00 int32 0x42414E4B 'BANK'; * 0x04 int32[0xF0] InstrumentPointers * ---- NOTE: If the instrument pointer is 0, then the instrument for that index is NULL! */ public JInstrument[] loadBank(BeBinaryReader binStream, int Base) { if (binStream.ReadUInt32() != BANK) // Check if first 4 bytes are BANK { throw new InvalidDataException("Data is not a BANK"); } var InstrumentPoiners = new int[0xF0]; // Table of pointers for the instruments; var Instruments = new JInstrument[0xF0]; InstrumentPoiners = Helpers.readInt32Array(binStream, 0xF0); // Read instrument pointers. for (int i = 0; i < 0xF0; i++) { binStream.BaseStream.Position = InstrumentPoiners[i] + Base; // Seek to pointer position var type = binStream.ReadInt32(); // Read type binStream.BaseStream.Seek(-4, SeekOrigin.Current); // Seek back 4 bytes to undo header read. currentInstID = (short)i; switch (type) { case INST: Instruments[i] = loadInstrument(binStream, Base); // Load instrument break; case PER2: Instruments[i] = loadPercussionInstrument(binStream, Base); // Load percussion break; default: // no action, we don't know what it is and it won't misalign. break; } } return(Instruments); }
public JInstrument loadPercussionInstrument(BeBinaryReader binStream, int Base) { if (binStream.ReadInt32() != Perc) { throw new InvalidDataException("Perc section started with unexpected data"); } var newPERC = new JInstrument(); newPERC.IsPercussion = true; newPERC.Pitch = 1f; newPERC.Volume = 1f; var count = binStream.ReadInt32(); var ptrs = Helpers.readInt32Array(binStream, count); var iKeys = new JInstrumentKey[count]; for (int i = 0; i < count; i++) { var PmapOffset = ptrs[i]; if (PmapOffset > 0) { var newKey = new JInstrumentKey(); newKey.Velocities = new JInstrumentKeyVelocity[0x81]; var pmapDataOffs = PmapOffset + Base; // OH LOOK ANOTHER RELATIVE TO BANK BASE. binStream.BaseStream.Position = pmapDataOffs; if (binStream.ReadInt32() != Pmap) { Console.WriteLine("ERROR: Invalid PMAP data {0:X} -- Potential misalignment!", binStream.BaseStream.Position); continue; } newKey.Volume = binStream.ReadInt32(); newKey.Pitch = binStream.ReadInt32(); //binStream.ReadInt32(); // byte panning binStream.BaseStream.Seek(8, SeekOrigin.Current); // runtime. var velRegCount = binStream.ReadInt32(); var velLow = 0; for (int b = 0; b < velRegCount; b++) { var breg = readKeyVelRegion(binStream, Base); for (int c = 0; c < breg.baseVel - velLow; c++) { // They're velocity regions, so we're going to have a toothy / gappy piano. So we need to span the missing gaps with the previous region config. // This means that if the last key was 4, and the next key was 8 -- whatever parameters 4 had will span keys 4 5 6 and 7. newKey.Velocities[c] = breg; // store the region newKey.Velocities[127] = breg; } velLow = breg.baseVel; // store the velocity for spanning } iKeys[i] = newKey; } } newPERC.Keys = iKeys; newPERC.oscillatorCount = 0; return(newPERC); }
/* * JAIV1 PER2 Structure * 0x00 int32 0x50455232 'PER2' * 0x04 byte[0x84] unused; // the actual f**k? Waste of 0x84 perfectly delicious bytes. * 0x8C *PercussionKey[100] * * PER2 PercussionKey Structure * float pitch; * float volume; * byte[0x8] unused? * int32 velocityRegionCount * VelocityRegion[velocityRegionCount] velocities * */ public JInstrument loadPercussionInstrument(BeBinaryReader binStream, int Base) { var Inst = new JInstrument(); if (binStream.ReadUInt32() != PER2) // Check if first 4 bytes are PER2 { throw new InvalidDataException("Data is not an PER2"); } Inst.Pitch = 1.0f; Inst.Volume = 1.0f; Inst.IsPercussion = true; binStream.BaseStream.Seek(0x84, SeekOrigin.Current); JInstrumentKey[] keys = new JInstrumentKey[100]; int[] keyPointers = new int[100]; keyPointers = Helpers.readInt32Array(binStream, 100); // read the pointers. for (int i = 0; i < 100; i++) // Loop through all pointers. { if (keyPointers[i] == 0) { continue; } binStream.BaseStream.Position = keyPointers[i] + Base; // Set position to key pointer pos (relative to base) var newKey = new JInstrumentKey(); newKey.Pitch = binStream.ReadSingle(); // read the pitch newKey.Volume = binStream.ReadSingle(); // read the volume binStream.BaseStream.Seek(8, SeekOrigin.Current); // runtime values, skip var velRegCount = binStream.ReadInt32(); // read count of regions we have newKey.Velocities = new JInstrumentKeyVelocity[0xff]; // 0xFF just in case. int[] velRegPointers = Helpers.readInt32Array(binStream, velRegCount); var velLow = 0; // Again, these are regions -- see LoadInstrument for this exact code ( a few lines above ) for (int b = 0; b < velRegCount; b++) { binStream.BaseStream.Position = velRegPointers[b] + Base; var breg = readKeyVelRegion(binStream, Base); // Read the vel region. for (int c = 0; c < breg.baseVel - velLow; c++) { // They're velocity regions, so we're going to have a toothy / gappy piano. So we need to span the missing gaps with the previous region config. // This means that if the last key was 4, and the next key was 8 -- whatever parameters 4 had will span keys 4 5 6 and 7. newKey.Velocities[c] = breg; // store the region newKey.Velocities[127] = breg; } velLow = breg.baseVel; // store the velocity for spanning } keys[i] = newKey; } Inst.Keys = keys; return(Inst); }
/* JAIV2 Instrument Structure * 0x00 int32 = 0x496E7374 'Inst' * 0x04 int32 oscillatorCount * 0x08 int32[oscillatorCount] oscillatorIndicies * ???? int32 0 * ???? int32 keyRegionCount * ???? keyRegion[keyRegionCount] * ???? float gain * ???? float freqmultiplier * */ public JInstrument loadInstrument(BeBinaryReader binStream, int Base) { var newInst = new JInstrument(); newInst.IsPercussion = false; // Instrument isn't percussion // This is wrong, they come at the end of the instrument //newInst.Pitch = 1; // So these kinds of instruments don't initialize with a pitch value, which is strange. //newInst.Volume = 1; // I guess they figured that it was redundant since they're already doing it in 3 other places. if (binStream.ReadInt32() != Inst) { throw new InvalidDataException("Inst section started with unexpected data"); } var osciCount = binStream.ReadInt32(); // Read the count of the oscillators. newInst.oscillatorCount = (byte)osciCount; // Hope no instrument never ever ever has > 255 oscillators lol. newInst.oscillators = new JOscillator[osciCount]; // Initialize the instrument with the proper amount of oscillaotrs for (int i = 0; i < osciCount; i++) // Loop through and read each oscillator. { var osciIndex = binStream.ReadInt32(); // Each oscillator is stored as a 32 bit index. newInst.oscillators[i] = bankOscillators[osciIndex]; // We loaded the oscillators already, I hope. So this will grab them by their index. } var notpadding = binStream.ReadInt32(); // NOT PADDING. F**K. Probably effects. Helpers.readInt32Array(binStream, notpadding); var keyRegCount = binStream.ReadInt32(); var keyLow = 0; // For region spanning. JInstrumentKey[] keys = new JInstrumentKey[0x81]; // Always go for one more. for (int i = 0; i < keyRegCount; i++) // Loop through all pointers. { var bkey = readKeyRegion(binStream, Base); // Read the key region //Console.WriteLine("KREG BASE KEY {0}", bkey.baseKey); for (int b = 0; b < bkey.baseKey - keyLow; b++) { // They're key regions, so we're going to have a toothy / gappy piano. So we need to span the missing gaps with the previous region config. // This means that if the last key was 4, and the next key was 8 -- whatever parameters 4 had will span keys 4 5 6 and 7. keys[b + keyLow] = bkey; // span the keys keys[127] = bkey; } keyLow = bkey.baseKey; // Store our last key } newInst.Keys = keys; newInst.Volume = binStream.ReadSingle(); // ^^ newInst.Pitch = binStream.ReadSingle(); // Pitch and volume come last??? // WE HAVE READ EVERY BYTE IN THE INST, WOOO return(newInst); }
/* * JAIV2 LIST STRUCTURE * 0x00 - int32 0x4C495354 'LIST'; * 0x04 - int32 length * 0x08 - int32 count * 0x0c - int32[count] instrumentPointers (RELATIVE TO IBANK 0x00) */ public JInstrument[] loadInstrumentList(BeBinaryReader binStream, int Base) { JInstrument[] instruments = new JInstrument[0xF0]; // JSystem doesn't have more than 0xF0 instruments in each bank if (binStream.ReadInt32() != LIST) // Verify we're loading the right section { throw new InvalidDataException("LIST data section started with unexpected data " + binStream.BaseStream.Position); // Throw if it's not the right data } binStream.ReadInt32(); // Section Length // Section lenght doesn't matter, but we have to read it to keep alignment. var count = binStream.ReadInt32(); // Count of entries in the section (Including nulls.) // why are these F***S relative whenever literally nothing else in the file is ? // var pointers = Helpers.readInt32Array(binStream, count); // This will be an in32[] of pointers for (int i = 0; i < count; i++) { if (pointers[i] < 1) // Instrument is empty. { continue; // Instrument is empty. Skip this iteration } binStream.BaseStream.Position = Base + pointers[i]; // F**K THIS. Err I mean. Seek to the position of the instrument index + the base of the bank. var IID = binStream.ReadInt32(); // read the identity at the base of each section binStream.BaseStream.Seek(-4, SeekOrigin.Current); // Seek back identity (We read 4 bytes for the ID) switch (IID) // Switch ID { case Inst: // It's a regular instrument instruments[i] = loadInstrument(binStream, Base); // Ask it to load (We're already just behind the Inst) break; case Perc: // Percussion Instrument instruments[i] = loadPercussionInstrument(binStream, Base); break; default: Console.WriteLine("unknown inst index {0:X}", binStream.BaseStream.Position); break; } } return(instruments); }
/* * JAIV1 INST Structure * 0x00 int32 0x494E5354 'INST' * 0x04 int32 0 - unused? * 0x08 float frequencyMultiplier * 0x0C float gainMultiplier * 0x10 int32 oscillator table offset * 0x14 int32 oscillator table count * 0x18 int32 effect table offset * 0x1C int32 effect table size * 0x20 int32 sensor object table * 0x24 int32 sensor object table count * 0x28 int32 key_region_count * 0x2C *KeyRegion[key_region_count] * */ public JInstrument loadInstrument(BeBinaryReader binStream, int Base) { var Inst = new JInstrument(); if (binStream.ReadUInt32() != INST) // Check if first 4 bytes are INST { throw new InvalidDataException("Data is not an INST"); } binStream.ReadUInt32(); // oh god oh god oh god its null Inst.Pitch = binStream.ReadSingle(); Inst.Volume = binStream.ReadSingle(); var osc1Offset = binStream.ReadUInt32(); var osc2Offset = binStream.ReadUInt32(); binStream.ReadUInt32(); // *NOT IMPLEMENTED* // binStream.ReadUInt32(); // *NOT IMPLEMENTED* // binStream.ReadUInt32(); // *NOT IMPLEMENTED* // binStream.ReadUInt32(); // *NOT IMPLEMENTED* // // * trashy furry screaming * // int keyregCount = binStream.ReadInt32(); // Read number of key regions JInstrumentKey[] keys = new JInstrumentKey[0x81]; // Always go for one more. int[] keyRegionPointers = new int[keyregCount]; keyRegionPointers = Helpers.readInt32Array(binStream, keyregCount); var keyLow = 0; // For region spanning. for (int i = 0; i < keyregCount; i++) // Loop through all pointers. { binStream.BaseStream.Position = keyRegionPointers[i] + Base; // Set position to key pointer pos (relative to base) var bkey = readKeyRegion(binStream, Base); // Read the key region for (int b = 0; b < bkey.baseKey - keyLow; b++) { // They're key regions, so we're going to have a toothy / gappy piano. So we need to span the missing gaps with the previous region config. // This means that if the last key was 4, and the next key was 8 -- whatever parameters 4 had will span keys 4 5 6 and 7. keys[b + keyLow] = bkey; // span the keys keys[127] = bkey; } keyLow = bkey.baseKey; // Store our last key } Inst.Keys = keys; byte oscCount = 0; if (osc1Offset > 0) { oscCount++; } if (osc2Offset > 0) { oscCount++; } Inst.oscillatorCount = oscCount; // Redundant? Inst.oscillators = new JOscillator[oscCount]; // new oscillator array if (osc1Offset != 0) // if the oscillator isnt null { binStream.BaseStream.Position = osc1Offset + Base; // seek to it's position Inst.oscillators[0] = loadOscillator(binStream, Base); // then load it. } if (osc2Offset != 0) // if the second oscillator isn't null { binStream.BaseStream.Position = osc2Offset + Base; // seek to its position Inst.oscillators[1] = loadOscillator(binStream, Base); // and load it. } return(Inst); }