@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.
- package/README.md +243 -0
- package/dist/RNBO.css +113 -0
- package/dist/RNBO.svelte +230 -0
- package/dist/RNBO.svelte.d.ts +97 -0
- package/dist/RNBOcomponents/AudioDropIn.svelte +133 -0
- package/dist/RNBOcomponents/AudioDropIn.svelte.d.ts +17 -0
- package/dist/RNBOcomponents/ExtMidiIn.svelte +93 -0
- package/dist/RNBOcomponents/ExtMidiIn.svelte.d.ts +13 -0
- package/dist/RNBOcomponents/ExtMidiOut.svelte +93 -0
- package/dist/RNBOcomponents/ExtMidiOut.svelte.d.ts +13 -0
- package/dist/RNBOcomponents/MicIn.svelte +88 -0
- package/dist/RNBOcomponents/MicIn.svelte.d.ts +17 -0
- package/dist/RNBOcomponents/RNBOInlet.svelte +66 -0
- package/dist/RNBOcomponents/RNBOInlet.svelte.d.ts +55 -0
- package/dist/RNBOcomponents/RNBOInport.svelte +21 -0
- package/dist/RNBOcomponents/RNBOInport.svelte.d.ts +13 -0
- package/dist/RNBOcomponents/RNBOMidiIn.svelte +63 -0
- package/dist/RNBOcomponents/RNBOMidiIn.svelte.d.ts +31 -0
- package/dist/RNBOcomponents/RNBOMidiOut.svelte +38 -0
- package/dist/RNBOcomponents/RNBOMidiOut.svelte.d.ts +27 -0
- package/dist/RNBOcomponents/RNBOOutport.svelte +36 -0
- package/dist/RNBOcomponents/RNBOOutport.svelte.d.ts +21 -0
- package/dist/RNBOcomponents/RNBOParam.svelte +37 -0
- package/dist/RNBOcomponents/RNBOParam.svelte.d.ts +15 -0
- package/dist/RNBOcomponents/XYMidiIn.svelte +101 -0
- package/dist/RNBOcomponents/XYMidiIn.svelte.d.ts +17 -0
- package/dist/UIcomponents/Controls.svelte +12 -0
- package/dist/UIcomponents/Controls.svelte.d.ts +7 -0
- package/dist/UIcomponents/FileDropZone.svelte +118 -0
- package/dist/UIcomponents/FileDropZone.svelte.d.ts +37 -0
- package/dist/UIcomponents/ProgressBar.svelte +50 -0
- package/dist/UIcomponents/ProgressBar.svelte.d.ts +17 -0
- package/dist/UIcomponents/RadioGroup.svelte +35 -0
- package/dist/UIcomponents/RadioGroup.svelte.d.ts +13 -0
- package/dist/UIcomponents/RadioItem.svelte +96 -0
- package/dist/UIcomponents/RadioItem.svelte.d.ts +21 -0
- package/dist/UIcomponents/RangeSlider.svelte +38 -0
- package/dist/UIcomponents/RangeSlider.svelte.d.ts +21 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +20 -0
- 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>
|