Start a new topic

ChatGPT v Orba 1

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...

 

 

 

 

 

 

song
(31.2 KB)
txt
(1.28 KB)
xlsx

1 person likes this idea

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:

=IF(C12<0,255-ABS(C12)+1+C13*2^8+D4,C12+C13*2^8+D4) This formula calculates the timing for a note based on the given inputs: C12 represents the startTicksLSB for the current note block. C13 represents the startTicksMSB for the current note block. D4 represents the startTicksLSB for the previous note block. The formula first checks if C12 is less than 0. If it is, it performs the calculation: 255-ABS(C12)+1+C13*2^8+D4. Otherwise, it calculates: C12+C13*2^8+D4. Remember to replace C12, C13, and D4 with the appropriate values from the note block data when using the formula.
Now, that was written by ChatGPT after I'd fed it a bunch of information culled from the thread. I never really understood the formula, but ChatGPT apparently does. (Although it's also good at bluffing; you need to keep an eye on it.)

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

mp3
(744 KB)

ChatGPT v Orba 1

Part 10

And here it is - Flight of the Bumblebee on an Orba 1. Almost. ;-)

https://youtu.be/wbUQEP_8NZE 


1 person likes this

This one worked a bit better...Bach prelude. 


https://youtu.be/ZSkyBMTFmmw


1 person likes this

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...


1 person likes this

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.)


image

 
- Run "py miditovals-dedup.py scom.mid midi_notes.txt". All the Python routines were generated by ChatGPT. This one extracts the MIDI note values from the file and generates a simple comma-separated list. I've attached "miditovals.py" as well as "miditovals-dedpu.py". The reason for using the "dedup" version is that the regular version sometimes presents every note twice with certain MIDI files. This is probably because the original file contains both note-on and note-off and it reads them both or something.

 - Extract the value of the eventData string (everything within quotes) from the "Sequence" song file. This simply contains a long, rapid sequence of random approximately evenly spaced notes with no performance data. Copy the string into "note_string.txt".

 - Run "py stringtoraw.py". This converts the Base64 string into a file "note_blocks.txt" in this format:

16,103,0,64,80,48,45,0

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.)

txt
song
(36.8 KB)
txt
(12.1 KB)
txt
(5.26 KB)
txt
py
(896 Bytes)
py
(745 Bytes)
mid
(10.4 KB)
mp3
(1.44 MB)

More attachments...


py
(1.13 KB)
py
(807 Bytes)
py
(788 Bytes)

Corrected video link.

https://youtu.be/OGS634SKncE


Login or Signup to post a comment