Novation Bass Station 2 SysEx format

Reverse engineering of the Novation Bass Station II SysEx format.

See also my other project: Bass Station II Web Interface

Generic MIDI SysEx specification

Ref: https://www.midi.org/specifications/item/table-1-summary-of-midi-message

A MIDI System Exclusive message has the following format:

N is the number of bytes of the messages.

OffsetDataBS-IIDescription
0F0Mark the start of the SysEx message
1id0x00Manufacturer's ID
2id0x20Manufacturer's ID
3id0x29Manufacturer's ID
4..N-2xxdata (N-5 bytes)
N-1F7Mark the end of the SysEx message

Note: "Japanese Group" manufacturers have only one ID byte. See [https://www.midi.org/specifications-old/item/manufacturer-id-numbers] for more details.

Manufacturer ID for Focusrite/Novation is 0x00 0x20 0x29 (in decimal: 00 32 41)

SysEx messages understood by the Bass Station II

MessageDescription
F0 00 20 29 00 33 00 40 F7Request for SysEx. After receiving this message, the Bass Station II will send a SysEx dump of its current configuration.

SysEx data sent by the Bass Station II

By default, the Bass Station II send 154 bytes. However, a patch (.syx file) may be smaller.

OffsetBytesHex maskBin maskBitsDescription
137F 7F 7F01111111 01111111 011111113x 8Manufacturer ID
917F011111118Patch number
13203 7C00000011 011111007Portamento Time
1517E011111107?
1617F011111117Osc Pitch Bend Range
18140010000001Osc 1-2 Sync
19160011000002Osc 1 Waveform
1920F 7000001111 011100007Osc 1 Manual PW
20207 7800000111 011110007Osc 1 Range
21207 7C00000111 011111008Osc 1 Coarse
22203 7E00000011 011111108Osc 1 Fine
24103000000112Osc 2 Waveform
2523F 4000111111 010000007Osc 2 Manual PW
2621F 6000011111 011000007Osc 2 Range
2721F 7000011111 011100008Osc 2 Coarse
2820F 7800001111 011110008Osc 2 Fine
36130001100002Sub Osc Wave
37108000010001Sub Osc Oct
37207 7C00000111 011111008Mixer Osc 1 Level
38203 7E00000011 011111108Mixer Osc 2 Level
39201 7F00000001 011111118Mixer Sub Osc Level
4127F 4001111111 010000008Mixer Noise Level
4223F 6000111111 011000008Mixer Ring Mod Level
4321F 7000011111 011100008Mixer External Signal Level
4420F 7800001111 011110008Filter Frequency
45203 7C00000011 011111007Filter Resonance
46201 7E00000001 011111107Filter Overdrive
48108000010001Filter Slope
48104000001001Filter Type
48103000000112Filter Shape
4923F 4000111111 010000007Velocity Amp Env
5021F 6000011111 011000007Amp Env Attack
5120F 7000001111 011100007Amp Env Decay
52207 7800000111 011110007Amp Env Sustain
53203 7C00000011 011111007Amp Env Release
55106000001102Amp Env Triggering
5617F011111117Velocity Mod Env
5723F 4000111111 010000007Mod Env Attack
5821F 6000011111 011000007Mod Env Decay
5920F 7000001111 011100007Mod Env Sustain
60207 7800000111 011110007Mod Env Release
6210C000011002Mod Env Triggering
63106000001102LFO1 Wave
6417F011111117LFO1 Delay
6523F 4000111111 010000007LFO1 Slew
6623F 6000111111 011000008LFO1 Speed
67207 7000000111 011100006LFO1 Sync Value
69108000010001LFO1 Speed/Sync
69110000100001LFO1 Key Sync
70201 7E00000001 011111107LFO2 Delay
7010C000011002LFO2 Wave
7217F011111117LFO2 Slew
7327F 4001111111 010000008LFO2 Speed
7420F 6000001111 011000006LFO2 Sync Value
76110000100001LFO2 Speed/Sync
76120001000001LFO2 Key Sync
77108000010001Arp On
77120001000001Arp Seq Retrig
7811C000111003Arp Octaves
7910E000011103Arp Note Mode
8011F000111115Arp Rhythm
8123F 4000111111 010000007Arp Swing
8221F 6000011111 011000007Mod Wheel Filter Freq
8320F 7000001111 011100007Mod Wheel LFO1 to Osc Pitch
84207 7800000111 011110007Mod Wheel LFO2 to Filter Freq
85203 7C00000011 011111007Mod Wheel Osc2 Pitch
86201 7E00000001 011111107Aftertouch Filter Freq
8817F011111117Aftertouch LFO1 to Osc 1+2 Pitch
8923F 4000111111 010000007Aftertouch LFO2 Speed
9023F 6000111111 011000008Osc1 LFO1 Depth
9121F 7000011111 011100008Osc2 LFO1 Depth
93203 7C00000011 011111007Osc1 LFO2 PW Mod
94201 7E00000001 011111107Osc2 LFO2 PW Mod
9727F 4001111111 010000008Filter LFO2 Depth
9821F 6000011111 011000007Osc1 Mod Env Depth
9920F 7000001111 011100007Osc2 Mod Env Depth
101201 7C00000001 011111006Osc1 Mod Env PW Mod
102201 7E00000001 011111107Osc2 Mod Env PW Mod
10523F 4000111111 010000007Filter Mod Env Depth
10621F 6000011111 011000007Fx Osc Filter Mod
10720F 7000001111 011100007Fx Distortion
108207 7800000111 011110007VCA Limit
111102000000101Paraphonic Off (0) / On (1)
112107000001113Filter tracking
114140010000001Amp Env Retriggering
115120001000001Mod Env Retriggering
115201 7000000001 011100004Tuning table
117138001110003Osc Error
1371616x 0x7F16x 0111111116x 8Patch name (16 ASCII chars)

Two-bytes values

Some parameters use two bytes to increase the value range from 0..127 to 0..255.

Two-bytes values in SysEx dump:

Two-bytes values in MIDI messages:

Sending:

Value = 201. In binary : 11001001

  1. The seven most significants bits are 1100100. Left-pad them to form a byte: 01100100 = 100. This will be the first byte to send.
  2. The least significant bit is 1. We left-shift it by 6 positions : 01000000 = 64. This will be the second byte so send.

In summary:

byte1 = integer part of value/2
byte2 = 0 if value is even, 64 if value is odd
Receiving:

We receive two bytes: 01100100 and 01000000

  1. Left-shift byte 1 by one position: 01100100 << 1 = 11001000
  2. Right-shift byte 2 by 6 position: 01000000 >>> 6 = 00000001
  3. Add them: 11001000 + 00000001 = 11001001 = 201

In summary:

value = byte1*2 + byte2/64

Notes:

Some bytes seem to always have the same value. This is confirmed accross all patch I could get.

SysEx example

BS II SysEx Message in decimal:

240 000 032 041 000 051 000 000 000 000 000 000 000 000 001 000 
076 000 000 072 004 000 002 000 002 032 016 000 007 048 001 000
067 064 032 000 035 127 127 127 117 000 000 000 013 081 124 000
008 032 001 068 032 000 000 000 113 002 071 071 000 000 000 000
000 000 014 032 000 000 012 000 000 043 000 000 032 040 004 008
029 025 019 104 004 002 001 020 064 032 032 016 008 002 001 000
064 064 016 015 116 002 001 000 064 054 064 000 000 003 016 000
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000 000 247 

BS II SysEx Message in hexadecimal:

f0 00 20 29 00 33 00 00 00 00 00 00 00 00 01 00 
4c 00 00 48 04 00 02 00 02 20 10 00 07 30 01 00
43 40 20 00 23 7f 7f 7f 75 00 00 00 0d 51 7c 00
08 20 01 44 20 00 00 00 71 02 47 47 00 00 00 00
00 00 0e 20 00 00 0c 00 00 2b 00 00 20 28 04 08
1d 19 13 68 04 02 01 14 40 20 20 10 08 02 01 00
40 40 10 0f 74 02 01 00 40 36 40 00 00 03 10 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 f7 

Decoding example

Let's decode the Osc 1 Range value. The definition is:

OffsetBytesHex maskBin maskBitsDescription
20207 7800000111 011110007Osc 1 Range

Take bytes 20 and 21 from the above example:

hex: 48 04
bin: 01001000 00000100

Apply masks:

bin:  01001000 00000100
mask: 00000111 01111000
      -----------------
           000 00000

Value:

value: 00000000 (bin) == 0 (dec)

Encoding example

Let's encode the Osc 1 Coarse value. The definition is:

OffsetBytesHex maskBin maskBitsDescription
21207 7C00000111 011111008Osc 1 Coarse

The value we want to encode is 91:

91 (dec) == 01011011 (bin) 

If the mask comprises two bytes, convert the value to encode to a sixteen-bits number, else take the value has an eight-bits value.

01011011 --> 00000000 01011011 (MSB LSB) 

From the mask LSB, count how many bits we need to shift to the left:

01111100 --> 2 bits

Shit the value:

0000000001011011 << 2 --> 0000000101101100   

Get the sysex LSB:

0000000101101100 & 01111100 = 1101100

How many bits has gone into the sysex_lsb?:

LSB_bits = 7 - 2 = 5

Discard, from the original value, this number of bits used for the sysex LSB:

0000000001011011 >>> 5 --> 0000000000000010  

Get the sysex MSB:

0000000000000010 & 00000111 = 00000010 

We can now inject these sysex values into the sysex data:

first, reset the target bits to zero with the inverted masks:     
 
sysex MSB: sysex_data[offset]   = sysex_data[offset]   & 11111000   
sysex LSB: sysex_data[offset+1] = sysex_data[offset+1] & 10000011
 
then inject the value bits: 
 
sysex MSB: sysex_data[offset]   = sysex_data[offset]   | 00000010   
sysex LSB: sysex_data[offset+1] = sysex_data[offset+1] | 01101100

Init patch

$ xxd -g 1 factory-patches/70-127_INIT\ PATCH.syx
0000000: f0 00 20 29 00 33 00 00 00 00 00 00 00 00 01 00  .. ).3..........
0000010: 4c 00 00 48 04 04 02 00 02 20 10 10 08 00 01 00  L..H..... ......
0000020: 43 40 20 00 03 7f 7c 00 00 00 00 00 0f 78 00 00  C@ ...|......x..
0000030: 08 20 00 00 07 78 00 00 40 00 00 0f 70 00 00 00  . ...x..@...p...
0000040: 00 00 12 63 10 00 00 00 00 1a 06 20 20 20 04 00  ...c.......   ..
0000050: 1f 19 10 09 24 02 01 14 40 20 20 10 08 02 01 00  ....$...@  .....
0000060: 40 40 10 08 04 02 01 00 40 20 00 00 00 03 10 00  @@......@ ......
0000070: 00 00 00 00 00 00 00 00 00 f7                    ..........

MIDI resources

Trademarks

Novation is a registered trade mark of Focusrite Audio Engineering Limited.

Bass Station II is a trade mark of Focusrite Audio Engineering Limited.

© StudioCode.dev - Made with 11ty and tailwindcss.