fiddling
This commit is contained in:
parent
2ea2002951
commit
9c0cde9d6b
8 changed files with 567 additions and 10 deletions
|
@ -24,7 +24,8 @@
|
|||
"react-redux": "^7.1.3",
|
||||
"react-scripts": "3.2.0",
|
||||
"redux": "^4.0.4",
|
||||
"typescript": "3.7.2"
|
||||
"typescript": "3.7.2",
|
||||
"webcast.js": "ssh://git@github.com/UniversityRadioYork/webcast.js"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
@ -46,5 +47,8 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^1.19.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,5 +39,8 @@
|
|||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
<script src="//cdn.rawgit.com/toots/shine/master/js/dist/libshine.js"></script>
|
||||
<script src="//cdn.rawgit.com/webcast/libsamplerate.js/master/dist/libsamplerate.js"></script>
|
||||
<script src="//cdn.rawgit.com/webcast/taglib.js/master/dist/taglib.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
14
src/App.css
14
src/App.css
|
@ -84,4 +84,16 @@ html, body, #root {
|
|||
|
||||
.react-contextmenu-item--disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.sp-state-playing {
|
||||
background-color: #2cdb2c;
|
||||
}
|
||||
|
||||
.sp-state-paused {
|
||||
background-color: #dbbb2c;
|
||||
}
|
||||
|
||||
.sp-state-stopped {
|
||||
background-color: #db2c2c;
|
||||
}
|
||||
|
|
447
src/lib/webcast.js
Normal file
447
src/lib/webcast.js
Normal file
|
@ -0,0 +1,447 @@
|
|||
// Generated by CoffeeScript 1.11.1
|
||||
/* eslint-disable */
|
||||
(function() {
|
||||
var AudioContext, Webcast;
|
||||
|
||||
Webcast = {
|
||||
Encoder: {}
|
||||
};
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.Webcast = Webcast;
|
||||
}
|
||||
|
||||
if (typeof self !== "undefined") {
|
||||
self.Webcast = Webcast;
|
||||
}
|
||||
|
||||
Webcast.Encoder.Asynchronous = (function() {
|
||||
function Asynchronous(arg) {
|
||||
var blob, j, len1, script, scripts;
|
||||
this.encoder = arg.encoder, scripts = arg.scripts;
|
||||
this.mime = this.encoder.mime;
|
||||
this.info = this.encoder.info;
|
||||
this.channels = this.encoder.channels;
|
||||
this.pending = [];
|
||||
this.scripts = [];
|
||||
for (j = 0, len1 = scripts.length; j < len1; j++) {
|
||||
script = scripts[j];
|
||||
this.scripts.push("'" + script + "'");
|
||||
}
|
||||
script = "var window;\nimportScripts(" + (this.scripts.join()) + ");\nvar encoder = " + (this.encoder.toString()) + ";\nself.onmessage = function (e) {\n var type = e.data.type;\n var data = e.data.data;\n if (type === \"buffer\") {\n encoder.encode(data, function (encoded) {\n postMessage(encoded);\n });\n return;\n }\n if (type === \"close\") {\n encoder.close(function (buffer) {\n postMessage({close:true, buffer:buffer});\n self.close();\n });\n return;\n }\n};";
|
||||
blob = new Blob([script], {
|
||||
type: "text/javascript"
|
||||
});
|
||||
this.worker = new Worker(URL.createObjectURL(blob));
|
||||
this.worker.onmessage = (function(_this) {
|
||||
return function(arg1) {
|
||||
var data;
|
||||
data = arg1.data;
|
||||
return _this.pending.push(data);
|
||||
};
|
||||
})(this);
|
||||
}
|
||||
|
||||
Asynchronous.prototype.toString = function() {
|
||||
return "(new Webcast.Encoder.Asynchronous({\n encoder: " + (this.encoder.toString()) + ",\n scripts: [" + (this.scripts.join()) + "]\n}))";
|
||||
};
|
||||
|
||||
Asynchronous.prototype.close = function(fn) {
|
||||
this.worker.onmessage = (function(_this) {
|
||||
return function(arg) {
|
||||
var chunk, data, j, k, len, len1, len2, offset, ref, ref1, ret;
|
||||
data = arg.data;
|
||||
if (!data.close) {
|
||||
_this.pending.push(data);
|
||||
return;
|
||||
}
|
||||
_this.pending.push(data.buffer);
|
||||
len = 0;
|
||||
ref = _this.pending;
|
||||
for (j = 0, len1 = ref.length; j < len1; j++) {
|
||||
chunk = ref[j];
|
||||
len += chunk.length;
|
||||
}
|
||||
ret = new Uint8Array(len);
|
||||
offset = 0;
|
||||
ref1 = _this.pending;
|
||||
for (k = 0, len2 = ref1.length; k < len2; k++) {
|
||||
chunk = ref1[k];
|
||||
ret.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
return fn(ret);
|
||||
};
|
||||
})(this);
|
||||
return this.worker.postMessage({
|
||||
type: "close"
|
||||
});
|
||||
};
|
||||
|
||||
Asynchronous.prototype.encode = function(buffer, fn) {
|
||||
this.worker.postMessage({
|
||||
type: "buffer",
|
||||
data: buffer
|
||||
});
|
||||
return fn(this.pending.shift());
|
||||
};
|
||||
|
||||
return Asynchronous;
|
||||
|
||||
})();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
AudioContext.prototype.createWebcastSource = function(bufferSize, channels, passThrough) {
|
||||
var context, node, options;
|
||||
context = this;
|
||||
node = context.createScriptProcessor(bufferSize, channels, channels);
|
||||
passThrough || (passThrough = false);
|
||||
options = {
|
||||
recorderSource: null,
|
||||
encoder: null,
|
||||
socket: null,
|
||||
passThrough: passThrough || false
|
||||
};
|
||||
node.onaudioprocess = function(buf) {
|
||||
var audio, channel, channelData, j, ref, ref1;
|
||||
audio = [];
|
||||
for (channel = j = 0, ref = buf.inputBuffer.numberOfChannels - 1; 0 <= ref ? j <= ref : j >= ref; channel = 0 <= ref ? ++j : --j) {
|
||||
channelData = buf.inputBuffer.getChannelData(channel);
|
||||
audio[channel] = channelData;
|
||||
if (options.passThrough) {
|
||||
buf.outputBuffer.getChannelData(channel).set(channelData);
|
||||
} else {
|
||||
buf.outputBuffer.getChannelData(channel).set(new Float32Array(channelData.length));
|
||||
}
|
||||
}
|
||||
return (ref1 = options.encoder) != null ? typeof ref1.encode === "function" ? ref1.encode(audio, function(data) {
|
||||
var ref2;
|
||||
if (data != null) {
|
||||
return (ref2 = options.socket) != null ? ref2.sendData(data) : void 0;
|
||||
}
|
||||
}) : void 0 : void 0;
|
||||
};
|
||||
node.setPassThrough = function(b) {
|
||||
return options.passThrough = b;
|
||||
};
|
||||
node.connectSocket = function(encoder, url) {
|
||||
if (encoder instanceof Webcast.Recorder) {
|
||||
options.recorderSource = context.createMediaStreamDestination();
|
||||
node.connect(options.recorderSource);
|
||||
encoder.start(options.recoderSource.stream, function(data) {
|
||||
var ref;
|
||||
if (data != null) {
|
||||
return (ref = options.socket) != null ? ref.sendData(data) : void 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
options.encoder = encoder;
|
||||
return options.socket = new Webcast.Socket({
|
||||
url: url,
|
||||
mime: options.encoder.mime,
|
||||
info: options.encoder.info
|
||||
});
|
||||
};
|
||||
node.close = function(cb) {
|
||||
var fn, ref, ref1;
|
||||
if ((ref = options.recorderSource) != null) {
|
||||
ref.disconnect();
|
||||
}
|
||||
options.recorderSource = null;
|
||||
fn = function() {
|
||||
var ref1;
|
||||
if ((ref1 = options.socket) != null) {
|
||||
ref1.close();
|
||||
}
|
||||
options.socket = options.encoder = null;
|
||||
return typeof cb === "function" ? cb() : void 0;
|
||||
};
|
||||
if (((ref1 = options.encoder) != null ? ref1.close : void 0) == null) {
|
||||
return fn();
|
||||
}
|
||||
return options.encoder.close(function(data) {
|
||||
var ref2;
|
||||
if ((ref2 = options.socket) != null) {
|
||||
ref2.sendData(data);
|
||||
}
|
||||
return fn();
|
||||
});
|
||||
};
|
||||
node.getSocket = function() {
|
||||
return options.socket;
|
||||
};
|
||||
node.sendMetadata = (function(_this) {
|
||||
return function(metadata) {
|
||||
var ref;
|
||||
return (ref = options.socket) != null ? ref.sendMetadata(metadata) : void 0;
|
||||
};
|
||||
})(this);
|
||||
node.isOpen = function() {
|
||||
return options != null ? options.socket.isOpen() : void 0;
|
||||
};
|
||||
return node;
|
||||
};
|
||||
}
|
||||
|
||||
Webcast.Encoder.Mp3 = (function() {
|
||||
Mp3.prototype.mime = "audio/mpeg";
|
||||
|
||||
function Mp3(arg) {
|
||||
this.samplerate = arg.samplerate, this.bitrate = arg.bitrate, this.channels = arg.channels;
|
||||
this.shine = new Shine({
|
||||
samplerate: this.samplerate,
|
||||
bitrate: this.bitrate,
|
||||
channels: this.channels,
|
||||
mode: this.channels === 1 ? Shine.MONO : Shine.JOINT_STEREO
|
||||
});
|
||||
this.info = {
|
||||
audio: {
|
||||
channels: this.channels,
|
||||
samplerate: this.samplerate,
|
||||
bitrate: this.bitrate,
|
||||
encoder: "libshine"
|
||||
}
|
||||
};
|
||||
this;
|
||||
}
|
||||
|
||||
Mp3.prototype.toString = function() {
|
||||
return "(new Webcast.Encoder.Mp3({\n bitrate: " + this.bitrate + ",\n channels: " + this.channels + ",\n samplerate: " + this.samplerate + "\n }))";
|
||||
};
|
||||
|
||||
Mp3.prototype.close = function(data, fn) {
|
||||
var flushed, rem;
|
||||
rem = new Uint8Array;
|
||||
if (fn != null) {
|
||||
if ((data != null ? data.length : void 0) > 0) {
|
||||
rem = this.shine.encode(data);
|
||||
}
|
||||
} else {
|
||||
fn = data;
|
||||
}
|
||||
flushed = this.shine.close();
|
||||
data = new Uint8Array(rem.length + flushed.length);
|
||||
data.set(rem);
|
||||
data.set(flushed, rem.length);
|
||||
return fn(data);
|
||||
};
|
||||
|
||||
Mp3.prototype.encode = function(data, fn) {
|
||||
data = data.slice(0, this.channels);
|
||||
return fn(this.shine.encode(data));
|
||||
};
|
||||
|
||||
return Mp3;
|
||||
|
||||
})();
|
||||
|
||||
Webcast.Encoder.Raw = (function() {
|
||||
function Raw(arg) {
|
||||
this.channels = arg.channels, this.samplerate = arg.samplerate;
|
||||
this.mime = "audio/x-raw,format=S8,channels=" + this.channels + ",layout=interleaved,rate=" + this.samplerate;
|
||||
this.info = {
|
||||
audio: {
|
||||
channels: this.channels,
|
||||
samplerate: this.samplerate,
|
||||
encoder: "RAW u8 encoder"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Raw.prototype.toString = function() {
|
||||
return "(new Webcast.Encoder.Raw({\n channels: " + this.channels + ",\n samplerate: " + this.samplerate + "\n }))";
|
||||
};
|
||||
|
||||
Raw.prototype.doEncode = function(data) {
|
||||
var buf, chan, channels, i, j, k, ref, ref1, samples;
|
||||
channels = data.length;
|
||||
samples = data[0].length;
|
||||
buf = new Int8Array(channels * samples);
|
||||
for (chan = j = 0, ref = channels - 1; 0 <= ref ? j <= ref : j >= ref; chan = 0 <= ref ? ++j : --j) {
|
||||
for (i = k = 0, ref1 = samples - 1; 0 <= ref1 ? k <= ref1 : k >= ref1; i = 0 <= ref1 ? ++k : --k) {
|
||||
buf[channels * i + chan] = data[chan][i] * 127;
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
};
|
||||
|
||||
Raw.prototype.close = function(data, fn) {
|
||||
var ret;
|
||||
ret = new Uint8Array;
|
||||
if (fn != null) {
|
||||
if ((data != null ? data.count : void 0) > 0) {
|
||||
ret = this.doEncode(data);
|
||||
}
|
||||
} else {
|
||||
fn = data;
|
||||
}
|
||||
return fn(ret);
|
||||
};
|
||||
|
||||
Raw.prototype.encode = function(data, fn) {
|
||||
return fn(this.doEncode(data));
|
||||
};
|
||||
|
||||
return Raw;
|
||||
|
||||
})();
|
||||
|
||||
Webcast.Recorder = (function() {
|
||||
Recorder.prototype.mime = "audio/ogg";
|
||||
|
||||
function Recorder(arg) {
|
||||
this.samplerate = arg.samplerate, this.bitrate = arg.bitrate, this.channels = arg.channels;
|
||||
this.info = {
|
||||
audio: {
|
||||
channels: this.channels,
|
||||
samplerate: this.samplerate,
|
||||
bitrate: this.bitrate,
|
||||
encoder: "MediaRecorder"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Recorder.prototype.start = function(stream, cb) {
|
||||
var recorder;
|
||||
recorder = new MediaRecorder(stream);
|
||||
return recorder.ondataavailable = (function(_this) {
|
||||
return function(e) {
|
||||
var blob;
|
||||
if (recorder.state === "recording") {
|
||||
blob = new Blob([e.data], _this.mime);
|
||||
return cb(blob);
|
||||
}
|
||||
};
|
||||
})(this);
|
||||
};
|
||||
|
||||
return Recorder;
|
||||
|
||||
})();
|
||||
|
||||
Webcast.Encoder.Resample = (function() {
|
||||
function Resample(arg) {
|
||||
var i, j, ref;
|
||||
this.encoder = arg.encoder, this.samplerate = arg.samplerate, this.type = arg.type;
|
||||
this.mime = this.encoder.mime;
|
||||
this.info = this.encoder.info;
|
||||
this.channels = this.encoder.channels;
|
||||
this.ratio = parseFloat(this.encoder.samplerate) / parseFloat(this.samplerate);
|
||||
this.type = this.type || Samplerate.FASTEST;
|
||||
this.resamplers = [];
|
||||
this.remaining = [];
|
||||
for (i = j = 0, ref = this.channels - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
|
||||
this.resamplers[i] = new Samplerate({
|
||||
type: this.type
|
||||
});
|
||||
this.remaining[i] = new Float32Array;
|
||||
}
|
||||
}
|
||||
|
||||
Resample.prototype.toString = function() {
|
||||
return "(new Webcast.Encoder.Resample({\n encoder: " + (this.encoder.toString()) + ",\n samplerate: " + this.samplerate + ",\n type: " + this.type + "\n }))";
|
||||
};
|
||||
|
||||
Resample.prototype.close = function(fn) {
|
||||
var data, i, j, ref;
|
||||
for (i = j = 0, ref = this.remaining.length - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
|
||||
data = this.resamplers[i].process({
|
||||
data: this.remaining[i],
|
||||
ratio: this.ratio,
|
||||
last: true
|
||||
}).data;
|
||||
}
|
||||
return this.encoder.close(data, fn);
|
||||
};
|
||||
|
||||
Resample.prototype.concat = function(a, b) {
|
||||
var ret;
|
||||
if (typeof b === "undefined") {
|
||||
return a;
|
||||
}
|
||||
ret = new Float32Array(a.length + b.length);
|
||||
ret.set(a);
|
||||
ret.subarray(a.length).set(b);
|
||||
return ret;
|
||||
};
|
||||
|
||||
Resample.prototype.encode = function(buffer, fn) {
|
||||
var data, i, j, ref, ref1, used;
|
||||
for (i = j = 0, ref = this.channels - 1; 0 <= ref ? j <= ref : j >= ref; i = 0 <= ref ? ++j : --j) {
|
||||
buffer[i] = this.concat(this.remaining[i], buffer[i]);
|
||||
ref1 = this.resamplers[i].process({
|
||||
data: buffer[i],
|
||||
ratio: this.ratio
|
||||
}), data = ref1.data, used = ref1.used;
|
||||
this.remaining[i] = buffer[i].subarray(used);
|
||||
buffer[i] = data;
|
||||
}
|
||||
return this.encoder.encode(buffer, fn);
|
||||
};
|
||||
|
||||
return Resample;
|
||||
|
||||
})();
|
||||
|
||||
Webcast.Socket = function(arg) {
|
||||
var hello, info, key, mime, parser, password, send, socket, url, user, value;
|
||||
url = arg.url, mime = arg.mime, info = arg.info;
|
||||
parser = document.createElement("a");
|
||||
parser.href = url;
|
||||
user = parser.username;
|
||||
password = parser.password;
|
||||
parser.username = parser.password = "";
|
||||
url = parser.href;
|
||||
socket = new WebSocket(url, "webcast");
|
||||
socket.mime = mime;
|
||||
socket.info = info;
|
||||
hello = {
|
||||
mime: mime
|
||||
};
|
||||
if ((user != null) && user !== "") {
|
||||
hello.user = socket.user = user;
|
||||
}
|
||||
if ((password != null) && password !== "") {
|
||||
hello.password = socket.password = password;
|
||||
}
|
||||
for (key in info) {
|
||||
value = info[key];
|
||||
hello[key] = value;
|
||||
}
|
||||
send = socket.send;
|
||||
socket.send = null;
|
||||
socket.addEventListener("open", function() {
|
||||
return send.call(socket, JSON.stringify({
|
||||
type: "hello",
|
||||
data: hello
|
||||
}));
|
||||
});
|
||||
socket.sendData = function(data) {
|
||||
if (!socket.isOpen()) {
|
||||
return;
|
||||
}
|
||||
if (!((data != null ? data.length : void 0) > 0)) {
|
||||
return;
|
||||
}
|
||||
if (!(data instanceof ArrayBuffer)) {
|
||||
data = data.buffer.slice(data.byteOffset, data.length * data.BYTES_PER_ELEMENT);
|
||||
}
|
||||
return send.call(socket, data);
|
||||
};
|
||||
socket.sendMetadata = function(metadata) {
|
||||
if (!socket.isOpen()) {
|
||||
return;
|
||||
}
|
||||
return send.call(socket, JSON.stringify({
|
||||
type: "metadata",
|
||||
data: metadata
|
||||
}));
|
||||
};
|
||||
socket.isOpen = function() {
|
||||
return socket.readyState === WebSocket.OPEN;
|
||||
};
|
||||
return socket;
|
||||
};
|
||||
|
||||
}).call(this);
|
|
@ -83,9 +83,18 @@ function Player({ id }: { id: number }) {
|
|||
<div>
|
||||
{playerState.loadedItem !== null && (<div>{playerState.loadedItem.title}</div>)}
|
||||
{playerState.loading && <b>LOADING</b>}
|
||||
<button onClick={() => dispatch(PlayerState.play(id))}>p</button>
|
||||
<button onClick={() => dispatch(PlayerState.pause(id))}>u</button>
|
||||
<button onClick={() => dispatch(PlayerState.stop(id))}>s</button>
|
||||
<button
|
||||
onClick={() => dispatch(PlayerState.play(id))}
|
||||
className={playerState.state === "playing" ? "sp-state-playing" : ""}
|
||||
>p</button>
|
||||
<button
|
||||
onClick={() => dispatch(PlayerState.pause(id))}
|
||||
className={playerState.state === "paused" ? "sp-state-paused" : ""}
|
||||
>a</button>
|
||||
<button
|
||||
onClick={() => dispatch(PlayerState.stop(id))}
|
||||
className={playerState.state === "stopped" ? "sp-state-stopped" : ""}
|
||||
>s</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,16 +1,26 @@
|
|||
import "../../lib/webcast";
|
||||
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { PlanItem } from "../state";
|
||||
import { Track, MYRADIO_NON_API_BASE } from "../../api";
|
||||
import { AppThunk } from "../../store";
|
||||
|
||||
/// <reference path="webcast.d.ts" />
|
||||
|
||||
const audioContext = new AudioContext();
|
||||
const playerSources: MediaElementAudioSourceNode[] = [];
|
||||
const playerGains: GainNode[] = [];
|
||||
const destination = audioContext.createMediaStreamDestination();
|
||||
// TODO
|
||||
// const destination = audioContext.createWebcastSource(4096, 2);
|
||||
const destination = audioContext.createDynamicsCompressor();
|
||||
destination.connect(audioContext.destination);
|
||||
|
||||
type PlayerStateEnum = "playing" | "paused" | "stopped";
|
||||
|
||||
interface SinglePlayerState {
|
||||
loadedItem: PlanItem | Track | null
|
||||
loading: boolean;
|
||||
state: PlayerStateEnum;
|
||||
}
|
||||
|
||||
interface PlayerState {
|
||||
|
@ -22,13 +32,16 @@ const playerState = createSlice({
|
|||
initialState: {
|
||||
players: [{
|
||||
loadedItem: null,
|
||||
loading: false
|
||||
loading: false,
|
||||
state: "stopped"
|
||||
}, {
|
||||
loadedItem: null,
|
||||
loading: false
|
||||
loading: false,
|
||||
state: "stopped"
|
||||
}, {
|
||||
loadedItem: null,
|
||||
loading: false
|
||||
loading: false,
|
||||
state: "stopped"
|
||||
}]
|
||||
} as PlayerState,
|
||||
reducers: {
|
||||
|
@ -38,6 +51,9 @@ const playerState = createSlice({
|
|||
},
|
||||
itemLoadComplete(state, action: PayloadAction<{ player: number}>) {
|
||||
state.players[action.payload.player].loading = false;
|
||||
},
|
||||
setPlayerState(state, action: PayloadAction<{ player: number, state: PlayerStateEnum }>) {
|
||||
state.players[action.payload.player].state = action.payload.state;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -69,25 +85,29 @@ export const load = (player: number, item: PlanItem | Track): AppThunk => dispat
|
|||
const sauce = audioContext.createMediaElementSource(el);
|
||||
const gain = audioContext.createGain();
|
||||
sauce.connect(gain);
|
||||
gain.connect(audioContext.destination);
|
||||
gain.connect(destination);
|
||||
console.log("Connected to", destination);
|
||||
playerSources[player] = sauce;
|
||||
playerGains[player] = gain;
|
||||
}
|
||||
|
||||
export const play = (player: number): AppThunk => dispatch => {
|
||||
playerSources[player].mediaElement.play();
|
||||
dispatch(playerState.actions.setPlayerState({ player, state: "playing" }));
|
||||
};
|
||||
|
||||
export const pause = (player: number): AppThunk => dispatch => {
|
||||
if (playerSources[player].mediaElement.paused) {
|
||||
playerSources[player].mediaElement.play();
|
||||
dispatch(playerState.actions.setPlayerState({ player, state: "playing" }));
|
||||
} else {
|
||||
playerSources[player].mediaElement.pause();
|
||||
dispatch(playerState.actions.setPlayerState({ player, state: "paused" }));
|
||||
}
|
||||
};
|
||||
|
||||
export const stop = (player: number): AppThunk => dispatch => {
|
||||
playerSources[player].mediaElement.pause();
|
||||
playerSources[player].mediaElement.currentTime = 0;
|
||||
dispatch(playerState.actions.setPlayerState({ player, state: "stopped" }));
|
||||
};
|
53
src/showplanner/player/webcast.d.ts
vendored
Normal file
53
src/showplanner/player/webcast.d.ts
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
class WebcastEncoder {
|
||||
doEncode(data: any): any;
|
||||
}
|
||||
|
||||
interface WebcastAudioSourceNode extends AudioNode {
|
||||
setPassThrough(val: boolean): boolean;
|
||||
connectSocket(encoder: WebcastEncoder, url: string): Webcast.Socket;
|
||||
close(cb: any): any; // TODO
|
||||
getSocket(): Webcast.Socket;
|
||||
sendMetadata(meta: any): any;
|
||||
isOpen(): boolean | undefined;
|
||||
}
|
||||
|
||||
declare interface AudioContext {
|
||||
createWebcastSource(bufferSize: number, channels: number, passThrough?: boolean): WebcastAudioSourceNode;
|
||||
}
|
||||
|
||||
declare namespace Webcast {
|
||||
declare namespace Encoder {
|
||||
class Asynchronous extends WebcastEncoder {
|
||||
constructor(options: {
|
||||
encoder: WebcastEncoder,
|
||||
scripts: string[]
|
||||
})
|
||||
}
|
||||
|
||||
class Resample extends WebcastEncoder {
|
||||
constructor(options: {
|
||||
encoder: WebcastEncoder,
|
||||
samplerate: number
|
||||
})
|
||||
}
|
||||
|
||||
class Mp3 extends WebcastEncoder {
|
||||
constructor(options: {
|
||||
samplerate: number,
|
||||
bitrate: number,
|
||||
channels: number,
|
||||
})
|
||||
}
|
||||
|
||||
class Raw extends WebcastEncoder {
|
||||
constructor(options: {
|
||||
samplerate: number,
|
||||
channels: number,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Socket extends WebSocket {
|
||||
// TODO
|
||||
}
|
||||
}
|
|
@ -7896,6 +7896,11 @@ prepend-http@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||
|
||||
prettier@^1.19.1:
|
||||
version "1.19.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
|
||||
|
||||
pretty-bytes@^5.1.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
|
||||
|
@ -9997,6 +10002,10 @@ wbuf@^1.1.0, wbuf@^1.7.3:
|
|||
dependencies:
|
||||
minimalistic-assert "^1.0.0"
|
||||
|
||||
"webcast.js@ssh://git@github.com/UniversityRadioYork/webcast.js":
|
||||
version "0.0.0"
|
||||
resolved "ssh://git@github.com/UniversityRadioYork/webcast.js#1ebc76dbe05033208a107feae986c0edb38e8261"
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
|
|
Loading…
Reference in a new issue