Skip to content

Commit 4b634ab

Browse files
authored
feat: Added marker support, closes #5 (#9)
1 parent 3bce8f7 commit 4b634ab

File tree

10 files changed

+255
-57
lines changed

10 files changed

+255
-57
lines changed

Mother Project.RPP

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<REAPER_PROJECT 0.1 "7.11/linux-x86_64" 1710608575
1+
<REAPER_PROJECT 0.1 "7.11/linux-x86_64" 1710765457
22
<NOTES 0 2
33
>
44
RIPPLE 0
@@ -16,8 +16,8 @@
1616
TIMEMODE 1 5 -1 30 0 0 -1
1717
VIDEO_CONFIG 0 0 256
1818
PANMODE 3
19-
CURSOR 1126.9562400000714
20-
ZOOM 0.96542783854914 0 0
19+
CURSOR 843.13022400005377
20+
ZOOM 0.96502131908623 0 0
2121
VZOOMEX 0 0
2222
USE_REC_CFG 0
2323
RECMODE 1
@@ -65,8 +65,8 @@
6565
GROUP_NAME 0 "Group 1"
6666
GROUP_NAME 1 "Group 2"
6767
GROUP_NAME 2 "Group 3"
68-
SELECTION 684.52156800004377 1126.9562400000714
69-
SELECTION2 684.52156800004377 1126.9562400000714
68+
SELECTION 1126.9562400000714 684.52156800004377
69+
SELECTION2 1126.9562400000714 684.52156800004377
7070
MASTERAUTOMODE 0
7171
MASTERTRACKHEIGHT 0 0
7272
MASTERPEAKCOL 16576
@@ -95,15 +95,19 @@
9595
DEFSHAPE 1 -1 -1
9696
PT 0.000000000001 115.0000287500 1
9797
>
98-
MARKER 1 0 "" 0 0 1 B {1F9B8B34-1C66-84DF-D437-2AD7A326359B} 0
99-
MARKER 1 0.00000000000097 "Song 1" 1 0 1 B {BAA5B1BE-F406-6AE8-77EA-111BEE4305F7} 0
98+
MARKER 1 0 "Song 1" 1 0 1 B {BAA5B1BE-F406-6AE8-77EA-111BEE4305F7} 0
10099
MARKER 1 250.43472000001663 "" 1
100+
MARKER 1 0.000000000001 "" 0 0 1 B {1F9B8B34-1C66-84DF-D437-2AD7A326359B} 0
101101
MARKER 2 333.91296000002183 "Song 2" 1 25921319 1 B {48FB3B12-9C2D-B592-5400-2AE20785DF92} 0
102102
MARKER 2 550.95638400003531 "" 1
103103
MARKER 2 333.91296000002183 "" 0 0 1 B {C5377EDF-29FC-FEAA-0D4E-B2069F2AE417} 0
104104
MARKER 3 684.52156800004377 "Song 3" 1 20716939 1 B {06E043B5-5ED2-0A7D-A4E9-43018CD84F17} 0
105105
MARKER 3 1126.9562400000714 "" 1
106106
MARKER 3 684.52156800004377 "" 0 0 1 B {D49AE53A-CE94-B9E2-9615-1CA54E4E9C2D} 0
107+
MARKER 4 767.99980800004903 a 0 0 1 B {B3288D66-0FFD-C8D3-464A-1FEAB765C9B8} 0
108+
MARKER 5 843.13022400005377 "" 0 17715871 1 B {5CD95A30-4F1E-F14C-A20F-E6A1A59AE7CC} 0
109+
MARKER 6 943.30411200005994 "" 0 16777216 1 B {2204888A-45F3-66BD-3378-73652DF4690C} 0
110+
MARKER 7 1043.4780000000662 "" 0 0 1 B {DFCC72D3-B7F0-0A1A-492B-C976E1A373FA} 0
107111
<PROJBAY
108112
>
109113
<TRACK {93203F69-DDEF-ED48-8FC3-902D10C8FDA2}
@@ -145,7 +149,7 @@
145149
ISBUS 0 0
146150
BUSCOMP 0 0 0 0 0
147151
SHOWINMIX 1 0.6667 0.5 1 0.5 0 -1 0
148-
SEL 1
152+
SEL 0
149153
REC 0 0 1 0 0 0 0 0
150154
VU 2
151155
TRACKHEIGHT 31 0 1 0 0 0 0

screenshots/Reaper Project.png

2.87 KB
Loading

screenshots/Web - Control.png

19.4 KB
Loading

src/App.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const SUBSCRIPTIONS = [
1010
{ request: "TRANSPORT", interval: 100 },
1111
// Query every 100ms for peak levels
1212
{ request: "TRACK", interval: 100 },
13-
{ request: "REGION", interval: 4000 },
13+
{ request: "REGION;MARKER", interval: 4000 },
1414
{ request: "GET/PROJEXTSTATE/BANDUI/regions", interval: 4000 },
1515
];
1616

src/Components/Control/Regions.tsx

Lines changed: 117 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import {
99
import { useReaper } from "../../Data/Context";
1010
import {
1111
type CurrentTime,
12-
type ParsedMeta,
12+
type Marker,
1313
PlayState,
1414
type Region,
1515
type RegionMeta,
16+
type RegionsMeta,
1617
} from "../../Data/State";
1718
import { Icons } from "../UI/Icons";
1819
import { Toggle } from "../UI/Toggle";
@@ -41,11 +42,6 @@ function time(totalSeconds: number): string {
4142
function Progress(p: { region: Region; currentTime: CurrentTime }) {
4243
return (
4344
<>
44-
<div
45-
class="absolute top-0 right-0 bottom-0 left-0 bg-black opacity-40"
46-
style={{ width: `${progress(p.region, p.currentTime.seconds)}%` }}
47-
/>
48-
4945
<div class="absolute top-0 right-0 bottom-0 flex items-center px-1 opacity-90">
5046
<p class="rounded-lg bg-neutral-900/40 p-1 font-mono text-gray-300 text-xs">
5147
{time(p.currentTime.seconds - p.region.startTime)} /{" "}
@@ -64,7 +60,10 @@ function filterRegions(regions: RegionWithMeta[]): Region[] {
6460
return regions.filter((r) => !(r.meta?.disabled ?? false));
6561
}
6662

67-
function prepareRegions(regions: Region[], meta: ParsedMeta): RegionWithMeta[] {
63+
function prepareRegions(
64+
regions: Region[],
65+
meta: RegionsMeta,
66+
): RegionWithMeta[] {
6867
const result = regions.map((r) => ({ ...r, meta: meta[r.id] }));
6968
result.sort(
7069
(a, b) => (a.meta?.index ?? 10000 + a.id) - (b.meta?.index ?? 10000 + b.id),
@@ -84,10 +83,37 @@ function playStateClass(playState: PlayState): string {
8483
}
8584
}
8685

86+
function markerPosition(region: Region, marker: Marker): number {
87+
return (
88+
((marker.startTime - region.startTime) /
89+
(region.endTime - region.startTime)) *
90+
100
91+
);
92+
}
93+
94+
function textColor(color?: string): "text-white" | "text-black" {
95+
if (color == null) {
96+
return "text-white";
97+
}
98+
99+
const squareDist =
100+
color
101+
.match(/^#(..)(..)(..)$/)
102+
?.slice(1)
103+
?.map((c) => Number.parseInt(c, 16) ** 2)
104+
?.reduce((acc, c) => acc + c, 0) ?? 0;
105+
106+
if (squareDist > (255 * 255 * 3) / 2) {
107+
return "text-black";
108+
}
109+
110+
return "text-white";
111+
}
112+
87113
function RegionList() {
88114
const {
89-
actions: { moveToRegion },
90-
data: { playState, currentTime, regions, regionMeta },
115+
actions: { moveToRegion, moveToMarker },
116+
data: { playState, currentTime, regions, regionMeta, regionMarkers },
91117
} = useReaper();
92118

93119
const isPlaying = createSelector(
@@ -101,36 +127,88 @@ function RegionList() {
101127
);
102128

103129
return (
104-
<div class="flex flex-col">
105-
<For each={processedRegions()}>
106-
{(region) => (
107-
<button
108-
type="button"
109-
class={`btn-outlined relative my-1 flex h-10 grow overflow-clip px-3 hover:brightness-125 ${
110-
isPlaying(region) && `selected ${playStateClass(playState())}`
111-
}`}
112-
style={
113-
region.color != null ? { "background-color": region.color } : {}
114-
}
115-
onClick={() => moveToRegion(region)}
116-
>
117-
{isPlaying(region) && (
118-
<Progress region={region} currentTime={currentTime()} />
119-
)}
120-
121-
<div class="absolute top-1 bottom-0 left-0.5 opacity-90">
122-
<div class="rounded-lg bg-neutral-900/40 p-1 font-normal text-base text-gray-200">
123-
{region.id}. {region.name}
130+
<div>
131+
<div
132+
class="flex justify-center rounded-md shadow-sm h-10 mb-4"
133+
role="group"
134+
>
135+
<button
136+
class="flex items-center btn-primary border rounded-none rounded-l basis-1/2"
137+
type="button"
138+
onclick={() => moveToMarker("previous")}
139+
>
140+
<Icons.Left />
141+
<span class="flex-grow">Previous marker</span>
142+
</button>
143+
<button
144+
class="flex items-center btn-primary border border-l-0 rounded-none rounded-r basis-1/2"
145+
type="button"
146+
onclick={() => moveToMarker("next")}
147+
>
148+
<span class="flex-grow">Next marker</span>
149+
<Icons.Right />
150+
</button>
151+
</div>
152+
153+
<div class="flex flex-col">
154+
<For each={processedRegions()}>
155+
{(region) => (
156+
<button
157+
type="button"
158+
class={`btn-outlined relative my-1 flex h-10 grow overflow-clip px-3 hover:brightness-125 ${
159+
isPlaying(region) && `selected ${playStateClass(playState())}`
160+
}`}
161+
style={
162+
region.color != null ? { "background-color": region.color } : {}
163+
}
164+
onClick={() => moveToRegion(region)}
165+
>
166+
{isPlaying(region) && (
167+
<div
168+
class="absolute top-0 right-0 bottom-0 left-0 bg-black opacity-40"
169+
style={{
170+
width: `${progress(region, currentTime().seconds)}%`,
171+
}}
172+
/>
173+
)}
174+
175+
<For each={regionMarkers()[region.id] ?? []}>
176+
{(marker) => (
177+
<div
178+
class="absolute top-0 bottom-0 border-l border-solid border-red-600 opacity-50"
179+
style={{
180+
left: `${markerPosition(region, marker)}%`,
181+
"border-color": marker.color,
182+
}}
183+
>
184+
<div
185+
class={`absolute top-[-3px] text-xs bg-red-600 px-1 rounded-br-md ${textColor(
186+
marker.color,
187+
)}`}
188+
style={{ "background-color": marker.color }}
189+
>
190+
{marker.id}
191+
</div>
192+
</div>
193+
)}
194+
</For>
195+
{isPlaying(region) && (
196+
<Progress region={region} currentTime={currentTime()} />
197+
)}
198+
<div class="absolute top-0 bottom-0 left-0.5 opacity-90 flex justify-center items-center">
199+
<div class="rounded-lg bg-neutral-900/40 p-1 font-normal text-base text-gray-200">
200+
{region.id}. {region.name}
201+
</div>
124202
</div>
125-
</div>
126-
</button>
127-
)}
128-
</For>
203+
</button>
204+
)}
205+
</For>
206+
</div>
129207
</div>
130208
);
131209
}
132210

133-
function moveUp(regions: RegionWithMeta[], index: number): ParsedMeta {
211+
function moveUp(regions: RegionWithMeta[], index: number): RegionsMeta {
134212
const newRegions = [...regions];
135213

136214
const temp = newRegions[index];
@@ -145,10 +223,10 @@ function moveUp(regions: RegionWithMeta[], index: number): ParsedMeta {
145223
};
146224

147225
return acc;
148-
}, {} as ParsedMeta);
226+
}, {} as RegionsMeta);
149227
}
150228

151-
function moveDown(regions: RegionWithMeta[], index: number): ParsedMeta {
229+
function moveDown(regions: RegionWithMeta[], index: number): RegionsMeta {
152230
const newRegions = [...regions];
153231

154232
const temp = newRegions[index];
@@ -163,10 +241,10 @@ function moveDown(regions: RegionWithMeta[], index: number): ParsedMeta {
163241
};
164242

165243
return acc;
166-
}, {} as ParsedMeta);
244+
}, {} as RegionsMeta);
167245
}
168246

169-
function toggleEnabled(regions: RegionWithMeta[], index: number): ParsedMeta {
247+
function toggleEnabled(regions: RegionWithMeta[], index: number): RegionsMeta {
170248
const parsedMeta = regions.reduce((acc, r, i) => {
171249
acc[r.id] = {
172250
id: r.id,
@@ -175,7 +253,7 @@ function toggleEnabled(regions: RegionWithMeta[], index: number): ParsedMeta {
175253
};
176254

177255
return acc;
178-
}, {} as ParsedMeta);
256+
}, {} as RegionsMeta);
179257

180258
parsedMeta[regions[index].id].disabled = !(
181259
regions[index].meta?.disabled ?? false

src/Components/UI/Icons.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,40 @@ const Up = () => (
132132
</svg>
133133
);
134134

135+
const Left = (p: { class?: string }) => (
136+
<svg
137+
xmlns="http://www.w3.org/2000/svg"
138+
fill="none"
139+
viewBox="0 0 24 24"
140+
stroke-width={1.5}
141+
stroke="currentColor"
142+
class={`h-5 w-5 ${p.class}`}
143+
>
144+
<path
145+
stroke-linecap="round"
146+
stroke-linejoin="round"
147+
d="M15.75 19.5 8.25 12l7.5-7.5"
148+
/>
149+
</svg>
150+
);
151+
152+
const Right = (p: { class?: string }) => (
153+
<svg
154+
xmlns="http://www.w3.org/2000/svg"
155+
fill="none"
156+
viewBox="0 0 24 24"
157+
stroke-width={1.5}
158+
stroke="currentColor"
159+
class={`h-5 w-5 ${p.class}`}
160+
>
161+
<path
162+
stroke-linecap="round"
163+
stroke-linejoin="round"
164+
d="m8.25 4.5 7.5 7.5-7.5 7.5"
165+
/>
166+
</svg>
167+
);
168+
135169
const Control = (p: { class?: string }) => (
136170
<svg
137171
class={`mb-1 h-6 w-6 ${p.class}`}
@@ -177,6 +211,8 @@ export const Icons = {
177211
Checked,
178212
Down,
179213
Up,
214+
Left,
215+
Right,
180216

181217
Control,
182218
Mix,

src/Data/Actions.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@ interface SetRegionsMeta {
4545
value: string;
4646
}
4747

48+
interface PreviousMarker {
49+
type: "PreviousMarker";
50+
}
51+
52+
interface NextMarker {
53+
type: "NextMarker";
54+
}
55+
4856
export type Action =
4957
| PlayAction
5058
| PauseAction
@@ -55,7 +63,9 @@ export type Action =
5563
| SetSendVolumeAction
5664
| ToggleSendMuteAction
5765
| ToggleRepeatAction
58-
| SetRegionsMeta;
66+
| SetRegionsMeta
67+
| PreviousMarker
68+
| NextMarker;
5969

6070
export function reduceActions(actions: Action[]): Action[] {
6171
const latest: { [k: string]: Action } = {};
@@ -132,6 +142,10 @@ export function actionsToCommands(actions: Action[]): string {
132142
return "1013;TRANSPORT";
133143
case "ToggleRepeat":
134144
return "1068;TRANSPORT";
145+
case "PreviousMarker":
146+
return "40172;TRANSPORT";
147+
case "NextMarker":
148+
return "40173;TRANSPORT";
135149
case "Move":
136150
return `SET/POS/${action.end};40626;SET/POS/${action.pos};40625;TRANSPORT`;
137151
case "SetTrackVolume":

0 commit comments

Comments
 (0)