ChatGPT v Orba 1
Part 2
Let's consider the note blocks. Eg:
PlayNote: 16
startTicksLSB: -100
startTicksMSB: 7
Note #: 70
Vel On: 127
Vel Off: 91
DurTicksLSB: -20
DurTicksMSB: 1
They start with the value 16, a special instruction signifying "play this note". eventData also includes performance data which muddies the water, but at the moment I'm just concentrating on note data, and sequences which only contain note data.
startTicksLSB: -100
startTicksMSB: 7
These are used to calculate the timing of the note, along with the timing of the previous note.
Note #: 70
Vel On: 127
Vel Off: 91
These are the MIDI note number with attack/release velocity.
DurTicksLSB:
DurTicksMSB:
These are used to calculate the note duration.
@Subsky came up with a formula for calculating the timing of the note which appears in the spreadsheet attached above. Consider the timing of the second note in the "DPC" sequence:
ChatGPT v Orba 1
Part 3
I've also been trying to educate ChatGPT in the art of converting between eventData strings and note blocks. Here's an example conversation.
PROMPT
Consider trhis string:
"EAcAPnha9QMQWQdAf1zvAxCXB0FxXNIFEIYHQ3lQ4QUQbAdFdjqlAxCcB0Z/W+wBEHEHSFc3dAE="
It is encoded in Base64, yielding the following list of numbers:
16
7
0
62
120
90
-11
1
16
89
7
64
127
92
-17
1
16
-105
7
65
113
92
-46
3
16
-122
7
67
121
80
-31
3
16
108
7
69
118
58
-91
1
16
-100
7
70
127
91
-20
1
16
113
7
72
87
55
116
1
These can be arranged in note blocks as follows: (list of note blocks as above)
ChatGPT v Orba 1
Part 4
REPLY:
(Formatting is messed up but you get the idea)
Based on the provided information, the EventData string can be decoded and parsed into a list of note blocks with the following structure:
Note Block 1:
PlayNote: 16startTicksLSB: 7startTicksMSB: 0Note #: 62Vel On: 120Vel Off: 90DurTicksLSB: -11DurTicksMSB: 1
Note Block 2:
PlayNote: 16startTicksLSB: 89startTicksMSB: 7Note #: 64Vel On: 127Vel Off: 92DurTicksLSB: -17DurTicksMSB: 1
Note Block 3:
PlayNote: 16startTicksLSB: -105startTicksMSB: 7Note #: 65Vel On: 113Vel Off: 92DurTicksLSB: -46DurTicksMSB: 3
Note Block 4:
PlayNote: 16startTicksLSB: -122startTicksMSB: 7Note #: 67Vel On: 121Vel Off: 80DurTicksLSB: -31DurTicksMSB: 3
Note Block 5:
PlayNote: 16startTicksLSB: 108startTicksMSB: 7Note #: 69Vel On: 118Vel Off: 58DurTicksLSB: -91DurTicksMSB: 1
Note Block 6:
PlayNote: 16startTicksLSB: -100startTicksMSB: 7Note #: 70Vel On: 127Vel Off: 91DurTicksLSB: -20DurTicksMSB: 1
Note Block 7:
PlayNote: 16startTicksLSB: 113startTicksMSB: 7Note #: 72Vel On: 87Vel Off: 55DurTicksLSB: 116DurTicksMSB: 1
Each note block represents a musical note event with various properties:
PlayNote: This value indicates the type of event. In this case, it is always 16, which typically represents a note-on event.startTicksLSB and startTicksMSB: These two values represent the starting time of the note event. They are combined to form a 14-bit value, which determines the precise timing of the note.Note #: This value represents the pitch of the note. The number 62, 64, 65, 67, 69, 70, and 72 correspond to specific notes in the musical scale.Vel On: This value represents the velocity or intensity of the note when it is played. Higher values typically indicate a louder note.Vel Off: This value represents the velocity when the note is released or turned off. It can affect the release behavior of the note.DurTicksLSB and DurTicksMSB: These values determine the duration of the note event. They are combined to form a 14-bit value, which specifies the length of time the note is played.
Please note that the provided information is based on the given note block structure and does not provide context or additional details about the musical composition or any specific system or software used for encoding or decoding.
ChatGPT v Orba 1
Part 5
PROMPT:
Write a Python program that can generate a list of "note blocks" like this for any string encoded in this way.
REPLY:
Certainly! Here's a Python program that decodes a given Base64-encoded string and generates a list of note blocks based on the provided note block structure:
import base64
def decode_note_blocks(encoded_string):
decoded_data = base64.b64decode(encoded_string)
note_blocks = []
# Each note block consists of 8 consecutive bytes
for i in range(0, len(decoded_data), 8):
block_data = decoded_data[i:i+8]
play_note = block_data[0]
start_ticks_lsb = block_data[1]
start_ticks_msb = block_data[2]
note_number = block_data[3]
vel_on = block_data[4]
vel_off = block_data[5]
dur_ticks_lsb = block_data[6]
dur_ticks_msb = block_data[7]
note_block = {
'PlayNote': play_note,
'startTicksLSB': start_ticks_lsb,
'startTicksMSB': start_ticks_msb,
'Note #': note_number,
'Vel On': vel_on,
'Vel Off': vel_off,
'DurTicksLSB': dur_ticks_lsb,
'DurTicksMSB': dur_ticks_msb
}
note_blocks.append(note_block)
return note_blocks
# Example usage
encoded_string = "EAcAPnha9QMQWQdAf1zvAxCXB0FxXNIFEIYHQ3lQ4QUQbAdFdjqlAxCcB0Z/W+wBEHEHSFc3dAE="
note_blocks = decode_note_blocks(encoded_string)
# Print the generated note blocks
for i, block in enumerate(note_blocks, start=1):
print(f"Note Block {i}:")
for key, value in block.items():
print(f"- {key}: {value}")
print()
You can run this program with the provided encoded string or any other string encoded in the same format. It will output the note blocks in the desired format, as shown in the example.
************************************
...now, I realise that ChatGPT's coding efforts are a bit controversial, and can be a bit rubbish, but it can be a useful learning tool for novices like me. I haven't tested this program, but it indicates possibilities perhaps.
ChatGPT v Orba 1
Part 6
PROMPT
(TIming calculation formula as above)
Please calculate and verify the timings for the note blocks using the given formula. Compare the calculated timings with the note timings provided in the console output. Provide the full results and a summary to confirm the alignment between the two.
(Provide note blocks, console output, eventData string, etc)
REPLY
Based on the provided note block data and the formula:
=IF(C12<0,255-ABS(C12)+1+C132^8+D4,C12+C132^8+D4)
Let's calculate the timings for each note block and compare them with the note timings provided in the console output.
Note Block 1: C12 = 7, C13 = 0, D4 = 0 Timing = 7 + 0 * 2^8 + 0 = 7
Note Block 2: C12 = 89, C13 = 7, D4 = 7 Timing = 89 + 7 * 2^8 + 7 = 1888
Note Block 3: C12 = -105, C13 = 7, D4 = 89 Timing = 255 - abs(-105) + 1 + 7 * 2^8 + 89 = 3831
Note Block 4: C12 = -122, C13 = 7, D4 = -105 Timing = 255 - abs(-122) + 1 + 7 * 2^8 + (-105) = 5757
Note Block 5: C12 = 108, C13 = 7, D4 = -122 Timing = 108 + 7 * 2^8 + (-122) = 7657
Note Block 6: C12 = -100, C13 = 7, D4 = 108 Timing = 255 - abs(-100) + 1 + 7 * 2^8 + 108 = 9605
Note Block 7: C12 = 113, C13 = 7, D4 = -100 Timing = 113 + 7 * 2^8 + (-100) = 11510
Comparing the calculated timings with the note timings provided in the console output, we can see that they match:
Console Output:
Note Block 1: MIDI Note at tick 7Note Block 2: MIDI Note at tick 1888Note Block 3: MIDI Note at tick 3831Note Block 4: MIDI Note at tick 5757Note Block 5: MIDI Note at tick 7657Note Block 6: MIDI Note at tick 9605Note Block 7: MIDI Note at tick 11510
Summary: The calculated timings using the formula align perfectly with the note timings provided in the console output. This confirms that the calculations are correct and the formula accurately represents the timing calculation for the note blocks.
ChatGPT v Orba 1
Part 7
...again, the formatting is a bit of a mess, ChatGPT can be hard work when it comes to getting accurate results and code, but I've had a pretty long conversation with it about this stuff and it's helped me to understand some points. I think it could potentially be a useful tool to assist working with sequenced data, and could possibly help to write an "offline" file-based quantisation program, though that's some way off and it's not something I'm personally particularly interested in. But, I'm interested in trying to use it to put together some cool demo like the opening bars of "Flight of the Bumble Bee" or something that would be impossible to play and very difficult to sequence manually. I though I'd throw this out there in case anyone was interested in looking at it. :-)
ChatGPT v Orba 1
Part 8
ChatGPT and its ilk tend to get a bit of a bad press.Most of the news articles now are about how soon it's expected to cause human extinction. You're also on thin ice when you start discussing programming. I'm aware that there are several talented programmers on this forum who I highly respect and wouldn't touch it with a bargepole. When people started using ChatGPT to try and generate code for Monome products and posted it on Lines (Monome community forum), they didn't get a warm welcome...
...nevertheless, I'm a fan. It's the arrival of ChatGPT that prompted me to re-engage with the Orba, and with programming in general, and I've found this quite rewarding.
To some extent you can engage with ChatGPT directly on this stuff, but it's very wayward and unpredictable, as you'll know if you've spent any time with it. It has flashes of brilliance but never does anything the same way twice. It's forgetful and makes basic errors. That's just its nature.
I've spent a number of hours now engaging with ChatGPT in discussions about note sequence data, and found the best approach for me was to use it to write Python routines. I like the idea of using ChatGPT as a human-language-level programming tool ("...create the Orba XML file to play Beethoven's Fifth on an ocarina...") but it's too frustrating to try and work that way, partly because it's so forgetful. It will give you the right answer one minute and the wrong answer the next. So I started working with it via Python instead. Because at least the action of a Python program, once it's been established, is predictable. It takes quite a lot of effort to get it to produce working code, and I expect it's not good code, but for a non-programmer it can nevertheless be a useful crutch. With patience.
I'll present the code at some point, after I've refined it. I've been developing routines to convert between Base64 eventData strings, raw "note block" data, formatted human-readable "note-block data"; routines to modify note data with new timing information, and interpret note data to produce "console output" with lists of notes and event times.
I was pleased with today's progress, and finally had a set of Python utilities that allowed me to create the XML to play a two-octave ascending and descending chromatic scale on Orba 1. Without ChatGPT, I would have found that so difficult that I wouldn't even have tried.
The challenge I've set myself is to sequence the opening of "Flight of the Bumble Bee". Unfortunately ChatGPT doesn't seem familiar with the note data for it so I'll have to round it up from somewhere else. If I can get that far I'll post up a YT link and some code.
ChatGPT v Orba 1
Part 9
It's not Flight of the Bumble Bee yet, but it's progress.
This is an MP3 recording of a 32-bar Orba sequence with a single-line melody provided by ChatGPT. I don't know what it is. I asked for "Toccata and Fugue" so it's probably Bach.
The method was:
- Record a 32-bar sequence with repeated rapid random notes and save it.
- Extract the eventData string
- Get ChatGPT to generate sequence of MIDI note numbers
I then used a set of Python utilites I've persuaded ChatGPT to write:
- Decode the string into note block data and export to a file
- Replace the notes with new ones which are read from a file
- Replace the durations with a set read from a file (all 100s here)
- Replace the timing information with a set of relative times read from a file (all 100s)
- Encode the blocks back into a string
Finally I made a copy of the original song and replaced the eventData string with the new copy.
The next things I want to work out are being able to generate note sequence XML from scratch rather than having to modify recorded sequences, and working with expression data as well as note data.
Here's sample output from the Python routines.
Start of eventData string:
"EBAASnNEZAAQZABNYT5kABBkAFFzQWQAEGQAT31EZAAQZABNekNkABBkA"
Start of raw note block data:
16,16,0,74,115,68,100,0
16,100,0,77,97,62,100,0
16,100,0,81,115,65,100,0
16,100,0,79,125,68,100,0
16,100,0,77,122,67,100,0
16,100,0,74,92,51,100,0
16,100,0,76,92,56,100,0
16,100,0,74,102,51,100,0
16,100,0,77,102,52,100,0
16,100,0,79,92,51,100,0
16,100,0,80,76,48,100,0
Start of formatted note block data:
Note Block:
PlayNote: 16
startTicksLSB: 16
startTicksMSB: 0
Note #: 74
Vel On: 115
Vel Off: 68
DurTicksLSB: 100
DurTicksMSB: 0
Note Block:
PlayNote: 16
startTicksLSB: 100
startTicksMSB: 0
Note #: 77
Vel On: 97
Vel Off: 62
DurTicksLSB: 100
DurTicksMSB: 0
Note Block:
PlayNote: 16
startTicksLSB: 100
startTicksMSB: 0
Note #: 81
Vel On: 115
Vel Off: 65
DurTicksLSB: 100
DurTicksMSB: 0
MemIdx event strings (simulated "console output"):
MemIdx = 0 - MIDI Note at tick 16, channel 1, note 74, duration 100, von 115, voff 68
MemIdx = 8 - MIDI Note at tick 116, channel 1, note 77, duration 100, von 97, voff 62
MemIdx = 16 - MIDI Note at tick 216, channel 1, note 81, duration 100, von 115, voff 65
MemIdx = 24 - MIDI Note at tick 316, channel 1, note 79, duration 100, von 125, voff 68
MemIdx = 32 - MIDI Note at tick 416, channel 1, note 77, duration 100, von 122, voff 67
MemIdx = 40 - MIDI Note at tick 516, channel 1, note 74, duration 100, von 92, voff 51
MemIdx = 48 - MIDI Note at tick 616, channel 1, note 76, duration 100, von 92, voff 56
MemIdx = 56 - MIDI Note at tick 716, channel 1, note 74, duration 100, von 102, voff 51
MemIdx = 64 - MIDI Note at tick 816, channel 1, note 77, duration 100, von 102, voff 52
MemIdx = 72 - MIDI Note at tick 916, channel 1, note 79, duration 100, von 92, voff 51
ChatGPT v Orba 1
Part 10
And here it is - Flight of the Bumblebee on an Orba 1. Almost. ;-)
https://youtu.be/wbUQEP_8NZE
Now we just need a MIDI to Orba Base64 converter.. I'll dig up my old notes because I had mapped out a number of the command types (e.g. note = 16) but there were quite a few more data structures for pitch bend, cc control, etc...
Here's a simple example to start introducing the scripts I've got so far. Start by finding a MIDI file - eg "Sweet Child O' Mine". So far I'm working on simple one-line examples with equally spaced notes.
- Check the MIDI file in a DAW. (I'm using Cakewalk by BandLab on Windows. MP3 is scom.mp3, MIDI file is scom.mid.)
16,121,0,64,106,63,45,0
16,114,0,64,108,61,44,0
...etc. These are the first three notes of the original Orba data, based on the structure:
PlayNote: (16)
startTicksLSB:
startTicksMSB:
Note #:
Vel On:
Vel Off:
DurTicksLSB:
DurTicksMSB:
- Run "py tweaknotes.py". This substitutes the note values in note_blocks with the ones in midi_notes. In other words, the random notes are replaced by the ones from the MIDI file. Note that this approach will only work with a simple evenly-spaced single melody line. It generates "adjusted_note_blocks". Eg the Orba sequence now starts:
16,103,0,61,80,48,45,0
16,121,0,73,106,63,45,0
16,114,0,68,108,61,44,0
...where 61, 73, 68 are the first three notes of the sequence.
- Replace the contents of "note_blocks" with this new adjusted data, and run "py rawtostring.py" to convert it back into a Base64 string. Make a copy of the .song file with suitable name/tempo and replace the value of eventData with this new string.
- Upload to Orba:
https://youtu.be/0doaResDKJc
***********************************
(I've been working on other stuff but I'll present that separately.)
More attachments...
Corrected video link.
https://youtu.be/OGS634SKncE
BJG145
ChatGPT v Orba 1
Part 1
Around page 22 of the "Orba hacking knowledge base", a year or so ago, me and @Subskybox were dissecting the eventData string the Orba 1 uses to represent sequences. @Subsky did some clever mathematical analysis while I did the donkey work of setting up experiments and recording the results.
Some of the experiments were based on a song called "DPC" which played the first seven notes of a minor scale. I've attached the song file, console output, and a spreadsheet @Subsky put together after analysing the data.
The eventData string is a mix of note and performance data, but this "DPC" test simplifies things to only include note data. This is organised as a series of "note blocks":
Note Block 1:
PlayNote: 16
startTicksLSB: 7
startTicksMSB: 0
Note #: 62
Vel On: 120
Vel Off: 90
DurTicksLSB: -11
DurTicksMSB: 1
Note Block 2:
PlayNote: 16
startTicksLSB: 89
startTicksMSB: 7
Note #: 64
Vel On: 127
Vel Off: 92
DurTicksLSB: -17
DurTicksMSB: 1
Note Block 3:
PlayNote: 16
startTicksLSB: -105
startTicksMSB: 7
Note #: 65
Vel On: 113
Vel Off: 92
DurTicksLSB: -46
DurTicksMSB: 3
Note Block 4:
PlayNote: 16
startTicksLSB: -122
startTicksMSB: 7
Note #: 67
Vel On: 121
Vel Off: 80
DurTicksLSB: -31
DurTicksMSB: 3
Note Block 5:
PlayNote: 16
startTicksLSB: 108
startTicksMSB: 7
Note #: 69
Vel On: 118
Vel Off: 58
DurTicksLSB: -91
DurTicksMSB: 1
Note Block 6:
PlayNote: 16
startTicksLSB: -100
startTicksMSB: 7
Note #: 70
Vel On: 127
Vel Off: 91
DurTicksLSB: -20
DurTicksMSB: 1
Note Block 7:
PlayNote: 16
startTicksLSB: 113
startTicksMSB: 7
Note #: 72
Vel On: 87
Vel Off: 55
DurTicksLSB: 116
DurTicksMSB: 1
If you take this series of values and encode them as a Base64 string, you get the corresponding following eventData string from the .song file:
"EAcAPnha9QMQWQdAf1zvAxCXB0FxXNIFEIYHQ3lQ4QUQbAdFdjqlAxCcB0Z/W+wBEHEHSFc3dAE="
This appears in the .song XML as follows:
<LoopData writeIndex="56" recordStartTime="0" recordStopTime="11882" lastEventTime="4809"
nBars="7" eventData="EAcAPnha9QMQWQdAf1zvAxCXB0FxXNIFEIYHQ3lQ4QUQbAdFdjqlAxCcB0Z/W+wBEHEHSFc3dAE="
eventDataCrc="1ff6d4c4"/>
The problem we found is that the timing data is relative...the timing of each note, ie when it plays, is affected by the one before. That makes real-time quantisation a bit of a nightmare. It might be posisble to implement "offline" quantisation, processing a .song file to quantise the data, or create new sequences based on MIDI data, but it's a hassle and we pretty much abandoned the investigation at that point.
A few months later, ChatGPT arrived on the scene...
1 person likes this idea