AK47m Project Part 4 (Making sense of Roland style files)
I have installed Lazarus IDE and Free Pascal on my Windows PC. The installation is easy and it works out of the box !. There is a function for converting Delphi projects to Lazarus projects. The BK7m performance editor I wrote several years back was a Delphi project. I converted this to Lazarus and it required a few tweaks to get it working but nothing major. The advantage of doing this is that I can develop software more efficiently on the PC and then transfer the projects to a Raspberry PI and compile them with the native Lazarus system. Most objects can be transferred easily, but obviously some of the interfaces to MIDI and ALSA need to be tweaked.
One of the last features I was working on for the BK7m editor was a style file editor, this was shelved due to other work, however I started looking at the work completed as a basis for the AK47m project. There is scant information on the Roland style (.stl) file format around, so I had to do some reverse engineering on the format. Any extra information would be welcome.
Style file (.STL) format
The style file basically has a header block at the start followed by a heap. The header contains tables of indexes into the heap and tables of corresponding object sizes. Each object in the heap, which I have termed as a “snippet” contains a list of MIDI commands and time offsets as in a MIDI file. The “snippet” can be played to a MIDI device and it will play a 1..n bar phrase for a single instrument.
Snippets are created for up to 8 instruments (Drums, Bass, 6 harmony instruments). There are three versions of a snippet for an instrument, namely Major, Minor and Seventh. Each snippet is in the key of C, a Major snippet will play a musical phrase for C Major, and a Seventh snippet will play a musical phrase for C7. The arranger software identifies what chord is being played with the left hand and then applies offsets to the pitches of a snippet to play harmony corresponding to the chord. i.e. transposing C to G would add 7 semitones to each note in the snippet. Some snippets are used for the drum track and these would not get transposed at all.
I asked myself about what happens when a Major Seventh chord is played. i.e. C E G B. I assume that the Seventh harmony snippet (C E G Bb) is used as a basis and a transformation is applied that converts the Bb to B natural before the pitch offset is applied. Similarly a transformation to get a Cm7 harmony (C Eb G Bb) would convert the E natural to Eb.
One thing I wondered about was whether the simple Major and Minor snippets are necessary since a Major could be obtained by dropping the Bb from the Seventh snippet. Maybe the Major snippet is used to produce a Sixth harmony ( C E G A) by adding a tone above the fifth note. More information on this would help me !!
There is a higher level grouping which I have named as sections. There are 16 sections. The structure is 16 sections of 8 instruments of 3 harmonies. The sections correspond to the variations, intro, outro and fills. Some of the sections are completely empty which is a bit of a puzzle.
I believe that the sections are used as follows:
- Intro 3
- Variation 1
- Variation 3
- — spare —
- 1 bar fill Variation 2
- 1 bar fill Variation 1
- — spare —
- Outro 4
- Intro 4
- Variation 2
- — spare —
- 1 bar fill Variation 4
- 1 bar fill Variation 3
- — spare —
- Outro 3
There is also a table of Word values for each snippet (snippetUnknown), what this relates to I am unsure – are they flags or offsets. Help required.
Unfortunately my BK7m unit does not work so I am unable to compare what the BK7m does with each snippet.
What puzzles me about this is that there are no snippets for intro 1 & 2 and outro 1 & 2.
On closer inspection of the file, I have discovered that there is another header record that starts with G8<name> . It seems that the header maps to the same structure as the first header. There doesn’t seem to be any record size fields, so I have to search for the string G8<name> in the raw data to find the start of the second record. This format is unbelievable spaghetti.
The snippets missing from the first record are in the second record. Note that the snippet pointers are relative to the start of the record in the file.
I suspect that the second record was added for backwards compatibility. There looks like a third record related to the one touch settings. ( i wondered where these were in the BK7 performance file !)
styleHeader = record id: packed array [1..2] of char; name: packed array [0..15] of char; d1: packed array [1..2] of byte; tempo: packed array [0..1] of byte; d2: packed array [1..2] of byte; timeSignatures: packed array [0..15] of timeSig; d3: packed array [1..2] of byte; snippetPointers: packed array [0..7] of //instrument packed array [0..15] of //section packed array [0..2] of longint; //maj min 7th snippetUnknown: packed array [0..7] of //instrument packed array [0..15] of //section packed array [0..2] of word; //maj min 7th snippetSizes: packed array [0..7] of //instrument packed array [0..15] of //section packed array [0..2] of word; //maj min 7th end;
“id” is set to the string “G8”.
The snippet pointers reference a list of 6 byte records that have the MIDI data and timing data :-
type timeSig = record beatsPerMeasure: byte; beatValue: byte; // 01 = 1/2 02=1/4 03= /8 end; noteEvent = record channel: byte; velocity: byte; dollar40: byte; timeLength: byte; end; controllerEvent = record channel: byte; effect: packed array[0..2] of byte; end; pitchWheelEvent = record channel: byte; pitchTimesThree: packed array[0..2] of byte; //pitchWheelCommandAgain : byte; end; programChangeEvent = record channel: byte; prog: packed array[0..2] of byte; end; nrpnEvent = record channel: byte; msb: byte; lsb: byte; Data: byte; end; eventRecord = packed record timeIncrement: byte; case pitchOrCommand: byte of 0: (raw: packed array [0..3] of byte); 1: (note: noteEvent); $E6: (controller: controllerEvent); $EB: (pitchWheel: pitchWheelEvent); $E5: (programChange: programChangeEvent); $EA: (nrpn: nrpnEvent); $8F: (); // end of list $80: (); //data ?? end; eventList = array of eventRecord;
The BK7m Editor can interface to a PC MIDI device and output MIDI commands. When MIDI is connected, it is possible to load a style file and play the individual snippets or any combination of them. This was helpful in deciphering the file structure.
The next project goals are to write a PC based program based on the BK7m editor that can perform the MIDI arranger functions. There are five parts to this:
- Create general purpose MIDI input output module that works on Windows and PI.
- Load a selected style file into appropiate internal data structures.
- Identify the chord harmony from notes played on the lower MIDI keyboard and output the chord name.
- Apply transformations of snippets using (3) and play the MIDI notes.
- Handle transitions between variations with appropriate fills.