├── image.png ├── .vscode ├── settings.json └── launch.json ├── src ├── main │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── com.bitwig.extension.ExtensionDefinition │ └── java │ │ └── com │ │ └── centomila │ │ ├── customPresetsTxt │ │ ├── Macros │ │ │ ├── Demo │ │ │ │ ├── 1-Demo.txt │ │ │ │ ├── 2-Demo.txt │ │ │ │ ├── 6-Demo.txt │ │ │ │ ├── 3-Demo.txt │ │ │ │ ├── 5-Demo.txt │ │ │ │ └── 4-Demo.txt │ │ │ ├── BitX │ │ │ │ ├── BitX Bpm.txt │ │ │ │ ├── BitX LDR.txt │ │ │ │ ├── BitX LIR.txt │ │ │ │ ├── BitX SMW.txt │ │ │ │ ├── BitX STS.txt │ │ │ │ ├── BitX SNF.txt │ │ │ │ ├── BitX SPN.txt │ │ │ │ ├── BitX OSC.txt │ │ │ │ ├── BitX SCF.txt │ │ │ │ ├── BitX SNT.txt │ │ │ │ └── BitX ALL.txt │ │ │ ├── Arrangement │ │ │ │ ├── Arrangement - Delete All Cue Markers.txt │ │ │ │ ├── Arrangement - Melodic Techno.txt │ │ │ │ ├── Arrangement - House.txt │ │ │ │ ├── Arrangement - Pop.txt │ │ │ │ ├── Arrangement - Rock.txt │ │ │ │ ├── Arrangement - Minimal Techno Extended Mix.txt │ │ │ │ ├── Arrangement - Melodic Techno Original Mix.txt │ │ │ │ ├── Arrangement - Raw Deep Techno Extended Mix.txt │ │ │ │ ├── Arrangement - Tech House Extended Mix.txt │ │ │ │ └── Arrangement - Hypnotic Techno Extended Mix.txt │ │ │ ├── Samples │ │ │ │ ├── Arranger Loop Export.txt │ │ │ │ ├── Check Arm Status.txt │ │ │ │ ├── Step Set Loop.txt │ │ │ │ ├── Insert VST3.txt │ │ │ │ ├── Rename Loop.txt │ │ │ │ ├── Get Functions and Vars.txt │ │ │ │ └── Rainbow.txt │ │ │ ├── BIP and Reverse.txt │ │ │ ├── Color Tracks in Group.txt │ │ │ └── Create 8 Tracks.txt │ │ └── Custom Presets │ │ │ ├── Kick_Half_Note.txt │ │ │ ├── Kick_1_and_3.txt │ │ │ ├── Kick_Triplet_Feel.txt │ │ │ ├── Kick_Dotted_8th.txt │ │ │ ├── Kick_Off-Beats.txt │ │ │ ├── Snare_Half-Time.txt │ │ │ ├── Snare_Only_on_Beat_2.txt │ │ │ ├── HiHat_8th_Notes.txt │ │ │ ├── HiHat_Closed_8th_Dotted.txt │ │ │ ├── HiHat_Quarter_Notes.txt │ │ │ ├── Open_HiHat_Quarter.txt │ │ │ ├── Snare_Backbeat_2_and_4.txt │ │ │ ├── HiHat_Off-Beat_8ths.txt │ │ │ ├── HiHat_Shuffle.txt │ │ │ ├── Kick_Four_on_the_Floor.txt │ │ │ ├── Kick_Syncopated.txt │ │ │ ├── Open_HiHat_Off_Beats.txt │ │ │ ├── HiHat_16th_Notes.txt │ │ │ ├── Snare_16th_Ghost_Notes.txt │ │ │ ├── Kick_Driving_8th_Notes.txt │ │ │ ├── HiHat_Closed_Triplet_Basic.txt │ │ │ ├── HiHat_Closed_Triplet_Quarter.txt │ │ │ ├── Snare_16th_Machine_Gun.txt │ │ │ ├── HiHat_Closed_Eighth_Notes_8Step.txt │ │ │ ├── HiHat_Closed_Eighth_Accents.txt │ │ │ ├── HiHat_Closed_Eighths_Crescendo.txt │ │ │ ├── HiHat_Closed_Triplet_Shuffle.txt │ │ │ ├── HiHat_Closed_Triplet_Accented.txt │ │ │ ├── Open_HiHat_Two_Four.txt │ │ │ └── HiHat_32nd_Notes.txt │ │ ├── utils │ │ ├── commands │ │ │ ├── utility │ │ │ │ ├── package-info.java │ │ │ │ ├── WaitCommand.java │ │ │ │ ├── ListCommandsCommand.java │ │ │ │ ├── PrintActionsCommand.java │ │ │ │ ├── MacroCommand.java │ │ │ │ ├── MessageCommand.java │ │ │ │ └── ConsoleCommand.java │ │ │ ├── clip │ │ │ │ ├── package-info.java │ │ │ │ ├── ClipLoopOnCommand.java │ │ │ │ ├── ClipLoopOffCommand.java │ │ │ │ ├── ClipRenameCommand.java │ │ │ │ ├── ClipOffsetCommand.java │ │ │ │ ├── ClipAccentCommand.java │ │ │ │ ├── ClipColorCommand.java │ │ │ │ ├── ClipDuplicateCommand.java │ │ │ │ ├── ClipLengthCommand.java │ │ │ │ ├── ClipDeleteCommand.java │ │ │ │ ├── ClipSelectCommand.java │ │ │ │ ├── ClipMoveCommand.java │ │ │ │ └── ClipCreateCommand.java │ │ │ ├── device │ │ │ │ ├── package-info.java │ │ │ │ ├── InsertDeviceCommand.java │ │ │ │ ├── InsertVST3Command.java │ │ │ │ └── InsertFileCommand.java │ │ │ ├── drum │ │ │ │ ├── package-info.java │ │ │ │ ├── SelectDrumPadCommand.java │ │ │ │ ├── InsertFileInDrumPadCommand.java │ │ │ │ ├── InsertVST3InDrumPadCommand.java │ │ │ │ ├── CreateDrumPadCommand.java │ │ │ │ └── InsertBitwigDeviceInDrumPadCommand.java │ │ │ ├── project │ │ │ │ ├── package-info.java │ │ │ │ └── ProjectNameCommand.java │ │ │ ├── track │ │ │ │ ├── package-info.java │ │ │ │ ├── TrackNextCommand.java │ │ │ │ ├── TrackPrevCommand.java │ │ │ │ ├── TrackDeleteCommand.java │ │ │ │ ├── TrackRenameCommand.java │ │ │ │ ├── TrackSelectCommand.java │ │ │ │ ├── TrackColorCommand.java │ │ │ │ └── TrackColorAllCommand.java │ │ │ ├── navigation │ │ │ │ ├── package-info.java │ │ │ │ ├── EnterCommand.java │ │ │ │ ├── EscapeCommand.java │ │ │ │ ├── UpCommand.java │ │ │ │ ├── DownCommand.java │ │ │ │ ├── LeftCommand.java │ │ │ │ └── RightCommand.java │ │ │ ├── marker │ │ │ │ ├── package-info.java │ │ │ │ ├── DeleteAllCueMarkersCommand.java │ │ │ │ └── CueMarkerNameCommand.java │ │ │ ├── step │ │ │ │ ├── package-info.java │ │ │ │ ├── StepSelectedIsMutedCommand.java │ │ │ │ ├── StepSelectedRepeatCountCommand.java │ │ │ │ ├── StepSelectedIsChanceEnabledCommand.java │ │ │ │ ├── StepSelectedIsRepeatEnabledCommand.java │ │ │ │ ├── StepSelectedRepeatCurveCommand.java │ │ │ │ ├── StepSelectedIsOccurrenceEnabledCommand.java │ │ │ │ ├── StepSelectedIsRecurrenceEnabledCommand.java │ │ │ │ ├── StepSelectedRepeatVelocityEndCommand.java │ │ │ │ ├── StepSelectedRepeatVelocityCurveCommand.java │ │ │ │ ├── StepSelectedPanCommand.java │ │ │ │ ├── StepSelectedGainCommand.java │ │ │ │ ├── StepSelectedTimbreCommand.java │ │ │ │ ├── StepSelectedChanceCommand.java │ │ │ │ ├── StepSelectedRecurrenceCommand.java │ │ │ │ ├── StepSelectedTransposeCommand.java │ │ │ │ ├── StepSelectedLengthCommand.java │ │ │ │ ├── StepSelectedOccurrenceCommand.java │ │ │ │ ├── StepSelectedPressureCommand.java │ │ │ │ ├── StepSelectedVelocityCommand.java │ │ │ │ ├── StepSelectedVelocitySpreadCommand.java │ │ │ │ ├── StepSelectedReleaseVelocityCommand.java │ │ │ │ └── StepSetCommand.java │ │ │ ├── bb │ │ │ │ ├── package-info.java │ │ │ │ ├── BBGenerateCommand.java │ │ │ │ ├── BBPresetCommand.java │ │ │ │ ├── BBArrangerModeCommand.java │ │ │ │ ├── BBLauncherModeCommand.java │ │ │ │ ├── BBClosePanelCommand.java │ │ │ │ ├── BBPatternRepeatCommand.java │ │ │ │ ├── BBPostActionAutoResizeCommand.java │ │ │ │ └── BBToggleLauncherArrangerModeCommand.java │ │ │ ├── transport │ │ │ │ ├── package-info.java │ │ │ │ ├── TimeSignatureCommand.java │ │ │ │ ├── BpmCommand.java │ │ │ │ ├── TransportPositionCommand.java │ │ │ │ ├── ArrangerLoopEndCommand.java │ │ │ │ └── ArrangerLoopStartCommand.java │ │ │ ├── CommandFactory.java │ │ │ └── BaseCommand.java │ │ ├── PopupUtils.java │ │ ├── DrumPadUtils.java │ │ ├── OpenWebUrl.java │ │ └── ExtensionPath.java │ │ ├── macro │ │ ├── state │ │ │ └── BitwigStateProvider.java │ │ └── commands │ │ │ └── MacroCommand.java │ │ ├── BitwigBuddyExtensionDefinition.java │ │ ├── EditClipSettings.java │ │ └── DefaultPatterns.java └── assembly │ └── distribution.xml ├── .gitignore ├── .github └── ISSUE_TEMPLATE ├── LICENSE.md └── readme.md /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/centomila/BitwigBuddy-Bitwig-Extension/HEAD/image.png -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "automatic" 3 | } -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/com.bitwig.extension.ExtensionDefinition: -------------------------------------------------------------------------------- 1 | com.centomila.BitwigBuddyExtensionDefinition -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/1-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "1-Demo" 2 | Description: "" 3 | Author: "Centomila" 4 | 5 | Message ("Hello!") 6 | Wait(2000) 7 | Macro ("Demo/2-Demo") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Half_Note.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Half Notes" 2 | DefaultNote: "C1" 3 | Pattern: [100, 80, 100, 80] 4 | StepSize: "1/2" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/2" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX Bpm.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX BPM (Tempo)" 2 | Descritpion: "Change the track tempo (Requires BitX)" 3 | Author: "Centomila" 4 | 5 | Clip Color ("FF595E") 6 | Clip Rename ("()BPM 130") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_1_and_3.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick 1 and 3" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Triplet_Feel.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Triplet Feel 12 Steps" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 0, 0, 80, 0, 0, 0, 100, 0, 0, 0] 4 | StepSize: "1/8" 5 | Subdivisions: "3t" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX LDR.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX LDR (Load Drum Rack)" 2 | Descritpion: "Load a drum rack preset (Requires BitX)" 3 | Author: "Centomila" 4 | 5 | Clip Color ("FF924C") 6 | Clip Rename ("()LDR Electronic Kit") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX LIR.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX LIR (Load Instrument)" 2 | Descritpion: "Load an instrument preset (Requires BitX)" 3 | Author: "Centomila" 4 | 5 | Clip Color ("FFCA3A") 6 | Clip Rename ("()LIR Synth Lead:3") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Dotted_8th.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Dotted 8th Pattern" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 100, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "." 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Off-Beats.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Off-Beats (2 and 4)" 2 | DefaultNote: "C1" 3 | Pattern: [0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Snare_Half-Time.txt: -------------------------------------------------------------------------------- 1 | Name: "Snare Single Hit Half-Time" 2 | DefaultNote: "C#1" 3 | Pattern: [0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Snare_Only_on_Beat_2.txt: -------------------------------------------------------------------------------- 1 | Name: "Snare Only on Beat 2" 2 | DefaultNote: "C#1" 3 | Pattern: [0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing utility command implementations. 3 | * These commands handle general operations like message display, waiting, etc. 4 | */ 5 | package com.centomila.utils.commands.utility; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_8th_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed 8th Notes" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_8th_Dotted.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Dotted Eighth" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 0, 80, 0, 0, 80, 0, 0, 80, 0, 0, 80, 0, 0, 80] 4 | StepSize: "1/8" 5 | Subdivisions: "." 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Quarter_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Quarter Notes" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Open_HiHat_Quarter.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Open Quarter Notes" 2 | DefaultNote: "D#1" 3 | Pattern: [80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Snare_Backbeat_2_and_4.txt: -------------------------------------------------------------------------------- 1 | Name: "Snare Backbeat 2 and 4" 2 | DefaultNote: "C#1" 3 | Pattern: [0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Delete All Cue Markers.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement: DELETE ALL CUE MARKERS" 2 | Descritpion: "Delete all cue markers" 3 | Author: "Centomila" 4 | 5 | DeleteAllCueMarkers 6 | 7 | Message ("Macro Finished") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX SMW.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX SMW (Setting Message)" 2 | Descritpion: "Display a message about setting up (Requires BitX)" 3 | Author: "Centomila" 4 | 5 | Clip Color ("52A675") 6 | Clip Rename ("()SMW Setting up drum sounds") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX STS.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX STS (Set Time Signature)" 2 | Descritpion: "Set Time Signature for the track (Requires BitX). Usage: ()STS 4:8" 3 | Author: "Centomila" 4 | 5 | Clip Color ("B5A6C9") 6 | Clip Rename ("()STS 4:8") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Off-Beat_8ths.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Off-Beat 8ths" 2 | DefaultNote: "D1" 3 | Pattern: [0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Shuffle.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Accent on Downbeat" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 40, 0, 80, 0, 40, 0, 80, 0, 40, 0, 80, 0, 40, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Four_on_the_Floor.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Four on the Floor" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Syncopated.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Syncopated (1, & of 2, 3)" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 0, 0, 0, 0, 100, 0, 100, 0, 0, 0, 0, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Open_HiHat_Off_Beats.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Open Off-Beat 8ths" 2 | DefaultNote: "D#1" 3 | Pattern: [0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for clip operations. 3 | * These commands handle clip creation, deletion, and manipulation in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.clip; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/device/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for device operations. 3 | * These commands handle device insertion and manipulation in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.device; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for drum pad operations. 3 | * These commands handle drum pad creation, manipulation, and device insertion. 4 | */ 5 | package com.centomila.utils.commands.drum; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_16th_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed 16th Notes" 2 | DefaultNote: "D1" 3 | Pattern: [80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Snare_16th_Ghost_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "Snare 16th Ghost Notes" 2 | DefaultNote: "C#1" 3 | Pattern: [20, 0, 20, 0, 100, 0, 20, 0, 20, 0, 20, 0, 100, 0, 20, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/project/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for project-level operations. 3 | * These commands handle project settings and properties in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.project; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for track operations. 3 | * These commands handle track selection, creation, and manipulation in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.track; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Kick_Driving_8th_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "Kick Driving 8th Notes" 2 | DefaultNote: "C1" 3 | Pattern: [100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for navigation actions. 3 | * These commands handle keyboard navigation and UI movement in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.navigation; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Triplet_Basic.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Triplet Basic 18 Step" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0] 4 | StepSize: "1/8" 5 | Subdivisions: "3t" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Triplet_Quarter.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Triplet Quarter 18 Step" 2 | DefaultNote: "D1" 3 | Pattern: [80, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0] 4 | StepSize: "1/8" 5 | Subdivisions: "3t" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Snare_16th_Machine_Gun.txt: -------------------------------------------------------------------------------- 1 | Name: "Snare 16th Machine Gun" 2 | DefaultNote: "C#1" 3 | Pattern: [80, 100, 80, 100, 80, 100, 80, 100, 80, 100, 80, 100, 80, 100, 80, 100] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Eighth_Notes_8Step.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Eighth Notes" 2 | DefaultNote: "D1" 3 | Pattern: [80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80] 4 | StepSize: "1/8" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/marker/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for cue marker operations. 3 | * These commands handle cue marker creation, deletion, and manipulation in Bitwig Studio. 4 | */ 5 | package com.centomila.utils.commands.marker; 6 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Eighth_Accents.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Eighth Accented" 2 | DefaultNote: "D1" 3 | Pattern: [100, 60, 100, 60, 100, 60, 100, 60, 100, 60, 100, 60, 100, 60, 100, 60] 4 | StepSize: "1/8" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Eighths_Crescendo.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Eighth Crescendo" 2 | DefaultNote: "D1" 3 | Pattern: [40, 50, 60, 70, 80, 90, 100, 110, 40, 50, 60, 70, 80, 90, 100, 110] 4 | StepSize: "1/8" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Triplet_Shuffle.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Triplet Alt Velocity 18 Step" 2 | DefaultNote: "D1" 3 | Pattern: [90, 0, 40, 0, 90, 0, 40, 0, 90, 0, 40, 0, 90, 0, 40, 0, 90, 0] 4 | StepSize: "1/8" 5 | Subdivisions: "3t" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | *.class 3 | *.jar 4 | *.war 5 | *.ear 6 | *.zip 7 | *.tar.gz 8 | *.rar 9 | *.7z 10 | *.log 11 | *.iml 12 | *.ipr 13 | *.iws 14 | .settings/ 15 | .idea/ 16 | .m2/ 17 | *.iml 18 | *.ipr 19 | *.iws 20 | *.log 21 | *.ser 22 | *.bak 23 | api/ 24 | .vscodecounter/ 25 | .semanticgraphs/ 26 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_Closed_Triplet_Accented.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed Triplet Accented 18 Step" 2 | DefaultNote: "D1" 3 | Pattern: [80, 20, 60, 20, 80, 20, 60, 20, 80, 20, 60, 20, 80, 20, 60, 20, 80, 20] 4 | StepSize: "1/8" 5 | Subdivisions: "3t" 6 | NoteLength: "1/8" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX SNF.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX SNF (Set Note Filter)" 2 | Descritpion: "Set Note Filter - Change the key range for the note filter device (Requires BitX). Usage: ()SNF D1:E5" 3 | Author: "Centomila" 4 | 5 | Clip Color ("1982C4") 6 | Clip Rename ("()SNF C2:C5") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX SPN.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX SPN (Assign Name)" 2 | Descritpion: "Show a Popup Notification that dissapears after a few seconds (Requires BitX). Usage: ()SPN \"Message\"" 3 | Author: "Centomila" 4 | 5 | Clip Color ("6A4C93") 6 | Clip Rename ("()SPN I Love BitX") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX OSC.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX OSC (Send OSC Message)" 2 | Descritpion: "Sends an OSC message (Requires BitX). Usage: ()OSC /address arg1 arg2 ..." 3 | Author: "Centomila" 4 | 5 | Clip Color ("C5CA30") 6 | Clip Rename ("()OSC /example/address 1 "string arg" 3.14") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/Open_HiHat_Two_Four.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Open Two and Four" 2 | DefaultNote: "D#1" 3 | Pattern: [0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0] 4 | StepSize: "1/16" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/16" 7 | Notes: "Use with HiHat Closed Quarter Notes pattern" 8 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX SCF.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX SCF (Scale Function)" 2 | Descritpion: "Set Channel filter - Will get the first channel filter on the track and set the channels (Requires BitX). Usage: ()SCF 1:5:9" 3 | Author: "Centomila" 4 | 5 | Clip Color ("8AC926") 6 | Clip Rename ("()SCF 1:3:5") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Custom Presets/HiHat_32nd_Notes.txt: -------------------------------------------------------------------------------- 1 | Name: "HiHat Closed 32nd Roll" 2 | DefaultNote: "D1" 3 | Pattern: [80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60, 80, 60] 4 | StepSize: "1/32" 5 | Subdivisions: "Straight" 6 | NoteLength: "1/32" 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX SNT.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX SNT (Set Note Transform)" 2 | Descritpion: "Set Note Transpose - Adjusts octave, coarse and fine tuning (Requires BitX). Usage: ()SNT octave:coarse:fine e.g. ()SNT 2:30:-40" 3 | Author: "Centomila" 4 | 5 | Clip Color ("4267AC") 6 | Clip Rename ("()SNT 1:20:-10") 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Arranger Loop Export.txt: -------------------------------------------------------------------------------- 1 | Macro: "Export Loop Selection" 2 | Description: "Set the Arranger Loop position, go to export page and confirm with last used settings." 3 | Author: "Centomila" 4 | 5 | Arranger Loop Start (16.0) 6 | Wait (100) 7 | Arranger Loop End (128.0) 8 | Export Audio 9 | Enter -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for step sequencer operations. 3 | * These commands handle note step manipulation in the Bitwig Studio step sequencer, 4 | * such as setting velocity, length, and other note properties. 5 | */ 6 | package com.centomila.utils.commands.step; 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for BeatBuddy-specific operations. 3 | * These commands handle operations specific to the BeatBuddy extension functionality 4 | * such as preset selection, pattern generation, and custom features. 5 | */ 6 | package com.centomila.utils.commands.bb; 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Package containing command implementations for transport-related actions. 3 | * These commands handle operations related to the transport controls in Bitwig Studio, 4 | * such as playback position, tempo, time signature, and looping. 5 | */ 6 | package com.centomila.utils.commands.transport; 7 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Check Arm Status.txt: -------------------------------------------------------------------------------- 1 | Macro: "Check Arm Status" 2 | Description: "Check if tracks are armed for recording and show status messages." 3 | Author: "Centomila" 4 | 5 | for (i = 0 to 7) { 6 | 7 | if (isCurrentTrackArmed()) { 8 | Message("${getCurrentTrackName()} is ARMED for recording!") 9 | } 10 | 11 | if (!isCurrentTrackArmed()) { 12 | Message("${getCurrentTrackName()} IS NOT armed for recording!") 13 | } 14 | Track Next 15 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/EnterCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the Enter key. 8 | */ 9 | public class EnterCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().enter(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "java", 9 | "name": "Attach to Bitwig", 10 | "request": "attach", 11 | "hostName": "localhost", 12 | "port": "5005" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/EscapeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the Escape key. 8 | */ 9 | public class EscapeCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().escape(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/UpCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the up arrow key. 8 | */ 9 | public class UpCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().arrowKeyUp(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/DownCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the down arrow key. 8 | */ 9 | public class DownCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().arrowKeyDown(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/LeftCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the left arrow key. 8 | */ 9 | public class LeftCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().arrowKeyLeft(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBGenerateCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to generate a BeatBuddy drum pattern preset. 8 | */ 9 | public class BBGenerateCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.generateDrumPattern(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/navigation/RightCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.navigation; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to press the right arrow key. 8 | */ 9 | public class RightCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getApplication().arrowKeyRight(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Step Set Loop.txt: -------------------------------------------------------------------------------- 1 | Macro: "Test Step Set For Loop" 2 | Description: "This macro tests the 'Step Set' command by setting alternating steps in the currently selected clip using a for loop and math." 3 | Author: "Centomila" 4 | 5 | // Parameters: channel, stepIndex, noteDestination, velocity, duration 6 | for (i = 0 to 7) { 7 | var velocity = ${i} * 10 8 | var stepIndex = ${i}*2 9 | Step Set(0, ${stepIndex}, 60, ${velocity}, 0.25) 10 | Message ("${i}") 11 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipLoopOnCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to enable looping for the selected clip. 8 | */ 9 | public class ClipLoopOnCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getLauncherOrArrangerAsClip().isLoopEnabled().set(true); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipLoopOffCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to disable looping for the selected clip. 8 | */ 9 | public class ClipLoopOffCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | extension.getLauncherOrArrangerAsClip().isLoopEnabled().set(false); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/2-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "2-Demo" 2 | Description: "" 3 | Author: "Centomila" 4 | 5 | Message ("This is a Bitwig Buddy Macro") 6 | Wait (3000) 7 | Message ("Now I will mess up with this project to show you what I can do") 8 | Wait (3000) 9 | Message ("You have 5 seconds to stop me") 10 | Wait (1000) 11 | Message ("5") 12 | Wait (1000) 13 | Message ("4") 14 | Wait (1000) 15 | Message ("3") 16 | Wait (1000) 17 | Message ("2") 18 | Wait (1000) 19 | Message ("1") 20 | Wait (1000) 21 | BB Close Panel 22 | Macro ("Demo/3-Demo") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BIP and Reverse.txt: -------------------------------------------------------------------------------- 1 | Macro: "Bounce In Place then Reverse" 2 | Description: "Bounce in place the Time Selection and reverse it 1 bar earlier." 3 | Author: "Centomila" 4 | 5 | BB Arranger Mode 6 | BB Close Panel 7 | select_time_selection_tool 8 | bounce_in_place 9 | Wait (500) 10 | 11 | switch_between_event_and_time_selection 12 | select_object_selection_tool 13 | reverse 14 | Wait (100) 15 | 16 | Clip Move (-4) 17 | Wait (100) 18 | 19 | Right 20 | Wait (100) 21 | 22 | Clip Move (-4) 23 | Wait (100) 24 | 25 | Clip Offset (0) 26 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackNextCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.utils.commands.BaseCommand; 4 | import com.centomila.BitwigBuddyExtension; 5 | 6 | /** 7 | * Command to navigate to the next track. 8 | */ 9 | public class TrackNextCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] args, BitwigBuddyExtension extension) { 13 | // Logic to navigate to the next track using the provided extension 14 | extension.getCursorTrack().selectNext(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackPrevCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.utils.commands.BaseCommand; 4 | import com.centomila.BitwigBuddyExtension; 5 | 6 | /** 7 | * Command to navigate to the previous track. 8 | */ 9 | public class TrackPrevCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] args, BitwigBuddyExtension extension) { 13 | // Logic to navigate to the previous track using the provided extension 14 | extension.getCursorTrack().selectPrevious(); 15 | } 16 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/6-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "6-Demo" 2 | Description: "Final part of the demo sequence with clip creation and selection." 3 | Author: "Centomila" 4 | 5 | switch_between_event_and_time_selection 6 | Right 7 | Down 8 | Track Select (2) 9 | move_time_selection_one_event_earlier 10 | Clip Create (1, 4) 11 | Wait (200) 12 | Clip Select 13 | Message ("Weeeeeeeeeeeeeee!") 14 | Macro ("Samples/Rainbow Clip") 15 | Select All 16 | zoom_to_fit_selection_or_all 17 | Stop Transport 18 | Wait (1000) 19 | Message ("End of demo!") 20 | Wait (2000) 21 | Message ("Bye bye!") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/3-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "3-Demo" 2 | Description: "" 3 | Author: "Centomila" 4 | 5 | 6 | Message ("First, we need some tracks...") 7 | Track Select (1) 8 | Wait (1500) 9 | focus_track_header_area 10 | Wait (500) 11 | Macro ("Create 8 Tracks") 12 | Wait (1500) 13 | Message ("Much better!") 14 | Wait (1500) 15 | Message ("Now let's add some cue markers") 16 | Wait (1500) 17 | Macro ("Arrangement/Arrangement - Melodic Techno Original Mix") 18 | Wait (1500) 19 | Message ("Zoom to Fit the arranger...") 20 | arranger_zoom_to_fit 21 | Wait (3000) 22 | Macro ("Demo/4-Demo") 23 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Color Tracks in Group.txt: -------------------------------------------------------------------------------- 1 | Macro: "Color Tracks in Group" 2 | Description: "Remove the color of all tracks in a group and inherit the color of the group track." 3 | Author: "Centomila" 4 | 5 | // Close the BitwigBuddy Panel 6 | BB Close Panel 7 | // Enter the selected group track 8 | Enter Group 9 | 10 | // Assign color (0) to the group track. 0 means no color 11 | Track Color All (0) 12 | Wait (100) 13 | Track Select (1) 14 | 15 | // Wait 100 ms to ensure the color is applied 16 | Wait (100) 17 | 18 | // Exit the group track 19 | Exit Group 20 | 21 | // Go back to the group track 22 | Track Previous -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBPresetCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.PatternSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to select a BeatBuddy preset. 9 | */ 10 | public class BBPresetCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | PatternSettings.setCustomPreset(params[0]); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/project/ProjectNameCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.project; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import static com.centomila.utils.PopupUtils.showPopup; 6 | 7 | /** 8 | * Command to display the current project name. 9 | */ 10 | public class ProjectNameCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | extension.getApplication().projectName(); 15 | showPopup(extension.getApplication().projectName().toString()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBArrangerModeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.SettableEnumValue; 7 | 8 | /** 9 | * Command to switch to Arranger mode. 10 | */ 11 | public class BBArrangerModeCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | ((SettableEnumValue) ModeSelectSettings.toggleLauncherArrangerSetting).set("Arranger"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBLauncherModeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.SettableEnumValue; 7 | 8 | /** 9 | * Command to switch to Launcher mode. 10 | */ 11 | public class BBLauncherModeCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | ((SettableEnumValue) ModeSelectSettings.toggleLauncherArrangerSetting).set("Launcher"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipRenameCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to rename the currently selected clip. 8 | */ 9 | public class ClipRenameCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | String newName = params[0].trim(); 18 | extension.getLauncherOrArrangerAsClip().setName(newName); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Insert VST3.txt: -------------------------------------------------------------------------------- 1 | Macro: "Insert VST3" 2 | Description: "This is just for testing." 3 | Author: "Centomila" 4 | 5 | // Time Signature (3/4) 6 | // Clip Loop On 7 | // Select Next 8 | // Clip Loop Off 9 | // Clip Accent (0.752) 10 | // Insert File (2, "c:\Program Files\Common Files\Native Instruments\Kontakt 8\Content\Tools\Phrases\Library Data\MIDI Files\ZA_Last_Dance_Phrases_E_Ionian_Flat_2\06_Last Dance_E_Phrygian Dom_E_Phrygian Dom.mid") 11 | // Insert File (2, "c:\Users\Bach\AppData\Local\Bitwig Studio\installed-packages\5.0\Bitwig\Bitwig Drum Machines\Dub Techno Beat 2 126bpm.bwclip") 12 | 13 | 14 | Insert VST3 ("Serum 2") 15 | Insert VST3 ("Valhalla Supermassive") 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/5-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "5-Demo" 2 | Description: "Demonstrates Zoom to Selection and Bounce in Place operations." 3 | Author: "Centomila" 4 | 5 | Zoom to Selection 6 | play_from_time_selection 7 | Wait (2000) 8 | Stop Transport 9 | 10 | // Select the first note 11 | move_time_selection_one_event_earlier 12 | extend_time_selection_range_to_next_item 13 | extend_time_selection_range_to_next_item 14 | Wait (100) 15 | Macro ("Bounce In Place then Reverse") 16 | Wait (100) 17 | Left 18 | Play Transport 19 | Wait (2000) 20 | Stop Transport 21 | 22 | Message ("Ops. I left the two initial tracks") 23 | 24 | Track Delete (1) 25 | Track Delete (9) 26 | Wait (100) 27 | Track Select (1) 28 | Wait (1000) 29 | Play Transport 30 | Macro ("Demo/6-Demo") -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/WaitCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to wait for a specified duration in milliseconds. 8 | */ 9 | public class WaitCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | int waitTime = params.length > 0 ? Integer.parseInt(params[0]) : 50; 14 | 15 | try { 16 | Thread.sleep(waitTime); 17 | } catch (InterruptedException e) { 18 | Thread.currentThread().interrupt(); 19 | reportError("Wait interrupted: " + e.getMessage(), extension); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/TimeSignatureCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.transport; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the project time signature. 8 | */ 9 | public class TimeSignatureCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | extension.transport.timeSignature().set(params[0].trim()); 19 | } catch (Exception e) { 20 | reportError("Invalid time signature format: " + params[0], extension); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/PopupUtils.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils; 2 | 3 | import com.bitwig.extension.controller.api.ControllerHost; 4 | 5 | public class PopupUtils { 6 | private static ControllerHost host; 7 | 8 | /** 9 | * @param controllerHost 10 | */ 11 | public static void init(ControllerHost controllerHost) { 12 | host = controllerHost; 13 | } 14 | 15 | /** 16 | * @param message // The message to display 17 | */ 18 | public static void showPopup(String message) { 19 | if (host != null) { 20 | host.showPopupNotification(message); 21 | } 22 | } 23 | 24 | // Console message 25 | public static void console(String message) { 26 | if (host != null) { 27 | host.println(message); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/BpmCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.transport; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the BPM (tempo) of the project. 8 | */ 9 | public class BpmCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | int bpm = Integer.parseInt(params[0].trim()); 19 | extension.transport.tempo().setRaw(bpm); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid BPM value: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBClosePanelCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to close the BeatBuddy panel by using a workaround. 8 | * This command opens the Export Audio panel and then sends an Escape key press 9 | * to force the BeatBuddy panel to close. 10 | */ 11 | public class BBClosePanelCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | // Open the Export Audio panel 16 | extension.getApplication().getAction("Export Audio").invoke(); 17 | 18 | // Then press the Escape key to close it and force BB panel to close 19 | extension.getApplication().escape(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/TransportPositionCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.transport; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the transport position. 8 | */ 9 | public class TransportPositionCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | double position = Double.parseDouble(params[0].trim()); 19 | extension.transport.setPosition(position); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid position value: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipOffsetCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the playback start offset of a clip. 8 | */ 9 | public class ClipOffsetCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | double offset = Double.parseDouble(params[0].trim()); 19 | extension.getLauncherOrArrangerAsClip().getPlayStart().set(offset); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid offset value: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipAccentCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the accent value for the selected clip. 8 | */ 9 | public class ClipAccentCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | double accentValue = Double.parseDouble(params[0].trim()); 19 | extension.getLauncherOrArrangerAsClip().getAccent().setRaw(accentValue); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid accent value: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Demo/4-Demo.txt: -------------------------------------------------------------------------------- 1 | Macro: "4-Demo" 2 | Description: "Demonstrates track selection, device insertion, and clip creation." 3 | Author: "Centomila" 4 | 5 | Track Select (2) 6 | Insert Device ("Drum Machine") 7 | Wait (300) 8 | Message ("This Drum Machine needs a kick") 9 | BB Close Panel 10 | Drum Pad Insert Device ("C1", "V0 Kick") 11 | Wait (1500) 12 | Message ("Let's create a clip in the Arranger") 13 | BB Arranger Mode 14 | focus_or_toggle_arranger 15 | toggle_clip_launcher 16 | Down 17 | Wait (100) 18 | move_time_selection_one_step_later 19 | 20 | Clip Create (0, 1) 21 | Wait (1200) 22 | switch_between_event_and_time_selection 23 | Message ("We need some notes") 24 | Wait (1500) 25 | Wait (100) 26 | BB Preset ("Kick Four on the Floor") 27 | BB Pattern Repeat (8) 28 | BB Post Action AutoResize ("Off") 29 | BB Generate 30 | Wait (1000) 31 | 32 | Macro ("Demo/5-Demo") 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/ArrangerLoopEndCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.transport; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the end position of the arranger loop. 8 | */ 9 | public class ArrangerLoopEndCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | double duration = Double.parseDouble(params[0].trim()); 19 | extension.transport.arrangerLoopDuration().set(duration); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid loop duration: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBPatternRepeatCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.PatternSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to set the pattern repeat quantity for BeatBuddy. 9 | */ 10 | public class BBPatternRepeatCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | int repeatQty = Integer.parseInt(params[0]); 20 | PatternSettings.setRepeatPattern(repeatQty); 21 | } catch (NumberFormatException e) { 22 | reportError("Invalid repeat quantity: " + params[0], extension); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/transport/ArrangerLoopStartCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.transport; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to set the start position of the arranger loop. 8 | */ 9 | public class ArrangerLoopStartCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | double startPos = Double.parseDouble(params[0].trim()); 19 | extension.transport.arrangerLoopStart().set(startPos); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid loop start position: " + params[0], extension); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBPostActionAutoResizeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.PostActionSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to set the auto-resize option for post-pattern-generation. 9 | */ 10 | public class BBPostActionAutoResizeCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | String setting = params[0].trim(); 19 | if (setting.equals("true") || setting.equals("On")) { 20 | PostActionSettings.setAutoResizeLoopLengthSetting("On"); 21 | } else { 22 | PostActionSettings.setAutoResizeLoopLengthSetting("Off"); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipColorCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.api.Color; 6 | 7 | /** 8 | * Command to set the color of the currently selected clip. 9 | */ 10 | public class ClipColorCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | String colorStr = params[0].trim(); 20 | Color color = Color.fromHex(colorStr); 21 | extension.getLauncherOrArrangerAsClip().color().set(color); 22 | } catch (Exception e) { 23 | reportError("Invalid color format: " + params[0], extension); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/marker/DeleteAllCueMarkersCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.marker; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.controller.api.CueMarker; 6 | 7 | /** 8 | * Command to delete all cue markers in the project. 9 | */ 10 | public class DeleteAllCueMarkersCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | // Pass through multiple times to ensure all markers are deleted 15 | for (int pass = 0; pass < 16; pass++) { 16 | for (int i = 0; i < 128; i++) { 17 | CueMarker cueMarker = extension.cueMarkerBank.getItemAt(i); 18 | if (cueMarker.exists().get()) { 19 | cueMarker.deleteObject(); 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipDuplicateCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to duplicate the currently selected clip. 9 | */ 10 | public class ClipDuplicateCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Arranger")) { 15 | extension.application.duplicate(); 16 | } else { 17 | extension.getLauncherOrArrangerAsClip().clipLauncherSlot().duplicateClip(); 18 | int clipPosition = extension.getLauncherOrArrangerAsClip().clipLauncherSlot().sceneIndex().get(); 19 | extension.sceneBank.scrollIntoView(clipPosition); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/BitX/BitX ALL.txt: -------------------------------------------------------------------------------- 1 | Macro: "BitX All" 2 | Descritpion: "Change the track tempo (Requires BitX)" 3 | Author: "Centomila" 4 | 5 | Clip Create (1, 16) 6 | Macro ("BitX/BitX BPM (Tempo)") 7 | Clip Duplicate 8 | Select next item 9 | Macro ("BitX/BitX LDR (Load Drum Rack)") 10 | Select next item 11 | Clip Duplicate 12 | Macro ("BitX/BitX LIR (Load Instrument)") 13 | Select next item 14 | Clip Duplicate 15 | Macro ("BitX/BitX OSC (Send OSC Message)") 16 | Select next item 17 | Clip Duplicate 18 | Macro ("BitX/BitX SCF (Scale Function)") 19 | Select next item 20 | Clip Duplicate 21 | Macro ("BitX/BitX SMW (Setting Message)") 22 | Select next item 23 | Clip Duplicate 24 | Macro ("BitX/BitX SNF (Set Note Filter)") 25 | Select next item 26 | Clip Duplicate 27 | Macro ("BitX/BitX SNT (Set Note Transform)") 28 | Select next item 29 | Clip Duplicate 30 | Macro ("BitX/BitX SPN (Assign Name)") 31 | Select next item 32 | Clip Duplicate 33 | Macro ("BitX/BitX STS (Set Time Signature)") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Rename Loop.txt: -------------------------------------------------------------------------------- 1 | Macro: "Rename Loop Incremental" 2 | Description: "Example of using loops and vars to rename tracks and clips." 3 | Author: "Centomila" 4 | 5 | // Go to the first track 6 | select_track1 7 | 8 | 9 | // Set variables 10 | var trackName = "My Track" 11 | var clipLength = 4 12 | var basePosition = 4 13 | 14 | // Loop to rename for 8 tracks 15 | for (i=1 to 8) { 16 | Track Rename ("${trackName} n. ${i}") 17 | Select next track 18 | } 19 | 20 | select_track1 21 | 22 | 23 | // Use loops with the variables 24 | for (i = 1 to 4) { 25 | Clip Create(${i}, ${clipLength}) 26 | Clip Rename ("${trackName} n. ${i}") 27 | Clip Color("#${i*2}F0000") 28 | } 29 | 30 | 31 | 32 | // Create clips with calculated positions and lengths 33 | for (i = 1 to 4) { 34 | // This is a comment! 35 | Clip Create(${i+basePosition}, ${clipLength*i/2}) 36 | Clip Rename ("${trackName} n. ${i+basePosition}") 37 | Clip Color("#FF1${i+1*2}${i*4}0") 38 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipLengthCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to set the length of the currently selected clip. 9 | */ 10 | public class ClipLengthCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | double length = Double.parseDouble(params[0].trim()); 20 | // extension.getLauncherOrArrangerAsClip().getLoopLength().set(length); 21 | ClipUtils.setLoopLength(extension.getLauncherOrArrangerAsClip(), 0.0, length); 22 | 23 | } catch (NumberFormatException e) { 24 | reportError("Invalid length value: " + params[0], extension); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackDeleteCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to delete a track by index. 8 | */ 9 | public class TrackDeleteCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | int trackIndex = Integer.parseInt(params[0].trim()) - 1; // Convert from 1-based to 0-based index 19 | extension.trackBank.getItemAt(trackIndex).deleteObject(); 20 | } catch (NumberFormatException e) { 21 | reportError("Invalid track index: " + params[0], extension); 22 | } catch (IndexOutOfBoundsException e) { 23 | reportError("Track index out of range: " + params[0], extension); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/bb/BBToggleLauncherArrangerModeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.bb; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.EnumValue; 7 | import com.bitwig.extension.controller.api.SettableEnumValue; 8 | 9 | /** 10 | * Command to toggle between Launcher and Arranger modes. 11 | */ 12 | public class BBToggleLauncherArrangerModeCommand extends BaseCommand { 13 | 14 | @Override 15 | public void execute(String[] params, BitwigBuddyExtension extension) { 16 | // String currentMode = ((EnumValue) ModeSelectSettings.toggleLauncherArrangerSetting).get(); 17 | String currentMode = ModeSelectSettings.getCurrentLauncherArrangerToggleString(); 18 | String newMode = currentMode.equals("Launcher") ? "Arranger" : "Launcher"; 19 | ((SettableEnumValue) ModeSelectSettings.toggleLauncherArrangerSetting).set(newMode); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/assembly/distribution.xml: -------------------------------------------------------------------------------- 1 | 4 | distribution 5 | 6 | zip 7 | 8 | false 9 | 10 | 11 | 12 | ${project.build.directory}/${project.build.finalName}.jar 13 | / 14 | BitwigBuddy.bwextension 15 | 16 | 17 | 18 | 19 | 20 | src/main/java/com/centomila/customPresetsTxt/ 21 | BitwigBuddy 22 | 23 | **/**/*.* 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report / Feature Request 3 | about: Create a bug report or feature request for BeatBuddy Extension 4 | title: "[BUG/FEATURE] - Brief Description" 5 | labels: '' 6 | assignees: 'centomila' 7 | --- 8 | 9 | ## Issue Type 10 | - [ ] Bug Report 11 | - [ ] Feature Request 12 | - [ ] Question/Support 13 | 14 | ## Description 15 | A clear and concise description of the issue or feature request. 16 | 17 | ## Steps to Reproduce (for bugs) 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | ## Expected Behavior 24 | A clear and concise description of what you expected to happen. 25 | 26 | ## Actual Behavior (for bugs) 27 | What actually happened instead. 28 | 29 | ## Screenshots 30 | If applicable, add screenshots to help explain your problem. 31 | 32 | ## Environment 33 | - Bitwig Studio version: [e.g. 5.0.5] 34 | - BitwigBuddy Extension version: [e.g. 1.0.0] 35 | - Operating System: [e.g. Windows 11, macOS 13.0, Ubuntu 22.04] 36 | 37 | ## Additional Context 38 | Add any other context about the problem here. -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipDeleteCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.bitwig.extension.controller.api.DeleteableObject; 4 | import com.centomila.BitwigBuddyExtension; 5 | import com.centomila.ModeSelectSettings; 6 | import com.centomila.utils.commands.BaseCommand; 7 | 8 | /** 9 | * Command to delete the currently selected clip. 10 | */ 11 | public class ClipDeleteCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | String currentMode = ModeSelectSettings.getCurrentLauncherArrangerToggleString(); 16 | 17 | if (currentMode.equals("Arranger")) { 18 | // In Arranger mode, use the application's delete action to delete the selected clip 19 | extension.getApplication().remove(); 20 | 21 | } else if (currentMode.equals("Launcher")) { 22 | // In Launcher mode, delete the selected clip 23 | extension.getLauncherOrArrangerAsClip().clipLauncherSlot().deleteObject(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedIsMutedCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to mute/unmute selected note steps. 12 | */ 13 | public class StepSelectedIsMutedCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | boolean isMuted = Boolean.parseBoolean(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setIsMuted(isMuted); 26 | } 27 | } catch (Exception e) { 28 | reportError("Invalid boolean value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackRenameCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to rename the selected track. 8 | */ 9 | public class TrackRenameCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 19 | if (currentTrack < 0) { 20 | extension.getHost().println("No track selected, using first track (index 0)"); 21 | currentTrack = 0; 22 | } 23 | 24 | String trackName = params[0].trim(); 25 | extension.trackBank.getItemAt(currentTrack).name().set(trackName); 26 | } catch (Exception e) { 27 | reportError("Error renaming track: " + e.getMessage(), extension); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2025] [Franco Baccarini / Centomila] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackSelectCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | 6 | /** 7 | * Command to select a track by index. 8 | */ 9 | public class TrackSelectCommand extends BaseCommand { 10 | 11 | @Override 12 | public void execute(String[] params, BitwigBuddyExtension extension) { 13 | if (!validateParamCount(params, 1, extension)) { 14 | return; 15 | } 16 | 17 | try { 18 | int trackIndex = Integer.parseInt(params[0].trim()) - 1; 19 | extension.trackBank.getItemAt(trackIndex).selectInMixer(); 20 | extension.trackBank.getItemAt(trackIndex).makeVisibleInArranger(); 21 | extension.trackBank.getItemAt(trackIndex).makeVisibleInMixer(); 22 | } catch (NumberFormatException e) { 23 | reportError("Invalid track index: " + params[0], extension); 24 | } catch (IndexOutOfBoundsException e) { 25 | reportError("Track index out of range: " + params[0], extension); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedRepeatCountCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the repeat count for selected note steps. 12 | */ 13 | public class StepSelectedRepeatCountCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | int count = Integer.parseInt(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setRepeatCount(count); 26 | } 27 | } catch (NumberFormatException e) { 28 | reportError("Invalid repeat count value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Create 8 Tracks.txt: -------------------------------------------------------------------------------- 1 | Macro: "Create 8 Tracks" 2 | Description: "Create eight tracks with colors: Drums, Bass, Bass 2, Pad, Keys, Lead, Arp, and Vocals." 3 | Author: "Centomila" 4 | 5 | // Track 1: Drums (Red) 6 | Create Instrument Track 7 | Track Color ("D32F2F") 8 | Track Rename ("Drums") 9 | 10 | 11 | // Track 2: Bass (Green) 12 | Create Instrument Track 13 | Track Color ("43A047") 14 | Track Rename ("Bass") 15 | 16 | // Track 3: Bass 2 (Teal) 17 | Create Instrument Track 18 | Track Color ("00897B") 19 | Track Rename ("Bass 2") 20 | 21 | // Track 4: Pad (Violet) 22 | Create Instrument Track 23 | Track Color ("7B1FA2") 24 | Track Rename ("Pad") 25 | 26 | // Track 5: Keys (Yellow) 27 | Create Instrument Track 28 | Track Color ("F9A825") 29 | Track Rename ("Keys") 30 | 31 | // Track 6: Lead (Cyan) 32 | Create Instrument Track 33 | Track Color ("00ACC1") 34 | Track Rename ("Lead") 35 | 36 | // Track 7: Arp (Fuchsia) 37 | Create Instrument Track 38 | Track Color ("C2185B") 39 | Track Rename ("Arp") 40 | 41 | // Track 8: Vocals (Pink) 42 | Create Audio Track 43 | Track Color ("EC407A") 44 | Track Rename ("Vocals") 45 | 46 | Track Select (1) -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedIsChanceEnabledCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to enable/disable chance for selected note steps. 12 | */ 13 | public class StepSelectedIsChanceEnabledCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | boolean isEnabled = Boolean.parseBoolean(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setIsChanceEnabled(isEnabled); 26 | } 27 | } catch (Exception e) { 28 | reportError("Invalid boolean value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedIsRepeatEnabledCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to enable/disable repeat for selected note steps. 12 | */ 13 | public class StepSelectedIsRepeatEnabledCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | boolean isEnabled = Boolean.parseBoolean(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setIsRepeatEnabled(isEnabled); 26 | } 27 | } catch (Exception e) { 28 | reportError("Invalid boolean value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedRepeatCurveCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the repeat timing curve for selected note steps. 12 | */ 13 | public class StepSelectedRepeatCurveCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double curve = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setRepeatCurve(curve); 26 | } 27 | } catch (NumberFormatException e) { 28 | reportError("Invalid repeat curve value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedIsOccurrenceEnabledCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to enable/disable occurrence for selected note steps. 12 | */ 13 | public class StepSelectedIsOccurrenceEnabledCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | boolean isEnabled = Boolean.parseBoolean(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setIsOccurrenceEnabled(isEnabled); 26 | } 27 | } catch (Exception e) { 28 | reportError("Invalid boolean value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedIsRecurrenceEnabledCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to enable/disable recurrence for selected note steps. 12 | */ 13 | public class StepSelectedIsRecurrenceEnabledCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | boolean isEnabled = Boolean.parseBoolean(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setIsRecurrenceEnabled(isEnabled); 26 | } 27 | } catch (Exception e) { 28 | reportError("Invalid boolean value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedRepeatVelocityEndCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the end velocity for note repeats. 12 | */ 13 | public class StepSelectedRepeatVelocityEndCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double velocityEnd = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setRepeatVelocityEnd(velocityEnd); 26 | } 27 | } catch (NumberFormatException e) { 28 | reportError("Invalid repeat velocity end value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedRepeatVelocityCurveCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the repeat velocity curve for selected note steps. 12 | */ 13 | public class StepSelectedRepeatVelocityCurveCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double curve = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | for (NoteStep note : selectedNotes) { 25 | note.setRepeatVelocityCurve(curve); 26 | } 27 | } catch (NumberFormatException e) { 28 | reportError("Invalid repeat velocity curve value: " + params[0], extension); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedPanCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the pan value of selected note steps. 12 | */ 13 | public class StepSelectedPanCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double pan = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setPan(pan); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid pan value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedGainCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the gain of selected note steps. 12 | */ 13 | public class StepSelectedGainCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double gain = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setGain(gain); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid gain value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedTimbreCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the timbre of selected note steps. 12 | */ 13 | public class StepSelectedTimbreCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double timbre = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setTimbre(timbre); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid timbre value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedChanceCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the chance value of selected note steps. 12 | */ 13 | public class StepSelectedChanceCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double chance = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setChance(chance); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid chance value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/ListCommandsCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.centomila.utils.commands.CommandRegistration; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Command to list all registered commands in the system. 11 | * Useful for debugging and documentation purposes. 12 | */ 13 | public class ListCommandsCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | List allCommands = CommandRegistration.getAllRegisteredCommands(); 18 | 19 | // Display total count 20 | extension.getHost().println("[BeatBuddy] === ALL REGISTERED COMMANDS (" + allCommands.size() + " total) ==="); 21 | 22 | // List all commands alphabetically 23 | allCommands.stream() 24 | .sorted() 25 | .forEach(cmd -> extension.getHost().println("[BeatBuddy] " + cmd)); 26 | 27 | // Open console so the user can see the output 28 | extension.getApplication().getAction("show_controller_script_console").invoke(); 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedRecurrenceCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set recurrence pattern for selected note steps. 12 | */ 13 | public class StepSelectedRecurrenceCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 2, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | int length = Integer.parseInt(params[0].trim()); 23 | int mask = Integer.parseInt(params[1].trim()); 24 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 25 | for (NoteStep note : selectedNotes) { 26 | note.setRecurrence(length, mask); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid recurrence parameters: " + params[0] + ", " + params[1], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedTransposeCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the transpose value of selected note steps. 12 | */ 13 | public class StepSelectedTransposeCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | int transpose = Integer.parseInt(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setTranspose(transpose); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid transpose value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedLengthCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the length of selected note steps. 12 | */ 13 | public class StepSelectedLengthCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double stepLength = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | 26 | for (NoteStep note : selectedNotes) { 27 | note.setDuration(stepLength); 28 | } 29 | } catch (NumberFormatException e) { 30 | reportError("Invalid length value: " + params[0], extension); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedOccurrenceCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteOccurrence; 7 | import com.bitwig.extension.controller.api.NoteStep; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Command to set the occurrence condition for selected note steps. 13 | */ 14 | public class StepSelectedOccurrenceCommand extends BaseCommand { 15 | 16 | @Override 17 | public void execute(String[] params, BitwigBuddyExtension extension) { 18 | if (!validateParamCount(params, 1, extension)) { 19 | return; 20 | } 21 | 22 | try { 23 | NoteOccurrence condition = NoteOccurrence.valueOf(params[0].trim()); 24 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 25 | for (NoteStep note : selectedNotes) { 26 | note.setOccurrence(condition); 27 | } 28 | } catch (IllegalArgumentException e) { 29 | reportError("Invalid occurrence value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedPressureCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the pressure (aftertouch) of selected note steps. 12 | */ 13 | public class StepSelectedPressureCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double pressure = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setPressure(pressure); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid pressure value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedVelocityCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the velocity of selected note steps. 12 | */ 13 | public class StepSelectedVelocityCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double stepVelocity = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | 26 | for (NoteStep note : selectedNotes) { 27 | note.setVelocity(stepVelocity); 28 | } 29 | } catch (NumberFormatException e) { 30 | reportError("Invalid velocity value: " + params[0], extension); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackColorCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.api.Color; 6 | 7 | /** 8 | * Command to set the color of the selected track. 9 | */ 10 | public class TrackColorCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 1, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 20 | if (currentTrack < 0) { 21 | extension.getHost().println("No track selected, using first track (index 0)"); 22 | currentTrack = 0; 23 | } 24 | 25 | String trackColorStr = params[0].trim(); 26 | Color trackColor = Color.fromHex(trackColorStr); 27 | extension.trackBank.getItemAt(currentTrack).color().set(trackColor); 28 | } catch (Exception e) { 29 | reportError("Invalid color format or track index: " + e.getMessage(), extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedVelocitySpreadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the velocity spread of selected note steps. 12 | */ 13 | public class StepSelectedVelocitySpreadCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double velocitySpread = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setVelocitySpread(velocitySpread); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid velocity spread value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSelectedReleaseVelocityCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ClipUtils; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.NoteStep; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Command to set the release velocity of selected note steps. 12 | */ 13 | public class StepSelectedReleaseVelocityCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | double releaseVelocity = Double.parseDouble(params[0].trim()); 23 | List selectedNotes = ClipUtils.getSelectedNotes(extension); 24 | extension.getHost().println("Selected notes: " + selectedNotes.size()); 25 | for (NoteStep note : selectedNotes) { 26 | note.setReleaseVelocity(releaseVelocity); 27 | } 28 | } catch (NumberFormatException e) { 29 | reportError("Invalid release velocity value: " + params[0], extension); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/SelectDrumPadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.drum; 2 | 3 | import com.bitwig.extension.controller.api.Device; 4 | import com.centomila.BitwigBuddyExtension; 5 | import com.centomila.Utils; 6 | import com.centomila.utils.DrumPadUtils; 7 | import com.centomila.utils.commands.BaseCommand; 8 | 9 | /** 10 | * Command to select a drum pad by note name. 11 | */ 12 | public class SelectDrumPadCommand extends BaseCommand { 13 | 14 | @Override 15 | public void execute(String[] params, BitwigBuddyExtension extension) { 16 | if (!validateParamCount(params, 1, extension)) { 17 | return; 18 | } 19 | 20 | try { 21 | // Subscribe to the device and drum pads if needed 22 | final Device device = DrumPadUtils.subscribeToDrumPads(extension); 23 | 24 | String noteNameFull = params[0].trim(); 25 | int midiNote = Utils.getMIDINoteNumberFromString(noteNameFull); 26 | extension.drumPadBank.scrollPosition().set(0); 27 | extension.drumPadBank.getItemAt(midiNote).selectInEditor(); 28 | 29 | // Unsubscribe from the device and drum pads if needed 30 | DrumPadUtils.unsubscribeFromDrumPads(extension, device); 31 | } catch (Exception e) { 32 | reportError("Failed to select drum pad: " + e.getMessage(), extension); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Get Functions and Vars.txt: -------------------------------------------------------------------------------- 1 | Macro: "Get Functions and Variables" 2 | Description: "Examples of get functions." 3 | Author: "Centomila" 4 | 5 | // Track and project information example 6 | 7 | // Assign the current track name to a variable 8 | var trackName = getCurrentTrackName() 9 | 10 | Wait (1500) 11 | // Show the track name in a message popup 12 | Message(Current track name is: ${trackName}) 13 | 14 | Wait (1500) 15 | // Get track position (number) 16 | var trackNumber = getCurrentTrackNumber() 17 | Message(Track position: ${trackNumber}) 18 | 19 | Wait (1500) 20 | // Get device information 21 | var deviceName = getCurrentDeviceName() 22 | Message(Current device: ${deviceName}) 23 | 24 | Wait (1500) 25 | 26 | // Get transport information 27 | var bpm = getCurrentBpm() 28 | Message("Project tempo: ${bpm} BPM") 29 | 30 | Wait (1500) 31 | 32 | // Time signature information 33 | var num = getTimeSignatureNumerator() 34 | var denom = getTimeSignatureDenominator() 35 | Message("Time signature: ${num}/${denom}") 36 | 37 | 38 | // Send information to the console for logging - both direct and wrapped syntax 39 | Console("== Bitwig State Information ==") 40 | Console("Track: ${trackName} (#${trackNumber})") 41 | 42 | // Direct function calls 43 | Console("Device: getCurrentDeviceName()") 44 | Console("Tempo: ${getCurrentBpm()} BPM") 45 | 46 | // Mixed syntax test 47 | Console("Time signature: ${getTimeSignatureNumerator()}/${getTimeSignatureDenominator()}") -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/InsertFileInDrumPadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.drum; 2 | 3 | import com.bitwig.extension.controller.api.Device; 4 | import com.centomila.BitwigBuddyExtension; 5 | import com.centomila.Utils; 6 | import com.centomila.utils.DrumPadUtils; 7 | import com.centomila.utils.commands.BaseCommand; 8 | 9 | /** 10 | * Command to insert a file into a drum pad. 11 | */ 12 | public class InsertFileInDrumPadCommand extends BaseCommand { 13 | 14 | @Override 15 | public void execute(String[] params, BitwigBuddyExtension extension) { 16 | if (!validateParamCount(params, 2, extension)) { 17 | return; 18 | } 19 | 20 | try { 21 | // Subscribe to the device and drum pads if needed 22 | final Device device = DrumPadUtils.subscribeToDrumPads(extension); 23 | 24 | String noteNameFull = params[0].trim(); 25 | int midiNote = Utils.getMIDINoteNumberFromString(noteNameFull); 26 | extension.drumPadBank.scrollPosition().set(0); 27 | String filePath = params[1].trim(); 28 | extension.drumPadBank.getItemAt(midiNote).insertionPoint().insertFile(filePath); 29 | 30 | // Unsubscribe from the device and drum pads if needed 31 | DrumPadUtils.unsubscribeFromDrumPads(extension, device); 32 | 33 | extension.getHost().println("Inserted file into drum pad: " + noteNameFull + " with file: " + filePath); 34 | } catch (Exception e) { 35 | reportError("Failed to insert file in drum pad: " + e.getMessage(), extension); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/device/InsertDeviceCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.device; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.centomila.utils.ReturnBitwigDeviceUUID; 6 | 7 | import java.util.UUID; 8 | import static com.centomila.utils.PopupUtils.showPopup; 9 | 10 | /** 11 | * Command to insert a Bitwig device in the selected track. 12 | */ 13 | public class InsertDeviceCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 23 | if (currentTrack < 0) { 24 | extension.getHost().println("No track selected, using first track (index 0)"); 25 | currentTrack = 0; 26 | } 27 | 28 | UUID deviceUUID = ReturnBitwigDeviceUUID.getDeviceUUID(params[0]); 29 | if (deviceUUID != null) { 30 | extension.trackBank.getItemAt(currentTrack).endOfDeviceChainInsertionPoint() 31 | .insertBitwigDevice(deviceUUID); 32 | } else { 33 | extension.getHost().println("Device not found: " + params[0]); 34 | showPopup("Device not found: " + params[0]); 35 | } 36 | } catch (Exception e) { 37 | reportError("Error inserting device: " + e.getMessage(), extension); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/step/StepSetCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.step; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.controller.api.Clip; 6 | 7 | /** 8 | * Command to set a step value using the Bitwig API. 9 | */ 10 | public class StepSetCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateMinParamCount(params, 5, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | int channel = Integer.parseInt(params[0]); 20 | int stepIndex = Integer.parseInt(params[1]); 21 | int noteDestination = Integer.parseInt(params[2]); 22 | int velocity = Integer.parseInt(params[3]); 23 | double duration = Double.parseDouble(params[4]); 24 | 25 | Clip clip = extension.getLauncherOrArrangerAsClip(); 26 | if (clip == null) { 27 | extension.getHost().errorln("No clip is currently selected."); 28 | return; 29 | } 30 | 31 | // Call the API method to set the step value 32 | extension.getHost().println("Setting step " + stepIndex + " on channel " + channel + " to value: " + velocity); 33 | clip.setStep(channel, stepIndex, noteDestination, velocity, duration); 34 | } catch (NumberFormatException e) { 35 | extension.getHost().errorln("Invalid number format in parameters: " + e.getMessage()); 36 | } catch (Exception e) { 37 | extension.getHost().errorln("Error setting step: " + e.getMessage()); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/device/InsertVST3Command.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.device; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.centomila.utils.ReturnVST3StringID; 6 | import static com.centomila.utils.PopupUtils.showPopup; 7 | 8 | /** 9 | * Command to insert a VST3 plugin in the selected track. 10 | */ 11 | public class InsertVST3Command extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | if (!validateParamCount(params, 1, extension)) { 16 | return; 17 | } 18 | 19 | try { 20 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 21 | if (currentTrack < 0) { 22 | extension.getHost().println("No track selected, using first track (index 0)"); 23 | currentTrack = 0; 24 | } 25 | 26 | String vst3Name = params[0].trim(); 27 | String VST3StringID = ReturnVST3StringID.getVST3StringID(vst3Name); 28 | showPopup(vst3Name + " - " + VST3StringID); 29 | 30 | if (VST3StringID != null) { 31 | extension.trackBank.getItemAt(currentTrack).endOfDeviceChainInsertionPoint() 32 | .insertVST3Device(VST3StringID); 33 | } else { 34 | extension.getHost().println("VST3 not found: " + vst3Name + " - " + VST3StringID); 35 | showPopup(VST3StringID + " not found: " + vst3Name + " - " + VST3StringID); 36 | } 37 | } catch (Exception e) { 38 | reportError("Error inserting VST3 device: " + e.getMessage(), extension); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/PrintActionsCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.GlobalPreferences; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | import java.io.FileNotFoundException; 8 | import java.io.PrintWriter; 9 | import java.io.UnsupportedEncodingException; 10 | 11 | /** 12 | * Command to print all available actions to the console and save them to a file. 13 | */ 14 | public class PrintActionsCommand extends BaseCommand { 15 | 16 | @Override 17 | public void execute(String[] params, BitwigBuddyExtension extension) { 18 | int actionsQty = extension.application.getActions().length; 19 | 20 | // Print actions to console 21 | for (int i = 0; i < actionsQty; i++) { 22 | extension.getHost().println(extension.application.getActions()[i].getName()); 23 | } 24 | 25 | // Open console 26 | extension.getApplication().getAction("show_controller_script_console").invoke(); 27 | 28 | // Save actions to file 29 | String path = GlobalPreferences.getCurrentPresetsPath() + "/Actions.txt"; 30 | try { 31 | PrintWriter writer = new PrintWriter(path, "UTF-8"); 32 | for (int i = 0; i < actionsQty; i++) { 33 | writer.println(extension.application.getActions()[i].getId() + " | " 34 | + extension.application.getActions()[i].getName()); 35 | } 36 | writer.close(); 37 | } catch (FileNotFoundException | UnsupportedEncodingException e) { 38 | reportError("Failed to save actions list: " + e.getMessage(), extension); 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/macro/state/BitwigStateProvider.java: -------------------------------------------------------------------------------- 1 | package com.centomila.macro.state; 2 | 3 | /** 4 | * Interface for providing state information from Bitwig Studio. 5 | * Allows macro scripts to access runtime values from the DAW. 6 | */ 7 | public interface BitwigStateProvider { 8 | // Track related methods 9 | String getCurrentTrackName(); 10 | int getCurrentTrackNumber(); 11 | String getCurrentTrackColor(); 12 | boolean isCurrentTrackMuted(); 13 | boolean isCurrentTrackSoloed(); 14 | boolean isCurrentTrackArmed(); 15 | double getCurrentTrackVolume(); 16 | double getCurrentTrackPan(); 17 | int getTrackCount(); 18 | 19 | // Device related methods 20 | String getCurrentDeviceName(); 21 | boolean isCurrentDeviceEnabled(); 22 | boolean isCurrentDeviceWindowOpen(); 23 | int getDeviceCount(); 24 | 25 | // Clip related methods 26 | String getCurrentClipName(); 27 | String getCurrentClipColor(); 28 | boolean isCurrentClipLooping(); 29 | double getCurrentClipLength(); 30 | boolean isCurrentClipPlaying(); 31 | boolean isCurrentClipRecording(); 32 | boolean isCurrentClipSelected(); 33 | 34 | // Transport related methods 35 | double getCurrentBpm(); 36 | int getTimeSignatureNumerator(); 37 | int getTimeSignatureDenominator(); 38 | boolean isPlaying(); 39 | boolean isRecording(); 40 | double getPlayPosition(); 41 | boolean isMetronomeEnabled(); 42 | boolean isArrangerLoopEnabled(); 43 | 44 | // Project related methods 45 | String getProjectName(); 46 | 47 | // Scene related methods 48 | int getCurrentSceneIndex(); 49 | String getCurrentSceneName(); 50 | 51 | // Utility methods 52 | boolean supportsMethod(String methodName); 53 | Object callMethod(String methodName); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/track/TrackColorAllCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.track; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.api.Color; 6 | import com.bitwig.extension.controller.api.Track; 7 | 8 | /** 9 | * Command to set the color of all tracks in the track bank. 10 | */ 11 | public class TrackColorAllCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | if (!validateParamCount(params, 1, extension)) { 16 | return; 17 | } 18 | 19 | try { 20 | String trackColorStr = params[0].trim(); 21 | Color trackColor = Color.fromHex(trackColorStr); 22 | int trackCount = extension.trackBank.getSizeOfBank(); 23 | 24 | // Apply color to all visible tracks in the trackbank 25 | int coloredCount = 0; 26 | for (int i = 0; i < trackCount; i++) { 27 | Track track = extension.trackBank.getItemAt(i); 28 | if (track.exists().get()) { 29 | track.color().set(trackColor); 30 | extension.getHost().println("Applied color to track: " + track.name().get()); 31 | coloredCount++; 32 | } 33 | } 34 | 35 | if (coloredCount > 0) { 36 | extension.getHost().println("Applied color to " + coloredCount + " tracks"); 37 | } else { 38 | extension.getHost().println("No tracks found to apply color"); 39 | } 40 | 41 | } catch (Exception e) { 42 | reportError("Invalid color format or track error: " + e.getMessage(), extension); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/DrumPadUtils.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils; 2 | 3 | import com.bitwig.extension.controller.api.Device; 4 | import com.bitwig.extension.controller.api.DrumPad; 5 | import com.centomila.BitwigBuddyExtension; 6 | import com.centomila.NoteDestinationSettings; 7 | 8 | /** 9 | * Utility class for drum pad operations. 10 | */ 11 | public class DrumPadUtils { 12 | 13 | /** 14 | * Subscribes to drum pads if they are not already subscribed. 15 | * 16 | * @param extension The BitwigBuddy extension instance 17 | * @return The device that was subscribed to 18 | */ 19 | public static Device subscribeToDrumPads(BitwigBuddyExtension extension) { 20 | Device device = extension.deviceBank.getDevice(0); 21 | 22 | if (!(NoteDestinationSettings.getLearnNoteSelectorAsString()).equals("DM")) { 23 | // Subscribe to the device and drum pads 24 | device.subscribe(); 25 | extension.drumPadBank.scrollPosition().set(0); 26 | 27 | for (int i = 0; i < extension.drumPadBank.getSizeOfBank(); i++) { 28 | DrumPad drumPad = extension.drumPadBank.getItemAt(i); 29 | drumPad.subscribe(); 30 | } 31 | } 32 | 33 | return device; 34 | } 35 | 36 | /** 37 | * Unsubscribes from drum pads if they were temporarily subscribed. 38 | * 39 | * @param extension The BitwigBuddy extension instance 40 | * @param device The device to unsubscribe from 41 | */ 42 | public static void unsubscribeFromDrumPads(BitwigBuddyExtension extension, Device device) { 43 | if (!(NoteDestinationSettings.getLearnNoteSelectorAsString()).equals("DM")) { 44 | for (int i = 0; i < extension.drumPadBank.getSizeOfBank(); i++) { 45 | DrumPad drumPad = extension.drumPadBank.getItemAt(i); 46 | drumPad.unsubscribe(); 47 | } 48 | device.unsubscribe(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/InsertVST3InDrumPadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.drum; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.Utils; 5 | import com.centomila.utils.ReturnVST3StringID; 6 | import com.centomila.utils.commands.BaseCommand; 7 | import com.centomila.utils.DrumPadUtils; 8 | import static com.centomila.utils.PopupUtils.showPopup; 9 | 10 | import com.bitwig.extension.controller.api.Device; 11 | 12 | /** 13 | * Command to insert a VST3 plugin into a drum pad. 14 | */ 15 | public class InsertVST3InDrumPadCommand extends BaseCommand { 16 | 17 | @Override 18 | public void execute(String[] params, BitwigBuddyExtension extension) { 19 | if (!validateParamCount(params, 2, extension)) { 20 | return; 21 | } 22 | 23 | try { 24 | // Subscribe to the device and drum pads if needed 25 | final Device device = DrumPadUtils.subscribeToDrumPads(extension); 26 | 27 | String noteNameFull = params[0].trim(); 28 | int midiNote = Utils.getMIDINoteNumberFromString(noteNameFull); 29 | extension.drumPadBank.scrollPosition().set(0); 30 | String vst3Name = params[1].trim(); 31 | String vst3StringID = ReturnVST3StringID.getVST3StringID(vst3Name); 32 | if (vst3StringID != null) { 33 | extension.drumPadBank.getItemAt(midiNote).insertionPoint().insertVST3Device(vst3StringID); 34 | extension.getHost().println("Inserted VST3 into drum pad: " + noteNameFull + " with VST3: " + vst3Name); 35 | } else { 36 | extension.getHost().println("VST3 not found: " + vst3Name); 37 | showPopup("VST3 not found: " + vst3Name); 38 | } 39 | 40 | // Unsubscribe from the device and drum pads if needed 41 | DrumPadUtils.unsubscribeFromDrumPads(extension, device); 42 | } catch (Exception e) { 43 | reportError("Failed to insert VST3 in drum pad: " + e.getMessage(), extension); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/BitwigBuddyExtensionDefinition.java: -------------------------------------------------------------------------------- 1 | package com.centomila; 2 | import java.util.UUID; 3 | 4 | import com.bitwig.extension.api.PlatformType; 5 | import com.bitwig.extension.controller.AutoDetectionMidiPortNamesList; 6 | import com.bitwig.extension.controller.ControllerExtensionDefinition; 7 | import com.bitwig.extension.controller.api.ControllerHost; 8 | 9 | public class BitwigBuddyExtensionDefinition extends ControllerExtensionDefinition 10 | { 11 | private static final UUID DRIVER_ID = UUID.fromString("27615dbe-6d9f-4bb6-9b32-fe6707040a02"); 12 | 13 | public BitwigBuddyExtensionDefinition() 14 | { 15 | } 16 | 17 | @Override 18 | public String getName() 19 | { 20 | return "BitwigBuddy"; 21 | } 22 | 23 | @Override 24 | public String getAuthor() 25 | { 26 | return "centomila"; 27 | } 28 | 29 | @Override 30 | public String getVersion() 31 | { 32 | return "1.0.0 alpha 2"; 33 | } 34 | 35 | @Override 36 | public UUID getId() 37 | { 38 | return DRIVER_ID; 39 | } 40 | 41 | @Override 42 | public String getHardwareVendor() 43 | { 44 | return "centomila"; 45 | } 46 | 47 | @Override 48 | public String getHardwareModel() 49 | { 50 | return "BitwigBuddy"; 51 | } 52 | 53 | /** {@inheritDoc} */ 54 | @Override 55 | public String getHelpFilePath() { 56 | return "https://bitwigbuddy.centomila.com/"; 57 | } 58 | 59 | @Override 60 | public int getRequiredAPIVersion() 61 | { 62 | return 20; 63 | } 64 | 65 | @Override 66 | public int getNumMidiInPorts() 67 | { 68 | return 0; 69 | } 70 | 71 | @Override 72 | public int getNumMidiOutPorts() 73 | { 74 | return 0; 75 | } 76 | 77 | @Override 78 | public void listAutoDetectionMidiPortNames(final AutoDetectionMidiPortNamesList list, final PlatformType platformType) 79 | { 80 | } 81 | 82 | @Override 83 | public BitwigBuddyExtension createInstance(final ControllerHost host) 84 | { 85 | return new BitwigBuddyExtension(this, host); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/CreateDrumPadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.drum; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.Utils; 5 | import com.centomila.utils.DrumPadUtils; 6 | import com.centomila.utils.commands.BaseCommand; 7 | import com.bitwig.extension.api.Color; 8 | import com.bitwig.extension.controller.api.Device; 9 | 10 | /** 11 | * Command to create and configure a drum pad. 12 | * `Drum Pad Insert Empty("C#2", "Name", "#D00000)` 13 | */ 14 | public class CreateDrumPadCommand extends BaseCommand { 15 | 16 | @Override 17 | public void execute(String[] params, BitwigBuddyExtension extension) { 18 | if (!validateParamCount(params, 3, extension)) { 19 | return; 20 | } 21 | 22 | try { 23 | // Subscribe to the device and drum pads if needed 24 | final Device device = DrumPadUtils.subscribeToDrumPads(extension); 25 | 26 | // Parse parameters 27 | String noteNameFull = params[0].trim(); 28 | String drumBankName = params[1].trim(); 29 | String drumBankColor = params[2].trim(); 30 | 31 | // Convert note name to MIDI note number 32 | int midiNote = Utils.getMIDINoteNumberFromString(noteNameFull); 33 | extension.drumPadBank.scrollPosition().set(0); 34 | 35 | // Create the drum pad 36 | extension.drumPadBank.getItemAt(midiNote).insertionPoint().browse(); 37 | extension.application.getAction("Dialog: OK").invoke(); 38 | 39 | // Set name and color 40 | extension.drumPadBank.getItemAt(midiNote).name().set(drumBankName); 41 | Color color = Color.fromHex(drumBankColor); 42 | extension.drumPadBank.getItemAt(midiNote).color().set(color); 43 | 44 | // Unsubscribe from the device and drum pads if needed 45 | DrumPadUtils.unsubscribeFromDrumPads(extension, device); 46 | } catch (Exception e) { 47 | reportError("Failed to create drum pad: " + e.getMessage(), extension); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # BitwigBuddy - Bitwig Studio Extension for Generating Drum Patterns (v1.0.0 Alpha 2) 2 | 3 | ![Screenshot v1.0.0 Alpha 2](image.png) 4 | 5 | ## ⚙️ Installation 6 | 7 | 1. ⬇️ Download **_BitwigBuddy-1.0.0-alpha-2.zip_** from the Release page 8 | 2. 📂 Extract the zip to the **_/Bitwig Studio/Extensions/_** folder. Be sure to extract the BitwigBuddy subfolder as well. It contains sample custom presets as txt files. 9 | 1. **Windows:** `%USERPROFILE%\Documents\Bitwig Studio\Extensions` 10 | 1. If your Documents folder is in OneDrive, it might be in `%USERPROFILE%\OneDrive\Documents\Bitwig Studio\Extensions` 11 | 2. **macOS:** `~/Documents/Bitwig Studio/Extensions` 12 | 3. **Linux:** `~/Bitwig Studio/Extensions` 13 | 3. 🟧 In Bitwig, go to **Settings > Controller > Add Extension > Centomila > BitwigBuddy** 14 | 15 | For more details, see https://bitwigbuddy.centomila.com/guide/bitwigbuddy/installation.html 16 | 17 | 18 | # Changelog 19 | 20 | All the notable changes to this project are documented in this file. 21 | 22 | 📃 [View Changelog](CHANGELOG) 23 | 24 | # 🚀 Coming Soon 25 | 26 | Check all the upcoming features and improvements in the [Project Page](https://github.com/users/centomila/projects/3) 27 | 28 | # Support me on Patreon 29 | 30 | Do you like this project? Consider [supporting me on Patreon!](https://www.patreon.com/centomila) 31 | 32 | [![Support me on Patreon](https://centomila.com/images/Patreon-Wordmark.png)](https://www.patreon.com/centomila) 33 | 34 | # Listen to my Music 35 | 36 | Do you like this project? Perhaps you'll enjoy my music as well!😳 37 | 38 | - [YouTube](https://www.youtube.com/@centomila) 39 | - [Spotify](https://open.spotify.com/artist/6bdrEk5R3Ic7nZUufyUfsE) 40 | - [Apple Music](https://music.apple.com/us/artist/centomila/962423083) 41 | - [Tidal](https://tidal.com/browse/artist/32065687/) 42 | - [Qobuz](https://play.qobuz.com/artist/24750477) 43 | - [Zvuk](https://zvuk.com/artist/3300399) 44 | - [Deezer](https://www.deezer.com/artist/7463204) 45 | - [Amazon Music](https://music.amazon.com/artists/B0B12FQRKF/centomila) 46 | - [SoundCloud](https://soundcloud.com/centomila) 47 | - [Beatport](https://www.beatport.com/artist/centomila/1136112) 48 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/CommandFactory.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | /** 10 | * Factory class for creating and retrieving command objects. 11 | * This class centralizes command registration and lookup. 12 | */ 13 | public class CommandFactory { 14 | 15 | /** 16 | * Interface for all commands that can be executed by the extension 17 | */ 18 | public interface BitwigCommand { 19 | /** 20 | * Executes the command with the given parameters 21 | * 22 | * @param params Parameters parsed from the command string 23 | * @param extension The extension instance providing access to Bitwig API 24 | */ 25 | void execute(String[] params, BitwigBuddyExtension extension); 26 | } 27 | 28 | // Registry of all available commands 29 | private static final Map commandRegistry = new HashMap<>(); 30 | 31 | /** 32 | * Registers a command with the factory 33 | * 34 | * @param commandName The name of the command (action ID) 35 | * @param command The command implementation 36 | */ 37 | public static void registerCommand(String commandName, BitwigCommand command) { 38 | commandRegistry.put(commandName, command); 39 | } 40 | 41 | /** 42 | * Retrieves a command from the registry 43 | * 44 | * @param commandName The name of the command to retrieve 45 | * @return The command implementation, or null if not found 46 | */ 47 | public static BitwigCommand getCommand(String commandName) { 48 | return commandRegistry.get(commandName); 49 | } 50 | 51 | /** 52 | * Checks if a command exists in the registry 53 | * 54 | * @param commandName The name of the command to check 55 | * @return true if the command exists, false otherwise 56 | */ 57 | public static boolean hasCommand(String commandName) { 58 | return commandRegistry.containsKey(commandName); 59 | } 60 | 61 | /** 62 | * Gets all registered command names 63 | * 64 | * @return List of command names 65 | */ 66 | public static List getAllCommandNames() { 67 | return new ArrayList<>(commandRegistry.keySet()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/drum/InsertBitwigDeviceInDrumPadCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.drum; 2 | 3 | import com.bitwig.extension.controller.api.Device; 4 | import com.centomila.BitwigBuddyExtension; 5 | import com.centomila.Utils; 6 | import com.centomila.utils.commands.BaseCommand; 7 | import com.centomila.utils.DrumPadUtils; 8 | 9 | import java.util.UUID; 10 | 11 | import static com.centomila.utils.PopupUtils.showPopup; 12 | 13 | /** 14 | * Command to insert a Bitwig device in a drum pad. 15 | */ 16 | public class InsertBitwigDeviceInDrumPadCommand extends BaseCommand { 17 | 18 | @Override 19 | public void execute(String[] params, BitwigBuddyExtension extension) { 20 | if (!validateParamCount(params, 2, extension)) { 21 | return; 22 | } 23 | 24 | try { 25 | int bankSize = extension.deviceBank.getSizeOfBank(); 26 | if (bankSize == 0) { 27 | extension.getHost().println("No devices found in the bank."); 28 | showPopup("No devices found in the bank."); 29 | return; 30 | } 31 | 32 | // Subscribe to the device and drum pads if needed 33 | final Device device = DrumPadUtils.subscribeToDrumPads(extension); 34 | 35 | String noteNameFull = params[0].trim(); 36 | int midiNote = Utils.getMIDINoteNumberFromString(noteNameFull); 37 | extension.drumPadBank.scrollPosition().set(0); 38 | 39 | UUID deviceUUID = getDeviceUUID(params[1].trim()); 40 | if (deviceUUID != null) { 41 | extension.drumPadBank.getItemAt(midiNote).insertionPoint().insertBitwigDevice(deviceUUID); 42 | } else { 43 | extension.getHost().println("Device not found: " + params[1]); 44 | showPopup("Device not found: " + params[1]); 45 | } 46 | 47 | // Unsubscribe from the device and drum pads if needed 48 | DrumPadUtils.unsubscribeFromDrumPads(extension, device); 49 | } catch (Exception e) { 50 | reportError("Error inserting device in drum pad: " + e.getMessage(), extension); 51 | } 52 | } 53 | 54 | private UUID getDeviceUUID(String deviceName) { 55 | // Reuse existing method from ReturnBitwigDeviceUUID or use a simpler approach 56 | return com.centomila.utils.ReturnBitwigDeviceUUID.getDeviceUUID(deviceName); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Melodic Techno.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Melodic Techno (Detailed Cue Markers)" 2 | Description: "Cue Markers for a Melodic Techno Track." 3 | Author: "Centomila" 4 | Bpm (124) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | Wait (200) 9 | 10 | // Atmospheric intro 11 | jump_to_beginning_of_arranger_window 12 | Wait (100) 13 | insert_arranger_cue_marker_at_play_position 14 | CueMarkerName (1, "Intro - Pads & Atmos") 15 | 16 | // 16 bars - Kick & basic bass elements enter 17 | jump_forward_8_bars 18 | jump_forward_8_bars 19 | Wait (100) 20 | insert_arranger_cue_marker_at_play_position 21 | CueMarkerName (2, "Kick & Bass Intro") 22 | 23 | // 16 bars - Main groove with percussion fills 24 | jump_forward_8_bars 25 | jump_forward_8_bars 26 | Wait (100) 27 | insert_arranger_cue_marker_at_play_position 28 | CueMarkerName (3, "Main Groove") 29 | 30 | // 16 bars - First build-up section 31 | jump_forward_8_bars 32 | jump_forward_8_bars 33 | Wait (100) 34 | insert_arranger_cue_marker_at_play_position 35 | CueMarkerName (4, "Build-Up") 36 | 37 | // 16 bars - First main section with lead elements 38 | jump_forward_8_bars 39 | jump_forward_8_bars 40 | Wait (100) 41 | insert_arranger_cue_marker_at_play_position 42 | CueMarkerName (5, "First Main Section") 43 | 44 | // 16 bars - Breakdown with atmospheric elements 45 | jump_forward_8_bars 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (6, "Breakdown") 50 | 51 | // 16 bars - Tension building section 52 | jump_forward_8_bars 53 | jump_forward_8_bars 54 | Wait (100) 55 | insert_arranger_cue_marker_at_play_position 56 | CueMarkerName (7, "Second Build-Up") 57 | 58 | // 16 bars - Second main section (climax) 59 | jump_forward_8_bars 60 | jump_forward_8_bars 61 | Wait (100) 62 | insert_arranger_cue_marker_at_play_position 63 | CueMarkerName (8, "Main Peak Section") 64 | 65 | // 16 bars - Energy sustain section 66 | jump_forward_8_bars 67 | jump_forward_8_bars 68 | Wait (100) 69 | insert_arranger_cue_marker_at_play_position 70 | CueMarkerName (9, "Sustain Section") 71 | 72 | // 16 bars - Gradual elements removal 73 | jump_forward_8_bars 74 | jump_forward_8_bars 75 | Wait (100) 76 | insert_arranger_cue_marker_at_play_position 77 | CueMarkerName (10, "Elements Removal") 78 | 79 | // 16 bars - Outro with pad and atmosphere 80 | jump_forward_8_bars 81 | jump_forward_8_bars 82 | Wait (100) 83 | insert_arranger_cue_marker_at_play_position 84 | CueMarkerName (11, "Outro") 85 | 86 | Message ("Melodic Techno Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/BaseCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.PopupUtils; 5 | 6 | /** 7 | * Base abstract class for all command implementations. 8 | * Provides common functionality for command execution. 9 | */ 10 | public abstract class BaseCommand implements CommandFactory.BitwigCommand { 11 | 12 | /** 13 | * Validates that the command has a specific number of parameters 14 | * 15 | * @param params The parameters array 16 | * @param expectedCount The expected number of parameters 17 | * @param extension The extension instance for error reporting 18 | * @return true if validation passes, false if it fails 19 | */ 20 | protected boolean validateParamCount(String[] params, int expectedCount, BitwigBuddyExtension extension) { 21 | if (params.length != expectedCount) { 22 | extension.getHost().errorln(getClass().getSimpleName() + 23 | " requires " + expectedCount + " parameters, but " + params.length + " were provided"); 24 | PopupUtils.showPopup("Command error: Wrong number of parameters"); 25 | return false; 26 | } 27 | return true; 28 | } 29 | 30 | /** 31 | * Validates that the command has at least a minimum number of parameters 32 | * 33 | * @param params The parameters array 34 | * @param minCount The minimum number of parameters 35 | * @param extension The extension instance for error reporting 36 | * @return true if validation passes, false if it fails 37 | */ 38 | protected boolean validateMinParamCount(String[] params, int minCount, BitwigBuddyExtension extension) { 39 | if (params.length < minCount) { 40 | extension.getHost().errorln(getClass().getSimpleName() + 41 | " requires at least " + minCount + " parameters, but " + params.length + " were provided"); 42 | PopupUtils.showPopup("Command error: Not enough parameters"); 43 | return false; 44 | } 45 | return true; 46 | } 47 | 48 | /** 49 | * Reports an error during command execution 50 | * 51 | * @param message The error message 52 | * @param extension The extension instance for error reporting 53 | */ 54 | protected void reportError(String message, BitwigBuddyExtension extension) { 55 | extension.getHost().errorln(getClass().getSimpleName() + ": " + message); 56 | PopupUtils.showPopup("Command error: " + message); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipSelectCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | 7 | /** 8 | * Command to select a clip. 9 | * If an index parameter is provided, selects a specific clip in the launcher. 10 | * Without a parameter, selects the current clip. 11 | */ 12 | public class ClipSelectCommand extends BaseCommand { 13 | 14 | @Override 15 | public void execute(String[] params, BitwigBuddyExtension extension) { 16 | // If no parameters, select the current clip 17 | if (params.length == 0) { 18 | extension.getLauncherOrArrangerAsClip().clipLauncherSlot().select(); 19 | return; 20 | } 21 | 22 | try { 23 | // If we're in launcher mode and an index is provided, select the clip at that index 24 | if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Launcher")) { 25 | // Get the current track index 26 | int trackIndex = extension.trackBank.cursorIndex().getAsInt(); 27 | if (trackIndex < 0) { 28 | extension.getHost().println("No track selected, using first track (index 0)"); 29 | trackIndex = 0; 30 | } 31 | 32 | // Parse the clip index parameter (1-based in the command, converted to 0-based) 33 | int slotIndex = Integer.parseInt(params[0].trim()) - 1; 34 | 35 | if (slotIndex >= 0 && slotIndex < 128) { 36 | // Select the clip at the specified index 37 | extension.trackBank.getItemAt(trackIndex).clipLauncherSlotBank().getItemAt(slotIndex).select(); 38 | extension.getHost().println("Selected clip at index: " + (slotIndex + 1)); 39 | } else { 40 | reportError("Invalid slot index: " + (slotIndex + 1), extension); 41 | } 42 | } else { 43 | // In arranger mode, we can't select by index, so just select current clip 44 | extension.getLauncherOrArrangerAsClip().clipLauncherSlot().select(); 45 | extension.getHost().println("In Arranger mode, index parameter is ignored"); 46 | } 47 | } catch (NumberFormatException e) { 48 | reportError("Invalid index parameter: " + params[0], extension); 49 | } catch (IndexOutOfBoundsException e) { 50 | reportError("Index out of bounds: " + params[0], extension); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/EditClipSettings.java: -------------------------------------------------------------------------------- 1 | package com.centomila; 2 | 3 | import static com.centomila.utils.PopupUtils.showPopup; 4 | import static com.centomila.utils.SettingsHelper.createSignalSetting; 5 | import static com.centomila.utils.SettingsHelper.createStringSetting; 6 | import static com.centomila.utils.SettingsHelper.disableSetting; 7 | import static com.centomila.utils.SettingsHelper.hideSetting; 8 | import static com.centomila.utils.SettingsHelper.showSetting; 9 | import static com.centomila.utils.SettingsHelper.titleWithLine; 10 | 11 | import com.bitwig.extension.controller.api.Clip; 12 | import com.bitwig.extension.controller.api.Setting; 13 | import com.bitwig.extension.controller.api.Signal; 14 | 15 | public class EditClipSettings { 16 | private static BitwigBuddyExtension extension; 17 | public static Setting editClipSpacer; 18 | public static Setting editClipBtnSignal; 19 | public static Setting[] allSettings; 20 | private static final String CATEGORY_EDIT_CLIP = "3 Edit Clip"; 21 | 22 | public static void init(BitwigBuddyExtension extension) { 23 | EditClipSettings.extension = extension; 24 | initEditClipSettings(); 25 | } 26 | 27 | private static void initEditClipSettings() { 28 | editClipSpacer = (Setting) createStringSetting(titleWithLine("EDIT CLIP ------------------------------------"), CATEGORY_EDIT_CLIP,9999, 29 | "---------------------------------------------------"); 30 | disableSetting(editClipSpacer); 31 | 32 | editClipBtnSignal = (Setting) createSignalSetting("Update Selected Steps Velocity", CATEGORY_EDIT_CLIP, "Update Selected Steps Velocity"); 33 | 34 | 35 | // Obsereve the edit clip button signal 36 | ((Signal) editClipBtnSignal).addSignalObserver(() -> { 37 | editClipAction(); 38 | }); 39 | allSettings = new Setting[] { editClipSpacer, editClipBtnSignal }; 40 | } 41 | 42 | public static void editClipAction() { 43 | // Create an array with the selected notes and randomize the velocity of each note 44 | 45 | // Get the selected clip 46 | Clip clip = extension.getLauncherOrArrangerAsClip(); 47 | if (clip == null) { 48 | showPopup("No clip selected"); 49 | return; 50 | } 51 | 52 | 53 | try { 54 | ClipUtils.applyVelocityShapeToSelectedNotes(extension); 55 | } catch (Exception e) { 56 | 57 | e.printStackTrace(); 58 | } 59 | 60 | 61 | 62 | 63 | 64 | } 65 | 66 | public static void showAllSettings() { 67 | showSetting(allSettings); 68 | } 69 | 70 | public static void hideAllSettings() { 71 | hideSetting(allSettings); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipMoveCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import com.bitwig.extension.controller.api.ClipLauncherSlot; 7 | 8 | import static com.centomila.utils.PopupUtils.showPopup; 9 | 10 | /** 11 | * Command to move the selected clip by a specified amount. 12 | */ 13 | public class ClipMoveCommand extends BaseCommand { 14 | 15 | @Override 16 | public void execute(String[] params, BitwigBuddyExtension extension) { 17 | if (!validateParamCount(params, 1, extension)) { 18 | return; 19 | } 20 | 21 | try { 22 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 23 | if (currentTrack < 0) { 24 | extension.getHost().println("No track selected, using first track (index 0)"); 25 | currentTrack = 0; 26 | } 27 | 28 | double moveAmount = Double.parseDouble(params[0].trim()); 29 | 30 | if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Arranger")) { 31 | moveInArranger(moveAmount, extension); 32 | } else { 33 | moveInLauncher(moveAmount, extension, currentTrack); 34 | } 35 | } catch (NumberFormatException e) { 36 | reportError("Invalid move amount: " + params[0], extension); 37 | } 38 | } 39 | 40 | private void moveInArranger(double moveAmount, BitwigBuddyExtension extension) { 41 | if (moveAmount > 0) { 42 | for (int i = 0; i < moveAmount; i++) { 43 | extension.getApplication().getAction("nudge_events_one_step_later").invoke(); 44 | } 45 | } else if (moveAmount < 0) { 46 | for (int i = 0; i < Math.abs(moveAmount); i++) { 47 | extension.getApplication().getAction("nudge_events_one_step_earlier").invoke(); 48 | } 49 | } 50 | } 51 | 52 | private void moveInLauncher(double moveAmount, BitwigBuddyExtension extension, int currentTrack) { 53 | ClipLauncherSlot sourceSlot = extension.getLauncherOrArrangerAsClip().clipLauncherSlot(); 54 | 55 | int sourceSlotIndex = sourceSlot.sceneIndex().get(); 56 | int targetSlotIndex = sourceSlotIndex + (int) moveAmount; 57 | 58 | showPopup("Source slot index: " + sourceSlotIndex + " Target slot index: " + targetSlotIndex); 59 | 60 | if (targetSlotIndex >= 0 && targetSlotIndex < 128) { 61 | extension.trackBank.getItemAt(currentTrack).clipLauncherSlotBank().getItemAt(targetSlotIndex) 62 | .replaceInsertionPoint().moveSlotsOrScenes(sourceSlot); 63 | 64 | extension.trackBank.getItemAt(currentTrack).clipLauncherSlotBank().getItemAt(targetSlotIndex).select(); 65 | extension.sceneBank.cursorIndex().set(targetSlotIndex); 66 | } else { 67 | showPopup("Invalid target slot index: " + targetSlotIndex); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/device/InsertFileCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.device; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.ModeSelectSettings; 5 | import com.centomila.utils.commands.BaseCommand; 6 | import static com.centomila.utils.PopupUtils.showPopup; 7 | 8 | /** 9 | * Command to insert a file (preset or sample) into a track. 10 | */ 11 | public class InsertFileCommand extends BaseCommand { 12 | 13 | @Override 14 | public void execute(String[] params, BitwigBuddyExtension extension) { 15 | if (!validateParamCount(params, 2, extension)) { 16 | return; 17 | } 18 | 19 | try { 20 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 21 | if (currentTrack < 0) { 22 | extension.getHost().println("No track selected, using first track (index 0)"); 23 | currentTrack = 0; 24 | } 25 | 26 | int slotIndexInsertFile = Integer.parseInt(params[0].trim()); 27 | String filePath = params[1].trim(); 28 | 29 | if (filePath.endsWith(".bwpreset")) { 30 | handlePresetFile(extension, currentTrack, slotIndexInsertFile, filePath); 31 | } else { 32 | handleSampleFile(extension, currentTrack, slotIndexInsertFile, filePath); 33 | } 34 | } catch (NumberFormatException e) { 35 | reportError("Invalid slot index: " + params[0], extension); 36 | } catch (Exception e) { 37 | reportError("Error inserting file: " + e.getMessage(), extension); 38 | } 39 | } 40 | 41 | private void handlePresetFile(BitwigBuddyExtension extension, int currentTrack, int slotIndex, String filePath) { 42 | if (slotIndex == 0) { 43 | extension.trackBank.getItemAt(currentTrack).clipLauncherSlotBank().getItemAt(0).replaceInsertionPoint() 44 | .insertFile(filePath); 45 | } else if (slotIndex > 0) { 46 | extension.trackBank.getItemAt(currentTrack).endOfDeviceChainInsertionPoint().insertFile(filePath); 47 | } else if (slotIndex < 0) { 48 | extension.trackBank.getItemAt(currentTrack).startOfDeviceChainInsertionPoint().insertFile(filePath); 49 | } 50 | } 51 | 52 | private void handleSampleFile(BitwigBuddyExtension extension, int currentTrack, int slotIndex, String filePath) { 53 | if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Launcher")) { 54 | slotIndex = slotIndex - 1; 55 | extension.trackBank.getItemAt(currentTrack).clipLauncherSlotBank() 56 | .createEmptyClip(slotIndex, 4); 57 | extension.trackBank.getItemAt(currentTrack).clipLauncherSlotBank().getItemAt(slotIndex) 58 | .replaceInsertionPoint().insertFile(filePath); 59 | } else if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Arranger")) { 60 | showPopup("I can't insert files in the arranger. Launcher only."); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/marker/CueMarkerNameCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.marker; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.utils.commands.BaseCommand; 5 | import com.bitwig.extension.controller.api.CueMarker; 6 | 7 | /** 8 | * Command to rename a cue marker. 9 | */ 10 | public class CueMarkerNameCommand extends BaseCommand { 11 | 12 | @Override 13 | public void execute(String[] params, BitwigBuddyExtension extension) { 14 | if (!validateParamCount(params, 2, extension)) { 15 | return; 16 | } 17 | 18 | try { 19 | final int itemNumber = Integer.parseInt(params[0].trim()) - 1; 20 | final String name = params[1].trim(); 21 | 22 | // Use a longer delay (200ms) to ensure Bitwig has time to register the marker 23 | extension.getHost().scheduleTask(() -> { 24 | try { 25 | // Force a refresh of the cue marker bank 26 | extension.cueMarkerBank.scrollPosition().set(0); 27 | 28 | CueMarker cueMarker = extension.cueMarkerBank.getItemAt(itemNumber); 29 | if (cueMarker != null && cueMarker.exists().get()) { 30 | cueMarker.name().set(name); 31 | extension.getHost().println("Renamed cue marker " + (itemNumber + 1) + " to: " + name); 32 | } else { 33 | // Try refreshing and retrying once if the marker isn't found 34 | extension.getHost().scheduleTask(() -> { 35 | try { 36 | CueMarker retryMarker = extension.cueMarkerBank.getItemAt(itemNumber); 37 | if (retryMarker != null && retryMarker.exists().get()) { 38 | retryMarker.name().set(name); 39 | extension.getHost().println("Renamed cue marker " + (itemNumber + 1) + " to: " + name + " (retry successful)"); 40 | } else { 41 | reportError("Cue marker at index " + (itemNumber + 1) + " doesn't exist after retry", extension); 42 | } 43 | } catch (Exception e) { 44 | reportError("Error in retry setting cue marker name: " + e.getMessage(), extension); 45 | } 46 | }, 100); // Additional 100ms delay for retry 47 | } 48 | } catch (IndexOutOfBoundsException e) { 49 | reportError("Cue marker index out of range: " + (itemNumber + 1), extension); 50 | } catch (Exception e) { 51 | reportError("Error setting cue marker name: " + e.getMessage(), extension); 52 | } 53 | }, 500); // Increased delay to 200ms 54 | 55 | } catch (NumberFormatException e) { 56 | reportError("Invalid cue marker index: " + params[0], extension); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - House.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - House (Detailed Cue Markers)" 2 | Description: "Generate Detailed Cue Markers for a House Track Arrangement." 3 | Author: "Centomila" 4 | Bpm (126) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // Start of track 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "Intro - Ambient") 14 | 15 | // 8 bars - Basic percussion begins 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Basic Percussion") 20 | 21 | // 8 bars - Full drum kit enters 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Full Drums") 26 | 27 | // 8 bars - Bass enters 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements") 32 | 33 | // 8 bars - First build-up begins 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Build-Up 1") 38 | 39 | // 4 bars - Pre-drop tension (using jump_to_beginning_of_next_bar x4) 40 | jump_to_beginning_of_next_bar 41 | jump_to_beginning_of_next_bar 42 | jump_to_beginning_of_next_bar 43 | jump_to_beginning_of_next_bar 44 | Wait (100) 45 | insert_arranger_cue_marker_at_play_position 46 | CueMarkerName (6, "Pre-Drop") 47 | 48 | // 4 bars - First drop (using jump_to_beginning_of_next_bar x4) 49 | jump_to_beginning_of_next_bar 50 | jump_to_beginning_of_next_bar 51 | jump_to_beginning_of_next_bar 52 | jump_to_beginning_of_next_bar 53 | Wait (100) 54 | insert_arranger_cue_marker_at_play_position 55 | CueMarkerName (7, "Drop 1") 56 | 57 | // 16 bars - Main section continues 58 | jump_forward_8_bars 59 | jump_forward_8_bars 60 | Wait (100) 61 | insert_arranger_cue_marker_at_play_position 62 | CueMarkerName (8, "Breakdown") 63 | 64 | // 8 bars - Minimal break section 65 | jump_forward_8_bars 66 | Wait (100) 67 | insert_arranger_cue_marker_at_play_position 68 | CueMarkerName (9, "Break Elements") 69 | 70 | // 8 bars - Build-up to second drop 71 | jump_forward_8_bars 72 | Wait (100) 73 | insert_arranger_cue_marker_at_play_position 74 | CueMarkerName (10, "Build-Up 2") 75 | 76 | // 8 bars - Second drop (main section) 77 | jump_forward_8_bars 78 | Wait (100) 79 | insert_arranger_cue_marker_at_play_position 80 | CueMarkerName (11, "Drop 2") 81 | 82 | // 16 bars - Extended drop section 83 | jump_forward_8_bars 84 | jump_forward_8_bars 85 | Wait (100) 86 | insert_arranger_cue_marker_at_play_position 87 | CueMarkerName (12, "Final Section") 88 | 89 | // 8 bars - Elements removal begins 90 | jump_forward_8_bars 91 | Wait (100) 92 | insert_arranger_cue_marker_at_play_position 93 | CueMarkerName (13, "Outro - Elements Removal") 94 | 95 | // 8 bars - Final outro 96 | jump_forward_8_bars 97 | Wait (100) 98 | insert_arranger_cue_marker_at_play_position 99 | CueMarkerName (14, "Outro - Final") 100 | 101 | Message ("House Arrangement Cue Markers Created") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Pop.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Modern Pop (Detailed Cue Markers)" 2 | Description: "Generate Detailed Cue Markers for a Modern Pop Track Arrangement." 3 | Author: "Centomila" 4 | Bpm (100) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // Start with a short intro 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "Intro - Atmospheric") 14 | 15 | // 4 bars - Vocal intro or hook teaser 16 | jump_to_beginning_of_next_bar 17 | jump_to_beginning_of_next_bar 18 | jump_to_beginning_of_next_bar 19 | jump_to_beginning_of_next_bar 20 | Wait (100) 21 | insert_arranger_cue_marker_at_play_position 22 | CueMarkerName (2, "Intro - Vocal Hook") 23 | 24 | // 8 bars - First verse starts 25 | jump_forward_8_bars 26 | Wait (100) 27 | insert_arranger_cue_marker_at_play_position 28 | CueMarkerName (3, "Verse 1") 29 | 30 | // 4 bars - Pre-chorus build 31 | jump_to_beginning_of_next_bar 32 | jump_to_beginning_of_next_bar 33 | jump_to_beginning_of_next_bar 34 | jump_to_beginning_of_next_bar 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (4, "Pre-Chorus 1") 38 | 39 | // 8 bars - Chorus with hook 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (5, "Chorus 1") 44 | 45 | // 4 bars - Post-chorus hook section 46 | jump_to_beginning_of_next_bar 47 | jump_to_beginning_of_next_bar 48 | jump_to_beginning_of_next_bar 49 | jump_to_beginning_of_next_bar 50 | Wait (100) 51 | insert_arranger_cue_marker_at_play_position 52 | CueMarkerName (6, "Post-Chorus") 53 | 54 | // 8 bars - Second verse 55 | jump_forward_8_bars 56 | Wait (100) 57 | insert_arranger_cue_marker_at_play_position 58 | CueMarkerName (7, "Verse 2") 59 | 60 | // 4 bars - Pre-chorus build 61 | jump_to_beginning_of_next_bar 62 | jump_to_beginning_of_next_bar 63 | jump_to_beginning_of_next_bar 64 | jump_to_beginning_of_next_bar 65 | Wait (100) 66 | insert_arranger_cue_marker_at_play_position 67 | CueMarkerName (8, "Pre-Chorus 2") 68 | 69 | // 8 bars - Second chorus 70 | jump_forward_8_bars 71 | Wait (100) 72 | insert_arranger_cue_marker_at_play_position 73 | CueMarkerName (9, "Chorus 2") 74 | 75 | // 4 bars - Post-chorus hook section 76 | jump_to_beginning_of_next_bar 77 | jump_to_beginning_of_next_bar 78 | jump_to_beginning_of_next_bar 79 | jump_to_beginning_of_next_bar 80 | Wait (100) 81 | insert_arranger_cue_marker_at_play_position 82 | CueMarkerName (10, "Post-Chorus 2") 83 | 84 | // 8 bars - Bridge/breakdown section 85 | jump_forward_8_bars 86 | Wait (100) 87 | insert_arranger_cue_marker_at_play_position 88 | CueMarkerName (11, "Bridge") 89 | 90 | // 4 bars - Build to final chorus 91 | jump_to_beginning_of_next_bar 92 | jump_to_beginning_of_next_bar 93 | jump_to_beginning_of_next_bar 94 | jump_to_beginning_of_next_bar 95 | Wait (100) 96 | insert_arranger_cue_marker_at_play_position 97 | CueMarkerName (12, "Build-Up") 98 | 99 | // 8 bars - Final chorus (often extended or with variations) 100 | jump_forward_8_bars 101 | Wait (100) 102 | insert_arranger_cue_marker_at_play_position 103 | CueMarkerName (13, "Final Chorus") 104 | 105 | // 8 bars - Outro with hook elements 106 | jump_forward_8_bars 107 | Wait (100) 108 | insert_arranger_cue_marker_at_play_position 109 | CueMarkerName (14, "Outro") 110 | 111 | Message ("Modern Pop Arrangement Cue Markers Created") 112 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/OpenWebUrl.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils; 2 | 3 | import java.io.IOException; 4 | import com.bitwig.extension.controller.api.ControllerHost; 5 | import static com.centomila.utils.PopupUtils.showPopup; 6 | 7 | /** 8 | * Utility class for opening URLs in the system's default web browser. 9 | * Provides platform-specific command handling for Windows, macOS, and Linux. 10 | */ 11 | public class OpenWebUrl { 12 | /** 13 | * Enum defining platform-specific commands for opening URLs and files. 14 | * Contains command configurations for Windows, macOS, and Linux systems. 15 | */ 16 | public enum PlatformCommand { 17 | /** Windows-specific commands using explorer.exe and cmd */ 18 | WINDOWS("explorer.exe", "cmd", "/c", "start"), 19 | /** macOS-specific commands using the 'open' command */ 20 | MAC("open", "open", "", ""), 21 | /** Linux-specific commands using xdg-open */ 22 | LINUX("xdg-open", "xdg-open", "", ""); 23 | 24 | final String fileExplorer; 25 | final String browserCommand; 26 | final String browserParam1; 27 | final String browserParam2; 28 | 29 | /** 30 | * Constructs a PlatformCommand with specific command parameters. 31 | * 32 | * @param fileExplorer Command to open file explorer 33 | * @param browserCommand Command to open web browser 34 | * @param browserParam1 First parameter for browser command 35 | * @param browserParam2 Second parameter for browser command 36 | */ 37 | PlatformCommand(String fileExplorer, String browserCommand, String browserParam1, String browserParam2) { 38 | this.fileExplorer = fileExplorer; 39 | this.browserCommand = browserCommand; 40 | this.browserParam1 = browserParam1; 41 | this.browserParam2 = browserParam2; 42 | } 43 | } 44 | 45 | /** 46 | * Opens a URL in the system's default web browser. 47 | * 48 | * @param host The Bitwig ControllerHost instance for platform detection 49 | * @param url The URL to open 50 | * @param pageName The name of the page (used for error reporting) 51 | */ 52 | public static void openUrl(ControllerHost host, String url, String pageName) { 53 | try { 54 | PlatformCommand cmd = getPlatformCommand(host); 55 | String[] command = cmd.browserParam1.isEmpty() 56 | ? new String[] { cmd.browserCommand, url } 57 | : new String[] { cmd.browserCommand, cmd.browserParam1, cmd.browserParam2, url }; 58 | 59 | Runtime.getRuntime().exec(command); 60 | } catch (IOException e) { 61 | host.errorln("Failed to open " + pageName + " page: " + e.getMessage()); 62 | showPopup("Please visit " + url + " in your web browser."); 63 | } 64 | } 65 | 66 | /** 67 | * Determines the appropriate platform command configuration based on the host 68 | * system. 69 | * 70 | * @param host The Bitwig ControllerHost instance used for platform detection 71 | * @return The PlatformCommand enum corresponding to the current operating 72 | * system 73 | */ 74 | private static PlatformCommand getPlatformCommand(ControllerHost host) { 75 | if (host.platformIsWindows()) 76 | return PlatformCommand.WINDOWS; 77 | if (host.platformIsMac()) 78 | return PlatformCommand.MAC; 79 | if (host.platformIsLinux()) 80 | return PlatformCommand.LINUX; 81 | return null; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/ExtensionPath.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils; 2 | 3 | import java.nio.file.Files; 4 | import java.nio.file.Path; 5 | import java.nio.file.Paths; 6 | 7 | import com.bitwig.extension.controller.api.ControllerHost; 8 | 9 | /** 10 | * Utility class for handling Bitwig Studio extension paths across different operating systems. 11 | * This class helps locate the correct extensions directory based on the user's OS and language settings. 12 | */ 13 | public class ExtensionPath { 14 | private static ControllerHost host; 15 | 16 | /** 17 | * Initializes the ExtensionPath utility with a ControllerHost instance. 18 | * This method must be called before using any other methods in this class. 19 | * 20 | * @param controllerHost The Bitwig Studio ControllerHost instance 21 | */ 22 | public static void init(ControllerHost controllerHost) { 23 | host = controllerHost; 24 | } 25 | 26 | /** 27 | * Array of localized "Documents" folder names for Windows systems in different languages. 28 | */ 29 | private static final String[] WINDOWS_DOCUMENTS_LOCALIZED = { 30 | "Documents", "Documenti", "Documentos", "Dokumente", 31 | "文档", "文書", "문서", "Документы" 32 | }; 33 | 34 | /** 35 | * Gets the default path to the Bitwig Studio extensions folder. 36 | * Handles different locations based on operating system: 37 | * - Windows: Checks both OneDrive and regular Documents folders with localization support 38 | * - MacOS: Uses ~/Documents/Bitwig Studio/Extensions 39 | * - Linux: Uses ~/Bitwig Studio/Extensions 40 | * 41 | * @return String representation of the extensions directory path 42 | */ 43 | public static String getDefaultExtensionsPath() { 44 | String userHome = System.getProperty("user.home"); 45 | 46 | // Windows 47 | if (host.platformIsWindows()) { 48 | // Check OneDrive paths first 49 | Path oneDriveBase = Paths.get(userHome, "OneDrive"); 50 | if (Files.exists(oneDriveBase)) { 51 | for (String docName : WINDOWS_DOCUMENTS_LOCALIZED) { 52 | Path path = Paths.get(userHome, "OneDrive", docName, "Bitwig Studio", "Extensions"); 53 | if (Files.exists(path)) { 54 | return path.toString(); 55 | } 56 | } 57 | } 58 | // Then check regular Documents folders 59 | for (String docName : WINDOWS_DOCUMENTS_LOCALIZED) { 60 | Path path = Paths.get(userHome, docName, "Bitwig Studio", "Extensions"); 61 | if (Files.exists(path)) { 62 | return path.toString(); 63 | } 64 | } 65 | 66 | // MacOS 67 | } else if (host.platformIsMac()) { 68 | return Paths.get(userHome, "Documents", "Bitwig Studio", "Extensions").toString(); 69 | 70 | // Linux 71 | } else if (host.platformIsLinux()) { 72 | return Paths.get(userHome, "Bitwig Studio", "Extensions").toString(); 73 | } 74 | // Fallback 75 | return Paths.get(userHome, "Documents", "Bitwig Studio", "Extensions").toString(); 76 | } 77 | 78 | /** 79 | * Gets the path to a specific subfolder within the Bitwig Studio extensions directory. 80 | * 81 | * @param subfolder Name of the subfolder to append to the extensions path 82 | * @return String representation of the complete path including the subfolder 83 | */ 84 | public static String getExstensionsSubfolderPath(String subfolder) { 85 | return Paths.get(getDefaultExtensionsPath(), subfolder).toString(); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Rock.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Modern Rock (Cue Markers)" 2 | Description: "Generate Cue Markers for a Modern Rock Track." 3 | Author: "Centomila" 4 | Bpm (120) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | Wait (200) 9 | 10 | // Start with atmospheric intro 11 | jump_to_beginning_of_arranger_window 12 | Wait (100) 13 | insert_arranger_cue_marker_at_play_position 14 | CueMarkerName (1, "Ambient Intro") 15 | 16 | // 4-bar ambient intro 17 | jump_to_beginning_of_next_bar 18 | jump_to_beginning_of_next_bar 19 | jump_to_beginning_of_next_bar 20 | jump_to_beginning_of_next_bar 21 | Wait (100) 22 | insert_arranger_cue_marker_at_play_position 23 | CueMarkerName (2, "Main Intro") 24 | 25 | // 8-bar main intro with basic instruments 26 | jump_forward_8_bars 27 | Wait (100) 28 | insert_arranger_cue_marker_at_play_position 29 | CueMarkerName (3, "Verse 1") 30 | 31 | // 8-bar first verse 32 | jump_forward_8_bars 33 | Wait (100) 34 | insert_arranger_cue_marker_at_play_position 35 | CueMarkerName (4, "Pre-Chorus") 36 | 37 | // 4-bar pre-chorus 38 | jump_to_beginning_of_next_bar 39 | jump_to_beginning_of_next_bar 40 | jump_to_beginning_of_next_bar 41 | jump_to_beginning_of_next_bar 42 | Wait (100) 43 | insert_arranger_cue_marker_at_play_position 44 | CueMarkerName (5, "Chorus 1") 45 | 46 | // 8-bar powerful first chorus 47 | jump_forward_8_bars 48 | Wait (100) 49 | insert_arranger_cue_marker_at_play_position 50 | CueMarkerName (6, "Transition") 51 | 52 | // 4-bar transition 53 | jump_to_beginning_of_next_bar 54 | jump_to_beginning_of_next_bar 55 | jump_to_beginning_of_next_bar 56 | jump_to_beginning_of_next_bar 57 | Wait (100) 58 | insert_arranger_cue_marker_at_play_position 59 | CueMarkerName (7, "Verse 2") 60 | 61 | // 8-bar second verse (often with added elements) 62 | jump_forward_8_bars 63 | Wait (100) 64 | insert_arranger_cue_marker_at_play_position 65 | CueMarkerName (8, "Pre-Chorus 2") 66 | 67 | // 4-bar pre-chorus 68 | jump_to_beginning_of_next_bar 69 | jump_to_beginning_of_next_bar 70 | jump_to_beginning_of_next_bar 71 | jump_to_beginning_of_next_bar 72 | Wait (100) 73 | insert_arranger_cue_marker_at_play_position 74 | CueMarkerName (9, "Chorus 2") 75 | 76 | // 8-bar second chorus 77 | jump_forward_8_bars 78 | Wait (100) 79 | insert_arranger_cue_marker_at_play_position 80 | CueMarkerName (10, "Bridge") 81 | 82 | // 8-bar dynamic bridge section 83 | jump_forward_8_bars 84 | Wait (100) 85 | insert_arranger_cue_marker_at_play_position 86 | CueMarkerName (11, "Breakdown") 87 | 88 | // 4-bar breakdown/quiet section 89 | jump_to_beginning_of_next_bar 90 | jump_to_beginning_of_next_bar 91 | jump_to_beginning_of_next_bar 92 | jump_to_beginning_of_next_bar 93 | Wait (100) 94 | insert_arranger_cue_marker_at_play_position 95 | CueMarkerName (12, "Guitar Solo") 96 | 97 | // 8-bar guitar solo 98 | jump_forward_8_bars 99 | Wait (100) 100 | insert_arranger_cue_marker_at_play_position 101 | CueMarkerName (13, "Build-Up") 102 | 103 | // 4-bar build-up to final chorus 104 | jump_to_beginning_of_next_bar 105 | jump_to_beginning_of_next_bar 106 | jump_to_beginning_of_next_bar 107 | jump_to_beginning_of_next_bar 108 | Wait (100) 109 | insert_arranger_cue_marker_at_play_position 110 | CueMarkerName (14, "Final Chorus") 111 | 112 | // 8-bar extended final chorus 113 | jump_forward_8_bars 114 | Wait (100) 115 | insert_arranger_cue_marker_at_play_position 116 | CueMarkerName (15, "Extended Chorus") 117 | 118 | // 8-bar extended chorus variation 119 | jump_forward_8_bars 120 | Wait (100) 121 | insert_arranger_cue_marker_at_play_position 122 | CueMarkerName (16, "Outro") 123 | 124 | // 4-bar outro 125 | jump_to_beginning_of_next_bar 126 | jump_to_beginning_of_next_bar 127 | jump_to_beginning_of_next_bar 128 | jump_to_beginning_of_next_bar 129 | 130 | Message ("Modern Rock Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Minimal Techno Extended Mix.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Minimal Techno Extended Mix" 2 | Description: "Cue Markers for a Minimal Techno extended mix." 3 | Author: "Centomila" 4 | Bpm (130) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // 1 - Silent intro for DJ mixing 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "DJ Intro - Silent") 14 | 15 | // 2 - Initial minimal elements 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Textural Elements") 20 | 21 | // 3 - First percussion elements 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Minimal Percussion") 26 | 27 | // 4 - Basic rhythm pattern starts 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements - Basic Rhythm") 32 | 33 | // 5 - Kick drum enters 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Main Elements - Kick Added") 38 | 39 | // 6 - Groove development 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (6, "Main Elements - Full Groove") 44 | 45 | // 7 - First subtle change 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (7, "Variation 1 - Subtle Change") 50 | 51 | // 8 - First main section 52 | jump_forward_8_bars 53 | Wait (100) 54 | insert_arranger_cue_marker_at_play_position 55 | CueMarkerName (8, "Main Section 1 - Added Bass Elements") 56 | 57 | // 9 - First minimal breakdown 58 | jump_forward_8_bars 59 | Wait (100) 60 | insert_arranger_cue_marker_at_play_position 61 | CueMarkerName (9, "Minimal Breakdown - Kick Removed") 62 | 63 | // 10 - Percussion focus 64 | jump_forward_8_bars 65 | Wait (100) 66 | insert_arranger_cue_marker_at_play_position 67 | CueMarkerName (10, "Build-Up - Percussion Focus") 68 | 69 | // 11 - Main groove returns with variations 70 | jump_forward_8_bars 71 | Wait (100) 72 | insert_arranger_cue_marker_at_play_position 73 | CueMarkerName (11, "Main Section 2 - Kick Returns") 74 | 75 | // 12 - Main section intensifies 76 | jump_forward_8_bars 77 | Wait (100) 78 | insert_arranger_cue_marker_at_play_position 79 | CueMarkerName (12, "Main Section 2 - Added Elements") 80 | 81 | // 13 - Second breakdown with atmospherics 82 | jump_forward_8_bars 83 | Wait (100) 84 | insert_arranger_cue_marker_at_play_position 85 | CueMarkerName (13, "Main Breakdown - Sparse Elements") 86 | 87 | // 14 - Main tension build 88 | jump_forward_8_bars 89 | Wait (100) 90 | insert_arranger_cue_marker_at_play_position 91 | CueMarkerName (14, "Main Build-Up - Rising Tension") 92 | 93 | // 15 - Peak energy section 94 | jump_forward_8_bars 95 | Wait (100) 96 | insert_arranger_cue_marker_at_play_position 97 | CueMarkerName (15, "Peak Section - All Elements") 98 | 99 | // 16 - Sustained groove section 100 | jump_forward_8_bars 101 | Wait (100) 102 | insert_arranger_cue_marker_at_play_position 103 | CueMarkerName (16, "Main Section 3 - Groove Focus") 104 | 105 | // 17 - Beginning of outro 106 | jump_forward_8_bars 107 | Wait (100) 108 | insert_arranger_cue_marker_at_play_position 109 | CueMarkerName (17, "Outro - Element Reduction") 110 | 111 | // 18 - DJ-friendly outro 112 | jump_forward_8_bars 113 | Wait (100) 114 | insert_arranger_cue_marker_at_play_position 115 | CueMarkerName (18, "DJ Outro - Percussion Only") 116 | 117 | // 19 - Final elements 118 | jump_forward_8_bars 119 | Wait (100) 120 | insert_arranger_cue_marker_at_play_position 121 | CueMarkerName (19, "Track End - Final Elements") 122 | 123 | Message ("Minimal Techno Extended Mix Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Melodic Techno Original Mix.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Melodic Techno Original Mix" 2 | Description: "Cue Markers for a Melodic Techno extended mix." 3 | Author: "Centomila" 4 | Bpm (124) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // 1 - DJ-friendly intro 10 | jump_to_beginning_of_arranger_window 11 | Wait (200) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "Intro - Ambient Pads & Textures") 14 | 15 | // 2 - Basic percussion elements begin 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Percussion Layer Added") 20 | 21 | // 3 - Bassline enters 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Sub Bass & Percussion") 26 | 27 | // 4 - Main kick drum enters 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements - Kick Drum Enters") 32 | 33 | // 5 - Hi-hats and fuller groove develops 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Main Elements - Hi-Hats & Full Rhythm") 38 | 39 | // 6 - First synth elements added 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (6, "Main Elements - Synth Arpeggios Added") 44 | 45 | // 7 - First breakdown begins 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (7, "First Break - Kick Removed, Pads & FX") 50 | 51 | // 8 - Building back elements with filter sweeps 52 | jump_forward_8_bars 53 | Wait (100) 54 | jump_to_beginning_of_next_bar 55 | insert_arranger_cue_marker_at_play_position 56 | CueMarkerName (8, "Build-Up - Filter Sweeps & Rising Tension") 57 | 58 | // 9 - First main drop with all elements 59 | jump_forward_8_bars 60 | Wait (100) 61 | jump_to_beginning_of_next_bar 62 | insert_arranger_cue_marker_at_play_position 63 | CueMarkerName (9, "Drop 1 - Full Groove & Energy") 64 | 65 | // 10 - Melodic section develops further 66 | jump_forward_8_bars 67 | Wait (100) 68 | insert_arranger_cue_marker_at_play_position 69 | CueMarkerName (10, "Main Section - Lead Melody Introduced") 70 | 71 | // 11 - Main breakdown begins 72 | jump_forward_8_bars 73 | Wait (100) 74 | insert_arranger_cue_marker_at_play_position 75 | CueMarkerName (11, "Main Breakdown - Percussion Removed") 76 | 77 | // 12 - Rising tension with FX and build elements 78 | jump_forward_8_bars 79 | Wait (100) 80 | jump_to_beginning_of_next_bar 81 | insert_arranger_cue_marker_at_play_position 82 | CueMarkerName (12, "Main Build-Up - Risers & Tension Elements") 83 | 84 | // 13 - Main peak drop section 85 | jump_forward_8_bars 86 | Wait (100) 87 | jump_to_beginning_of_next_bar 88 | insert_arranger_cue_marker_at_play_position 89 | CueMarkerName (13, "Main Drop - Peak Energy & Full Mix") 90 | 91 | // 14 - Variation with focus on bass elements 92 | jump_forward_8_bars 93 | Wait (100) 94 | insert_arranger_cue_marker_at_play_position 95 | CueMarkerName (14, "Main Drop Variation - Bass Focus") 96 | 97 | // 15 - Beginning of outro elements removal 98 | jump_forward_8_bars 99 | Wait (100) 100 | insert_arranger_cue_marker_at_play_position 101 | CueMarkerName (15, "Outro Part 1 - Main Elements Fading") 102 | 103 | // 16 - DJ-friendly outro section 104 | jump_forward_8_bars 105 | Wait (100) 106 | jump_to_beginning_of_next_bar 107 | insert_arranger_cue_marker_at_play_position 108 | CueMarkerName (16, "Outro Part 2 - Only Pads & Atmosphere") 109 | 110 | // 17 - Fade to silence 111 | jump_forward_8_bars 112 | Wait (100) 113 | insert_arranger_cue_marker_at_play_position 114 | CueMarkerName (17, "Track End - Final Reverb Tails") 115 | 116 | Message ("Melodic Techno Original Mix Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/macro/commands/MacroCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.macro.commands; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.macro.MacroExecutor; 5 | import com.centomila.macro.processor.MacroProcessor; 6 | import com.centomila.macro.state.BitwigStateProvider; 7 | import com.centomila.macro.state.DefaultBitwigStateProvider; 8 | import com.centomila.utils.PopupUtils; 9 | import com.centomila.utils.commands.BaseCommand; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | import java.util.List; 16 | 17 | /** 18 | * Command for executing a macro file which can contain loops and variable assignments. 19 | */ 20 | public class MacroCommand extends BaseCommand { 21 | @Override 22 | public void execute(String[] params, BitwigBuddyExtension extension) { 23 | if (!validateMinParamCount(params, 1, extension)) { 24 | return; 25 | } 26 | 27 | String macroName = params[0]; 28 | String macrosFolder = extension.getMacrosFolder(); 29 | Path macroPath = Paths.get(macrosFolder, macroName + ".txt"); 30 | 31 | try { 32 | if (!Files.exists(macroPath)) { 33 | reportError("Macro file not found: " + macroPath, extension); 34 | return; 35 | } 36 | 37 | List macroLines = Files.readAllLines(macroPath); 38 | 39 | // Process loops and variables 40 | // Create a BitwigStateProvider for the macro processor 41 | BitwigStateProvider stateProvider = new DefaultBitwigStateProvider(extension); 42 | MacroProcessor macroProcessor = new MacroProcessor(stateProvider); 43 | 44 | // Enable debug if a debug parameter is passed 45 | if (params.length > 1 && params[1].equalsIgnoreCase("debug")) { 46 | macroProcessor.setDebug(true); 47 | extension.getHost().println("Debug mode enabled for macro processing"); 48 | } 49 | 50 | List processedLines; 51 | try { 52 | processedLines = macroProcessor.processCommands(macroLines); 53 | } catch (RuntimeException e) { 54 | reportError("Error parsing macro: " + e.getMessage(), extension); 55 | 56 | // Print the lines for debugging 57 | extension.getHost().println("Macro content:"); 58 | for (int i = 0; i < macroLines.size(); i++) { 59 | extension.getHost().println(i + ": " + macroLines.get(i)); 60 | } 61 | return; 62 | } 63 | 64 | // Execute each processed line 65 | for (String line : processedLines) { 66 | line = line.trim(); 67 | // Skip empty lines, comments, variable definitions and loop closing braces 68 | if (!line.isEmpty() && !line.startsWith("//") && 69 | !line.startsWith("var ") && !line.matches("\\s*\\}\\s*")) { 70 | 71 | // Execute the actual Bitwig command 72 | boolean success = MacroExecutor.executeCommand(line, extension); 73 | 74 | if (!success && !line.trim().startsWith("//")) { 75 | // Only report errors for non-comment lines 76 | reportError("Failed to execute command: " + line, extension); 77 | } 78 | } 79 | } 80 | 81 | PopupUtils.showPopup("Macro executed: " + macroName); 82 | 83 | } catch (IOException e) { 84 | reportError("Error reading macro file: " + e.getMessage(), extension); 85 | } catch (RuntimeException e) { 86 | reportError("Error processing macro: " + e.getMessage(), extension); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Raw Deep Techno Extended Mix.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Raw Deep Techno Extended Mix" 2 | Description: "Cue Markers for a Raw Deep Techno extended mix." 3 | Author: "Centomila" 4 | Bpm (132) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // 1 - Beatless industrial atmosphere 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "Intro - Industrial Atmosphere") 14 | 15 | // 2 - First deep texture elements 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Dark Textures") 20 | 21 | // 3 - Basic percussive elements 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Basic Percussion") 26 | 27 | // 4 - Deep sub bass enters 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements - Sub Bass Added") 32 | 33 | // 5 - Kick drum enters 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Main Elements - Raw Kick Added") 38 | 39 | // 6 - Full drum pattern established 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (6, "Main Elements - Full Drums") 44 | 45 | // 7 - First phase with all elements 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (7, "Main Section 1 - Full Elements") 50 | 51 | // 8 - Metallic percussion focus 52 | jump_forward_8_bars 53 | Wait (100) 54 | insert_arranger_cue_marker_at_play_position 55 | CueMarkerName (8, "Main Section 1 - Metallic Percussion") 56 | 57 | // 9 - First dark breakdown 58 | jump_forward_8_bars 59 | Wait (100) 60 | insert_arranger_cue_marker_at_play_position 61 | CueMarkerName (9, "First Breakdown - Raw Textures") 62 | 63 | // 10 - First tension build 64 | jump_forward_8_bars 65 | Wait (100) 66 | insert_arranger_cue_marker_at_play_position 67 | CueMarkerName (10, "Build-Up 1 - Industrial Elements") 68 | 69 | // 11 - Main drop section 70 | jump_forward_8_bars 71 | Wait (100) 72 | insert_arranger_cue_marker_at_play_position 73 | CueMarkerName (11, "Main Section 2 - Hard Drop") 74 | 75 | // 12 - Sustained groove with acid elements 76 | jump_forward_8_bars 77 | Wait (100) 78 | insert_arranger_cue_marker_at_play_position 79 | CueMarkerName (12, "Main Section 2 - Acid Elements") 80 | 81 | // 13 - Second main section 82 | jump_forward_8_bars 83 | Wait (100) 84 | insert_arranger_cue_marker_at_play_position 85 | CueMarkerName (13, "Main Section 2 - Full Energy") 86 | 87 | // 14 - Main deep breakdown 88 | jump_forward_8_bars 89 | Wait (100) 90 | insert_arranger_cue_marker_at_play_position 91 | CueMarkerName (14, "Main Breakdown - Deep Atmosphere") 92 | 93 | // 15 - Main tension build 94 | jump_forward_8_bars 95 | Wait (100) 96 | insert_arranger_cue_marker_at_play_position 97 | CueMarkerName (15, "Main Build-Up - Dark Risers") 98 | 99 | // 16 - Peak energy section 100 | jump_forward_8_bars 101 | Wait (100) 102 | insert_arranger_cue_marker_at_play_position 103 | CueMarkerName (16, "Peak Section - Maximum Impact") 104 | 105 | // 17 - Sustained energy with variations 106 | jump_forward_8_bars 107 | Wait (100) 108 | insert_arranger_cue_marker_at_play_position 109 | CueMarkerName (17, "Peak Section - Rhythm Variations") 110 | 111 | // 18 - Beginning of outro 112 | jump_forward_8_bars 113 | Wait (100) 114 | insert_arranger_cue_marker_at_play_position 115 | CueMarkerName (18, "Outro - Element Reduction") 116 | 117 | // 19 - Final percussion 118 | jump_forward_8_bars 119 | Wait (100) 120 | insert_arranger_cue_marker_at_play_position 121 | CueMarkerName (19, "DJ Outro - Raw Percussion") 122 | 123 | // 20 - Final atmospherics 124 | jump_forward_8_bars 125 | Wait (100) 126 | insert_arranger_cue_marker_at_play_position 127 | CueMarkerName (20, "Track End - Dark Atmosphere") 128 | 129 | Message ("Raw Deep Techno Extended Mix Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/MacroCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.macro.MacroExecutor; 5 | import com.centomila.utils.LoopProcessor; 6 | import com.centomila.utils.PopupUtils; 7 | import com.centomila.utils.commands.BaseCommand; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * Command for executing a macro file which can contain loops and variable assignments. 18 | */ 19 | public class MacroCommand extends BaseCommand { 20 | @Override 21 | public void execute(String[] params, BitwigBuddyExtension extension) { 22 | if (!validateMinParamCount(params, 1, extension)) { 23 | return; 24 | } 25 | 26 | String macroName = params[0]; 27 | String macrosFolder = extension.getMacrosFolder(); 28 | Path macroPath = Paths.get(macrosFolder, macroName + ".txt"); 29 | 30 | try { 31 | if (!Files.exists(macroPath)) { 32 | reportError("Macro file not found: " + macroPath, extension); 33 | return; 34 | } 35 | 36 | List macroLines = Files.readAllLines(macroPath); 37 | 38 | // Normalize indentation to avoid tab issues 39 | List normalizedLines = new ArrayList<>(); 40 | for (String line : macroLines) { 41 | // Convert all whitespace sequences (including tabs) to single spaces 42 | // except for spaces around braces and within expressions 43 | normalizedLines.add(line); 44 | } 45 | 46 | // Process loops and variables 47 | LoopProcessor loopProcessor = new LoopProcessor(extension.getStateProvider()); 48 | 49 | // Enable debug if a debug parameter is passed 50 | if (params.length > 1 && params[1].equalsIgnoreCase("debug")) { 51 | loopProcessor.setDebug(true); 52 | extension.getHost().println("Debug mode enabled for macro processing"); 53 | } 54 | 55 | List processedLines; 56 | try { 57 | processedLines = loopProcessor.processLoop(normalizedLines); 58 | } catch (RuntimeException e) { 59 | reportError("Error parsing macro: " + e.getMessage(), extension); 60 | 61 | // Print the lines for debugging 62 | extension.getHost().println("Macro content:"); 63 | for (int i = 0; i < normalizedLines.size(); i++) { 64 | extension.getHost().println(i + ": " + normalizedLines.get(i)); 65 | } 66 | return; 67 | } 68 | 69 | // Execute each processed line 70 | for (String line : processedLines) { 71 | line = line.trim(); 72 | // Skip empty lines, comments, variable definitions and loop closing braces 73 | if (!line.isEmpty() && !line.startsWith("//") && 74 | !line.startsWith("var ") && !line.matches("\\s*\\}\\s*")) { 75 | 76 | // Execute the actual Bitwig command 77 | boolean success = MacroExecutor.executeCommand(line, extension); 78 | 79 | if (!success && !line.trim().startsWith("//")) { 80 | // Only report errors for non-comment lines 81 | reportError("Failed to execute command: " + line, extension); 82 | } 83 | } 84 | } 85 | 86 | PopupUtils.showPopup("Macro executed: " + macroName); 87 | 88 | } catch (IOException e) { 89 | reportError("Error reading macro file: " + e.getMessage(), extension); 90 | } catch (RuntimeException e) { 91 | reportError("Error processing macro: " + e.getMessage(), extension); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Tech House Extended Mix.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Tech House Extended Mix" 2 | Description: "Cue Markers for a Tech House extended mix." 3 | Author: "Centomila" 4 | Bpm (130) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // 1 - DJ-friendly intro 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "DJ Intro - Minimal Loop") 14 | 15 | // 2 - First percussion elements 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Percussion & FX") 20 | 21 | // 3 - Bass elements begin 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Bass Elements") 26 | 27 | // 4 - Kick drum enters 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements - Kick Added") 32 | 33 | // 5 - Hi-hats and rhythm development 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Main Elements - Hi-Hats & Rhythm") 38 | 39 | // 6 - Full groove established 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (6, "Main Elements - Full Groove") 44 | 45 | // 7 - First vocal/sample elements 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (7, "Main Section 1 - Vocal Samples Added") 50 | 51 | // 8 - First main section evolves 52 | jump_forward_8_bars 53 | Wait (100) 54 | insert_arranger_cue_marker_at_play_position 55 | CueMarkerName (8, "Main Section 1 - Full Energy") 56 | 57 | // 9 - First breakdown (house influenced) 58 | jump_forward_8_bars 59 | Wait (100) 60 | insert_arranger_cue_marker_at_play_position 61 | CueMarkerName (9, "First Breakdown - House Elements") 62 | 63 | // 10 - First tension build 64 | jump_forward_8_bars 65 | Wait (100) 66 | insert_arranger_cue_marker_at_play_position 67 | CueMarkerName (10, "Build-Up 1 - Rising Tension") 68 | 69 | // 11 - First drop with rolling bassline 70 | jump_forward_8_bars 71 | Wait (100) 72 | insert_arranger_cue_marker_at_play_position 73 | CueMarkerName (11, "Drop 1 - Rolling Bass") 74 | 75 | // 12 - Main groove continues with variations 76 | jump_forward_8_bars 77 | Wait (100) 78 | insert_arranger_cue_marker_at_play_position 79 | CueMarkerName (12, "Main Section 2 - Groove Variations") 80 | 81 | // 13 - Additional vocal/sample elements 82 | jump_forward_8_bars 83 | Wait (100) 84 | insert_arranger_cue_marker_at_play_position 85 | CueMarkerName (13, "Main Section 2 - Added Hooks") 86 | 87 | // 14 - Main breakdown with filter sweeps 88 | jump_forward_8_bars 89 | Wait (100) 90 | insert_arranger_cue_marker_at_play_position 91 | CueMarkerName (14, "Main Breakdown - Filter Sweeps") 92 | 93 | // 15 - Main build-up section 94 | jump_forward_8_bars 95 | Wait (100) 96 | insert_arranger_cue_marker_at_play_position 97 | CueMarkerName (15, "Main Build-Up - Rising Elements") 98 | 99 | // 16 - Peak energy section with full mix 100 | jump_forward_8_bars 101 | Wait (100) 102 | insert_arranger_cue_marker_at_play_position 103 | CueMarkerName (16, "Main Drop - Full Energy") 104 | 105 | // 17 - Peak section with tech elements 106 | jump_forward_8_bars 107 | Wait (100) 108 | insert_arranger_cue_marker_at_play_position 109 | CueMarkerName (17, "Main Drop - Tech Focus") 110 | 111 | // 18 - Sustained groove with house elements 112 | jump_forward_8_bars 113 | Wait (100) 114 | insert_arranger_cue_marker_at_play_position 115 | CueMarkerName (18, "Main Drop - House Elements") 116 | 117 | // 19 - Beginning of outro section 118 | jump_forward_8_bars 119 | Wait (100) 120 | insert_arranger_cue_marker_at_play_position 121 | CueMarkerName (19, "Outro - Element Reduction") 122 | 123 | // 20 - DJ-friendly outro section 124 | jump_forward_8_bars 125 | Wait (100) 126 | insert_arranger_cue_marker_at_play_position 127 | CueMarkerName (20, "DJ Outro - Rhythm & Bass") 128 | 129 | // 21 - Final percussion elements 130 | jump_forward_8_bars 131 | Wait (100) 132 | insert_arranger_cue_marker_at_play_position 133 | CueMarkerName (21, "Track End - Final Elements") 134 | 135 | Message ("Tech House Extended Mix Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Arrangement/Arrangement - Hypnotic Techno Extended Mix.txt: -------------------------------------------------------------------------------- 1 | Macro: "Arrangement - Hypnotic Techno Extended Mix" 2 | Description: "Cue Markers for a Hypnotic Techno extended mix." 3 | Author: "Centomila" 4 | Bpm (135) 5 | Time Signature ("4/4") 6 | 7 | DeleteAllCueMarkers 8 | 9 | // 1 - Atmospheric hypnotic intro 10 | jump_to_beginning_of_arranger_window 11 | Wait (100) 12 | insert_arranger_cue_marker_at_play_position 13 | CueMarkerName (1, "Intro - Ethereal Atmosphere") 14 | 15 | // 2 - First rhythmic elements 16 | jump_forward_8_bars 17 | Wait (100) 18 | insert_arranger_cue_marker_at_play_position 19 | CueMarkerName (2, "Intro - Rhythmic Patterns") 20 | 21 | // 3 - Initial repetitive elements 22 | jump_forward_8_bars 23 | Wait (100) 24 | insert_arranger_cue_marker_at_play_position 25 | CueMarkerName (3, "Intro - Repetitive Synth Motif") 26 | 27 | // 4 - First percussion additions 28 | jump_forward_8_bars 29 | Wait (100) 30 | insert_arranger_cue_marker_at_play_position 31 | CueMarkerName (4, "Main Elements - Basic Percussion") 32 | 33 | // 5 - Main kick enters 34 | jump_forward_8_bars 35 | Wait (100) 36 | insert_arranger_cue_marker_at_play_position 37 | CueMarkerName (5, "Main Elements - Kick Added") 38 | 39 | // 6 - First development of main pattern 40 | jump_forward_8_bars 41 | Wait (100) 42 | insert_arranger_cue_marker_at_play_position 43 | CueMarkerName (6, "Main Elements - Pattern Development") 44 | 45 | // 7 - First subtle evolution 46 | jump_forward_8_bars 47 | Wait (100) 48 | insert_arranger_cue_marker_at_play_position 49 | CueMarkerName (7, "Main Section 1 - Subtle Evolution") 50 | 51 | // 8 - Bass pattern intensifies 52 | jump_forward_8_bars 53 | Wait (100) 54 | insert_arranger_cue_marker_at_play_position 55 | CueMarkerName (8, "Main Section 1 - Bass Focus") 56 | 57 | // 9 - Main repetitive section 58 | jump_forward_8_bars 59 | Wait (100) 60 | insert_arranger_cue_marker_at_play_position 61 | CueMarkerName (9, "Main Section 1 - Hypnotic State") 62 | 63 | // 10 - First variation with acid elements 64 | jump_forward_8_bars 65 | Wait (100) 66 | insert_arranger_cue_marker_at_play_position 67 | CueMarkerName (10, "Variation 1 - Acid Elements") 68 | 69 | // 11 - First minimalist breakdown 70 | jump_forward_8_bars 71 | Wait (100) 72 | insert_arranger_cue_marker_at_play_position 73 | CueMarkerName (11, "First Breakdown - Reduced Elements") 74 | 75 | // 12 - First tension build 76 | jump_forward_8_bars 77 | Wait (100) 78 | insert_arranger_cue_marker_at_play_position 79 | CueMarkerName (12, "Build-Up 1 - Rising Repetition") 80 | 81 | // 13 - Second main hypnotic section 82 | jump_forward_8_bars 83 | Wait (100) 84 | insert_arranger_cue_marker_at_play_position 85 | CueMarkerName (13, "Main Section 2 - Full Hypnotic Elements") 86 | 87 | // 14 - Sustained groove with modulation 88 | jump_forward_8_bars 89 | Wait (100) 90 | insert_arranger_cue_marker_at_play_position 91 | CueMarkerName (14, "Main Section 2 - Pattern Modulation") 92 | 93 | // 15 - Main breakdown with focal synth 94 | jump_forward_8_bars 95 | Wait (100) 96 | insert_arranger_cue_marker_at_play_position 97 | CueMarkerName (15, "Main Breakdown - Focus Synth Element") 98 | 99 | // 16 - Main tension build 100 | jump_forward_8_bars 101 | Wait (100) 102 | insert_arranger_cue_marker_at_play_position 103 | CueMarkerName (16, "Main Build-Up - Rhythmic Tension") 104 | 105 | // 17 - Peak energy section 106 | jump_forward_8_bars 107 | Wait (100) 108 | insert_arranger_cue_marker_at_play_position 109 | CueMarkerName (17, "Peak Section - Maximum Hypnotic State") 110 | 111 | // 18 - Sustained energy with subtle variations 112 | jump_forward_8_bars 113 | Wait (100) 114 | insert_arranger_cue_marker_at_play_position 115 | CueMarkerName (18, "Peak Section - Subtle Evolving Variations") 116 | 117 | // 19 - Beginning of outro elements reduction 118 | jump_forward_8_bars 119 | Wait (100) 120 | insert_arranger_cue_marker_at_play_position 121 | CueMarkerName (19, "Outro - Gradual Reduction") 122 | 123 | // 20 - Final pattern loop 124 | jump_forward_8_bars 125 | Wait (100) 126 | insert_arranger_cue_marker_at_play_position 127 | CueMarkerName (20, "DJ Outro - Minimal Loop") 128 | 129 | // 21 - Final atmospheric elements 130 | jump_forward_8_bars 131 | Wait (100) 132 | insert_arranger_cue_marker_at_play_position 133 | CueMarkerName (21, "Track End - Ethereal Fade") 134 | 135 | Message ("Hypnotic Techno Extended Mix Arrangement Complete") -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/MessageCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.macro.state.BitwigStateProvider; 5 | import com.centomila.macro.state.DefaultBitwigStateProvider; 6 | import com.centomila.utils.PopupUtils; 7 | import com.centomila.utils.commands.BaseCommand; 8 | 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | /** 13 | * Shows a popup message to the user. 14 | * Supports variable and function expression interpolation. 15 | */ 16 | public class MessageCommand extends BaseCommand { 17 | // Match function calls in both formats: direct and wrapped in ${} 18 | private static final Pattern FUNCTION_CALL_DIRECT = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*)\\(\\)"); 19 | private static final Pattern EXPRESSION_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}"); 20 | 21 | @Override 22 | public void execute(String[] params, BitwigBuddyExtension extension) { 23 | if (!validateMinParamCount(params, 1, extension)) { 24 | return; 25 | } 26 | 27 | // Join all parameters with spaces to handle multi-part messages 28 | String message = String.join(" ", params); 29 | 30 | // Process any expressions within the message 31 | message = processExpressions(message, extension); 32 | 33 | // Show the message 34 | PopupUtils.showPopup(message); 35 | } 36 | 37 | /** 38 | * Process expressions in ${...} format and direct function calls 39 | */ 40 | private String processExpressions(String message, BitwigBuddyExtension extension) { 41 | BitwigStateProvider stateProvider = new DefaultBitwigStateProvider(extension); 42 | 43 | // First process wrapped expressions with ${...} syntax 44 | message = processWrappedExpressions(message, stateProvider); 45 | 46 | // Then process direct function calls like getCurrentTrackName() 47 | message = processDirectFunctionCalls(message, stateProvider); 48 | 49 | return message; 50 | } 51 | 52 | /** 53 | * Process expressions wrapped in ${...} 54 | */ 55 | private String processWrappedExpressions(String message, BitwigStateProvider stateProvider) { 56 | // If no expressions are present, return the original message 57 | if (!message.contains("${")) { 58 | return message; 59 | } 60 | 61 | // Find and replace all expressions 62 | Matcher matcher = EXPRESSION_PATTERN.matcher(message); 63 | StringBuffer result = new StringBuffer(); 64 | 65 | while (matcher.find()) { 66 | String expression = matcher.group(1); 67 | String replacement; 68 | 69 | // Check if the expression is a function call 70 | Matcher funcMatcher = FUNCTION_CALL_DIRECT.matcher(expression); 71 | if (funcMatcher.matches()) { 72 | String functionName = funcMatcher.group(1); 73 | replacement = executeFunctionCall(functionName, stateProvider); 74 | } else { 75 | // For other expressions, just use as is for now 76 | replacement = expression; 77 | } 78 | 79 | // Replace the expression with its result 80 | matcher.appendReplacement(result, replacement.replace("$", "\\$")); 81 | } 82 | matcher.appendTail(result); 83 | 84 | return result.toString(); 85 | } 86 | 87 | /** 88 | * Process direct function calls not wrapped in ${} 89 | */ 90 | private String processDirectFunctionCalls(String message, BitwigStateProvider stateProvider) { 91 | Matcher matcher = FUNCTION_CALL_DIRECT.matcher(message); 92 | StringBuffer result = new StringBuffer(); 93 | 94 | while (matcher.find()) { 95 | String functionName = matcher.group(1); 96 | String replacement = executeFunctionCall(functionName, stateProvider); 97 | matcher.appendReplacement(result, replacement.replace("$", "\\$")); 98 | } 99 | matcher.appendTail(result); 100 | 101 | return result.toString(); 102 | } 103 | 104 | /** 105 | * Execute a function call and return its result as a string 106 | */ 107 | private String executeFunctionCall(String functionName, BitwigStateProvider stateProvider) { 108 | if (stateProvider.supportsMethod(functionName)) { 109 | Object value = stateProvider.callMethod(functionName); 110 | return value != null ? value.toString() : "null"; 111 | } 112 | return "Function not supported: " + functionName; 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/utility/ConsoleCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.utility; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.macro.state.BitwigStateProvider; 5 | import com.centomila.macro.state.DefaultBitwigStateProvider; 6 | import com.centomila.utils.commands.BaseCommand; 7 | 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * Writes a message to the Bitwig Studio console. 13 | * Supports variable and function expression interpolation. 14 | */ 15 | public class ConsoleCommand extends BaseCommand { 16 | // Match function calls in both formats: direct and wrapped in ${} 17 | private static final Pattern FUNCTION_CALL_DIRECT = Pattern.compile("([a-zA-Z_][a-zA-Z0-9_]*)\\(\\)"); 18 | private static final Pattern EXPRESSION_PATTERN = Pattern.compile("\\$\\{([^}]+)\\}"); 19 | 20 | @Override 21 | public void execute(String[] params, BitwigBuddyExtension extension) { 22 | if (!validateMinParamCount(params, 1, extension)) { 23 | return; 24 | } 25 | 26 | // Join all parameters with spaces to handle multi-part messages 27 | String message = String.join(" ", params); 28 | 29 | // Process any expressions within the message 30 | message = processExpressions(message, extension); 31 | 32 | // Write the message to the Bitwig console 33 | extension.getHost().println("[BeatBuddy] " + message); 34 | } 35 | 36 | /** 37 | * Process expressions in ${...} format and direct function calls 38 | */ 39 | private String processExpressions(String message, BitwigBuddyExtension extension) { 40 | BitwigStateProvider stateProvider = new DefaultBitwigStateProvider(extension); 41 | 42 | // First process wrapped expressions with ${...} syntax 43 | message = processWrappedExpressions(message, stateProvider); 44 | 45 | // Then process direct function calls like getCurrentTrackName() 46 | message = processDirectFunctionCalls(message, stateProvider); 47 | 48 | return message; 49 | } 50 | 51 | /** 52 | * Process expressions wrapped in ${...} 53 | */ 54 | private String processWrappedExpressions(String message, BitwigStateProvider stateProvider) { 55 | // If no expressions are present, return the original message 56 | if (!message.contains("${")) { 57 | return message; 58 | } 59 | 60 | // Find and replace all expressions 61 | Matcher matcher = EXPRESSION_PATTERN.matcher(message); 62 | StringBuffer result = new StringBuffer(); 63 | 64 | while (matcher.find()) { 65 | String expression = matcher.group(1); 66 | String replacement; 67 | 68 | // Check if the expression is a function call 69 | Matcher funcMatcher = FUNCTION_CALL_DIRECT.matcher(expression); 70 | if (funcMatcher.matches()) { 71 | String functionName = funcMatcher.group(1); 72 | replacement = executeFunctionCall(functionName, stateProvider); 73 | } else { 74 | // For other expressions, just use as is for now 75 | replacement = expression; 76 | } 77 | 78 | // Replace the expression with its result 79 | matcher.appendReplacement(result, replacement.replace("$", "\\$")); 80 | } 81 | matcher.appendTail(result); 82 | 83 | return result.toString(); 84 | } 85 | 86 | /** 87 | * Process direct function calls not wrapped in ${} 88 | */ 89 | private String processDirectFunctionCalls(String message, BitwigStateProvider stateProvider) { 90 | Matcher matcher = FUNCTION_CALL_DIRECT.matcher(message); 91 | StringBuffer result = new StringBuffer(); 92 | 93 | while (matcher.find()) { 94 | String functionName = matcher.group(1); 95 | String replacement = executeFunctionCall(functionName, stateProvider); 96 | matcher.appendReplacement(result, replacement.replace("$", "\\$")); 97 | } 98 | matcher.appendTail(result); 99 | 100 | return result.toString(); 101 | } 102 | 103 | /** 104 | * Execute a function call and return its result as a string 105 | */ 106 | private String executeFunctionCall(String functionName, BitwigStateProvider stateProvider) { 107 | if (stateProvider.supportsMethod(functionName)) { 108 | Object value = stateProvider.callMethod(functionName); 109 | return value != null ? value.toString() : "null"; 110 | } 111 | return "Function not supported: " + functionName; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/utils/commands/clip/ClipCreateCommand.java: -------------------------------------------------------------------------------- 1 | package com.centomila.utils.commands.clip; 2 | 3 | import com.centomila.BitwigBuddyExtension; 4 | import com.centomila.MacroActionSettings; 5 | import com.centomila.ModeSelectSettings; 6 | import com.centomila.utils.commands.BaseCommand; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | /** 12 | * Command to create a new clip in the launcher or arranger. 13 | */ 14 | public class ClipCreateCommand extends BaseCommand { 15 | 16 | @Override 17 | public void execute(String[] params, BitwigBuddyExtension extension) { 18 | if (!validateMinParamCount(params, 1, extension)) { 19 | return; 20 | } 21 | 22 | // Get current track index 23 | int currentTrack = extension.trackBank.cursorIndex().getAsInt(); 24 | if (currentTrack < 0) { 25 | extension.getHost().println("No track selected, using first track (index 0)"); 26 | currentTrack = 0; 27 | } 28 | 29 | // Parse parameters 30 | int clipLength = 4; // Default value 31 | int slotIndex; 32 | 33 | try { 34 | // Parse first parameter (slot index) - handle potential floating point values 35 | String slotParam = params[0].trim(); 36 | if (slotParam.contains(".")) { 37 | // If it's a floating point, round to nearest integer 38 | double slotDouble = Double.parseDouble(slotParam); 39 | slotIndex = (int) Math.round(slotDouble); 40 | extension.getHost().println("Rounded slot index from " + slotDouble + " to " + slotIndex); 41 | } else { 42 | slotIndex = Integer.parseInt(slotParam); 43 | } 44 | 45 | // Adjust to 0-based index 46 | slotIndex = slotIndex - 1; 47 | 48 | // Parse second parameter (clip length) if available 49 | if (params.length > 1) { 50 | String lengthParam = params[1].trim(); 51 | if (lengthParam.contains(".")) { 52 | // If it's a floating point, round to nearest integer 53 | double lengthDouble = Double.parseDouble(lengthParam); 54 | clipLength = (int) Math.round(lengthDouble); 55 | extension.getHost().println("Rounded clip length from " + lengthDouble + " to " + clipLength); 56 | } else { 57 | clipLength = Integer.parseInt(lengthParam); 58 | } 59 | } 60 | } catch (NumberFormatException e) { 61 | reportError("Invalid parameter format: " + e.getMessage(), extension); 62 | return; 63 | } 64 | 65 | // Execute command based on mode 66 | if (ModeSelectSettings.getCurrentLauncherArrangerToggleString().equals("Arranger")) { 67 | handleArrangerModeClipCreation(extension, clipLength); 68 | } else { 69 | handleLauncherModeClipCreation(extension, currentTrack, slotIndex, clipLength); 70 | } 71 | } 72 | 73 | private void handleArrangerModeClipCreation(BitwigBuddyExtension extension, int clipLength) { 74 | // Schedule the actions sequentially 75 | extension.application.setPanelLayout("ARRANGE"); 76 | extension.arrangerClip.clipLauncherSlot().showInEditor(); 77 | 78 | List actions = new ArrayList<>(); 79 | actions.add("select_start_of_selection_range"); 80 | for (int i = 0; i < clipLength; i++) { 81 | actions.add("extend_time_selection_range_to_next_step"); 82 | } 83 | actions.add("Consolidate"); 84 | actions.add("switch_between_event_and_time_selection"); 85 | 86 | // Use the scheduler from MacroActionSettings to execute the actions 87 | MacroActionSettings.scheduleCommands( 88 | actions.toArray(new String[0]), 89 | 0, 90 | extension 91 | ); 92 | extension.arrangerClip.isLoopEnabled().set(true); 93 | } 94 | 95 | private void handleLauncherModeClipCreation(BitwigBuddyExtension extension, int trackIndex, int slotIndex, int clipLength) { 96 | if (slotIndex >= 0) { 97 | // Delete existing clip if present 98 | extension.trackBank.getItemAt(trackIndex).clipLauncherSlotBank().getItemAt(slotIndex).deleteObject(); 99 | 100 | // Create new clip 101 | extension.trackBank.getItemAt(trackIndex).clipLauncherSlotBank() 102 | .createEmptyClip(slotIndex, clipLength); 103 | 104 | // Make track visible 105 | extension.trackBank.getItemAt(trackIndex).makeVisibleInMixer(); 106 | extension.trackBank.getItemAt(trackIndex).makeVisibleInArranger(); 107 | 108 | extension.getHost().println("Created empty clip with length: " + clipLength); 109 | } else { 110 | extension.getHost().println("No clip slot selected. Please select a clip slot first."); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/com/centomila/customPresetsTxt/Macros/Samples/Rainbow.txt: -------------------------------------------------------------------------------- 1 | Macro: "Rainbow Clip" 2 | Descritpion: "Clip Duplicate the selected clip 64 times with rainbow colors" 3 | Author: "Centomila" 4 | 5 | BB Close Panel 6 | Wait (100) 7 | Clip Rename (FF0000 - Red) 8 | Clip Color (FF0000) 9 | Clip Duplicate 10 | Clip Rename (FF1100 - Red) 11 | Clip Color (FF1100) 12 | Clip Duplicate 13 | Clip Rename (FF2200 - Red) 14 | Clip Color (FF2200) 15 | Clip Duplicate 16 | Clip Rename (FF3300 - Red) 17 | Clip Color (FF3300) 18 | Clip Duplicate 19 | Clip Rename (FF4400 - Red) 20 | Clip Color (FF4400) 21 | Clip Duplicate 22 | Clip Rename (FF5500 - Red) 23 | Clip Color (FF5500) 24 | Clip Duplicate 25 | Clip Rename (FF6600 - Orange) 26 | Clip Color (FF6600) 27 | Clip Duplicate 28 | Clip Rename (FF7700 - Orange) 29 | Clip Color (FF7700) 30 | Clip Duplicate 31 | Clip Rename (FF8800 - Orange) 32 | Clip Color (FF8800) 33 | Clip Duplicate 34 | Clip Rename (FF9900 - Orange) 35 | Clip Color (FF9900) 36 | Clip Duplicate 37 | Clip Rename (FFAA00 - Orange) 38 | Clip Color (FFAA00) 39 | Clip Duplicate 40 | Clip Rename (FFBB00 - Orange) 41 | Clip Color (FFBB00) 42 | Clip Duplicate 43 | Clip Rename (FFCC00 - Orange) 44 | Clip Color (FFCC00) 45 | Clip Duplicate 46 | Clip Rename (FFDD00 - Yellow) 47 | Clip Color (FFDD00) 48 | Clip Duplicate 49 | Clip Rename (FFEE00 - Yellow) 50 | Clip Color (FFEE00) 51 | Clip Duplicate 52 | Clip Rename (FFFF00 - Yellow) 53 | Clip Color (FFFF00) 54 | Clip Duplicate 55 | Clip Rename (EEFF00 - Yellow) 56 | Clip Color (EEFF00) 57 | Clip Duplicate 58 | Clip Rename (DDFF00 - Yellow) 59 | Clip Color (DDFF00) 60 | Clip Duplicate 61 | Clip Rename (CCFF00 - Yellow) 62 | Clip Color (CCFF00) 63 | Clip Duplicate 64 | Clip Rename (BBFF00 - Yellow) 65 | Clip Color (BBFF00) 66 | Clip Duplicate 67 | Clip Rename (AAFF00 - Yellow) 68 | Clip Color (AAFF00) 69 | Clip Duplicate 70 | Clip Rename (99FF00 - Green) 71 | Clip Color (99FF00) 72 | Clip Duplicate 73 | Clip Rename (88FF00 - Green) 74 | Clip Color (88FF00) 75 | Clip Duplicate 76 | Clip Rename (77FF00 - Green) 77 | Clip Color (77FF00) 78 | Clip Duplicate 79 | Clip Rename (66FF00 - Green) 80 | Clip Color (66FF00) 81 | Clip Duplicate 82 | Clip Rename (55FF00 - Green) 83 | Clip Color (55FF00) 84 | Clip Duplicate 85 | Clip Rename (44FF00 - Green) 86 | Clip Color (44FF00) 87 | Clip Duplicate 88 | Clip Rename (33FF00 - Green) 89 | Clip Color (33FF00) 90 | Clip Duplicate 91 | Clip Rename (22FF00 - Green) 92 | Clip Color (22FF00) 93 | Clip Duplicate 94 | Clip Rename (11FF00 - Green) 95 | Clip Color (11FF00) 96 | Clip Duplicate 97 | Clip Rename (00FF00 - Green) 98 | Clip Color (00FF00) 99 | Clip Duplicate 100 | Clip Rename (00FF11 - Green) 101 | Clip Color (00FF11) 102 | Clip Duplicate 103 | Clip Rename (00FF22 - Green) 104 | Clip Color (00FF22) 105 | Clip Duplicate 106 | Clip Rename (00FF33 - Green) 107 | Clip Color (00FF33) 108 | Clip Duplicate 109 | Clip Rename (00FF44 - Green) 110 | Clip Color (00FF44) 111 | Clip Duplicate 112 | Clip Rename (00FF55 - Green) 113 | Clip Color (00FF55) 114 | Clip Duplicate 115 | Clip Rename (00FF66 - Green) 116 | Clip Color (00FF66) 117 | Clip Duplicate 118 | Clip Rename (00FF77 - Green) 119 | Clip Color (00FF77) 120 | Clip Duplicate 121 | Clip Rename (00FF88 - Green) 122 | Clip Color (00FF88) 123 | Clip Duplicate 124 | Clip Rename (00FF99 - Green) 125 | Clip Color (00FF99) 126 | Clip Duplicate 127 | Clip Rename (00FFAA - Green) 128 | Clip Color (00FFAA) 129 | Clip Duplicate 130 | Clip Rename (00FFBB - Green) 131 | Clip Color (00FFBB) 132 | Clip Duplicate 133 | Clip Rename (00FFCC - Green) 134 | Clip Color (00FFCC) 135 | Clip Duplicate 136 | Clip Rename (00FFDD - Green) 137 | Clip Color (00FFDD) 138 | Clip Duplicate 139 | Clip Rename (00FFEE - Green) 140 | Clip Color (00FFEE) 141 | Clip Duplicate 142 | Clip Rename (00FFFF - Cyan) 143 | Clip Color (00FFFF) 144 | Clip Duplicate 145 | Clip Rename (00EEFF - Cyan) 146 | Clip Color (00EEFF) 147 | Clip Duplicate 148 | Clip Rename (00DDFF - Cyan) 149 | Clip Color (00DDFF) 150 | Clip Duplicate 151 | Clip Rename (00CCFF - Cyan) 152 | Clip Color (00CCFF) 153 | Clip Duplicate 154 | Clip Rename (00BBFF - Cyan) 155 | Clip Color (00BBFF) 156 | Clip Duplicate 157 | Clip Rename (00AAFF - Cyan) 158 | Clip Color (00AAFF) 159 | Clip Duplicate 160 | Clip Rename (0099FF - Cyan) 161 | Clip Color (0099FF) 162 | Clip Duplicate 163 | Clip Rename (0088FF - Cyan) 164 | Clip Color (0088FF) 165 | Clip Duplicate 166 | Clip Rename (0077FF - Cyan) 167 | Clip Color (0077FF) 168 | Clip Duplicate 169 | Clip Rename (0066FF - Cyan) 170 | Clip Color (0066FF) 171 | Clip Duplicate 172 | Clip Rename (0055FF - Cyan) 173 | Clip Color (0055FF) 174 | Clip Duplicate 175 | Clip Rename (0044FF - Cyan) 176 | Clip Color (0044FF) 177 | Clip Duplicate 178 | Clip Rename (0033FF - Cyan) 179 | Clip Color (0033FF) 180 | Clip Duplicate 181 | Clip Rename (0022FF - Cyan) 182 | Clip Color (0022FF) 183 | Clip Duplicate 184 | Clip Rename (0011FF - Cyan) 185 | Clip Color (0011FF) 186 | Clip Duplicate 187 | Clip Rename (0000FF - Blue) 188 | Clip Color (0000FF) 189 | Clip Duplicate 190 | Clip Rename (1100FF - Blue) 191 | Clip Color (1100FF) 192 | Clip Duplicate 193 | Clip Rename (2200FF - Blue) 194 | Clip Color (2200FF) 195 | Clip Duplicate 196 | Clip Rename (3300FF - Blue) 197 | Clip Color (3300FF) 198 | Clip Duplicate 199 | 200 | Message ("Macro Finished") -------------------------------------------------------------------------------- /src/main/java/com/centomila/DefaultPatterns.java: -------------------------------------------------------------------------------- 1 | package com.centomila; 2 | 3 | public interface DefaultPatterns { 4 | 5 | final static Pattern[] patterns = { 6 | // --- Various --- 7 | new Pattern("Various: 8th Increasing Velocity", new int[] { 3, 0, 17, 0, 33, 0, 49, 0, 65, 0, 81, 0, 97, 0, 115, 0 }, "C1", "1/16", "1/16", "Straight"), 8 | new Pattern("Various: 16th Increasing Velocity", new int[] { 3, 9, 17, 25, 33, 41, 49, 57, 65, 73, 81, 89, 97, 105, 115, 127 }, "C1", "1/16", "1/16", "Straight"), 9 | new Pattern("Various: 8th Decreasing Velocity", new int[] { 115, 0, 97, 0, 81, 0, 65, 0, 49, 0, 33, 0, 17, 0, 3, 0 }, "C1", "1/16", "1/16", "Straight"), 10 | new Pattern("Various: 16th Decreasing Velocity", new int[] { 127, 115, 105, 97, 89, 81, 73, 65, 57, 49, 41, 33, 25, 17, 9, 3 }, "C1", "1/16", "1/16", "Straight"), 11 | // --- KICK-ONLY PATTERNS --- 12 | new Pattern("Kick: Four on the Floor", new int[] { 100, 0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0, 100, 0, 0, 0 }, "C1", "1/16", "1/16", "Straight"), 13 | new Pattern("Kick: 1 and 3", new int[] { 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0 }, "C1", "1/16", "1/16", "Straight"), 14 | new Pattern("Kick: Syncopated (1, & of 2, 3)", new int[] { 100, 0, 0, 0, 0, 0, 100, 0, 100, 0, 0, 0, 0, 0, 0, 0 }, "C1", "1/16", "1/16", "Straight"), 15 | new Pattern("Kick: Off-Beats (2 and 4)", new int[] { 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0 }, "C1", "1/16", "1/16", "Straight"), 16 | new Pattern("Kick: Driving 8th Notes", new int[] { 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0, 100, 0 }, "C1", "1/16", "1/16", "Straight"), 17 | // --- SNARE-ONLY PATTERNS --- 18 | new Pattern("Snare: Backbeat 2 and 4", new int[] { 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0 }, "C#1", "1/16", "1/16", "Straight"), 19 | new Pattern("Snare: Only on Beat 2", new int[] { 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, "C#1", "1/16", "1/16", "Straight"), 20 | new Pattern("Snare: Half-Time (3)", new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0 }, "C#1", "1/16", "1/16", "Straight"), 21 | new Pattern("Snare: 16th Ghost Notes", new int[] { 20, 0, 20, 0, 100, 0, 20, 0, 20, 0, 20, 0, 100, 0, 20, 0 }, "C#1", "1/16", "1/16", "Straight"), 22 | new Pattern("Snare: 16th Machine Gun", new int[] { 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100 }, "C#1", "1/16", "1/16", "Straight"), 23 | // --- HI-HAT-ONLY PATTERNS --- 24 | new Pattern("HiHat: Quarter Notes", new int[] { 80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0 }, "D1", "1/16", "1/16", "Straight"), 25 | new Pattern("HiHat: 8th Notes", new int[] { 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0 }, "D1", "1/16", "1/16", "Straight"), 26 | new Pattern("HiHat: 16th Notes", new int[] { 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80 }, "D1", "1/16", "1/16", "Straight"), 27 | new Pattern("HiHat: Off-Beat 8ths", new int[] { 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80, 0, 80 }, "D1", "1/16", "1/16", "Straight"), 28 | new Pattern("HiHat: On 3rd Beat", new int[] { 0, 0, 80, 0, 0, 0, 80, 0, 0, 0, 80, 0, 0, 0, 80, 0 }, "D1", "1/16", "1/16", "Straight"), 29 | new Pattern("HiHat: Shuffle (Swing 16ths)", new int[] { 80, 0, 40, 0, 80, 0, 40, 0, 80, 0, 40, 0, 80, 0, 40, 0 }, "D1", "1/16", "1/16", "Straight") 30 | }; 31 | 32 | /** 33 | * Retrieves a drum pattern by its name. 34 | * 35 | * @param name the name of the drum pattern to retrieve 36 | * @return the drum pattern as an array of integers 37 | * @throws IllegalArgumentException if no pattern is found with the specified name 38 | */ 39 | public static int[] getPatternByName(String name) { 40 | for (Pattern pattern : patterns) { 41 | if (pattern.getName().equals(name)) { 42 | return pattern.getPattern(); 43 | } 44 | } 45 | // Handle not found case appropriately 46 | throw new IllegalArgumentException("No pattern found with name: " + name); 47 | } 48 | 49 | void init(); 50 | } 51 | 52 | class Pattern { 53 | private String name; 54 | private int[] pattern; 55 | private String defaultNote; 56 | private String defaultStepSize; 57 | private String defaultNoteLength; 58 | private String defaultSubdivisions; 59 | 60 | public Pattern(String name, int[] pattern, String defaultNote, String defaultStepSize, String defaultNoteLength, String defaultSubdivisions) { 61 | this.name = name; 62 | this.pattern = pattern; 63 | this.defaultNote = defaultNote; 64 | this.defaultStepSize = defaultStepSize; 65 | this.defaultNoteLength = defaultNoteLength; 66 | this.defaultSubdivisions = defaultSubdivisions; 67 | } 68 | 69 | public String getName() { 70 | return name; 71 | } 72 | 73 | public int[] getPattern() { 74 | return pattern; 75 | } 76 | 77 | public String getDefaultNote() { 78 | return defaultNote; 79 | } 80 | 81 | public String getDefaultStepSize() { 82 | return defaultStepSize; 83 | } 84 | 85 | public String getDefaultNoteLength() { 86 | return defaultNoteLength; 87 | } 88 | 89 | public String getDefaultSubdivisions() { 90 | return defaultSubdivisions; 91 | } 92 | } 93 | --------------------------------------------------------------------------------