-
-
@@ -130,6 +255,7 @@ import {computed, ref, watch} from "vue";
import {useTranslate} from "~/vendor/gettext";
import {useInjectWebDjNode} from "~/components/Public/WebDJ/useWebDjNode";
import {usePassthroughSync} from "~/components/Public/WebDJ/usePassthroughSync";
+import {useInjectWebcaster} from "~/components/Public/WebDJ/useWebcaster";
const props = defineProps({
stationName: {
@@ -143,13 +269,18 @@ const djPassword = ref(null);
const {
doPassThrough,
- isConnected,
+ bitrate,
+ sampleRate,
startStream,
- stopStream,
- metadata,
- sendMetadata
+ stopStream
} = useInjectWebDjNode();
+const {
+ metadata,
+ sendMetadata,
+ isConnected
+} = useInjectWebcaster();
+
usePassthroughSync(doPassThrough, 'global');
const {$gettext} = useTranslate();
diff --git a/frontend/vue/components/Public/WebDJ/useWebDjNode.js b/frontend/vue/components/Public/WebDJ/useWebDjNode.js
index 714869841..8194e9053 100644
--- a/frontend/vue/components/Public/WebDJ/useWebDjNode.js
+++ b/frontend/vue/components/Public/WebDJ/useWebDjNode.js
@@ -1,4 +1,4 @@
-import {inject, provide, ref} from "vue";
+import {computed, inject, provide, ref} from "vue";
const injectKey = "webDjNode";
@@ -11,55 +11,88 @@ export function useProvideWebDjNode(node) {
}
export function useWebDjNode(webcaster) {
- const {isConnected, connect: connectSocket, metadata, sendMetadata} = webcaster;
+ const {connect: connectSocket} = webcaster;
const doPassThrough = ref(false);
- const context = new AudioContext({
- sampleRate: 44100
+ const bitrate = ref(128);
+ const sampleRate = ref(44100);
+ const channelCount = ref(2);
+ const bufferSize = ref(256);
+
+ const context = computed(() => {
+ return new AudioContext({
+ sampleRate: sampleRate.value
+ });
});
- const sink = context.createScriptProcessor(256, 2, 2);
+ const sink = computed(() => {
+ let currentContext = context.value;
- sink.onaudioprocess = (buf) => {
- for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
- let channelData = buf.inputBuffer.getChannelData(channel);
- buf.outputBuffer.getChannelData(channel).set(channelData);
- }
- };
+ let sink = currentContext.createScriptProcessor(
+ bufferSize.value,
+ channelCount.value,
+ channelCount.value
+ );
- const passThrough = context.createScriptProcessor(256, 2, 2);
-
- passThrough.onaudioprocess = (buf) => {
- for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
- let channelData = buf.inputBuffer.getChannelData(channel);
-
- if (doPassThrough.value) {
+ sink.onaudioprocess = (buf) => {
+ for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
+ let channelData = buf.inputBuffer.getChannelData(channel);
buf.outputBuffer.getChannelData(channel).set(channelData);
- } else {
- buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
}
- }
- };
+ };
- sink.connect(passThrough);
- passThrough.connect(context.destination);
+ return sink;
+ });
- const streamNode = context.createMediaStreamDestination();
- streamNode.channelCount = 2;
+ const passThrough = computed(() => {
+ let currentContext = context.value;
- sink.connect(streamNode);
+ let passThrough = currentContext.createScriptProcessor(
+ bufferSize.value,
+ channelCount.value,
+ channelCount.value
+ );
+
+ passThrough.onaudioprocess = (buf) => {
+ for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
+ let channelData = buf.inputBuffer.getChannelData(channel);
+
+ if (doPassThrough.value) {
+ buf.outputBuffer.getChannelData(channel).set(channelData);
+ } else {
+ buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
+ }
+ }
+ };
+
+ sink.value.connect(passThrough);
+ passThrough.value.connect(currentContext.destination);
+
+ return passThrough;
+ });
+
+ const streamNode = computed(() => {
+ let currentContext = context.value;
+
+ const streamNode = currentContext.createMediaStreamDestination();
+ streamNode.channelCount = channelCount.value;
+
+ sink.value.connect(streamNode);
+
+ return streamNode;
+ });
let mediaRecorder;
const startStream = (username = null, password = null) => {
- context.resume();
+ context.value.resume();
mediaRecorder = new MediaRecorder(
- streamNode.stream,
+ streamNode.value.stream,
{
mimeType: "audio/webm;codecs=opus",
- audioBitsPerSecond: 128 * 1000
+ audioBitsPerSecond: bitrate.value * 1000
}
);
@@ -72,77 +105,17 @@ export function useWebDjNode(webcaster) {
mediaRecorder?.stop();
};
- const createAudioSource = ({file, audio}, cb, onEnd) => {
- const el = new Audio(URL.createObjectURL(file));
- el.controls = false;
- el.autoplay = false;
- el.loop = false;
-
- let source = null;
-
- el.addEventListener("ended", () => {
- if (typeof onEnd === "function") {
- onEnd();
- }
- });
-
- el.addEventListener("canplay", () => {
- if (source) {
- return;
- }
-
- source = context.createMediaElementSource(el);
-
- source.play = () => el.play()
- source.position = () => el.currentTime;
- source.duration = () => el.duration;
- source.paused = () => el.paused;
- source.stop = () => {
- el.pause();
- return el.remove();
- };
- source.pause = () => el.pause;
- source.seek = (percent) => {
- let time = percent * parseFloat(audio.length);
- el.currentTime = time;
- return time;
- };
-
- return cb(source);
- });
- };
-
- const createMicrophoneSource = (audioDeviceId, cb) => {
- navigator.mediaDevices.getUserMedia({
- video: false,
- audio: {
- deviceId: audioDeviceId
- }
- }).then((stream) => {
- let source = context.createMediaStreamSource(stream);
- source.stop = () => {
- let ref = stream.getAudioTracks();
- return (ref !== null)
- ? ref[0].stop()
- : 0;
- }
-
- return cb(source);
- });
- };
-
return {
doPassThrough,
- isConnected,
+ bitrate,
+ sampleRate,
+ channelCount,
+ bufferSize,
context,
sink,
passThrough,
streamNode,
startStream,
- stopStream,
- createAudioSource,
- createMicrophoneSource,
- metadata,
- sendMetadata
+ stopStream
};
}
diff --git a/frontend/vue/components/Public/WebDJ/useWebDjSource.js b/frontend/vue/components/Public/WebDJ/useWebDjSource.js
new file mode 100644
index 000000000..876afbf4f
--- /dev/null
+++ b/frontend/vue/components/Public/WebDJ/useWebDjSource.js
@@ -0,0 +1,69 @@
+import {useInjectWebDjNode} from "~/components/Public/WebDJ/useWebDjNode";
+
+export function useWebDjSource() {
+ const {context} = useInjectWebDjNode();
+
+ const createAudioSource = ({file, audio}, cb, onEnd) => {
+ const el = new Audio(URL.createObjectURL(file));
+ el.controls = false;
+ el.autoplay = false;
+ el.loop = false;
+
+ let source = null;
+
+ el.addEventListener("ended", () => {
+ if (typeof onEnd === "function") {
+ onEnd();
+ }
+ });
+
+ el.addEventListener("canplay", () => {
+ if (source) {
+ return;
+ }
+
+ source = context.value.createMediaElementSource(el);
+
+ source.play = () => el.play()
+ source.position = () => el.currentTime;
+ source.duration = () => el.duration;
+ source.paused = () => el.paused;
+ source.stop = () => {
+ el.pause();
+ return el.remove();
+ };
+ source.pause = () => el.pause;
+ source.seek = (percent) => {
+ let time = percent * parseFloat(audio.length);
+ el.currentTime = time;
+ return time;
+ };
+
+ return cb(source);
+ });
+ };
+
+ const createMicrophoneSource = (audioDeviceId, cb) => {
+ navigator.mediaDevices.getUserMedia({
+ video: false,
+ audio: {
+ deviceId: audioDeviceId
+ }
+ }).then((stream) => {
+ let source = context.value.createMediaStreamSource(stream);
+ source.stop = () => {
+ let ref = stream.getAudioTracks();
+ return (ref !== null)
+ ? ref[0].stop()
+ : 0;
+ }
+
+ return cb(source);
+ });
+ };
+
+ return {
+ createAudioSource,
+ createMicrophoneSource
+ }
+}
diff --git a/frontend/vue/components/Public/WebDJ/useWebDjTrack.js b/frontend/vue/components/Public/WebDJ/useWebDjTrack.js
index 127aa35d3..6e610f02c 100644
--- a/frontend/vue/components/Public/WebDJ/useWebDjTrack.js
+++ b/frontend/vue/components/Public/WebDJ/useWebDjTrack.js
@@ -5,9 +5,8 @@ export function useWebDjTrack() {
const {
context,
sink,
- createMicrophoneSource,
- createAudioSource,
- sendMetadata
+ channelCount,
+ bufferSize
} = useInjectWebDjNode();
const trackGain = ref(55);
@@ -18,11 +17,14 @@ export function useWebDjTrack() {
let source = ref(null);
const createControlsNode = () => {
- const bufferSize = 4096;
- const bufferLog = Math.log(parseFloat(bufferSize));
+ const bufferLog = Math.log(parseFloat(bufferSize.value));
const log10 = 2.0 * Math.log(10);
- let newSource = context.createScriptProcessor(bufferSize, 2, 2);
+ let newSource = context.value.createScriptProcessor(
+ bufferSize.value,
+ channelCount.value,
+ channelCount.value
+ );
newSource.onaudioprocess = (buf) => {
if (typeof (source.value?.position) === "function") {
@@ -47,7 +49,11 @@ export function useWebDjTrack() {
};
const createPassThrough = () => {
- let newSource = context.createScriptProcessor(256, 2, 2);
+ let newSource = context.value.createScriptProcessor(
+ bufferSize.value,
+ channelCount.value,
+ channelCount.value
+ );
newSource.onaudioprocess = (buf) => {
for (let channel = 0; channel < buf.inputBuffer.numberOfChannels; channel++) {
@@ -79,17 +85,17 @@ export function useWebDjTrack() {
const prepare = () => {
controlsNode = createControlsNode();
- controlsNode.connect(sink);
+ controlsNode.connect(sink.value);
- trackGainNode = context.createGain();
+ trackGainNode = context.value.createGain();
trackGainNode.gain.value = parseFloat(trackGain.value) / 100.0;
trackGainNode.connect(controlsNode);
passThroughNode = createPassThrough();
- passThroughNode.connect(context.destination);
+ passThroughNode.connect(context.value.destination);
trackGainNode.connect(passThroughNode);
- context.resume();
+ context.value.resume();
return trackGainNode;
}
@@ -130,9 +136,6 @@ export function useWebDjTrack() {
};
return {
- createMicrophoneSource,
- createAudioSource,
- sendMetadata,
source,
trackGain,
trackPassThrough,
diff --git a/frontend/vue/components/Public/WebDJ/useWebcaster.js b/frontend/vue/components/Public/WebDJ/useWebcaster.js
index 66bfcbeef..b216c8602 100644
--- a/frontend/vue/components/Public/WebDJ/useWebcaster.js
+++ b/frontend/vue/components/Public/WebDJ/useWebcaster.js
@@ -1,4 +1,4 @@
-import {ref, shallowRef} from "vue";
+import {inject, provide, ref, shallowRef} from "vue";
import {useNotify} from "~/vendor/bootstrapVue";
import {useTranslate} from "~/vendor/gettext";
@@ -9,6 +9,16 @@ export const webcasterProps = {
}
};
+const injectKey = 'webDjWebcaster';
+
+export function useProvideWebcaster(webcaster) {
+ provide(injectKey, webcaster);
+}
+
+export function useInjectWebcaster() {
+ return inject(injectKey);
+}
+
export function useWebcaster(props) {
const {baseUri} = props;