-
Notifications
You must be signed in to change notification settings - Fork 261
1. Add the ability to record a Session Clip into Arrangement View and 2. Switch between Session and Arrangement Views #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughThe changes add two new command types— Changes
Sequence Diagram(s)sequenceDiagram
participant U as User
participant S as Server
participant R as Remote Script
participant A as Ableton Live
U->>S: Call record_arrangement_clip(track_index, start_time, length, clip_index, clip_name)
S->>A: send "record_arrangement_clip" command
A->>R: Process command in _process_command
R->>R: Execute _get_clip_slot_by_name & _record_clip_to_arrangement
R->>A: Arm track & record clip in Arrangement view
A-->>R: Return recording confirmation
R-->>S: Acknowledge successful command execution
S-->>U: Return success response
sequenceDiagram
participant U as User
participant S as Server
participant R as Remote Script
participant A as Ableton Live
U->>S: Call switch_to_view(view_name)
S->>A: send "switch_to_view" command
A->>R: Process command in _process_command
R->>R: Execute _switch_to_view to change view (Session/Arranger)
R->>A: Update Ableton view state
A-->>R: Confirm view switch
R-->>S: Acknowledge successful command execution
S-->>U: Return success response
Poem
Tip ⚡💬 Agentic Chat (Pro Plan, General Availability)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
|||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
AbletonMCP_Remote_Script/__init__.py (1)
501-536: Consider a more asynchronous approach for waiting during recording.The current implementation uses
time.sleep()to wait for the recording to complete, which blocks the thread and could potentially cause the UI to freeze or prevent other commands from being processed during this time.Consider implementing a more asynchronous approach or at least documenting this blocking behavior. For example, you could set up a timer callback instead of using sleep:
def _record_clip_to_arrangement(self, track_index, start_time, length, clip_index, clip_name): """Record a new Arrangement MIDI clip in the Arrangement View at the specified time.""" try: track = self.song().tracks[track_index] if track.can_be_armed and not track.arm: track.arm = True clip_slot = None if clip_name: clip_slot = self._get_clip_slot_by_name(track, clip_name) else: clip_slot = track.clip_slots[clip_index] clip_slot.fire() # Start Recording the Session Clip into Arrangement View self._song.record_mode = True self._song.start_playing() self._song.is_playing = True self._song.start_time = start_time # Calculate the duration in milliseconds duration_ms = int(length * 60.0 / self._song.tempo * 1000) # Use schedule_message instead of sleep def stop_recording(): self._song.is_playing = False self._song.stop_playing() self._song.record_mode = False clip_slot.stop() self.schedule_message(duration_ms, stop_recording) result = { "created_arrangement_clip": True } return result except Exception as e: self.log_message("Error creating arrangement clip: " + str(e)) raise
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
AbletonMCP_Remote_Script/__init__.py(4 hunks)MCP_Server/server.py(3 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
MCP_Server/server.py
673-673: f-string without any placeholders
Remove extraneous f prefix
(F541)
705-705: Local variable result is assigned to but never used
Remove assignment to unused variable result
(F841)
🔇 Additional comments (6)
MCP_Server/server.py (2)
108-109: Good addition to the state-modifying commands list.The new command types "record_arrangement_clip" and "switch_to_view" have been properly added to the list of state-modifying commands, which ensures appropriate timeouts and delays will be applied during communication with Ableton.
340-341: Good addition to ensure consistent view state.Adding the view switching after clip creation ensures users always end up in Session View after creating a clip, providing a consistent experience aligned with the PR objectives.
AbletonMCP_Remote_Script/__init__.py (4)
233-233: Good addition to the command processing list.The new command types "record_arrangement_clip" and "switch_to_view" have been properly added to the list of commands processed on the main thread, which is important for thread safety.
286-295: Good implementation of the new command handler.The code correctly extracts all necessary parameters and calls the appropriate method to handle the record arrangement clip functionality.
495-499: Well-designed helper method.The
_get_clip_slot_by_namemethod provides a clean way to find a clip slot by name, which is a useful utility for the record arrangement clip functionality.
537-552: Good implementation for view switching.The
_switch_to_viewmethod properly handles switching between Session and Arrangement views, focusing the appropriate view and updating the song'sback_to_arrangerproperty.
| @mcp.tool() | ||
| def switch_to_view(ctx: Context, view_name: str = "Arranger") -> str: | ||
| """ | ||
| Switch between Arrangement (called Arranger) and Session View | ||
| Parameters: | ||
| - view_name: Either "Arranger" or "Session" | ||
| """ | ||
| try: | ||
|
|
||
| ableton = get_ableton_connection() | ||
|
|
||
| # Validate view name | ||
| view_name = view_name.capitalize() | ||
| if view_name not in ["Arranger", "Session"]: | ||
| return f"Invalid view name. Must be either 'Arrangement' or 'Session'" | ||
|
|
||
| result = ableton.send_command("switch_to_view", { | ||
| "view_name": view_name | ||
| }) | ||
|
|
||
| return f"Switched to {view_name} view. Result: {result}" | ||
| except Exception as e: | ||
| logger.error(f"Error switching view: {str(e)}") | ||
| return f"Error switching view: {str(e)}" | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix the f-string and error message consistency.
There are two issues in this implementation:
- Line 673 uses an f-string without any variables
- The error message mentions "Arrangement" but the code checks for "Arranger" which could confuse users
- if view_name not in ["Arranger", "Session"]:
- return f"Invalid view name. Must be either 'Arrangement' or 'Session'"
+ if view_name not in ["Arranger", "Session"]:
+ return "Invalid view name. Must be either 'Arranger' or 'Session'"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @mcp.tool() | |
| def switch_to_view(ctx: Context, view_name: str = "Arranger") -> str: | |
| """ | |
| Switch between Arrangement (called Arranger) and Session View | |
| Parameters: | |
| - view_name: Either "Arranger" or "Session" | |
| """ | |
| try: | |
| ableton = get_ableton_connection() | |
| # Validate view name | |
| view_name = view_name.capitalize() | |
| if view_name not in ["Arranger", "Session"]: | |
| return f"Invalid view name. Must be either 'Arrangement' or 'Session'" | |
| result = ableton.send_command("switch_to_view", { | |
| "view_name": view_name | |
| }) | |
| return f"Switched to {view_name} view. Result: {result}" | |
| except Exception as e: | |
| logger.error(f"Error switching view: {str(e)}") | |
| return f"Error switching view: {str(e)}" | |
| @mcp.tool() | |
| def switch_to_view(ctx: Context, view_name: str = "Arranger") -> str: | |
| """ | |
| Switch between Arrangement (called Arranger) and Session View | |
| Parameters: | |
| - view_name: Either "Arranger" or "Session" | |
| """ | |
| try: | |
| ableton = get_ableton_connection() | |
| # Validate view name | |
| view_name = view_name.capitalize() | |
| if view_name not in ["Arranger", "Session"]: | |
| return "Invalid view name. Must be either 'Arranger' or 'Session'" | |
| result = ableton.send_command("switch_to_view", { | |
| "view_name": view_name | |
| }) | |
| return f"Switched to {view_name} view. Result: {result}" | |
| except Exception as e: | |
| logger.error(f"Error switching view: {str(e)}") | |
| return f"Error switching view: {str(e)}" |
🧰 Tools
🪛 Ruff (0.8.2)
673-673: f-string without any placeholders
Remove extraneous f prefix
(F541)
| @mcp.tool() | ||
| def record_arrangement_clip( | ||
| ctx: Context, | ||
| track_index: int, | ||
| start_time: float = 0.0, | ||
| length: float = 4.0, | ||
| clip_index: int = -1, | ||
| clip_name: str = "" | ||
| ) -> str: | ||
| """ | ||
| Record into the Arrangement View from the Session View | ||
| Parameters: | ||
| - track_index: Which track to create the clip on | ||
| - start_time: Start time in beats | ||
| - length: Length of the clip in beats | ||
| """ | ||
| try: | ||
| ableton = get_ableton_connection() | ||
|
|
||
| # Create the clip in arrangement view | ||
| result = ableton.send_command("record_arrangement_clip", { | ||
| "track_index": track_index, | ||
| "start_time": start_time, | ||
| "length": length, | ||
| "clip_index": clip_index, | ||
| "clip_name": clip_name | ||
| }) | ||
|
|
||
| # Switch to Arrangement View | ||
| ableton.send_command("switch_to_view", {"view_name": "Arranger"}) | ||
|
|
||
| return f"Created arrangement clip from Track ${track_index}" | ||
| except Exception as e: | ||
| logger.error(f"Error creating arrangement clip: {str(e)}") | ||
| return f"Error creating arrangement clip: {str(e)}" | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix string interpolation and unused variable.
There are two issues in this function:
- The result variable on line 705 is assigned but never used, which could indicate a missing check or operation
- On line 716, the string interpolation uses
$syntax instead of Python's f-string{}syntax
- # Create the clip in arrangement view
- result = ableton.send_command("record_arrangement_clip", {
+ # Create the clip in arrangement view and check result
+ arrangement_result = ableton.send_command("record_arrangement_clip", {
"track_index": track_index,
"start_time": start_time,
"length": length,
"clip_index": clip_index,
"clip_name": clip_name
})
# Switch to Arrangement View
ableton.send_command("switch_to_view", {"view_name": "Arranger"})
- return f"Created arrangement clip from Track ${track_index}"
+ return f"Created arrangement clip from Track {track_index}"Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 Ruff (0.8.2)
705-705: Local variable result is assigned to but never used
Remove assignment to unused variable result
(F841)
User description
Primary Motivations: I mainly work in Arrangement View when I make a song so I wanted to add two convenience methods to the MCP. I also think that if you're creating a Session Clip its easier for the user to see what's happening if you switch to the Session View since most people start in Arrangement View by default.
Switch between Session and Arrangement View Demonstration:
video link: https://drive.google.com/file/d/1Qr8NponBHczc7exToRxN30X9mc8_4KrM/view?usp=drive_link
Record a Session Clip into Arrangement View Demonstration:
video link: https://drive.google.com/file/d/1qIOPs7p4QXiCVZpaZ6RegrE3avzKqAdC/view?usp=drive_link
This way you can
This allows Claude to potentially chain the two actions together (create a musical idea in Session View and then automatically recording it into Arrangement Mode)
PR Type
Enhancement
Description
Added functionality to switch between Session and Arrangement views.
Implemented recording of Session Clips into Arrangement View.
Enhanced command handling with new commands:
record_arrangement_clipandswitch_to_view.Updated server-side tools to support new view-switching and recording features.
Changes walkthrough 📝
__init__.py
Introduced commands for view switching and clip recordingAbletonMCP_Remote_Script/init.py
record_arrangement_clipandswitch_to_viewcommands._record_clip_to_arrangementfor recording clips intoArrangement View.
_switch_to_viewfor toggling between Session and Arrangementviews.
server.py
Added tools for view switching and clip recordingMCP_Server/server.py
switch_to_viewtool for toggling views.record_arrangement_cliptool for recording Session Clips.create_clipto switch to Session View after creation.Summary by CodeRabbit