@tialro2/rnbokit 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +243 -0
  2. package/dist/RNBO.css +113 -0
  3. package/dist/RNBO.svelte +230 -0
  4. package/dist/RNBO.svelte.d.ts +97 -0
  5. package/dist/RNBOcomponents/AudioDropIn.svelte +133 -0
  6. package/dist/RNBOcomponents/AudioDropIn.svelte.d.ts +17 -0
  7. package/dist/RNBOcomponents/ExtMidiIn.svelte +93 -0
  8. package/dist/RNBOcomponents/ExtMidiIn.svelte.d.ts +13 -0
  9. package/dist/RNBOcomponents/ExtMidiOut.svelte +93 -0
  10. package/dist/RNBOcomponents/ExtMidiOut.svelte.d.ts +13 -0
  11. package/dist/RNBOcomponents/MicIn.svelte +88 -0
  12. package/dist/RNBOcomponents/MicIn.svelte.d.ts +17 -0
  13. package/dist/RNBOcomponents/RNBOInlet.svelte +66 -0
  14. package/dist/RNBOcomponents/RNBOInlet.svelte.d.ts +55 -0
  15. package/dist/RNBOcomponents/RNBOInport.svelte +21 -0
  16. package/dist/RNBOcomponents/RNBOInport.svelte.d.ts +13 -0
  17. package/dist/RNBOcomponents/RNBOMidiIn.svelte +63 -0
  18. package/dist/RNBOcomponents/RNBOMidiIn.svelte.d.ts +31 -0
  19. package/dist/RNBOcomponents/RNBOMidiOut.svelte +38 -0
  20. package/dist/RNBOcomponents/RNBOMidiOut.svelte.d.ts +27 -0
  21. package/dist/RNBOcomponents/RNBOOutport.svelte +36 -0
  22. package/dist/RNBOcomponents/RNBOOutport.svelte.d.ts +21 -0
  23. package/dist/RNBOcomponents/RNBOParam.svelte +37 -0
  24. package/dist/RNBOcomponents/RNBOParam.svelte.d.ts +15 -0
  25. package/dist/RNBOcomponents/XYMidiIn.svelte +101 -0
  26. package/dist/RNBOcomponents/XYMidiIn.svelte.d.ts +17 -0
  27. package/dist/UIcomponents/Controls.svelte +12 -0
  28. package/dist/UIcomponents/Controls.svelte.d.ts +7 -0
  29. package/dist/UIcomponents/FileDropZone.svelte +118 -0
  30. package/dist/UIcomponents/FileDropZone.svelte.d.ts +37 -0
  31. package/dist/UIcomponents/ProgressBar.svelte +50 -0
  32. package/dist/UIcomponents/ProgressBar.svelte.d.ts +17 -0
  33. package/dist/UIcomponents/RadioGroup.svelte +35 -0
  34. package/dist/UIcomponents/RadioGroup.svelte.d.ts +13 -0
  35. package/dist/UIcomponents/RadioItem.svelte +96 -0
  36. package/dist/UIcomponents/RadioItem.svelte.d.ts +21 -0
  37. package/dist/UIcomponents/RangeSlider.svelte +38 -0
  38. package/dist/UIcomponents/RangeSlider.svelte.d.ts +21 -0
  39. package/dist/index.d.ts +15 -0
  40. package/dist/index.js +20 -0
  41. package/package.json +72 -0
@@ -0,0 +1,133 @@
1
+ <script>
2
+ //A file drop in/media player for audio
3
+ import { flip } from 'svelte/animate';
4
+ import { crossfade } from 'svelte/transition';
5
+ const [send, receive] = crossfade({
6
+ duration: (d) => Math.sqrt(d * 200)
7
+ });
8
+
9
+ import FileDropZone from '../UIcomponents/FileDropZone.svelte';
10
+
11
+
12
+
13
+ /**
14
+ * @typedef {Object} Props
15
+ * @property {AudioContext} context
16
+ * @property {AudioNode|undefined} audio
17
+ */
18
+
19
+ /** @type {Props & { [key: string]: any }} */
20
+ let { context, audio = $bindable(), ...rest } = $props();
21
+
22
+ /** @type {Array<File>} */
23
+ let files = $state([]);
24
+ /** @type {HTMLMediaElement[]} */
25
+ let audioElements = $state([]);
26
+
27
+ /** @type {HTMLMediaElement|undefined} */
28
+ let current;
29
+
30
+ /**
31
+ * pause other running audio and connect new playing source on play
32
+ * @param {Event} e
33
+ */
34
+ function onPlay(e) {
35
+ if (current === e.target) return;
36
+
37
+ for (const audioElement of audioElements) {
38
+ if (audioElement !== e.target) {
39
+ // console.log('pause');
40
+ audioElement.pause();
41
+ continue;
42
+ }
43
+ // console.log('connect');
44
+ current = audioElement;
45
+ }
46
+ try {
47
+ audio = context.createMediaElementSource(current);
48
+ } catch (e) {
49
+ //ignore DOMexception error, as the correct source will still be used,
50
+ // no easy way around arror (need to investigate)
51
+ console.log('failed to (re)connect MediaElementSource');
52
+ }
53
+ }
54
+
55
+ /**
56
+ * put files in an array, so we can do array operations like splice. e.target.files is read-only
57
+ * @param {Event} e
58
+ */
59
+ function loadFiles(e) {
60
+ // @ts-expect-error - quick fix for: TS2339: Property 'files' does not exist on type 'Event'.
61
+ files = [...e.target.files];
62
+ }
63
+
64
+ /**
65
+ * remove a file from the files array when we click the 'x'. If no more files,
66
+ * show audio drop in again
67
+ * @param {Number} index
68
+ */
69
+ function removeFile(index) {
70
+ if (!files) return;
71
+ audioElements = [];
72
+
73
+ files.splice(index, 1);
74
+ files = files;
75
+ if (files.length === 0) {
76
+ files = [];
77
+ audioElements = [];
78
+ }
79
+ }
80
+ </script>
81
+
82
+ <div class="RNBOsubcomponent" {...rest}>
83
+ {#if files.length === 0}
84
+ <FileDropZone name="audio files" on:change={loadFiles} multiple accept="audio/*" />
85
+ {:else}
86
+ {#each files as file, i (file)}
87
+ <div
88
+ class="RNBOsection RNBOaudio"
89
+ in:receive={{ key: file }}
90
+ out:send={{ key: file }}
91
+ animate:flip={{ duration: 200 }}
92
+ >
93
+ <p class="RNBOtext">{file.name}</p>
94
+ <audio
95
+ src={URL.createObjectURL(file)}
96
+ bind:this={audioElements[i]}
97
+ onplay={onPlay}
98
+ controls
99
+ ></audio>
100
+ <button
101
+ type="button"
102
+ class="RNBObtn RNBOclose"
103
+ onclick={() => removeFile(i)}
104
+ onkeydown={() => removeFile(i)}
105
+ >
106
+ x
107
+ </button>
108
+ </div>
109
+ {/each}
110
+ {/if}
111
+ </div>
112
+
113
+ <style>
114
+ .RNBOclose {
115
+ position: absolute;
116
+ top: -0.5rem;
117
+ right: -0.5rem;
118
+ padding-top: 0.2rem;
119
+ padding-bottom: 0.2rem;
120
+ padding-left: 0.5rem;
121
+ padding-right: 0.5rem;
122
+ cursor: pointer;
123
+ border-radius: var(--local-rounded-base);
124
+ border-style: none;
125
+ background-color: rgb(var(--local-accent-color));
126
+ }
127
+ .RNBOaudio {
128
+ position: relative;
129
+ display: inline-block;
130
+ padding: 0.5rem;
131
+ margin: 0.5rem;
132
+ }
133
+ </style>
@@ -0,0 +1,17 @@
1
+ export default AudioDropIn;
2
+ type AudioDropIn = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props & {
5
+ [key: string]: any;
6
+ }>): void;
7
+ };
8
+ declare const AudioDropIn: import("svelte").Component<{
9
+ context: AudioContext;
10
+ audio: AudioNode | undefined;
11
+ } & {
12
+ [key: string]: any;
13
+ }, {}, "audio">;
14
+ type Props = {
15
+ context: AudioContext;
16
+ audio: AudioNode | undefined;
17
+ };
@@ -0,0 +1,93 @@
1
+ <script>
2
+ import RadioGroup from '../UIcomponents/RadioGroup.svelte';
3
+ import RadioItem from '../UIcomponents/RadioItem.svelte';
4
+ /** @type {MIDIAccess|null|false} */
5
+ let midi = $state(null); // global MIDIAccess object
6
+ /**@type {MIDIInputMap|null} */
7
+ let inports = $state(null); // available MIDI inports
8
+
9
+ //TODO: choose port by name rather than index
10
+
11
+
12
+ /** @type {MIDIInput|null} */
13
+ let inport = null;
14
+
15
+ /**
16
+ * @typedef {Object} Props
17
+ * @property {number|null} [port]
18
+ * @property {Uint8Array} midiMessage
19
+ */
20
+
21
+ /** @type {Props} */
22
+ let { port = $bindable(null), midiMessage = $bindable() } = $props();
23
+
24
+ /**
25
+ * set the midi access point and get the midi input port
26
+ * @param {MIDIAccess} midiAccess
27
+ */
28
+ function onMIDISuccess(midiAccess) {
29
+ midi = midiAccess;
30
+ inports = midi.inputs; // store in the global (in real usage, would probably keep in an object instance)
31
+ if (port) {
32
+ setPort();
33
+ }
34
+ }
35
+
36
+ /**
37
+ * handle if external MIDI not available
38
+ * @param {string} msg
39
+ */
40
+ function onMIDIFailure(msg) {
41
+ console.error(`Failed to get MIDI access - ${msg}`);
42
+ midi = false;
43
+ }
44
+
45
+ /**
46
+ * set the input port when selected
47
+ */
48
+ function setPort() {
49
+ if (inports === null || port === null) return;
50
+
51
+ // @ts-ignore - ts getting their types wrong...
52
+ inport = [...inports][port];
53
+ console.log(
54
+ // @ts-ignore - because inports will always exist, since we use the #if block
55
+ 'connecting to MIDI input port: %c' + inports.get(inport[0]).name,
56
+ 'font-style:italic'
57
+ );
58
+ // @ts-ignore - see previous comment
59
+ inports.get(inport[0]).onmidimessage = onMIDIMessage;
60
+ }
61
+
62
+ /** @param {MIDIMessageEvent} event */
63
+ function onMIDIMessage(event) {
64
+ midiMessage = event.data;
65
+ console.log(
66
+ 'MIDI message from %c' + event.target.name + ': ' + midiMessage,
67
+ 'font-style:italic'
68
+ );
69
+ }
70
+
71
+ navigator.requestMIDIAccess().then(onMIDISuccess, onMIDIFailure);
72
+ </script>
73
+
74
+ <div class="RNBOsubcomponent">
75
+ <p class="RNBOtext">External MIDI inputs</p>
76
+
77
+ {#if midi && inports}
78
+ <!-- <slot {midi} {inports} {port}> -->
79
+ <RadioGroup>
80
+ {#each [...inports.entries()] as input, i}
81
+ <!-- first index of the input array is the id, second the object -->
82
+ <RadioItem bind:group={port} name="inports" value={i} on:change={setPort}
83
+ >{input[1].name}
84
+ </RadioItem>
85
+ {/each}
86
+ </RadioGroup>
87
+ <!-- </slot> -->
88
+ {:else if midi === false}
89
+ <p>MIDI access failed, please try in a different browser</p>
90
+ {:else}
91
+ <p>loading MIDI ports</p>
92
+ {/if}
93
+ </div>
@@ -0,0 +1,13 @@
1
+ export default ExtMidiIn;
2
+ type ExtMidiIn = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const ExtMidiIn: import("svelte").Component<{
7
+ port?: number | null;
8
+ midiMessage: Uint8Array;
9
+ }, {}, "midiMessage" | "port">;
10
+ type Props = {
11
+ port?: number | null;
12
+ midiMessage: Uint8Array;
13
+ };
@@ -0,0 +1,93 @@
1
+ <script>
2
+ import RadioGroup from '../UIcomponents/RadioGroup.svelte';
3
+ import RadioItem from '../UIcomponents/RadioItem.svelte';
4
+ /** @type {MIDIAccess|null|false} */
5
+ let midi = $state(null); // global MIDIAccess object
6
+ /**@type {MIDIInputMap|null} */
7
+ let outports = $state(null); // available MIDI outports
8
+
9
+ //TODO: choose port by name rather than index
10
+
11
+
12
+ /** @type {MIDIInput|null} */
13
+ let outport = null;
14
+
15
+ /**
16
+ * @typedef {Object} Props
17
+ * @property {number|null} [port]
18
+ * @property {Uint8Array} midiMessage
19
+ */
20
+
21
+ /** @type {Props} */
22
+ let { port = $bindable(null), midiMessage = $bindable() } = $props();
23
+
24
+ /**
25
+ * set the midi access point and get the midi output port
26
+ * @param {MIDIAccess} midiAccess
27
+ */
28
+ function onMIDISuccess(midiAccess) {
29
+ midi = midiAccess;
30
+ outports = midi.outputs; // store in the global (in real usage, would probably keep in an object instance)
31
+ if (port) {
32
+ setPort();
33
+ }
34
+ }
35
+
36
+ /**
37
+ * handle if external MIDI not available
38
+ * @param {string} msg
39
+ */
40
+ function onMIDIFailure(msg) {
41
+ console.error(`Failed to get MIDI access - ${msg}`);
42
+ midi = false;
43
+ }
44
+
45
+ /**
46
+ * set the output port when selected
47
+ */
48
+ function setPort() {
49
+ if (!outports || !port) return;
50
+
51
+ // @ts-ignore - ts getting their types wrong...
52
+ outport = [...outports][port];
53
+ console.log(
54
+ // @ts-ignore - because outports will always exist, since we use the #if block
55
+ 'connecting to MIDI output port: %c' + outports.get(outport[0]).name,
56
+ 'font-style:italic'
57
+ );
58
+ // @ts-ignore - see previous comment
59
+ outports.get(outport[0]).onmidimessage = onMIDIMessage;
60
+ }
61
+
62
+ /** @param {MIDIMessageEvent} event */
63
+ function onMIDIMessage(event) {
64
+ midiMessage = event.data;
65
+ console.log(
66
+ 'MIDI message from %c' + event.target.name + ': ' + midiMessage,
67
+ 'font-style:italic'
68
+ );
69
+ }
70
+
71
+ navigator.requestMIDIAccess().then(onMIDISuccess, onMIDIFailure);
72
+ </script>
73
+
74
+ <div class="RNBOsubcomponent">
75
+ <p class="RNBOtext">External MIDI outputs</p>
76
+
77
+ {#if midi && outports}
78
+ <!-- <slot {midi} {outports} {port}> -->
79
+ <RadioGroup>
80
+ {#each [...outports.entries()] as output, i}
81
+ <!-- first index of the output array is the id, second the object -->
82
+ <RadioItem bind:group={port} name="outports" value={i} on:change={setPort}
83
+ >{output[1].name}
84
+ </RadioItem>
85
+ {/each}
86
+ </RadioGroup>
87
+ <!-- </slot> -->
88
+ {:else if midi === false}
89
+ <p>MIDI access failed, please try in a different browser</p>
90
+ {:else}
91
+ <p>loading MIDI ports</p>
92
+ {/if}
93
+ </div>
@@ -0,0 +1,13 @@
1
+ export default ExtMidiOut;
2
+ type ExtMidiOut = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const ExtMidiOut: import("svelte").Component<{
7
+ port?: number | null;
8
+ midiMessage: Uint8Array;
9
+ }, {}, "midiMessage" | "port">;
10
+ type Props = {
11
+ port?: number | null;
12
+ midiMessage: Uint8Array;
13
+ };
@@ -0,0 +1,88 @@
1
+ <script>
2
+ //TODO: create a nice way to visualise the average volume!
3
+ import { onDestroy } from 'svelte';
4
+
5
+
6
+ /** @type {MediaStream|null} */
7
+ let stream = $state();
8
+
9
+ /**
10
+ * @typedef {Object} Props
11
+ * @property {AudioContext} context
12
+ * @property {AudioNode} audio
13
+ */
14
+
15
+ /** @type {Props & { [key: string]: any }} */
16
+ let { context, audio = $bindable(), ...rest } = $props();
17
+
18
+ let volumeCallback = null;
19
+ /** @type {number|null} */
20
+ let averageVolume = $state(0);
21
+
22
+ /** @param {MediaStream} stream */
23
+ const handleSuccess = (stream) => {
24
+ const source = context.createMediaStreamSource(stream);
25
+ let analyser = context.createAnalyser();
26
+ analyser.fftSize = 512;
27
+ analyser.minDecibels = -127;
28
+ analyser.maxDecibels = 0;
29
+ analyser.smoothingTimeConstant = 0.4;
30
+ source.connect(analyser);
31
+ const volumes = new Uint8Array(analyser.frequencyBinCount);
32
+ //probe every 100ms for the average volume
33
+ volumeCallback = setInterval(() => {
34
+ analyser.getByteFrequencyData(volumes);
35
+ let volumeSum = 0;
36
+ for (const volume of volumes) volumeSum += volume;
37
+ averageVolume = volumeSum / volumes.length;
38
+ // console.log(`brightness-[${averageVolume / 100}]`);
39
+ }, 100);
40
+ source.connect(analyser);
41
+ audio = analyser;
42
+ // analyser.connect(device.node, inlet.index - 1);
43
+ };
44
+
45
+ async function connectMic() {
46
+ if (!stream) {
47
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
48
+ handleSuccess(stream);
49
+ } else {
50
+ disconnectMic();
51
+ }
52
+ }
53
+ function disconnectMic() {
54
+ if (!stream) return;
55
+ clearInterval(volumeCallback);
56
+ volumeCallback = null;
57
+ averageVolume = 0;
58
+ stream.getTracks().forEach((track) => track.stop());
59
+ stream = null;
60
+ }
61
+ onDestroy(disconnectMic);
62
+ </script>
63
+
64
+ <div class="RNBOsubcomponent RNBOalign" {...rest}>
65
+ <button onclick={connectMic} type="button" class="RNBObutton">
66
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 352 512" class:RNBOfill={stream}>
67
+ <path
68
+ d="M176 352c53.02 0 96-42.98 96-96V96c0-53.02-42.98-96-96-96S80 42.98 80 96v160c0 53.02 42.98 96 96 96zm160-160h-16c-8.84 0-16 7.16-16 16v48c0 74.8-64.49 134.82-140.79 127.38C96.71 376.89 48 317.11 48 250.3V208c0-8.84-7.16-16-16-16H16c-8.84 0-16 7.16-16 16v40.16c0 89.64 63.97 169.55 152 181.69V464H96c-8.84 0-16 7.16-16 16v16c0 8.84 7.16 16 16 16h160c8.84 0 16-7.16 16-16v-16c0-8.84-7.16-16-16-16h-56v-33.77C285.71 418.47 352 344.9 352 256v-48c0-8.84-7.16-16-16-16z"
69
+ />
70
+ </svg>
71
+ </button>
72
+ <p>{averageVolume}</p>
73
+ </div>
74
+
75
+ <style>
76
+ .RNBOfill {
77
+ fill: rgb(var(--local-accent-color));
78
+ transition: var(--local-animation) var(--local-animation-duration);
79
+ transition-timing-function: var(--local-animation-function);
80
+ }
81
+ .RNBObutton {
82
+ width: 3rem;
83
+ border-style: none;
84
+ background-color: transparent;
85
+ transition: var(--local-animation) var(--local-animation-duration);
86
+ transition-timing-function: var(--local-animation-function);
87
+ }
88
+ </style>
@@ -0,0 +1,17 @@
1
+ export default MicIn;
2
+ type MicIn = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props & {
5
+ [key: string]: any;
6
+ }>): void;
7
+ };
8
+ declare const MicIn: import("svelte").Component<{
9
+ context: AudioContext;
10
+ audio: AudioNode;
11
+ } & {
12
+ [key: string]: any;
13
+ }, {}, "audio">;
14
+ type Props = {
15
+ context: AudioContext;
16
+ audio: AudioNode;
17
+ };
@@ -0,0 +1,66 @@
1
+ <script>
2
+ import RadioGroup from '../UIcomponents/RadioGroup.svelte';
3
+ import RadioItem from '../UIcomponents/RadioItem.svelte';
4
+ import AudioDropIn from './AudioDropIn.svelte';
5
+ import MicIn from './MicIn.svelte';
6
+
7
+ let context = device.context;
8
+
9
+ /** @typedef {object} inlet
10
+ * @property {number} index - the port index
11
+ * @property {string} tag - the tag
12
+ * @property {'signal'} type - the type
13
+ * @property {string} meta - the meta
14
+ */
15
+
16
+ /** @type {AudioNode} */
17
+ let audio = $state();
18
+
19
+ /** @type {{default: 'default', mic: 'mic', dropIn: 'dropIn'}} */
20
+ let modes = { default: 'default', mic: 'mic', dropIn: 'dropIn' };
21
+
22
+ /**
23
+ * @typedef {Object} Props
24
+ * @property {import ('@rnbo/js').Device} device
25
+ * @property {inlet} [inlet]
26
+ * @property {'default'|'mic'|'dropIn'} [mode]
27
+ * @property {import('svelte').Snippet<[any]>} [children]
28
+ */
29
+
30
+ /** @type {Props & { [key: string]: any }} */
31
+ let {
32
+ device,
33
+ inlet = { index: 1, tag: 'in1', type: 'signal', meta: 'something' },
34
+ mode = $bindable(modes.default),
35
+ children,
36
+ ...rest
37
+ } = $props();
38
+ //set a defaultMode so the radio group only shows if no mode has been specified
39
+ /** @type {'default'|'mic'|'dropIn'} */
40
+ let defaultMode = mode;
41
+ //make sure the default mode is mic in the radio group
42
+ if (mode == modes.default) mode = modes.mic;
43
+
44
+ $effect(() => {
45
+ if (audio) {
46
+ audio.connect(device.node, inlet.index - 1);
47
+ }
48
+ });
49
+ </script>
50
+
51
+ <div class="RNBOcomponent RNBOsection" {...rest}>
52
+ {#if children}{@render children({ context, audio })}{:else}
53
+ <div class="RNBOtag">{inlet.tag}</div>
54
+ {#if defaultMode === modes.default}
55
+ <RadioGroup>
56
+ <RadioItem bind:group={mode} name="Mic" value={modes.mic}>Mic</RadioItem>
57
+ <RadioItem bind:group={mode} name="DropIn" value={modes.dropIn}>Drop-in</RadioItem>
58
+ </RadioGroup>
59
+ {/if}
60
+ {#if mode === modes.dropIn}
61
+ <AudioDropIn class="RNBOsubcomponent" bind:audio {context} />
62
+ {:else}
63
+ <MicIn bind:audio {context} />
64
+ {/if}
65
+ {/if}
66
+ </div>
@@ -0,0 +1,55 @@
1
+ export default RNBOInlet;
2
+ type RNBOInlet = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props & {
5
+ [key: string]: any;
6
+ }>): void;
7
+ };
8
+ declare const RNBOInlet: import("svelte").Component<{
9
+ device: import("@rnbo/js").Device;
10
+ inlet?: {
11
+ /**
12
+ * - the port index
13
+ */
14
+ index: number;
15
+ /**
16
+ * - the tag
17
+ */
18
+ tag: string;
19
+ /**
20
+ * - the type
21
+ */
22
+ type: "signal";
23
+ /**
24
+ * - the meta
25
+ */
26
+ meta: string;
27
+ };
28
+ mode?: "default" | "mic" | "dropIn";
29
+ children?: import("svelte").Snippet<[any]>;
30
+ } & {
31
+ [key: string]: any;
32
+ }, {}, "mode">;
33
+ type Props = {
34
+ device: import("@rnbo/js").Device;
35
+ inlet?: {
36
+ /**
37
+ * - the port index
38
+ */
39
+ index: number;
40
+ /**
41
+ * - the tag
42
+ */
43
+ tag: string;
44
+ /**
45
+ * - the type
46
+ */
47
+ type: "signal";
48
+ /**
49
+ * - the meta
50
+ */
51
+ meta: string;
52
+ };
53
+ mode?: "default" | "mic" | "dropIn";
54
+ children?: import("svelte").Snippet<[any]>;
55
+ };
@@ -0,0 +1,21 @@
1
+ <script>
2
+ import Controls from '../UIcomponents/Controls.svelte';
3
+ import rnbo from '@rnbo/js';
4
+ const { TimeNow, MessageEvent } = rnbo;
5
+
6
+ /**
7
+ * @typedef {Object} Props
8
+ * @property {string} tag
9
+ * @property {import ('@rnbo/js').Device} device
10
+ */
11
+
12
+ /** @type {Props} */
13
+ let { tag, device } = $props();
14
+
15
+ const sendMessage = () => {
16
+ const messageEvent = new MessageEvent(TimeNow, tag);
17
+ device.scheduleEvent(messageEvent);
18
+ };
19
+ </script>
20
+
21
+ <Controls {tag} {sendMessage} />
@@ -0,0 +1,13 @@
1
+ export default RNBOInport;
2
+ type RNBOInport = {
3
+ $on?(type: string, callback: (e: any) => void): () => void;
4
+ $set?(props: Partial<Props>): void;
5
+ };
6
+ declare const RNBOInport: import("svelte").Component<{
7
+ tag: string;
8
+ device: import("@rnbo/js").Device;
9
+ }, {}, "">;
10
+ type Props = {
11
+ tag: string;
12
+ device: import("@rnbo/js").Device;
13
+ };
@@ -0,0 +1,63 @@
1
+ <script>
2
+ import rnbo from '@rnbo/js';
3
+ const { MIDIEvent, TimeNow } = rnbo;
4
+ import XyMidiIn from './XYMidiIn.svelte';
5
+ import ExtMidiIn from './ExtMidiIn.svelte';
6
+ import RadioGroup from '../UIcomponents/RadioGroup.svelte';
7
+ import RadioItem from '../UIcomponents/RadioItem.svelte';
8
+
9
+ /** @type {{default: 'default', xy: 'xy', external: 'external'}} */
10
+ let modes = { default: 'default', xy: 'xy', external: 'external' };
11
+
12
+ /**
13
+ * @typedef {Object} Props
14
+ * @property {number} [port] - type {number} - the MIDI port index
15
+ * @property {import ('@rnbo/js').Device} device
16
+ * @property {import('@rnbo/js').MIDIData|undefined} [midiMessage]
17
+ * @property {number|undefined} [midiChannel]
18
+ * @property {'default'|'xy'|'external'} [mode]
19
+ * @property {import('svelte').Snippet<[any]>} [children]
20
+ */
21
+
22
+ /** @type {Props & { [key: string]: any }} */
23
+ let {
24
+ port = 0,
25
+ device,
26
+ midiMessage = $bindable(undefined),
27
+ midiChannel = 0,
28
+ mode = $bindable(modes.default),
29
+ children,
30
+ ...rest
31
+ } = $props();
32
+
33
+ //set a defaultMode so the radio group only shows if no mode has been specified
34
+ /** @type {'default'|'xy'|'external'} */
35
+ let defaultMode = mode;
36
+ //make sure the default mode is mic in the radio group
37
+ if (mode == modes.default) mode = modes.xy;
38
+
39
+ $effect(() => {
40
+ if (midiMessage && midiMessage.length > 0) {
41
+ console.log('from MidiIn', midiMessage);
42
+ device.scheduleEvent(new MIDIEvent(TimeNow, port, midiMessage));
43
+ }
44
+ });
45
+ </script>
46
+
47
+ <div class="RNBOcomponent RNBOsection" {...rest}>
48
+ {#if children}{@render children({ midiChannel })}{:else}
49
+ <div class="RNBOtag">in {port}</div>
50
+ {#if defaultMode === modes.default}
51
+ <RadioGroup>
52
+ <RadioItem bind:group={mode} name="xy" value={modes.xy}>XY-pad</RadioItem>
53
+ <RadioItem bind:group={mode} name="external" value={modes.external}>ext. MIDI</RadioItem>
54
+ </RadioGroup>
55
+ {/if}
56
+
57
+ {#if mode === modes.external}
58
+ <ExtMidiIn bind:midiMessage {midiChannel} />
59
+ {:else}
60
+ <XyMidiIn bind:midiMessage {midiChannel} />
61
+ {/if}
62
+ {/if}
63
+ </div>