unit uAkpZ48; {**************************************************************************** Targets: Win32 Compile: Delphi 7.00 uAkpZ48 contains the Z48 program structure for yaaep Version: History: -------- -------- 00.01.00 22.08.2005 - Created 00.01.01 23.08.2005 - Naming of lfo section corrected 00.02.00 24.08.2005 - Added drum program section ****************************************************************************} { CREDIT WHERE CREDIT IS DUE ========================== This documentation was inspired by the AKAI S5000 / S6000 AKP file analysis made by Seb Francis, who proved it really is possible. I began naively thinking I could take his original document, add a field here and there and I'd be done. It was not to be. The Z4/8 .akp file structure differs markedly from the s5/6k format and more time had to be invested than originally planned. Ain't that always the way? This analysis was originally made for my yaape program (Yet Another Akai Program Editor) but I felt to make the effort worthwhile I would release this to the public domain in the interests of moving fellow owners of this remarkable and possibly last-of-a-kind hardware rack sampler to also produce software for it. Yaape (and hence this page of code) is written in Borland Pascal. To understand the structure, some tips are in order. 1. Pascal works from the bottom up. The main structure is the record type trAkp at the bottom of this file. In the interests of clarity, I broke out parts that are repeated. There are, therefore, sub record types for envelopes, lfos, zones and, of course, keygroups. It has just struck me that as a result I may have failed miserably in the clarity department. Oh, well. 2. Borland Pascal supports enumerations which simplify debugging because you are not faced merely with numbers, but the actual names of the settings. I therefore make copious use of enumerations. An Enumeration as used here occupies the space of one 8-bit byte; and its members, unless otherwise stated, are numbered incrementally starting from 0. Other types you will encounter are: byte - Hungaran prefix: byXXX - 8 bits unsigned, 0..255 Shortint - Hungarian prefix: sbXXX - 8 bits signed, -128..127 Smallint - Hungarian prefix: swXXX - 16 bits signed, -32768..32767 Longword - Hungarian prefix: ulXXX - 32 bits unsigned, 0..4294967295 ...and the main component of structure is the Record, comparable to a C/C++ "struct". 3. As I analyzed the various sections, I found it easier just to lay out a string of numbered bytes, eg. byZone23, byZone24... and to give them their proper name as their various functions were identified. There are, therefore, many numbered bytes which either have not been identified yet, or which may be simply unused. Behind each such byte is the value which, by observation, it seems to normally contain. 4. The sections always begin with a 4 character tag (padded with blanks if less than 4 chars), followed by a longword indicating the number of bytes remaining which belong to this tag. This is not always true. Sometimes extra bytes are present, making it necessary to insert a dummy byte to bring everything in line again. Notice that the dummy bytes only occur whenever the length is an odd number? Hence this probably has to do with word alignment in the Z4/8 memory. 5. The files as saved by the Z4/8 contain only those keygroups actually used. This program reserves a full 128 keygroups in memory but only loads in up to as many as are found. There is a byte which keeps track of how many keygroups there are; byKeygroups. Use it. 6. The drum program format is identified by byProgramType in trAkp. Drum programs are essentially identical except that an entire section tagged 'mpc ' is added to the end. It is closely related to the S5/6k format but I cannot imagine what it is for. Possibly compatibility? I have not examined the contents to see whether they mirror those in the Z48 section, but as the S5/6k format is limited to 99 keygroups whilst the Z4/8 format has 128, I fail to see how this is possible. Since drum programs already contain the complete 128 keygroups and this also has 99 keygroups, it enlarges the file size by 35114 to 99158 bytes. ***** This is a work in progress and probably contains many errors. I make no claim to its correctness. I accept no responsibility for any damages arising from it's use. If you do not agree to this, do not use this reference. } interface {$align off} type (* mods modulation source *) enumModSource = ( NoSource, Modwheel, BendUp, BendDown, Aftertouch, Velocity, TiltVelo, OffVelo, Keyboard, Lfo1, Lfo2, AmpEnv, FiltEnv, AuxEnv, Controller, Ext1, Ext2, Ext3, Ext4, Ext5, Ext6, Ext7, Ext8, dModwheel, dBendUp, dBendDown, dExt1, dExt2, dExt3, dExt4, dExt5, dExt6, dExt7, dExt8, dLfo1, dLfo2, dCtrl ); (* mods modulation destination *) enumModDest = ( NoDest, Amplitude, Pan, Pitch, Lfo1Rate, Lfo1Depth, Lfo1Delay, Lfo1Phase, Lfo1Offset, Lfo2Rate, Lfo2Depth, Lfo2Delay, Lfo2Phase, Lfo2Offset, Cutoff, Resonance, TfCutoff1, TfRes1, TfCutoff2, TfRes2, TfCutoff3, TfRes3, AmpEnvAtt, mod_AmpEnvDec, AmpEnvRel, FiltEnvR1, FiltEnvR2, FiltEnvR4, AuxEnvR1, AuxEnvR2, AuxEnvR4, ZoneSelect, Zone1Level, Zone1Pan, Zone1Pitch, Zone1Start, Zone1Filter, Zone2Level, Zone2Pan, Zone2Pitch, Zone2Start, Zone2Filter, Zone3Level, Zone3Pan, Zone3Pitch, Zone3Start, Zone3Filter, Zone4Level, Zone4Pan, Zone4Pitch, Zone4Start, Zone4Filter ); (* Active tuning scale preset *) enumTuneScale = ( User, EvenTempered, Orchestral, Werkmeister, Meantone_1_5, Meantone_1_4, Just, Arabian ); (* Keygroup Fx output selection *) enumKeygroupFxOutput = ( OutMulti, OutOff, OutA, OutB, OutC, OutD, OutAB, OutCD ); enumLfoWaveform = (Triangle, Sine, Square, SawUp, SawDown, Random ); enumAmpEnvPreset = (AmpUserEnv, AmpPiano, AmpClavHarpsi, AmpElecOrgan, AmpPipeOrgan, AmpStringVox, AmpSlowString, AmpWoodwind, AmpPanpipe, AmpBrass, AmpShortPerc, AmpDryDrum, AmpLongDrum, AmpAmbiDrum, AmpCymbalGong, AmpSynthBass1, AmpSynthBass2, AmpSynthBass3 ); enumEnvPreset = (UserEnv, Piano, ClavHarpsi, ElecOrgan, PipeOrgan, StringVox, SlowString, Woodwind, Panpipe, Brass, BrassSwell, ShortPrec, DryDrum, LongDrum, AmbiDrum, CymbalGong, SynthBass1, SynthBass2, SynthBass3, InvFilter, Pyramids, PercSwell, SwellAttack, RiseRelease ); enumSingleFilterType = (SFilterOff, S2PoleLp, S2PoleLpPos, S4PoleLp, S4PoleLpPos, S6PoleLp, S2PoleBp, S2PoleBpPos, S4PoleBp, S4PoleBpPos, S6PoleBp, S1PoleHp, S1PoleHpPos, S2PoleHp, S2PoleHpPos, S4PoleHp, S4PoleHpPos, S6PoleHp, LoHi, LoBand, BandHi, Notch1, Notch2, Notch3, WideNotch, BiNotch, Peak1, Peak2, Peak3, WidePeak, BiPeak, Phaser1, Phaser2, BiPhase, Vowelizer, UseTripleFilterMode //activates triple filters ); enumTripleFilterType = (TFilterOff, T2PoleLp, T1PoleBp, T1PoleHp, T2PoleHp, TNotch1, Eq, EqPos ); enumOffOn = (IsOff, IsOn); enumNoteReassignment = (Quietest, Oldest); enumVelocityCurve = (Linear, Logarithmic); enumMonoLegato = (LegatoOff, LegatoPitch); enumPortaGliss = (Portamento, Glissando); enumTimeSpeed = (Time, Speed); enumPlaybackMode = (Sample, OneShot, NoLoop, Loop); enumOutputMode = (MultiOut, OutLR, Out1_2, Out3_4, Out5_6, Out7_8, OutL, OutR, Out1, Out2, Out3, Out4, Out5, Out6, Out7, Out8 ); enumPitchTracking = (Constant, KeyboardTracking); enumPrgXfadeMode = (XfadeModeLin, XfadeModeExp, XfadeModeLog); enumPlayTrigger = (NoteOn, NoteOff, Trigger); enumProgramType = (Keygroup, Drum); (* This Mpc extension gets tacked on in drum programs only. It doesn't seem to fulfill any purpose apart from, possibly, compatibility. The full complement of keygroups, namely 99, is present. This should remind us of the S5000/6000 format, and indeed, the structure bears some similarity, so I'm going to make some guesses, based on Seb Francis' work. If I am wrong, it will not affect any functionality because it is not used by the Z8. One question remains: how does the Z8 know it is dealing with a drum program? (The editor behaves differently, so obviously it does know. And ak.Sys displays keygroup numbers 0..127 instead of 1..128, so ak.Sys knows, too.) Does it measure the length? No other indicators seem to be present. I have not identified a "this is a drum program" byte yet. BEGIN MPC EXTENSION *) trMpcEnv = record achEnv :array[0..3] of char; // 'env ' ulEnvLen :longword; // (18) byEnv1 :byte; byAttack :byte; // 0 -> 100 byEnv3 :byte; byDecay :byte; // 0 -> 100 byRelease :byte; // 0 -> 100 byEnv6 :byte; byEnv7 :byte; bySustain :byte; // 0 -> 100 byEnv9 :byte; byEnv10 :byte; sbVeloAttack :Shortint; // -100 -> 100 byEnv12 :byte; sbKeyscale :Shortint; // -100 -> 100 byEnv14 :byte; sbOnVelRel :Shortint; // -100 -> 100 sbOffVelRel :Shortint; // -100 -> 100 byEnv17 :byte; byEnv18 :byte; end; trMpcAuxEnv = record achEnv :array[0..3] of char; // 'env ' ulEnvLen :longword; // (18) byEnv1 :byte; byRate1 :byte; // 0 -> 100 byRate2 :byte; // 0 -> 100 byRate3 :byte; // 0 -> 100 byRate4 :byte; // 0 -> 100 byLevel1 :byte; // 0 -> 100 byLevel2 :byte; // 0 -> 100 byLevel3 :byte; // 0 -> 100 byLevel4 :byte; // 0 -> 100 byEnv10 :byte; sbVeloRate1 :Shortint; // -100 -> 100 byEnv12 :byte; sbKeyboardR2R4 :Shortint; // -100 -> 100 byEnv14 :byte; sbVelRate4 :Shortint; // -100 -> 100 sbOffVelRate4 :Shortint; // -100 -> 100 sbVelOutLevel :Shortint; // -100 -> 100 byEnv18 :byte; end; trMpcZone = record achZone :array[0..3] of char; // 'zone' ulZoneLen :longword; // (48) byZone1 :byte; // 3 bySampleNameLen :byte; // number of chars in Sample Name (* if first character = 00h then no sample assigned *) achSampleName :array[1..20] of char; // right pad with 00h aZone :array[22..33] of byte; // these seem always just to be 0 byLoVel :byte; // 0 -> 127 byHiVel :byte; // 0 -> 127 sbFineTune :byte; // -50 -> 50 sbSemitoneTune :byte; // -36 -> 36 sbFilter :byte; // -100 -> 100 sbPanBalance :byte; // -50 -> 50 = L50 -> R50 (* 0 = NO LOOPING, 1 = ONE SHOT, 2 = LOOP IN REL, 3 = LOOP UNTIL REL, 4 = AS SAMPLE *) byPlayback :byte; (* 0 = MULTI, 1 = 1/2, 2 = 3/4, 3 = 5/6, 4 = 7/8, 5 = 9/10, 6 = 11/12, 7 = 13/14, 8 = 15/16, 9 = 1, 10 = 2, 11 = 3, 12 = 4, 13 = 5, 14 = 6, 15 = 7, 16 = 8, 17 = 9, 18 = 10, 19 = 11, 20 = 12, 21 = 13, 22 = 14, 23 = 15, 24 = 16 *) byOutput :byte; sbLevel :byte; // -100 -> 100 byKeyboardTrack :byte; // 0 = OFF, 1 = ON swVelStart :Smallint; // -9999 -> 9999 byZone47 :byte; byZone48 :byte; end; trMpcKgrp = record achKgrp :array[0..3] of char; // 'kgrp' ulKgrpLen :longword; // (344) achKloc :array[0..3] of char; // 'kloc' ulKlocLen :longword; // (16) byKloc1 :byte; // 1 byKloc2 :byte; // 3 byKloc3 :byte; // 1 byKloc4 :byte; // 4 byLoNote :byte; // 21 -> 127 byHiNote :byte; // 21 -> 127 sbSemitoneTune :Shortint; // -36 -> 36 sbFineTune :Shortint; // -50 -> 50 byOverrideFX :byte; // 0=off, 1=FX1, 2=FX2, 3=RV3, 4=RV4 byFXSendLevel :byte; // 0 -> 100 sbPitchMod1 :Shortint; // -100 -> 100 sbPitchMod2 :Shortint; // -100 -> 100 sbAmpMod :Shortint; // -100 -> 100 byZoneXfade :byte; // 0=off, 1=on byMuteGroup :byte; byKloc16 :byte; // 0 EnvAmp, EnvFilt :trMpcEnv; EnvAux :trMpcAuxEnv; achFilt :array[0..3] of char; // 'filt' ulFiltLen :longword; // (10) byFilt1 :byte; // 1 (* 0 = 2-POLE LP, 1 = 4-POLE LP, 2 = 2-POLE LP+, 3 = 2-POLE BP, 4 = 4-POLE BP, 5 = 2-POLE BP+, 6 = 1-POLE HP, 7 = 2-POLE HP, 8 = 1-POLE HP+, 9 = LO<>HI, 10 = LO<>BAND, 11 = BAND<>HI, 12 = NOTCH 1, 13 = NOTCH 2, 14 = NOTCH 3, 15 = WIDE NOTCH, 16 = BI-NOTCH, 17 = PEAK 1, 18 = PEAK 2, 19 = PEAK 3, 20 = WIDE PEAK, 21 = BI-PEAK, 22 = PHASER 1, 23 = PHASER 2, 24 = BI-PHASE, 25 = VOWELISER *) byFiltMode :byte; byFiltCutoff :byte; // 0 -> 100 byFiltResonance :byte; // 0 -> 12 sbFiltKeyboardTrack :Shortint; // (0) -36 -> 36 sbFiltModInput1 :Shortint; // -100 -> 100 sbFiltModInput2 :Shortint;// -100 -> 100 sbFiltModInput3 :Shortint; // -100 -> 100 (* 0 = 0db, 1 = 6db, 2 = 12db, 3 = 18db, 4 = 24db, 5 = 30db *) byFiltHeadroom :byte; byFilt10 :byte; rZone1 :trMpcZone; rZone2 :trMpcZone; rZone3 :trMpcZone; rZone4 :trMpcZone; end; trMpcLfo = record achLfo :array[0..3] of char; // 'lfo ' ulLfoLen :longword; // (14) arLfo :array[1..14] of byte; //Lfo1, 2:1:43:0:0:0:1:15:0:0:0:0:0:0 //Lfo2, 2:1:43:0:0:1:1:0:0:0:0:0:0:0 end; trMpc = record achMpc :array[0..3] of char; // 'mpc ' ulMpcLen :longword; // (106) achPadA :array[0..3] of char; // 'pada' ulPadALen :longword; // (98) byPadA1 :byte; // 1 byPadA2 :byte; // 96 aPadA1 :array[1..32] of byte; // Numbers 0 to 31 aPadA2 :array[1..64] of char; // All chars, ' ' to '_' achPrg :array[0..3] of char; // 'prg ' ulPrgLen :longword; // (98) byPrg1 :byte; // 2 byPrg2 :byte; // 0 byKeygroups :byte; // 99 byPrg4 :byte; // 0 byPrg5 :byte; // 2 byPrg6 :byte; // 0 achOut :array[0..3] of char; // 'out ' ulOutLen :longword; // (98) byOut1 :byte; // 2 byOut2 :byte; // 84 byOut3 :byte; // 0 byOut4 :byte; // 0 byOut5 :byte; // 0 byOut6 :byte; // 0 byOut7 :byte; // 0 byOut8 :byte; // 50 achTune :array[0..3] of char; // 'tune' ulTuneLen :longword; // (24) byTune1 :byte; // 2 aTune1 :array[1..14] of byte; // 0 byTune16 :byte; // 2 byTune17 :byte; // 2 aTune2 :array[1..7] of byte; // 0 rLfo1 :trMpcLfo; rLfo2 :trMpcLfo; achMods :array[0..3] of char; // 'mods' ulModsLen :longword; // (38) aMods :array[1..38] of byte; arKgrp :array[1..99] of trMpcKgrp; end; (* END MPC EXTENSION *) trZone = record achZone :array[0..3] of char; // 'zone' ulZoneLen :longword; // (49) byZone1 :byte; // 1 (* Note: achSampleName [1] = #0 indicates that there is no sample assigned to this zone. The zone is, in contrast to unused keygroups, nevertheless present. *) achSampleName :array[1..20] of char; abyZone1 :array[22..33] of byte; abyLo :byte; // 0 -> 127 abyHi :byte; // 0 -> 127 sbFilter :Shortint; // -100 -> 100 sbPan :Shortint; // L50 -> R50 byPlaybackMode :enumPlaybackMode; swTune :SmallInt; // -36 -> 36 swVelocityStart :Smallint; // -9999 -> 9999 byZone43 :byte; // 0 byZone44 :byte; // 0 byOutputMode :enumOutputMode; swLevel :Smallint; // -600 -> 60 (-60.0 -> 6.0) byZone48 :byte; // 0 byPitchTracking :enumPitchTracking; byDummy1 :byte; // 0 end; trSingleFilter = record achFilt :array[0..3] of char; // 'filt' ulFiltLen :longword; // (4) byFilt1 :byte; // 1 (* Note: When enumSingleFilterType = UseTripleFilterMode, this single filter is deactivated and the triple filters activate. *) byType :enumSingleFilterType; byCutoff :byte; // 0 -> 100 byResonance :byte; // 0 -> 64 end; trTripleFilter = record achFilt :array[0..3] of char; // 'filt' ulFiltLen :longword; // (4) byFilt1 :byte; // 1 byTypeMode :enumTripleFilterType; byCutoff :byte; // 0 -> 100 byResonance :byte; // 0 -> 64 end; trLfo = record achLfo :array[0..3] of char; // 'lfo ' ulLfoLen :longword; // (15) byLfo1 :byte; // 1 byRate :byte; // 0 -> 68 byDelay :byte; // 0 -> 100 byDepth :byte; // 0 -> 100 byWaveform :enumLfoWaveform; byPhase :byte; // 0 -> 360 byLfo7 :byte; // 0 byShift :Shortint; // -50 -> 50 byLfo9 :byte; // 0 byLfo10 :byte; // 0 byLfo11 :byte; // 0 (* Note: Overrides byRate when byLfoMidiSync = 1. Seems to set byRate to 0, although ak.Sys seems to remember what the previous value was and reactivates it when switching to and fro. Does it remember when ak.Sys has been restarted? *) byMidiSyncRate :byte; // 0=1/8, 1/6, 1/4, 1/3, 1/2, 1-64 (64=68) byRetrig :enumOffOn; bySync :enumOffOn; byMidiSync :enumOffOn; byDummy :byte; end; trAmpEnv = record achAmpEnv :array[0..3] of char; // 'env ' (Amp Env) ulAmpEnvLen :longword; // (12) byAmpEnv1 :byte; // 1 byAttack :byte; // 0 -> 100 byDecay :byte; // 0 -> 100 byRelease :byte; // 0 -> 100 byAmpEnv5 :byte; // bySustainAttackHold :byte; // Sustain 0 -> 100 bySustain :byte; // 0 -> 100 byAmpEnv8 :byte; // 0 byAmpEnv9 :byte; // 0 byAmpEnv10 :byte; // 0 byAmpEnv11 :byte; // 0 byAttackHold :enumOffOn; end; trEnv = record achEnv :array[0..3] of char; // 'env ' ulEnvLen :longword; // (18) byEnv1 :byte; // 1 byRate1 :byte; // 0 -> 100 byRate2 :byte; // 0 -> 100 byRate3 :byte; // 0 -> 100 byRate4 :byte; // Release 0 -> 100 byLevel1 :byte; // 0 -> 100 byLevel2 :byte; // 0 -> 100 byLevel3 :byte; // Sustain 0 -> 100 byLevel4 :byte; // 0 -> 100 byEnv10 :byte; // 0 byEnv11 :byte; // 0 byEnv12 :byte; // 0 end; trKeygroup = record achKgrp :array[0..3] of char; // 'kgrp' ulKgrpLen :longword; // (490) achKloc :array[0..3] of char; // 'kloc' ulKlocLen :longword; // (19) byKloc1 :byte; // 2 byKloc2 :byte; // 5 swTune :Smallint; // -3600 -> 3600 (-36.00 -> 36.00) byKloc5 :byte; // 0 byKloc6 :byte; // 0 byLoNote :byte; // low note 0 -> 127 byHiNote :byte; // high note 0 -> 127 byMuteGroup :byte; // Mute Group 0 -> 64 byFxOutput :enumKeygroupFxOutput; swFxLevel :Smallint; // FX Send Level -400 -> 0 (-40.0 -> 0) byLevel :Smallint; // -600 -> 60 (-60.0 -> 6.0) byZoneXfadeMode :enumPrgXfadeMode; // zones.range(Lin, Exp, Log) byPlayTrigger :enumPlayTrigger; byKloc17 :byte; // 0 byPolyphony :byte; // 1 -> 64 byZoneXfade :byte; // zones.range.X-fade 0 = Off, 1 = Vel, 2 = RealTime byDummy1 :byte; // 0 achKmod :array[0..3] of char; // 'kmod' ulKmodLen :longword; // (65) byKmod1 :byte; // 1 (* Note: These are the depth settings of modulation as selected in Akp.mods and corresponds to the positionally identical source/destination array pairs. *) abyKmod :array[1..64] of byte; byDummy2 :byte; rLfo1 :trLfo; rLfo2 :trLfo; (* Note: The envelopes can be pre-set with a set of templates provided in ak.Sys, speeding up building programs for many common instruments considerably. This wheeze of a function is, surprisingly, not available on the sampler itself and makes life much easier because there is no quick way to set the envelopes without this. Unlike the scale tuning templates, the program has no storage spot to remember which template was used. This explains why, after selecting a template and restarting ak.Sys, it remembers the shape of the envelope but forgets which template it came from. *) rAmpEnv :trAmpEnv; rFilterEnv :trEnv; rAuxiliaryEnv :trEnv; rSingleFilter :trSingleFilter; rTripleFilter1 :trTripleFilter; rTripleFilter2 :trTripleFilter; rTripleFilter3 :trTripleFilter; arZones :array[1..4] of trZone; end; trAkp = record achRiff :array[0..3] of char; // 'RIFF' ulAkpLen :longword; achAprg :array[0..3] of char; // 'APRG' achZ48 :array[0..3] of char; // 'z48 ' ulZ48Len :longword; achPrg :array[0..3] of char; // 'prg ' ulPrgLen :longword; // (11) byPrg1 :byte; // 2 byMidiProgNum :byte; // MIDI program number 0 = OFF (* Note: Only those keygroups in use are actually loaded into memory. When referencing the keygroups array, never exceed byKeygroups. *) byKeygroups :byte; // number of keygroups 1 -> 128 byProgramType :enumProgramType; // If "Drum", then adds trMod to end! byPrg5 :byte; // 2 byPrgXfade :enumOffOn; byPrgXfadeMode :enumPrgXfadeMode; swLevel :Smallint; // -600 -> 60 (-60.0 -> 6.0) sbPrgTranspose: Shortint; // Tune.trans -36 -> 36 (* Note: According to the 1.41 addendum, velocity curve was added to improve interchangeability with MPC3000, MPC2000 and MPC2000XL and that programs saved with 1.41 and later cannot be loaded by an earlier OS. Whether this means they will crash is not stated. It would be interesting to know whether the length value ulPrgLen was 10 (or less) in 1.40 and earlier or whether this was merely an unused byte. *) byPrgVelocityCurve :enumVelocityCurve; byDummy1 :byte; achMidi :array[0..3] of char; // 'midi' ulMidiLen :longword; // (6) byMidi1 :byte; // 1 byPolyphony :byte; // 1 -> 64 (* Note: Note Reassignment Priority does not seem to be availabe in ak.Sys *) byNoteReassignmentPriority :enumNoteReassignment; (* Note: The Soft Pedal assignments do not seem to be available in ak.Sys *) bySoftPedalLoudnessReduction :byte; // 0 -> 100 bySoftPedalAttackStretch :byte; // 0 -> 100 bySoftPedalFilterClose :byte; // 0 -> 100 achMods :array[0..3] of char; // 'mods' ulModsLen :longword; // (193) byMods1 :byte; // 2 (* Note: Whenever CONTROLLER or !CTRL is selected as the modulation source, the actual CC must be selected. byModsControllerValue is positionally identical to byModsSource and fulfills this function. *) abyModsSource :array[1..64] of enumModSource; byModsControllerValue :array[1..64] of byte; // 0 -> 127 byModDestination :array[1..64] of enumModDest; byDummy2 :byte; (* Note: The scale tuning functionality seems not to be available on ak.Sys, only from the sampler's front panel. *) achtune :array[0..3] of char; // 'tune' ulTuneLen :longword; // (17) byTune1 :byte; // 1 swTune :Smallint; // -3600 -> 3600 (-36.00 -> 36.00) sbTuneScaleTemplate :enumTuneScale; sbCDetune :Shortint; // C detune -50 -> 50 sbCsDetune :Shortint; // C# detune -50 -> 50 sbDDetune :Shortint; // D detune -50 -> 50 sbEbDetune :Shortint; // Eb detune -50 -> 50 sbEDetune :Shortint; // E detune -50 -> 50 sbFDetune :Shortint; // F detune -50 -> 50 sbFsDetune :Shortint; // F# detune -50 -> 50 sbGDetune :Shortint; // G detune -50 -> 50 sbGsDetune :Shortint; // G# detune -50 -> 50 sbADetune :Shortint; // A detune -50 -> 50 sbBbDetune :Shortint; // Bb detune -50 -> 50 sbBDetune :Shortint; // B detune -50 -> 50 byTune16 :byte; byTune17 :byte; achPich :array[0..3] of char; // 'pich' (Pitch) ulPichLen :longword; // (10) byPitch1 :byte; byPitchBendUp :byte; // 0 -> 24 byPitchBendDown :byte; // 0 -> 24 byPitchBendHeld :enumOffOn; sbPitchAftertouch :Shortint; // -12 -> 12 byPitchMonoLegato :enumMonoLegato; byPitchPortaGliss :enumOffOn; byPitchPortaGlissMode :enumPortaGliss; // 0=Portamento, 1=Glissando byPitchPortaSpeedTime :enumTimeSpeed; // 0=Time, 1=Speed byPitchPortaTime :byte; // 0 -> 100 (* Note: Only those keygroups in use are actually loaded into memory. When referencing the keygroups array, never exceed byKeygroups. This differs from the Zones behavour where all 4 zones are loaded with the keygroups and disabled ones are indicated by setting the leading character in the sample name field to #0 *) arKeygroups :array[1..128] of trKeygroup; (* Used by drum programs, an MPC section based on the S5/6k structure with a full 99 keygroups. *) rMpc :trMpc; end; implementation end.