Skip to main content
Glama
device_test.py.py30.9 kB
# device_test.py # name=Test Controller import transport import ui import midi import channels import playlist import patterns import arrangement import general import device import time import sys # Global variables running = True command_history = [] current_pattern = 0 current_channel = 0 terminal_active = False collecting_tempo_notes = False tempo_note_array = [] NOTE_TEMPO_START = 72 # C4 starts tempo note collection NOTE_TEMPO_END = 73 # C#4 ends collection and applies tempo change channel_to_edit = 0 step_to_edit = 0 def midi_notes_to_int(midi_notes): """ Convert an array of MIDI note values (7 bits each) into a single integer This function takes a list of MIDI notes and combines them into a single integer value, with the first note being the most significant. Args: midi_notes (list): A list of MIDI note values (each 0-127) Returns: int: The combined integer value """ result = 0 for note in midi_notes: # Ensure each value is within MIDI range (0-127) note_value = min(127, max(0, note)) # Shift left by 7 bits (MIDI values use 7 bits) and combine result = (result << 7) | note_value return result def OnInit(): """Called when the script is loaded by FL Studio""" print("FL Studio Terminal Beat Builder initialized") print("Type 'help' for a list of commands") return def OnDeInit(): """Called when the script is unloaded by FL Studio""" global running running = False # Signal the terminal thread to exit print("FL Studio Terminal Beat Builder deinitialized") return def OnRefresh(flags): """Called when FL Studio's state changes or when a refresh is needed""" # Update terminal with current state if needed return def OnMidiIn(event): """Called whenever the device sends a MIDI message to FL Studio""" #print(f"MIDI In - Status: {event.status}, Data1: {event.data1}, Data2: {event.data2}") return def change_tempo(bpm): """ Change the tempo in FL Studio to the specified BPM value Args: bpm (float): The desired tempo in beats per minute """ # FL Studio stores tempo as BPM * 1000 tempo_value = int(bpm * 1000) # Use processRECEvent to set the tempo # REC_Tempo is the event ID for tempo # REC_Control | REC_UpdateControl flags ensure the value is set and the UI updates general.processRECEvent( midi.REC_Tempo, tempo_value, midi.REC_Control | midi.REC_UpdateControl ) def process_received_midi(note, velocity): global current_note, current_velocity, current_length, current_position global decimal_state, decimal_value, decimal_target # Special MIDI commands DECIMAL_MARKER = 100 # Indicates next value is a decimal part LENGTH_MARKER = 101 # Next value affects length POSITION_MARKER = 102 # Next value affects position # Process based on message type if note == DECIMAL_MARKER: # Next value will be a decimal decimal_state = 1 return False elif note == LENGTH_MARKER: # Next value affects length decimal_target = "length" decimal_state = 0 decimal_value = 0 return False elif note == POSITION_MARKER: # Next value affects position decimal_target = "position" decimal_state = 0 decimal_value = 0 return False elif decimal_state == 1: # This is a decimal part value decimal_value = note / 10.0 # Convert to decimal (0-9 becomes 0.0-0.9) # Apply to the correct parameter if decimal_target == "length": current_length = (current_length or 0) + decimal_value print(f"Set length decimal: {current_length:.2f}") elif decimal_target == "position": current_position = (current_position or 0) + decimal_value print(f"Set position decimal: {current_position:.2f}") decimal_state = 0 return False elif decimal_target is not None: # This is a whole number part for a specific parameter if decimal_target == "length": current_length = float(note) print(f"Set length whole: {current_length:.2f}") elif decimal_target == "position": current_position = float(note) print(f"Set position whole: {current_position:.2f}") return False else: # This is a note value and velocity # Check if we have a complete previous note to add add_note = (current_note is not None and current_velocity is not None and current_length is not None and current_position is not None) # Start a new note current_note = note current_velocity = velocity # Use default values if not specified if current_length is None: current_length = 1.0 if current_position is None: current_position = 0.0 print(f"Started new note: {current_note}, velocity: {current_velocity}") return add_note def OnMidiMsg(event, timestamp=0): """Called when a processed MIDI message is received""" global receiving_mode, message_count, messages_received global current_note, current_velocity, current_length, current_position global decimal_state, decimal_target, midi_notes_array if 'receiving_mode' not in globals(): global receiving_mode receiving_mode = False if 'note_count' not in globals(): global note_count note_count = 0 if 'values_received' not in globals(): global values_received values_received = 0 if 'midi_data' not in globals(): global midi_data midi_data = [] if 'midi_notes_array' not in globals(): global midi_notes_array midi_notes_array = [] # Only process Note On messages with velocity > 0 if event.status >= midi.MIDI_NOTEON and event.status < midi.MIDI_NOTEON + 16 and event.data2 > 0: note_value = event.data1 # Toggle receiving mode with note 0 if note_value == 0 and not receiving_mode: receiving_mode = True print("Started receiving MIDI notes") midi_data = [] note_count = 0 values_received = 0 midi_notes_array = [] event.handled = True return # Only process further messages if in receiving mode if not receiving_mode: return # Second message is the note count if note_count == 0: note_count = note_value print(f"Expecting {note_count} notes") event.handled = True return # All subsequent messages are MIDI values (6 per note) midi_data.append(note_value) values_received += 1 # Process completed notes (every 6 values) if len(midi_data) >= 6 and len(midi_data) % 6 == 0: # Process the last complete note i = len(midi_data) - 6 note = midi_data[i] velocity = midi_data[i+1] length_whole = midi_data[i+2] length_decimal = midi_data[i+3] position_whole = midi_data[i+4] position_decimal = midi_data[i+5] # Calculate full values length = length_whole + (length_decimal / 10.0) position = position_whole + (position_decimal / 10.0) # Add to notes array midi_notes_array.append((note, velocity, length, position)) print(f"Added note: note={note}, velocity={velocity}, length={length:.1f}, position={position:.1f}") print(f"Current array size: {len(midi_notes_array)}") if len(midi_notes_array) >= note_count or note_value == 127: print(f"Received all {len(midi_notes_array)} notes or termination signal") receiving_mode = False # Only process if we have actual notes if midi_notes_array: # Print all collected notes print(f"Collected {len(midi_notes_array)} notes:") for i, (note, vel, length, pos) in enumerate(midi_notes_array): print(f" Note {i+1}: note={note}, velocity={vel}, length={length:.1f}, position={pos:.1f}") print("\nFinal array:") print(midi_notes_array) # Process the notes using the record_notes_batch function record_notes_batch(midi_notes_array) event.handled = True return # Check if we've received all expected notes # if len(midi_notes_array) >= note_count: # print(f"Received all {note_count} notes") # receiving_mode = False # # Print all collected notes # print(f"Collected {len(midi_notes_array)} notes:") # for i, (note, vel, length, pos) in enumerate(midi_notes_array): # print(f" Note {i+1}: note={note}, velocity={vel}, length={length:.1f}, position={pos:.1f}") # print("\nFinal array:") # print(midi_notes_array) # record_notes_batch(midi_notes_array) # # Process the notes here if needed # # record_notes_batch(midi_notes_array) # event.handled = True # elif note == 72: # collecting_tempo_notes = True # tempo_note_array = [] # print("Started collecting notes for tempo change") # event.handled = True # # End collection and apply tempo change # elif note == 73: # if collecting_tempo_notes and tempo_note_array: # bpm = change_tempo_from_notes(tempo_note_array) # print(f"Tempo changed to {bpm} BPM from collected notes: {tempo_note_array}") # collecting_tempo_notes = False # tempo_note_array = [] # else: # print("No tempo notes collected, tempo unchanged") # event.handled = True # # Collect notes for tempo if in collection mode # elif collecting_tempo_notes: # tempo_note_array.append(note) # print(f"Added note {note} to tempo collection, current array: {tempo_note_array}") # event.handled = True # Handle Control Change messages # elif event.status >= midi.MIDI_CONTROLCHANGE and event.status < midi.MIDI_CONTROLCHANGE + 16: # # CC 100: Select channel to edit # if event.data1 == 100: # channel_to_edit = event.data2 # channels.selectOneChannel(channel_to_edit) # print(f"Selected channel {channel_to_edit} for grid editing") # event.handled = True # # CC 110: Select step to edit # elif event.data1 == 110: # step_to_edit = event.data2 # print(f"Selected step {step_to_edit} for grid editing") # event.handled = True # # CC 111: Toggle step on/off # elif event.data1 == 111: # enabled = event.data2 > 0 # channels.setGridBit(channel_to_edit, step_to_edit, enabled) # print(f"Set grid bit for channel {channel_to_edit}, step {step_to_edit} to {enabled}") # commit_pattern_changes() # Force UI update # event.handled = True # # CC 112: Set step velocity/level # elif event.data1 == 112: # velocity = event.data2 # channels.setStepLevel(channel_to_edit, step_to_edit, velocity) # print(f"Set step level for channel {channel_to_edit}, step {step_to_edit} to {velocity}") # commit_pattern_changes() # Force UI update # event.handled = True # # Process other CC messages # else: # # Handle other CC messages with your existing code... # pass # # Handle other MIDI message types if needed # else: # # Process other MIDI message types # pass # Handle Note On messages with your existing code... # [rest of your existing OnMidiMsg function] #record_notes_batch(midi_notes_array) # Make sure your commit_pattern_changes function is defined: def commit_pattern_changes(pattern_num=None): """Force FL Studio to update the pattern data visually""" if pattern_num is None: pattern_num = patterns.patternNumber() # Force FL Studio to redraw and commit changes ui.crDisplayRect() # Force channel rack to update ui.setFocused(midi.widChannelRack) # Update playlist if needed playlist.refresh() def OnTransport(isPlaying): """Called when the transport state changes (play/stop)""" print(f"Transport state changed: {'Playing' if isPlaying else 'Stopped'}") return def OnTempoChange(tempo): """Called when the tempo changes""" print(f"Tempo changed to: {tempo} BPM") return # # Terminal interface functions # def start_terminal_thread(): # """Start a thread to handle terminal input""" # global terminal_active # if not terminal_active: # terminal_active = True # thread = threading.Thread(target=terminal_loop) # thread.daemon = True # thread.start() # def terminal_loop(): # """Main terminal input loop""" # global running, terminal_active # print("\n===== FL STUDIO TERMINAL BEAT BUILDER =====") # print("Enter commands to build your beat (type 'help' for commands)") # while running: # try: # command = input("\nFLBEAT> ") # command_history.append(command) # process_command(command) # except Exception as e: # print(f"Error processing command: {e}") # terminal_active = False def record_note(note=60, velocity=100, length_beats=1.0, position_beats=0.0, quantize=True): """ Records a single note to the piano roll synced with project tempo Args: note (int): MIDI note number (60 = middle C) velocity (int): Note velocity (0-127) length_beats (float): Length of note in beats (1.0 = quarter note) position_beats (float): Position to place note in beats from start quantize (bool): Whether to quantize the recording afterward """ # Make sure transport is stopped first if transport.isPlaying(): transport.stop() # Get the current channel channel = channels.selectedChannel() # Get the project's PPQ (pulses per quarter note) ppq = general.getRecPPQ() # Calculate ticks based on beats length_ticks = int(length_beats * ppq) position_ticks = int(position_beats * ppq) # Set playback position transport.setSongPos(position_ticks, 2) # 2 = SONGLENGTH_ABSTICKS # Toggle recording mode if needed if not transport.isRecording(): transport.record() print(f"Recording note {note} to channel {channel}") print(f"Position: {position_beats} beats, Length: {length_beats} beats") # Calculate the exact tick positions where we need to place note and note-off start_tick = position_ticks end_tick = start_tick + length_ticks # Start playback to begin recording transport.start() # Record the note at the exact start position channels.midiNoteOn(channel, note, velocity) # Get the current tempo (BPM) tempo = 120 # Default fallback # Try to get actual tempo from the project try: import mixer tempo = mixer.getCurrentTempo() tempo = tempo/1000 print(f"Using project tempo: {tempo} BPM") except (ImportError, AttributeError): print("Using default tempo: 120 BPM") # Calculate the time to wait in seconds seconds_to_wait = (length_beats * 60) / tempo print(f"Waiting for {seconds_to_wait:.2f} seconds...") # Wait the calculated time time.sleep(seconds_to_wait) # Send note-off event channels.midiNoteOn(channel, note, 0) # Stop playback transport.stop() # Exit recording mode if it was active if transport.isRecording(): transport.record() # Quantize if requested if quantize: channels.quickQuantize(channel) print("Recording quantized") print(f"Note {note} recorded to piano roll") # Return to beginning transport.setSongPos(0, 2) def rec_hihat_pattern(): """ Records a predefined hi-hat pattern to the piano roll using record_notes_batch This creates a 4-bar hi-hat pattern with variations in velocity, rhythm, and types of hats """ # Stop playback and rewind to beginning first if transport.isPlaying(): transport.stop() transport.setSongPos(0, 2) # Go to the beginning print("Recording hi-hat pattern...") # Common hi-hat MIDI notes: # 42 = Closed hi-hat # 44 = Pedal hi-hat # 46 = Open hi-hat # Define the pattern as a list of notes # Each tuple contains (note, velocity, length_beats, position_beats) hihat_pattern = [ # BAR 1 - Basic pattern (42, 90, 0.1, 0.0), # Closed hat on beat 1 (42, 65, 0.1, 0.5), # Closed hat on off-beat (42, 90, 0.1, 1.0), # Closed hat on beat 2 (42, 65, 0.1, 1.5), # Closed hat on off-beat (42, 90, 0.1, 2.0), # Closed hat on beat 3 (42, 65, 0.1, 2.5), # Closed hat on off-beat (42, 90, 0.1, 3.0), # Closed hat on beat 4 (42, 65, 0.1, 3.5), # Closed hat on off-beat # BAR 2 - Adding 16th notes (42, 90, 0.1, 4.0), # Closed hat on beat 1 (42, 60, 0.1, 4.25), # Closed hat on 16th (42, 70, 0.1, 4.5), # Closed hat on off-beat (42, 60, 0.1, 4.75), # Closed hat on 16th (42, 90, 0.1, 5.0), # Closed hat on beat 2 (42, 60, 0.1, 5.25), # Closed hat on 16th (42, 70, 0.1, 5.5), # Closed hat on off-beat (42, 60, 0.1, 5.75), # Closed hat on 16th (42, 90, 0.1, 6.0), # Closed hat on beat 3 (42, 60, 0.1, 6.25), # Closed hat on 16th (42, 70, 0.1, 6.5), # Closed hat on off-beat (42, 60, 0.1, 6.75), # Closed hat on 16th (42, 90, 0.1, 7.0), # Closed hat on beat 4 (46, 80, 0.2, 7.5), # Open hat on off-beat # BAR 3 - Mixing closed and open hats (42, 100, 0.1, 8.0), # Closed hat on beat 1 (42, 70, 0.1, 8.5), # Closed hat on off-beat (46, 85, 0.2, 9.0), # Open hat on beat 2 (42, 70, 0.1, 9.5), # Closed hat on off-beat (42, 95, 0.1, 10.0), # Closed hat on beat 3 (42, 70, 0.1, 10.5), # Closed hat on off-beat (46, 85, 0.2, 11.0), # Open hat on beat 4 # Triplet fill at the end of bar 3 (42, 80, 0.08, 11.33), # Closed hat - triplet 1 (42, 85, 0.08, 11.66), # Closed hat - triplet 2 (42, 90, 0.08, 11.99), # Closed hat - triplet 3 # BAR 4 - Complex pattern with pedal hats (42, 100, 0.1, 12.0), # Closed hat on beat 1 (44, 75, 0.1, 12.25), # Pedal hat on 16th (42, 80, 0.1, 12.5), # Closed hat on off-beat (44, 70, 0.1, 12.75), # Pedal hat on 16th (42, 90, 0.1, 13.0), # Closed hat on beat 2 (46, 85, 0.3, 13.5), # Open hat on off-beat # Beat 3-4: Building intensity (42, 95, 0.1, 14.0), # Closed hat on beat 3 (42, 75, 0.1, 14.25), # Closed hat on 16th (42, 85, 0.1, 14.5), # Closed hat on off-beat (42, 80, 0.1, 14.75), # Closed hat on 16th # Final fill (42, 85, 0.05, 15.0), # Closed hat - 32nd note 1 (42, 90, 0.05, 15.125), # Closed hat - 32nd note 2 (42, 95, 0.05, 15.25), # Closed hat - 32nd note 3 (42, 100, 0.05, 15.375),# Closed hat - 32nd note 4 (42, 105, 0.05, 15.5), # Closed hat - 32nd note 5 (42, 110, 0.05, 15.625),# Closed hat - 32nd note 6 (42, 115, 0.05, 15.75), # Closed hat - 32nd note 7 (46, 120, 0.25, 15.875),# Open hat - final accent ] # Record the hi-hat pattern using the batch recording function record_notes_batch(hihat_pattern) print("Hi-hat pattern recording complete!") # Quantize the hi-hat pattern channel = channels.selectedChannel() channels.quickQuantize(channel) # Return to beginning transport.setSongPos(0, 2) def record_notes_batch(notes_array): """ Records a batch of notes to FL Studio, handling simultaneous notes properly Args: notes_array: List of tuples, each containing (note, velocity, length_beats, position_beats) """ # Sort notes by their starting position sorted_notes = sorted(notes_array, key=lambda x: x[3]) # Group notes by their starting positions position_groups = {} for note in sorted_notes: position = note[3] # position_beats is the 4th element (index 3) if position not in position_groups: position_groups[position] = [] position_groups[position].append(note) # Process each position group positions = sorted(position_groups.keys()) for position in positions: notes_at_position = position_groups[position] # Find the longest note in this group to determine recording length max_length = max(note[2] for note in notes_at_position) # Make sure transport is stopped first if transport.isPlaying(): transport.stop() # Get the current channel channel = channels.selectedChannel() # Get the project's PPQ (pulses per quarter note) ppq = general.getRecPPQ() # Calculate ticks based on beats position_ticks = int(position * ppq) # Set playback position transport.setSongPos(position_ticks, 2) # 2 = SONGLENGTH_ABSTICKS # Toggle recording mode if needed if not transport.isRecording(): transport.record() print(f"Recording {len(notes_at_position)} simultaneous notes at position {position}") # Start playback to begin recording transport.start() # Record all notes at this position simultaneously for note, velocity, length, _ in notes_at_position: channels.midiNoteOn(channel, note, velocity) # Get the current tempo try: import mixer tempo = mixer.getCurrentTempo() tempo = tempo/1000 except (ImportError, AttributeError): tempo = 120 # Default fallback print(f"Using tempo: {tempo} BPM") # Calculate the time to wait in seconds based on the longest note seconds_to_wait = (max_length * 60) / tempo print(f"Waiting for {seconds_to_wait:.2f} seconds...") # Wait the calculated time time.sleep(seconds_to_wait) # Send note-off events for all notes for note, _, _, _ in notes_at_position: channels.midiNoteOn(channel, note, 0) # Stop playback transport.stop() # Exit recording mode if it was active if transport.isRecording(): transport.record() # Small pause between recordings to avoid potential issues time.sleep(0.2) print("All notes recorded successfully") # Return to beginning transport.setSongPos(0, 2) def rec_melody(): """ Records a predefined melody to the piano roll by calling record_notes_batch The melody is a robust 4-bar composition with melody notes and chord accompaniment """ # Stop playback and rewind to beginning first if transport.isPlaying(): transport.stop() transport.setSongPos(0, 2) # Go to the beginning print("Recording melody...") # Define the melody as a list of notes # Each tuple contains (note, velocity, length_beats, position_beats) melody = [ # BAR 1 # Beat 1: C major chord (60, 100, 1.0, 0.0), # C4 - root note (64, 85, 1.0, 0.0), # E4 - chord tone (67, 80, 1.0, 0.0), # G4 - chord tone # Beat 1.5: Melody note (72, 110, 0.5, 0.5), # C5 - melody # Beat 2: G7 chord (55, 90, 1.0, 1.0), # G3 - bass note (59, 75, 1.0, 1.0), # B3 - chord tone (62, 75, 1.0, 1.0), # D4 - chord tone (65, 75, 1.0, 1.0), # F4 - chord tone # Beat 2.5-3: Melody phrase (71, 105, 0.25, 1.5), # B4 - melody (69, 95, 0.25, 1.75), # A4 - melody (67, 90, 0.5, 2.0), # G4 - melody # Beat 3: C major chord (48, 95, 1.0, 2.0), # C3 - bass note (64, 75, 1.0, 2.0), # E4 - chord tone (67, 75, 1.0, 2.0), # G4 - chord tone # Beat 4: Melody fill (64, 100, 0.5, 3.0), # E4 - melody (65, 90, 0.25, 3.5), # F4 - melody (67, 95, 0.25, 3.75), # G4 - melody # BAR 2 # Beat 1: Am chord (57, 95, 1.0, 4.0), # A3 - bass note (60, 80, 1.0, 4.0), # C4 - chord tone (64, 80, 1.0, 4.0), # E4 - chord tone # Beat 1-2: Melody note (69, 110, 0.75, 4.0), # A4 - melody (67, 90, 0.25, 4.75), # G4 - melody # Beat 2: F major chord (53, 90, 1.0, 5.0), # F3 - bass note (57, 75, 1.0, 5.0), # A3 - chord tone (60, 75, 1.0, 5.0), # C4 - chord tone # Beat 2.5-3: Melody (65, 100, 0.5, 5.5), # F4 - melody (64, 90, 0.5, 6.0), # E4 - melody # Beat 3: G7 chord (55, 95, 1.0, 6.0), # G3 - bass note (59, 80, 1.0, 6.0), # B3 - chord tone (62, 80, 1.0, 6.0), # D4 - chord tone # Beat 3.5-4: Melody fill (62, 100, 0.25, 6.5), # D4 - melody (64, 95, 0.25, 6.75), # E4 - melody (65, 90, 0.25, 7.0), # F4 - melody (67, 105, 0.75, 7.25), # G4 - melody # BAR 3 # Beat 1: C major chord (48, 100, 1.0, 8.0), # C3 - bass note (60, 85, 1.0, 8.0), # C4 - chord tone (64, 85, 1.0, 8.0), # E4 - chord tone (67, 85, 1.0, 8.0), # G4 - chord tone # Beat 1-2: Melody (72, 110, 1.0, 8.0), # C5 - melody # Beat 2: Em chord (52, 90, 1.0, 9.0), # E3 - bass note (59, 75, 1.0, 9.0), # B3 - chord tone (64, 75, 1.0, 9.0), # E4 - chord tone # Beat 2.5-3.5: Melody run (71, 105, 0.25, 9.5), # B4 - melody (72, 100, 0.25, 9.75), # C5 - melody (74, 110, 0.5, 10.0), # D5 - melody (76, 115, 0.5, 10.5), # E5 - melody # Beat 3: Am chord (57, 95, 1.0, 10.0), # A3 - bass note (60, 80, 1.0, 10.0), # C4 - chord tone (64, 80, 1.0, 10.0), # E4 - chord tone # Beat 4: Descending run (74, 100, 0.25, 11.0), # D5 - melody (72, 95, 0.25, 11.25), # C5 - melody (71, 90, 0.25, 11.5), # B4 - melody (69, 85, 0.25, 11.75), # A4 - melody # BAR 4 # Beat 1: F major chord (53, 95, 1.0, 12.0), # F3 - bass note (60, 80, 1.0, 12.0), # C4 - chord tone (65, 80, 1.0, 12.0), # F4 - chord tone # Beat 1-2: Melody (67, 100, 1.0, 12.0), # G4 - melody # Beat 2: G7 chord (55, 90, 1.0, 13.0), # G3 - bass note (59, 75, 1.0, 13.0), # B3 - chord tone (62, 75, 1.0, 13.0), # D4 - chord tone # Beat 2-3: Melody (65, 95, 0.5, 13.0), # F4 - melody (64, 90, 0.5, 13.5), # E4 - melody # Beat 3-4: Final C major chord (48, 110, 2.0, 14.0), # C3 - bass note (60, 95, 2.0, 14.0), # C4 - chord tone (64, 95, 2.0, 14.0), # E4 - chord tone (67, 95, 2.0, 14.0), # G4 - chord tone # Final melody note (72, 120, 2.0, 14.0), # C5 - melody final note ] # Record the melody using the batch recording function record_notes_batch(melody) print("Melody recording complete!") def change_tempo_from_notes(note_array): """ Change the tempo in FL Studio based on an array of MIDI notes This function converts an array of MIDI notes to a single integer value and uses that value as the new tempo. Args: note_array (list): A list of MIDI note values (each 0-127) """ # Convert note array to integer bpm_value = midi_notes_to_int(note_array) # Limit to a reasonable BPM range if bpm_value < 20: bpm_value = 20 # Minimum reasonable tempo elif bpm_value > 999: bpm_value = 999 # Maximum reasonable tempo # Change the tempo print(f"Changing tempo to {bpm_value} BPM from note array {note_array}") change_tempo(bpm_value) return bpm_value # Start the terminal interface when loaded in FL Studio # No need to call this explicitly as OnInit will be called by FL Studio

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/veenastudio/flstudio-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server