// ///////////////////////////////////////////////////////////////////////////// // // Version 1.5 // // This fixes a bug in version 1.4 that seemingly trashed version 1.1 // patches that did not contain a 'Retrig=' item in the header section. // The patches were not really trashed but showed up empty in the v3 // editor. // // Changed the algorithm a bit to use the State variable instead of // all the individual boolean flags in the previous version. // // ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// // // A bit of explanation to dymistify some cryptics // // // The aStrings variable is a dynamic list of strings // // Actually it is a Class type variable of which the following 'members' // are usesd : // // LoadFromFile( Const aFileName: String); // // This will read a file line by line into the aStrings variable // every line (including empty lines) into a string. // // Strings[ anIndex]: String; // // an array property that allows us to index every string that is in // the aStrings variable. Valid Indices run from 0 upto but not including // Count (see next item). // // Count : Integer; // // Holds the number of strings in aStrings. // // Insert( anIndex: Integer; Const aString: String); // // This will insert a new string into the dynamic list at position // an index. All strings after it will move up one position. // // SaveToFile( Const aFileName: String); // // Writes out every string in aStrings to a separate line of a file. // // The With aStrings Do brings the members of aSrting into scope, such // that we can just write Strings[ i] instead of aStrings.Strings[ i] // etc. etc. // // The Try .. Finally block is used to be sure to loose no dynamic memory // due to not freeing the aStrings variable in case an exception might // occur while processing the patch. // // The variable State can assume values of type TState, an ennumerated type. // // Pos( Const aSubstring, aString: String): Integer; // // Searches aSubstring in aString and returns the first position where it // was found in aString. When not found the result will be 0. // // String indices start at 1 and end at Length( aString). // // Empty strings are denoted by '' // // Inc( Var a: Integer); // Inc( Var a: Integer; anAmount: Integer); // // These functions increment a variable a; the first version adds 1 // to a, the second version adds anAmount to a // // ///////////////////////////////////////////////////////////////////////////// Procedure ConvertPatch( Const aFileName: String); Type TState = ( inNone, in51, in66, in93, in94, in95 ); Var aStrings : TStringList; // Will hold all lines from the patch file State : TState; // The State our conversion tool is in Done : Boolean; // Hmmm ... S : String; // Holds the current line from the patch i : Integer; // indexes the current line in aStrings p : Integer; // temporary index into aStrings DoWrite : Boolean; // True when the patch was converted and must be saved LastLine : Boolean; // True when we are processing the last line of the patch SeenRetrig : Boolean; // True when we've seen a Retrig= item in the patch. Begin // Do not convert pchlists If Pos( '.PCHLIST', UpperCase( aFileName)) > 0 Then Exit; aStrings := TStringList.Create; // Create the aStrings variable With aStrings Do Try LoadFromFile( aFileName); // Read all lines from the patch into aStrings. Done := False; // True when the patch is completely processed State := inNone; // Not in a special state i := 0; // Start with the first line DoWrite := False; // True when the patch was converted and must be written back SeenRetrig := False; // 2.10 patches must have a hdr entry Retrig= // flag that we did not see that entry yet. While Not Done Do Begin LastLine := i >= Count - 1; // Signal being in last line when needed S := UpperCase( Strings[ i]); // Need the line in uppercase, get it from aStrings.Strings[ i] If Pos( '[NOTES]', S) = 1 // When we reach the notes section we can stop Then Break; // ---> break out of while loop If Pos( 'TYPE=', S) = 1 // Handle module type definitions Then Begin State := inNone; If S = 'TYPE=51' // Filter E ? Then State := in51 // yes, set a flag for it Else If S = 'TYPE=60' // Old Envelope follower ? Then Strings[ i] := 'Type=71' // Yes, convert it - no further handling needed Else If S = 'TYPE=66' // control mixer ? Then State := in66 // yes, set a flag for it Else If S = 'TYPE=93' // Inverter ? Then Begin State := in93; // yes, set a flag for it Strings[ i] := 'Type=66' // and convert the module type End Else If S = 'TYPE=94' // Stereo chorus ? Then State := in94 // yes, set a flag for it. Else If S = 'TYPE=95' // Perc Osc ? Then State := in95; // yes, set a flag for it. End Else If Pos( 'RETRIG', S) = 1 Then SeenRetrig := True Else If ( Pos( '[VOICES]', S) = 1) And Not SeenRetrig Then Begin // We're past the header section and did not see a // Retrig= item in it, we must add it at the end of // the header section, so back up a bit & fixit // Search 1st non blank line before the current line p := i - 1; While Strings[ p] = '' Do Dec( p); // Then one beyond that line insert the Retrig= Insert( p + 1, 'Retrig=0'); // Point our index to that line Inc( i); End Else If Pos( 'VERSION=', S) = 1 Then If ( Pos( '3.' , S) = 0) And // not Version 3 already ? And ( Pos( '2.10', S) = 0) // not Version 2.10 already ? Then Begin Strings[ i] := 'Version=Nord Modular patch 2.10'; DoWrite := True; End Else Break; // ---> From While loop, something comletely wrong here ... // This exit will *not* write the patch Case State Of in51 : If Pos( 'P7', S) = 1 Then Begin // Filter E completion (if needed) // If this is the last line or // P8 is not present If LastLine Or ( Pos( 'P8', Uppercase( Strings[ i + 1])) = 0) Then Begin Insert( i + 1, 'P8=0'); // Add the knob into aStrings.Strings[ i + 1] Inc( i); // Point to the added line State := inNone; End; End; in66 : If Pos( 'P3', S) = 1 Then Begin // Control signal mixer completion (if needed) // If this is the last line or // P4 is not present If LastLine Or ( Pos( 'P4', Uppercase( Strings[ i + 1])) = 0) Then Begin Insert( i + 1, 'P4=0'); // Add the button into aStrings.Strings[ i + 1] Inc( i); // Point to the added line State := inNone; End; End; in93 : If Pos( 'ROW', S) = 1 Then Begin // Inverter conversion, unconditional Insert( i + 1, 'P0=1' ); Insert( i + 2, 'P1=127'); Insert( i + 3, 'P2=0' ); Insert( i + 4, 'P3=0' ); Insert( i + 5, 'P4=1' ); Inc( i, 5); // Point to line last added. State := inNone; End; in94 : If Pos( 'P1', S) = 1 Then Begin // Stereo chorus completion (if needed) // If this is the last line or // P2 is not present If LastLine Or ( Pos( 'P2', Uppercase( Strings[ i + 1])) = 0) Then Begin Insert( i + 1, 'P2=1'); // Add the button into aStrings.Strings[ i + 1] Inc( i); // Point to the added line State := inNone; End; End; in95 : If Pos( 'P3', S) = 1 Then Begin // Perc Osc completion (if needed) // If this is the last line or // P4 is not present If LastLine Or ( Pos( 'P4', Uppercase( Strings[ i + 1])) = 0) Then Begin Insert( i + 1, 'P4=0' ); // Add the knob to aStrings.Strings[ i + 1] Insert( i + 2, 'P5=64'); // Add the knob to aStrings.Strings[ i + 1] Insert( i + 3, 'P6=0' ); // Add the knob to aStrings.Strings[ i + 1] Inc( i, 3); // Point to the added line State := inNone; End; End; End; // Case State Of Inc( i); // Point to the next line Done := i >= Count; // reached end of file ? End; If DoWrite Then SaveToFile( aFileName); // Write out aStrings to file. Finally Free; // Free the aStrings variable. End; End;