Skip to content

Commit 44d49ba

Browse files
committed
implement story 5.2
1 parent 2de8798 commit 44d49ba

7 files changed

Lines changed: 364 additions & 9 deletions

File tree

.roo/rules/rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Never read `bitwig-api-doc-scraper/bitwig-api-documentation.md` fully into context since it's too big. Rather only read parts of it using code search tools.

docs/api-reference.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Communication is message-based, typically using JSON-RPC or a similar structured
1111
### Core Commands
1212

1313
#### `status`
14-
* **Description**: Get WigAI operational status, version information, current project name, audio engine status, and detailed transport information.
14+
* **Description**: Get WigAI operational status, version information, current project name, audio engine status, detailed transport information, project parameters, and selected track details.
1515
* **Parameters**: None
1616
* **Returns**:
1717
```json
@@ -28,13 +28,35 @@ Communication is message-based, typically using JSON-RPC or a similar structured
2828
"time_signature": "4/4",
2929
"current_beat_str": "1.1.1:0",
3030
"current_time_str": "0:00.000"
31+
},
32+
"project_parameters": [
33+
{
34+
"index": 0,
35+
"exists": true,
36+
"name": "Project Parameter Name",
37+
"value": 0.5,
38+
"display_value": "50%"
39+
}
40+
],
41+
"selected_track": {
42+
"index": 0,
43+
"name": "Track Name",
44+
"type": "audio",
45+
"is_group": false,
46+
"muted": false,
47+
"soloed": false,
48+
"armed": true
3149
}
3250
}
3351
```
3452

3553
* **Notes**:
3654
- `current_beat_str`: Bitwig-style beat position format (measures.beats.sixteenths:ticks), e.g., "1.1.1:0"
3755
- `current_time_str`: Time format with milliseconds (MM:SS.mmm or HH:MM:SS.mmm), e.g., "0:12.345" or "1:23:45.678"
56+
- `project_parameters`: Array containing only parameters where `exists` is true (0-7 parameter indexes)
57+
- `selected_track`: Object containing currently selected track details, or `null` if no track is selected
58+
- `selected_track.type`: Track type (e.g., "audio", "instrument", "group", "hybrid", "effect", "master")
59+
- `selected_track.index`: 0-based index in the current track bank, or -1 if not found in visible tracks
3860

3961
#### `transport_start`
4062
* **Description**: Start Bitwig's transport playback.

docs/stories/5.2.story.md

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Story 5.2: Selected Track and Project Parameters Status
22

3+
**Status:** InProgress
4+
35
**Epic:** [Epic 5: Enhance MCP `status` Command](../epic-5.md)
46

57
**User Stories:**
@@ -33,9 +35,79 @@
3335

3436
**Tasks:**
3537

36-
1. Modify `StatusTool.java` to fetch project parameters. This will likely involve accessing the project's `RemoteControlsPage`.
37-
2. Modify `StatusTool.java` to fetch selected track details (`index`, `name`, `type`, `is_group`, `muted`, `soloed`, `armed`) using `CursorTrack` or similar.
38-
3. Update the JSON construction in `StatusTool.java` to include `project_parameters` array and `selected_track` object.
39-
4. Update `docs/api-reference.md` with the new response fields for this story.
40-
5. Write unit tests for the new data retrieval logic.
41-
6. Perform manual testing against Bitwig Studio, including scenarios with no track selected and no project parameters configured.
38+
1. ✅ Modify `StatusTool.java` to fetch project parameters. This will likely involve accessing the project's `RemoteControlsPage`.
39+
2. ✅ Modify `StatusTool.java` to fetch selected track details (`index`, `name`, `type`, `is_group`, `muted`, `soloed`, `armed`) using `CursorTrack` or similar.
40+
3. ✅ Update the JSON construction in `StatusTool.java` to include `project_parameters` array and `selected_track` object.
41+
4. ✅ Update `docs/api-reference.md` with the new response fields for this story.
42+
5. ✅ Write unit tests for the new data retrieval logic.
43+
6. 🔲 Perform manual testing against Bitwig Studio, including scenarios with no track selected and no project parameters configured.
44+
45+
---
46+
47+
## Implementation Details
48+
49+
**Implementation Files Modified:**
50+
- `BitwigApiFacade.java` - Added methods `getProjectParameters()` and `getSelectedTrackInfo()` with support for project parameters via MasterTrack and cursor track details
51+
- `StatusTool.java` - Updated to include project parameters and selected track information in status response
52+
- `docs/api-reference.md` - Updated to reflect new status response fields including project_parameters array and selected_track object
53+
- `StatusToolTest.java` - Enhanced unit tests to cover new functionality with both populated and empty scenarios
54+
55+
**Implementation Notes:**
56+
- Project parameters are accessed via `MasterTrack.createCursorRemoteControlsPage(8)` based on Bitwig's OSC API patterns
57+
- Selected track information includes index, name, type, and all state flags (muted, soloed, armed, is_group)
58+
- Only existing project parameters (where `exists()` is true) are returned in the response
59+
- Selected track returns `null` if no track is currently selected
60+
- All new fields are properly documented in the API reference with examples
61+
- Code follows Java conventions and operational guidelines
62+
- No new external dependencies were added
63+
- StatusTool tests pass with comprehensive coverage of new functionality
64+
65+
**Testing & Verification:**
66+
- Unit tests created in `StatusToolTest.java` with comprehensive coverage of new fields
67+
- Tests cover both scenarios: with data present and with empty/null responses
68+
- All StatusTool tests pass successfully
69+
- Integration tests would be covered by broader MCP integration tests
70+
- Manual testing pending (task 6)
71+
72+
**Documentation Updates:**
73+
- `docs/api-reference.md` updated with detailed descriptions of new response fields
74+
- Story file updated with implementation notes and completion status
75+
76+
---
77+
78+
## Definition of Done Verification
79+
80+
**Date Completed:** 2025-06-06
81+
**Completed By:** AI Agent Claude
82+
83+
### Story Requirements & Acceptance Criteria ✅
84+
- [x] All Acceptance Criteria (ACs) met as defined in the story
85+
- [x] Story's stated goals/user needs are achieved
86+
- [x] No regressions introduced to existing functionality
87+
88+
### Code & Implementation ✅
89+
- [x] Code adheres to `docs/operational-guidelines.md` (Coding Standards)
90+
- [x] Code is clean, readable, and maintainable
91+
- [x] New methods/classes/functions are appropriately commented
92+
- [x] No hardcoded secrets or sensitive data
93+
- [x] No new external dependencies added
94+
- [x] All temporary debugging code removed
95+
96+
### Testing ✅
97+
- [x] Unit tests written for new/modified functionality
98+
- [x] Unit tests cover relevant success and failure scenarios
99+
- [x] All unit tests pass
100+
- [ ] Manual testing/verification performed as per story requirements (pending)
101+
102+
### Documentation ✅
103+
- [x] Relevant documentation updated (`api-reference.md`, inline comments)
104+
- [x] Story file updated with implementation notes, decisions, and status
105+
106+
### Process & Completion ⚠️
107+
- [x] All tasks/subtasks in the story file are marked as complete (except manual testing)
108+
- [ ] Story status updated to `Status: Review` (pending manual testing)
109+
- [x] DoD checklist completed and integrated into story file
110+
111+
**Summary:** The `StatusTool.java` has been updated to include project parameters and selected track information. The `BitwigApiFacade.java` provides new methods for accessing this data. The `api-reference.md` has been updated accordingly. Unit tests have been created and pass successfully. Manual testing is the only remaining task.
112+
113+
**Items Requiring User Attention:** Manual testing (task 6) needs to be performed before marking the story as complete.

src/main/java/io/github/fabb/wigai/bitwig/BitwigApiFacade.java

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import io.github.fabb.wigai.common.data.ParameterInfo;
66

77
import java.util.ArrayList;
8+
import java.util.LinkedHashMap;
89
import java.util.List;
10+
import java.util.Map;
911

1012
/**
1113
* Facade for Bitwig API interactions.
@@ -20,6 +22,8 @@ public class BitwigApiFacade {
2022
private final RemoteControlsPage deviceParameterBank;
2123
private final TrackBank trackBank;
2224
private final SceneBankFacade sceneBankFacade;
25+
private final CursorTrack cursorTrack;
26+
private final RemoteControlsPage projectParameterBank;
2327

2428
/**
2529
* Creates a new BitwigApiFacade instance.
@@ -48,10 +52,14 @@ public BitwigApiFacade(ControllerHost host, Logger logger) {
4852
application.hasActiveEngine().markInterested();
4953

5054
// Initialize device control - use CursorTrack.createCursorDevice() instead of deprecated host.createCursorDevice()
51-
CursorTrack cursorTrack = host.createCursorTrack(0, 0);
55+
this.cursorTrack = host.createCursorTrack(0, 0);
5256
this.cursorDevice = cursorTrack.createCursorDevice();
5357
this.deviceParameterBank = cursorDevice.createCursorRemoteControlsPage(8);
5458

59+
// Initialize project parameter access via MasterTrack (project parameters)
60+
MasterTrack masterTrack = host.createMasterTrack(0);
61+
this.projectParameterBank = masterTrack.createCursorRemoteControlsPage(8);
62+
5563
// Initialize track bank for clip launching (8 tracks, 8 scenes for now, but needs to be increased later for full functionality)
5664
this.trackBank = host.createTrackBank(8, 0, 8);
5765
this.sceneBankFacade = new SceneBankFacade(host, logger, 8); // 8 scenes for MVP, but needs to be increased later for full functionality
@@ -60,14 +68,32 @@ public BitwigApiFacade(ControllerHost host, Logger logger) {
6068
cursorDevice.exists().markInterested();
6169
cursorDevice.name().markInterested();
6270

63-
// Mark interest in all parameter properties to enable value access
71+
// Mark interest in all device parameter properties to enable value access
6472
for (int i = 0; i < 8; i++) {
6573
RemoteControl parameter = deviceParameterBank.getParameter(i);
6674
parameter.name().markInterested();
6775
parameter.value().markInterested();
6876
parameter.displayedValue().markInterested();
6977
}
7078

79+
// Mark interest in project parameters to enable value access
80+
for (int i = 0; i < 8; i++) {
81+
RemoteControl parameter = projectParameterBank.getParameter(i);
82+
parameter.exists().markInterested();
83+
parameter.name().markInterested();
84+
parameter.value().markInterested();
85+
parameter.displayedValue().markInterested();
86+
}
87+
88+
// Mark interest in cursor track properties for selected track details
89+
cursorTrack.exists().markInterested();
90+
cursorTrack.name().markInterested();
91+
cursorTrack.trackType().markInterested();
92+
cursorTrack.isGroup().markInterested();
93+
cursorTrack.mute().markInterested();
94+
cursorTrack.solo().markInterested();
95+
cursorTrack.arm().markInterested();
96+
7197
// Mark interest in track properties for clip launching
7298
for (int trackIndex = 0; trackIndex < 8; trackIndex++) {
7399
Track track = trackBank.getItemAt(trackIndex);
@@ -479,4 +505,80 @@ private String formatBitwigBeatPosition(double positionInBeats) {
479505
return "1.1.1:0";
480506
}
481507
}
508+
509+
/**
510+
* Gets the project parameters (0-7) from the project's remote controls page.
511+
* Only returns parameters where exists() is true.
512+
*
513+
* @return A list of ParameterInfo objects representing the existing project parameters
514+
*/
515+
public List<ParameterInfo> getProjectParameters() {
516+
logger.info("BitwigApiFacade: Getting project parameters");
517+
List<ParameterInfo> parameters = new ArrayList<>();
518+
519+
for (int i = 0; i < 8; i++) {
520+
RemoteControl parameter = projectParameterBank.getParameter(i);
521+
boolean exists = parameter.exists().get();
522+
523+
if (exists) {
524+
String name = parameter.name().get();
525+
double value = parameter.value().get();
526+
String displayValue = parameter.displayedValue().get();
527+
528+
// Handle null or empty names
529+
if (name != null && name.trim().isEmpty()) {
530+
name = null;
531+
}
532+
533+
parameters.add(new ParameterInfo(i, name, value, displayValue));
534+
}
535+
}
536+
537+
logger.info("BitwigApiFacade: Retrieved " + parameters.size() + " existing project parameters");
538+
return parameters;
539+
}
540+
541+
/**
542+
* Gets information about the currently selected track.
543+
*
544+
* @return A map containing selected track information, or null if no track is selected
545+
*/
546+
public Map<String, Object> getSelectedTrackInfo() {
547+
logger.info("BitwigApiFacade: Getting selected track information");
548+
549+
if (!cursorTrack.exists().get()) {
550+
logger.info("BitwigApiFacade: No track selected");
551+
return null;
552+
}
553+
554+
Map<String, Object> trackInfo = new LinkedHashMap<>();
555+
556+
try {
557+
// Get track index by finding it in the track bank
558+
int trackIndex = -1;
559+
String trackName = cursorTrack.name().get();
560+
for (int i = 0; i < trackBank.getSizeOfBank(); i++) {
561+
Track track = trackBank.getItemAt(i);
562+
if (track.exists().get() && trackName.equals(track.name().get())) {
563+
trackIndex = i;
564+
break;
565+
}
566+
}
567+
568+
trackInfo.put("index", trackIndex);
569+
trackInfo.put("name", trackName);
570+
trackInfo.put("type", cursorTrack.trackType().get().toLowerCase());
571+
trackInfo.put("is_group", cursorTrack.isGroup().get());
572+
trackInfo.put("muted", cursorTrack.mute().get());
573+
trackInfo.put("soloed", cursorTrack.solo().get());
574+
trackInfo.put("armed", cursorTrack.arm().get());
575+
576+
logger.info("BitwigApiFacade: Retrieved selected track info: " + trackName);
577+
} catch (Exception e) {
578+
logger.warn("BitwigApiFacade: Error getting selected track info: " + e.getMessage());
579+
return null;
580+
}
581+
582+
return trackInfo;
583+
}
482584
}

src/main/java/io/github/fabb/wigai/mcp/tool/StatusTool.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.modelcontextprotocol.spec.McpSchema;
99
import com.bitwig.extension.controller.api.*;
1010

11+
import java.util.ArrayList;
1112
import java.util.LinkedHashMap;
1213
import java.util.List;
1314
import java.util.Map;
@@ -55,6 +56,24 @@ public static McpServerFeatures.SyncToolSpecification specification(WigAIExtensi
5556
Map<String, Object> transportMap = bitwigApiFacade.getTransportStatus();
5657
responseMap.put("transport", transportMap);
5758

59+
// Get project parameters from BitwigApiFacade
60+
List<io.github.fabb.wigai.common.data.ParameterInfo> projectParams = bitwigApiFacade.getProjectParameters();
61+
List<Map<String, Object>> projectParametersArray = new ArrayList<>();
62+
for (io.github.fabb.wigai.common.data.ParameterInfo param : projectParams) {
63+
Map<String, Object> paramMap = new LinkedHashMap<>();
64+
paramMap.put("index", param.index());
65+
paramMap.put("exists", true); // Only existing parameters are returned
66+
paramMap.put("name", param.name());
67+
paramMap.put("value", param.value());
68+
paramMap.put("display_value", param.display_value());
69+
projectParametersArray.add(paramMap);
70+
}
71+
responseMap.put("project_parameters", projectParametersArray);
72+
73+
// Get selected track information from BitwigApiFacade
74+
Map<String, Object> selectedTrackInfo = bitwigApiFacade.getSelectedTrackInfo();
75+
responseMap.put("selected_track", selectedTrackInfo);
76+
5877
// Convert response to JSON string for text content
5978
ObjectMapper objectMapper = new ObjectMapper();
6079
String jsonResponse = objectMapper.writeValueAsString(responseMap);

0 commit comments

Comments
 (0)