any help would be appreciated.
at the moment i'm guessing that the pattern_data_1 is a hex string but i could be well off, and would much rather not reinvent a wheel that might already be in someone's toolbox

Code: Select all
c:\Billy>python random_rhythm_generator.py --help
usage: random_rhythm_generator.py [-h] [--charset CHARSET] [--rotate-left] [--rotate-right]
optional arguments:
-h, --help show this help message and exit
--charset CHARSET character set to use for generating random rhythm
--rotate-left rotate the string to the left
--rotate-right rotate the string to the right
c:\Billy>python random_rhythm_generator.py
Todays Random Rhythm is : rsrhhhhmsssshrhh
c:\Billy>python musicrhythm.py --help
usage: musicrhythm.py [-h] [--bpm BPM] [--output OUTPUT] [--human] N
Generate a MIDI file from a list of notes with volume and rest.
positional arguments:
N a string of notes and reset (h=Hard, m=Medium, s=Soft r=Rest)
optional arguments:
-h, --help show this help message and exit
--bpm BPM the tempo in beats per minute (default: 120)
--output OUTPUT the name of the output MIDI file (default: output.mid)
--human adds slight random volume to the notes.
c:\Billy>python musicrhythm.py --bpm 90 --human rsrhhhhmsssshrhh
Sequence of notes:
Note 1: r, Onset: 0.3333333333333333, Duration: 0.3333333333333333
Note 2: s, Volume: 55, Onset: 0.6666666666666666, Duration: 0.3333333333333333
Note 3: r, Onset: 1.0, Duration: 0.3333333333333333
Note 4: h, Volume: 93, Onset: 1.3333333333333333, Duration: 0.3333333333333333
Note 5: h, Volume: 102, Onset: 1.6666666666666665, Duration: 0.3333333333333333
Note 6: h, Volume: 101, Onset: 1.9999999999999998, Duration: 0.3333333333333333
Note 7: h, Volume: 95, Onset: 2.333333333333333, Duration: 0.3333333333333333
Note 8: m, Volume: 78, Onset: 2.6666666666666665, Duration: 0.3333333333333333
Note 9: s, Volume: 49, Onset: 3.0, Duration: 0.3333333333333333
Note 10: s, Volume: 47, Onset: 3.3333333333333335, Duration: 0.3333333333333333
Note 11: s, Volume: 46, Onset: 3.666666666666667, Duration: 0.3333333333333333
Note 12: s, Volume: 53, Onset: 4.0, Duration: 0.3333333333333333
Note 13: h, Volume: 94, Onset: 4.333333333333333, Duration: 0.3333333333333333
Note 14: r, Onset: 4.666666666666666, Duration: 0.3333333333333333
Note 15: h, Volume: 94, Onset: 4.999999999999999, Duration: 0.3333333333333333
Note 16: h, Volume: 97, Onset: 5.333333333333332, Duration: 0.3333333333333333
midi files saved as : output.mid
c:\Billy>python random_rhythm_generator.py --rotate-left
Todays Random Rhythm is : rsmrmrsrrsrrshss
Rotation 1: smrmrsrrsrrshssr
Rotation 2: mrmrsrrsrrshssrs
Rotation 3: rmrsrrsrrshssrsm
Rotation 4: mrsrrsrrshssrsmr
Rotation 5: rsrrsrrshssrsmrm
Rotation 6: srrsrrshssrsmrmr
Rotation 7: rrsrrshssrsmrmrs
Rotation 8: rsrrshssrsmrmrsr
Rotation 9: srrshssrsmrmrsrr
Rotation 10: rrshssrsmrmrsrrs
Rotation 11: rshssrsmrmrsrrsr
Rotation 12: shssrsmrmrsrrsrr
Rotation 13: hssrsmrmrsrrsrrs
Rotation 14: ssrsmrmrsrrsrrsh
Rotation 15: srsmrmrsrrsrrshs
The 8380 twin flame symbolism is the act of satisfaction after you have met all your goals and dreams.
Code: Select all
<Value property="pattern_data_1" type="string" >
020110013D67028380023D5E028380033D63028380043D35028380053D6A028380063D54028380073D36028380083D69028380093D300283800A3D2E0283800B3D610283800C3D320283800D3D670283800E3D340283800F3D43028380
</Value>
Code: Select all
[Version?]0201 10 [16]=Steps
position | pitch+1 | velocity+1 | giggles
[1] 01 3D [103] 67 02 8380
[2] 02 3D [ 94] 5E 02 8380
[3] 03 3D [ 99] 63 02 8380
[4] 04 3D [ 53] 35 02 8380
[5] 05 3D [106] 6A 02 8380
[6] 06 3D [ 84] 54 02 8380
[7] 07 3D [ 54] 36 02 8380
[8] 08 3D [105] 69 02 8380
[9] 09 3D [ 48] 30 02 8380
[10] 0A 3D [ 46] 2E 02 8380
[11] 0B 3D [ 97] 61 02 8380
[12] 0C 3D [ 50] 32 02 8380
[13] 0D 3D [103] 67 02 8380
[14] 0E 3D [ 52] 34 02 8380
[15] 0F 3D [ 67] 43 02 8380
As a first step, you should ask RS to give you the schema for the data strings, and permission to discuss the schema publicly.
ah!Enlightenspeed wrote: ↑22 Sep 2023As a first step, you should ask RS to give you the schema for the data strings, and permission to discuss the schema publicly.
Without that, it's difficult for other devs to participate; any of us could do it easily enough, but it involves a level of reverse engineering, which is legally dubious - not that it would be any real threat of course, this stuff is far too basic.
The moral issue is still there, however, so if you can get RS to publicly agree to this first, then other RE devs can help.
I’d be surprised if they aren’t cool with it, but as a rule ask first. The patches are in plain text, not encrypted, so it’s easy to do this kind of thing, and this is probably intended by RS.Billy+ wrote: ↑22 Sep 2023
ah!
hadn't really considered that......
Sorry Reason Studios, if you see this thread would you possibly chip in and advise on any concerns![]()
if you would rather the info be removed please say so.
if however you're cool with it please give the go ahead![]()
again sorry, and if mods want to remove images / code please feel free to![]()
thanks manEnlightenspeed wrote: ↑22 Sep 2023I’d be surprised if they aren’t cool with it, but as a rule ask first. The patches are in plain text, not encrypted, so it’s easy to do this kind of thing, and this is probably intended by RS.Billy+ wrote: ↑22 Sep 2023
ah!
hadn't really considered that......
Sorry Reason Studios, if you see this thread would you possibly chip in and advise on any concerns![]()
if you would rather the info be removed please say so.
if however you're cool with it please give the go ahead![]()
again sorry, and if mods want to remove images / code please feel free to![]()
In the spirit of sharing, if they make the schema available, I’ll write the script for you, and make it public
Cheers,
Brian
Code: Select all
c:\Billy>python random_rhythm_generator.py --help
usage: random_rhythm_generator.py [-h] [--charset CHARSET] [--rotate-left] [--rotate-right] [--limiting-factor LIMITING_FACTOR] [--max-repeating MAX_REPEATING]
optional arguments:
-h, --help show this help message and exit
--charset CHARSET character set to use for generating random rhythm
--rotate-left rotate the string to the left
--rotate-right rotate the string to the right
--limiting-factor LIMITING_FACTOR
specify an individual character as a parameter to be the limiting factor
--max-repeating MAX_REPEATING
specify the maximum number of times a character can be repeated
c:\Billy>python random_rhythm_generator.py
Todays Random Rhythm is : smsshmmrrmmshsrh
c:\Billy>python random_rhythm_generator.py --rotate-right --limiting-factor h3 --max-repeating 2
Todays Random Rhythm is : shmhsmrhmrmmsrss
Rotation 01: sshmhsmrhmrmmsrs
Rotation 02: ssshmhsmrhmrmmsr
Rotation 03: rssshmhsmrhmrmms
Rotation 04: srssshmhsmrhmrmm
Rotation 05: msrssshmhsmrhmrm
Rotation 06: mmsrssshmhsmrhmr
Rotation 07: rmmsrssshmhsmrhm
Rotation 08: mrmmsrssshmhsmrh
Rotation 09: hmrmmsrssshmhsmr
Rotation 10: rhmrmmsrssshmhsm
Rotation 11: mrhmrmmsrssshmhs
Rotation 12: smrhmrmmsrssshmh
Rotation 13: hsmrhmrmmsrssshm
Rotation 14: mhsmrhmrmmsrsssh
Rotation 15: hmhsmrhmrmmsrsss
Code: Select all
import argparse
import random
import string
def rotate_string(string, rotation):
return string[rotation:] + string[:rotation]
def modify_string(string, limiting_factor, max_repeating):
limiting_character, limiting_count = limiting_factor[0], int(limiting_factor[1:])
limiting_factor_count = string.count(limiting_character)
if limiting_factor_count > limiting_count:
new_random_string = string.replace(limiting_character, limiting_character * limiting_count)
else:
new_random_string = string
if max_repeating is not None:
new_random_string = enforce_max_repeating(new_random_string, max_repeating)
return new_random_string
def enforce_max_repeating(string, max_repeating):
for char in set(string):
if string.count(char) > max_repeating:
string = string.replace(char * max_repeating, char * max_repeating + char)
return string
def generate_random_string(charset, length):
return ''.join(random.choices(charset, k=length))
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--charset', type=str, default='hmsr', help='character set to use for generating random rhythm')
parser.add_argument('--rotate-left', action='store_true', help='rotate the string to the left')
parser.add_argument('--rotate-right', action='store_true', help='rotate the string to the right')
parser.add_argument('--limiting-factor', type=str, default=None, help='specify an individual character as a parameter to be the limiting factor')
parser.add_argument('--max-repeating', type=int, default=None, help='specify the maximum number of times a character can be repeated')
args = parser.parse_args()
random_string = generate_random_string(args.charset, 16)
if args.rotate_left:
for i in range(len(random_string)):
rotated_string = rotate_string(random_string, i)
if i == 0:
print(f"Todays Random Rhythm is : {random_string} ")
elif i > 0:
print(f"Rotation {i:02}: {rotated_string}")
elif args.rotate_right:
for i in range(len(random_string)):
rotated_string = rotate_string(random_string, -i)
if i == 0:
print(f"Todays Random Rhythm is : {random_string} ")
elif i > 0:
print(f"Rotation {i:02}: {rotated_string}")
elif args.limiting_factor is not None:
new_random_string = modify_string(random_string, args.limiting_factor, args.max_repeating)
print(f"Todays Random Rhythm is : {new_random_string} ")
else:
print(f"Todays Random Rhythm is : {random_string} ")
if __name__ == "__main__":
main()
Funnily enough, a few months ago I'd made this track using instruments (as I normally do). But I had a drum kit loaded with a good 64+ chord and note stabs.
yeah its funny how just getting something you wouldn't normally do can inspire.
, I was trying to extend it but failed..it's got bugs
Code: Select all
def mutator_schema(hex_string):
# int(value, base [16=hex])
version = int(hex_string[:2], 16)
total_notes = int(hex_string[4:6], 16)
note_data = []
for i in range(len(hex_string) // 12):
# i think the about might be wrong ?
step = int(hex_string[6 + i * 12:8 + i * 12], 16)
pitch = int(hex_string[8 + i * 12:10 + i * 12], 16) - 1
velocity = int(hex_string[10 + i * 12:12 + i * 12], 16) - 1
lengthinsteps = int(hex_string[12 + i * 12:14 + i * 12], 16) - 1
# these are possibly less needed when converting from midi
flags = int(hex_string[14 + i * 12:16 + i * 12], 16)
mute = bool(flags & 32)
tied = bool(flags & 16)
octave_offset = flags & 7 - 3
note_data.append((step, pitch, velocity, lengthinsteps, mute, tied, octave_offset))
return version, total_notes, note_data
pattern_data_1 = "020110013D67028380023D5E028380033D63028380043D35028380053D6A028380063D54028380073D36028380083D69028380093D300283800A3D2E0283800B3D610283800C3D320283800D3D670283800E3D340283800F3D43028380"
version, total_notes, note_data = mutator_schema(pattern_data_1)
print(f'{version} {total_notes}')
for note in note_data:
step, pitch, velocity, lengthinsteps, mute, tied, octave_offset = note
print(f"Note: pos={step:02}, pitch={pitch}, velocity={velocity:03}, length={lengthinsteps}, mute={mute}, tied={tied}, octave_offset={octave_offset}")
Code: Select all
2 16
Note: pos=01, pitch=60, velocity=102, length=1, mute=False, tied=False, octave_offset=0
Note: pos=02, pitch=60, velocity=093, length=1, mute=False, tied=False, octave_offset=0
Note: pos=03, pitch=60, velocity=098, length=1, mute=False, tied=False, octave_offset=0
Note: pos=04, pitch=60, velocity=052, length=1, mute=False, tied=False, octave_offset=0
Note: pos=05, pitch=60, velocity=105, length=1, mute=False, tied=False, octave_offset=0
Note: pos=06, pitch=60, velocity=083, length=1, mute=False, tied=False, octave_offset=0
Note: pos=07, pitch=60, velocity=053, length=1, mute=False, tied=False, octave_offset=0
Note: pos=08, pitch=60, velocity=104, length=1, mute=False, tied=False, octave_offset=0
Note: pos=09, pitch=60, velocity=047, length=1, mute=False, tied=False, octave_offset=0
Note: pos=10, pitch=60, velocity=045, length=1, mute=False, tied=False, octave_offset=0
Note: pos=11, pitch=60, velocity=096, length=1, mute=False, tied=False, octave_offset=0
Note: pos=12, pitch=60, velocity=049, length=1, mute=False, tied=False, octave_offset=0
Note: pos=13, pitch=60, velocity=102, length=1, mute=False, tied=False, octave_offset=0
Note: pos=14, pitch=60, velocity=051, length=1, mute=False, tied=False, octave_offset=0
Note: pos=15, pitch=60, velocity=066, length=1, mute=False, tied=False, octave_offset=0
PS C:\Billy>
Code: Select all
pattern_data_1 = "020108013D65118380113D65098380193D650583801D3D650383801F3D65028380203D65028380213D65028380"
Code: Select all
2 7
Note: pos=01, pitch=60, velocity=100, length=16, mute=False, tied=False, octave_offset=0
Note: pos=17, pitch=60, velocity=100, length=8, mute=False, tied=False, octave_offset=0
Note: pos=25, pitch=60, velocity=100, length=4, mute=False, tied=False, octave_offset=0
Note: pos=29, pitch=60, velocity=100, length=2, mute=False, tied=False, octave_offset=0
Note: pos=31, pitch=60, velocity=100, length=1, mute=False, tied=False, octave_offset=0
Note: pos=32, pitch=60, velocity=100, length=1, mute=False, tied=False, octave_offset=0
Note: pos=33, pitch=60, velocity=100, length=1, mute=False, tied=False, octave_offset=0
Code: Select all
import sys
from mido import MidiFile
'''
Here is the pattern format for Mutator:
Pattern note data is packed into string properties in the RE "motherboard".
And as you have already guessed, string properties are stored in hex format in the .repatch file.
We can't use any 0 values in the data since it will terminate the string, so most values will range from 1 - 255 (01 - FF).
The first byte is the pattern data version, which is currently 2. (or "02" in hex). Don't change this ;)
The next two bytes represent the number of notes in the pattern, where the first byte is the most significant byte.
So if your pattern has 16 notes, the string sequence will be "0111" (remember that each byte must be offset by 1...)
A string property can hold a maximum of 2048 bytes, and each note uses 6 bytes,
so the maximum number of notes per pattern supported by Mutator is 340.
Next you have the data for each note in the pattern, and that is laid out as follows, per byte:
- Note start (in steps, 1 - 128)
- Note number (1 - 128, MIDI note number offset by 1)
- Note velocity (1 - 128, i e offset by 1)
- Note length (in steps, offset by 1)
- Note flags (see below)
- Length scale factor (in 1/254ths, offset by 1)
The note flags value is 128 + (32 if the note is muted) + (16 if the note is tied) + the octave offset (0 - 6 corresponds to -3 - +3.
the normal value is 3).
For a plain unmodified note, the flag value is 128 + 3 = 131, or "83" in hex.
The per-note mute and octave offset are not normally used by Mutator, they were inherited from Sequences.
But the playback engine supports them.
'''
ppqn = 0
whole_note_duration = 0
# could just be a version number, or might be more...
header = '0201'
# hex the step value!, update to count the line later....
steps = 16
# this defo has more meaning but for now it's all i got ;)
# giggle is no joke, it must have something to do with note lenght / off etc. (dump some midi from the repatch files you have ;))
giggle = '028380'
# first part of the string seams to be this ? on the small data set i have tested...
pattern_data_1 = f'{header}{steps:02X}'
def warningnotice():
try:
print("This script is almost certainty broken and should only be used if you accept responsibility")
agree = input("Do you argee (y/n): ")
if agree.lower() == "n":
print("Exiting the program...")
sys.exit(0)
except Exception as e:
print(f"An error occurred: {e}")
sys.exit(1)
def calculate_standard_notation(note_duration):
# Calculate the length of the note in standard notation
standard_notation = ""
if note_duration == whole_note_duration:
standard_notation = "Whole Note"
elif note_duration == whole_note_duration / 2:
standard_notation = "1/2 Note"
elif note_duration == whole_note_duration / 4:
standard_notation = "1/4 Note"
elif note_duration == whole_note_duration / 8:
standard_notation = "1/8 Note"
elif note_duration == whole_note_duration / 16:
standard_notation = "1/16 Note"
elif note_duration == whole_note_duration / 32:
standard_notation = "1/32 Note"
elif note_duration == whole_note_duration / 64:
standard_notation = "1/64 Note"
elif note_duration == whole_note_duration / 128:
standard_notation = "1/128 Note"
return standard_notation
def read_midi_file(file_path):
mid = MidiFile(file_path)
global ppqn
ppqn = mid.ticks_per_beat
global whole_note_duration
whole_note_duration = ppqn * 4
global pattern_data_1
pos = 1
offset = 1
for i, track in enumerate(mid.tracks):
if track.name > '':
print(f"Track [{i:03}] Name: {track.name}")
for j, msg in enumerate(track):
if msg.type == "note_on":
note_on_time = msg.time
note_off_time = track[j + 1].time
note_duration = note_off_time - note_on_time
standard_notation = calculate_standard_notation(note_duration)
if standard_notation >"":
print(f'{pos:02X} {msg.note} {msg.velocity}')
pattern_data_1 += f'{pos:02X}{msg.note+offset:02X}{msg.velocity+offset:02X}{str(giggle)}'
else:
pos +=1
print(f'{pos:02X} {msg.note} {msg.velocity}')
pattern_data_1 += f'{pos:02X}{msg.note+offset:02X}{msg.velocity+offset:02X}{str(giggle)}'
elif msg.type == "note_off":
pos += 1
# for now just print the string to screen, check solution (update_xml.py) for method of finding and writing to a repatch file ;)
print(f'\n{pattern_data_1}')
if len(sys.argv) > 1:
# i'm still working on this, so there is defo bugs....
warningnotice()
# once i'm happy this will be removed
read_midi_file(sys.argv[1])
else:
print("Please provide a random rhythm midi filename.")
# ;) notice ='random rhythm file'
I'm not sure if I understand what you mean by this?
Users browsing this forum: No registered users and 1 guest