orchard/resources/js/eq.js
2021-11-19 17:02:28 -06:00

591 lines
No EOL
20 KiB
JavaScript

var EAOverride = true;
var AErecorderNode;
var GCOverride = true;
var outputID = -1;
var EAoutputID = -1;
var queueExclusive = false;
var queueChromecast = false;
var selectedGC ;
var MVsource;
var windowAudioNode;
const workerOptions = {
OggOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/OggOpusEncoder.wasm',
WebMOpusEncoderWasmPath: 'https://cdn.jsdelivr.net/npm/opus-media-recorder@latest/WebMOpusEncoder.wasm'
};
var recorder;
window.MediaRecorder = OpusMediaRecorder;
var audioWorklet = `class RecorderWorkletProcessor extends AudioWorkletProcessor {
static get parameterDescriptors() {
return [{
name: 'isRecording',
defaultValue: 0
},
{
name: 'numberOfChannels',
defaultValue: 2
}
];
}
constructor() {
super();
this._bufferSize = 32768;
this._buffers = null;
this._initBuffer();
}
_initBuffers(numberOfChannels) {
this._buffers = [];
for (let channel=0; channel < numberOfChannels; channel++) {
this._buffers.push(new Float32Array(this._bufferSize));
}
}
_initBuffer() {
this._bytesWritten = 0;
}
_isBufferEmpty() {
return this._bytesWritten === 0;
}
_isBufferFull() {
return this._bytesWritten === this._bufferSize;
}
_pushToBuffers(audioRawData, numberOfChannels) {
if (this._isBufferFull()) {
this._flush();
}
let dataLength = audioRawData[0].length;
for (let idx=0; idx<dataLength; idx++) {
for (let channel=0; channel < numberOfChannels; channel++) {
let value = audioRawData[channel][idx];
this._buffers[channel][this._bytesWritten] = value;
}
this._bytesWritten += 1;
}
}
_flush() {
let buffers = [];
this._buffers.forEach((buffer, channel) => {
if (this._bytesWritten < this._bufferSize) {
buffer = buffer.slice(0, this._bytesWritten);
}
buffers[channel] = buffer;
});
this.port.postMessage({
eventType: 'data',
audioBuffer: buffers,
bufferSize: this._bufferSize
});
this._initBuffer();
}
_recordingStopped() {
this.port.postMessage({
eventType: 'stop'
});
}
process(inputs, outputs, parameters) {
const isRecordingValues = parameters.isRecording;
const numberOfChannels = parameters.numberOfChannels[0];
if (this._buffers === null) {
this._initBuffers(numberOfChannels);
}
for (let dataIndex = 0; dataIndex < isRecordingValues.length; dataIndex++)
{
const shouldRecord = isRecordingValues[dataIndex] === 1;
if (!shouldRecord && !this._isBufferEmpty()) {
this._flush();
this._recordingStopped();
}
if (shouldRecord) {
let audioRawData = inputs[0];
this._pushToBuffers(audioRawData, numberOfChannels);
}
}
return true;
}
}
registerProcessor('recorder-worklet', RecorderWorkletProcessor);`;
var GCstream;
var searchInt;
var AMEx = {
context: new AudioContext(),
result: {},
filter: [],
EQRanges: [{
f: 32,
type: 'lowshelf'
}, {
f: 64,
type: 'peaking'
}, {
f: 125,
type: 'peaking'
}, {
f: 250,
type: 'peaking'
}, {
f: 500,
type: 'peaking'
}, {
f: 1000,
type: 'peaking'
}, {
f: 2000,
type: 'peaking'
}, {
f: 4000,
type: 'peaking'
}, {
f: 8000,
type: 'peaking'
}, {
f: 16000,
type: 'highshelf'
}]
};
var bassFilter;
var trebleFilter;
var AudioOutputs = {
fInit: false,
eqReady: false,
activeCasts: [],
castUI() {
AMJavaScript.getRequest("ameres://html/cast_device.html", (content) => {
var vm = new Vue({
data: {
devices: {
cast: [],
airplay: []
},
scanning: false,
activeCasts: AudioOutputs.activeCasts
},
methods: {
scan() {
let self = this;
this.scanning = true;
AudioOutputs.getGCDevices();
setTimeout(()=>{
self.devices.cast = ipcRenderer.sendSync("getKnownCastDevices");
self.scanning = false;
}, 2000);
console.log(this.devices);
vm.$forceUpdate();
},
setCast(device) {
console.log(`requesting: ${device}`);
AudioOutputs.playGC(device);
},
stopCasting() {
AudioOutputs.stopGC();
this.activeCasts = AudioOutputs.activeCasts;
vm.$forceUpdate();
},
close() {
modal.close();
}
}
});
var modal = new AMEModal({
content: content,
CloseButton: false,
Style: {
maxWidth: "600px"
},
OnCreate() {
vm.$mount("#castdevices-vue");
vm.scan();
},
OnClose() {
_vues.destroy(vm);
}
});
})
},
init: function (cb = function () {}) {
AudioOutputs.fInit = true;
searchInt = setInterval(function () {
if (document.getElementById("apple-music-player")) {
AudioOutputs.eqReady = true;
document.getElementById("apple-music-player").crossOrigin = "anonymous";
AudioOutputs.amplifyMedia(document.getElementById("apple-music-player"), 0);
var context = AMEx.context;
var source = AMEx.result.source;
bassFilter = context.createBiquadFilter();
bassFilter.type = "lowshelf";
bassFilter.frequency.value = 200;
bassFilter.gain.value = 0;
trebleFilter = context.createBiquadFilter();
trebleFilter.type = "highshelf";
trebleFilter.frequency.value = 2000;
trebleFilter.gain.value = 0;
source.connect(bassFilter);
bassFilter.connect(trebleFilter);
trebleFilter.connect(context.destination);
console.log("Attached EQ");
if (queueExclusive){
console.log('we good');
AudioOutputs.startExclusiveAudio(outputID); }
cb();
clearInterval(searchInt);
}
}, 1000);
waitFor(()=>{return queueChromecast &&
((document.getElementById("apple-music-player") != null&&
document.getElementById("apple-music-player").readyState == 4) || (
document.querySelector('apple-music-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot.getElementById('apple-music-video-player') &&
document.querySelector('apple-music-video-player').shadowRoot.querySelector('amp-video-player-internal').shadowRoot.querySelector('amp-video-player').shadowRoot.getElementById('apple-music-video-player').readyState == 4)) ;},() => AudioOutputs.playGC(selectedGC))
},
amplifyMedia: function (mediaElem, multiplier) {
AMEx.context = new(window.AudioContext || window.webkitAudioContext),
AMEx.result = {
context: AMEx.context,
source: AMEx.context.createMediaElementSource(mediaElem),
gain: AMEx.context.createGain(),
media: mediaElem,
amplify: function (multiplier) {
AMEx.result.gain.gain.value = multiplier;
},
getAmpLevel: function () {
return AMEx.result.gain.gain.value;
}
};
AMEx.result.source.connect(AMEx.result.gain);
AMEx.result.gain.connect(AMEx.context.destination);
AMEx.result.amplify(multiplier);
return AMEx.result;
},
popup_generic: function ({
title = "",
content = document.createElement("div"),
closefn = function () {},
transparentBg = false,
windowStyle = {},
backdropStyle = {}
}) {
let backdrop = document.createElement("div");
backdrop.style.width = "100%";
backdrop.style.height = "100%";
backdrop.style.position = "fixed";
backdrop.style.top = 0;
backdrop.style.left = 0;
if (!transparentBg) {
backdrop.style.background = "rgba(0,0,0,0.5)";
} else {
backdrop.style.background = "rgba(0,0,0,0.0)";
};
backdrop.style.zIndex = 10000;
backdrop.style.display = "flex";
backdrop.style.alignItems = "center";
backdrop.style.justifyContent = "center";
let win = document.createElement("div");
win.style.width = "300px";
win.style.background = "var(--modalBGColor)";
win.style.zIndex = 10000;
win.style.padding = "16px";
win.style.borderRadius = "10px";
Object.assign(backdrop.style, backdropStyle);
Object.assign(win.style, windowStyle);
let closeBtn = document.createElement("button");
closeBtn.style.background = "var(--primaryColor)";
closeBtn.style.borderRadius = "4px";
closeBtn.style.padding = "8px 0px 8px 0px";
closeBtn.style.width = "100%";
closeBtn.style.fontWeight = "bold";
closeBtn.style.margin = "12px 0px 0px 0px";
closeBtn.innerHTML = "Close";
closeBtn.id = "eq-close";
closeBtn.addEventListener("click", function () {
backdrop.remove();
closefn();
});
let titleText = document.createElement("div");
titleText.innerHTML = (title);
titleText.style.fontWeight = "bold";
win.appendChild(titleText);
win.appendChild(content);
win.appendChild(closeBtn);
backdrop.appendChild(win);
document.body.appendChild(backdrop);
},
ShowEQ: function () {
if (!AudioOutputs.eqReady) {
alert("Audio is not ready, Play a song to use this function.");
};
let backdrop = document.createElement("div");
backdrop.style.width = "100%";
backdrop.style.height = "100%";
backdrop.style.position = "fixed";
backdrop.style.top = 0;
backdrop.style.left = 0;
backdrop.style.background = "rgba(0,0,0,0.5)";
backdrop.style.zIndex = 9999;
backdrop.style.display = "flex";
backdrop.style.alignItems = "center";
backdrop.style.justifyContent = "center";
backdrop.style.backdropFilter = "blur(12px) saturate(180%)";
let win = document.createElement("div");
win.style.width = "300px";
win.style.background = "var(--modalBGColor)";
win.style.zIndex = 10000;
win.style.padding = "16px";
win.style.borderRadius = "10px";
let closeBtn = document.createElement("button");
closeBtn.style.background = "var(--primaryColor)";
closeBtn.style.borderRadius = "4px";
closeBtn.style.padding = "8px 0px 8px 0px";
closeBtn.style.width = "100%";
closeBtn.style.fontWeight = "bold";
closeBtn.style.margin = "12px 0px 0px 0px";
closeBtn.innerHTML = "Close";
closeBtn.addEventListener("click", function () {
backdrop.remove()
});
let titleText = document.createElement("div");
let bassText = document.createElement("div");
let trebleText = document.createElement("div");
let gainText = document.createElement("div");
titleText.id = 'eq-menu';
titleText.innerHTML = (`Equalizer`);
titleText.style.fontWeight = "bold";
bassText.innerHTML = (`Bass (${bassFilter.gain.value})`);
trebleText.innerHTML = (`Treble (${trebleFilter.gain.value})`);
gainText.innerHTML = (`Gain (${AMEx.result.gain.gain.value})`);
let bassAdjust = document.createElement("input");
bassAdjust.style.width = "100%";
bassAdjust.type = "range";
bassAdjust.min = -10;
bassAdjust.max = 10;
bassAdjust.value = bassFilter.gain.value;
bassAdjust.addEventListener("input", function () {
bassFilter.gain.value = this.value;
bassText.innerHTML = `Bass (${bassFilter.gain.value})`;
});
let trebleAdjust = document.createElement("input");
trebleAdjust.style.width = "100%";
trebleAdjust.min = -10;
trebleAdjust.max = 10;
trebleAdjust.type = "range";
trebleAdjust.value = trebleFilter.gain.value;
trebleAdjust.addEventListener("input", function () {
trebleFilter.gain.value = this.value;
trebleText.innerHTML = `Treble (${trebleFilter.gain.value})`;
});
let gainAdjust = document.createElement("input");
gainAdjust.style.width = "100%";
gainAdjust.min = -1;
gainAdjust.max = 1;
gainAdjust.type = "range";
gainAdjust.value = AMEx.result.gain.gain.value;
gainAdjust.addEventListener("input", function () {
AMEx.result.gain.gain.value = this.value;
gainText.innerHTML = `Gain (${AMEx.result.gain.gain.value})`;
});
let bassLabel = document.createElement("label");
let trebleLabel = document.createElement("label");
let gainLabel = document.createElement("label");
bassLabel.appendChild(bassText);
trebleLabel.appendChild(trebleText);
gainLabel.appendChild(gainText);
bassLabel.appendChild(bassAdjust);
bassLabel.appendChild(document.createElement("br"));
trebleLabel.appendChild(trebleAdjust);
trebleLabel.appendChild(document.createElement("br"));
gainLabel.appendChild(gainAdjust);
win.appendChild(titleText);
win.appendChild(bassLabel);
win.appendChild(trebleLabel);
win.appendChild(gainLabel);
win.appendChild(closeBtn);
backdrop.appendChild(win);
document.body.appendChild(backdrop);
},
getAudioDevices: function(){
ipcRenderer.send('getAudioDevices','');
},
startExclusiveAudio: async function(id){
if(AMEx.result.source != null || MVsource != null){
if(EAoutputID!= id){
EAoutputID = id;
EAOverride = false;
ipcRenderer.send('muteAudio',true);
ipcRenderer.send('enableExclusiveAudio',id);
windowAudioNode = AMEx.context.createGain();
try{
AMEx.result.source.connect(windowAudioNode);}
catch(e){}
var options = {
mimeType : 'audio/wav'
};
var destnode = AMEx.context.createMediaStreamDestination();
windowAudioNode.connect(destnode);
if(!recorder){
recorder = new MediaRecorder(destnode.stream,options,workerOptions);
recorder.start(1);
recorder.ondataavailable = function(e) {
e.data.arrayBuffer().then(buffer => {
if(!GCOverride) {ipcRenderer.send('writeWAV',buffer,preferences.audio.castingBitDepth);}
if(!EAOverride) {ipcRenderer.send('writePCM',buffer);}
}
);
}}
} else {console.log('device already in exclusive mode');}
} else {
outputID = id;
queueExclusive = true;
}
},
stopExclusiveAudio: function(){
try{
recorder.stop();
recorder = null;
} catch(e){}
EAOverride = true;
EAoutputID = -1;
outputID = -1;
queueExclusive = false;
ipcRenderer.send('muteAudio',false);
ipcRenderer.send('disableExclusiveAudio','');
},
getGCDevices: function(){
ipcRenderer.send('getChromeCastDevices','');
},
playGC : async function(device){
console.log('wot');
AudioOutputs.activeCasts.push(device);
GCOverride = false;
if(AMEx.result.source != null || MVsource != null ){
queueChromecast = false;
const musicType = (MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem["type"] ?? '' : '';
const trackName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.title ?? '' : '');
const artistName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.artistName ?? '' : '');
const albumName = ((MusicKit.getInstance().nowPlayingItem != null) ? MusicKit.getInstance().nowPlayingItem.albumName ?? '' : '');
ipcRenderer.send('performGCCast',device, "Apple Music Electron","Playing ...","3.0.0 beta",'');
windowAudioNode = AMEx.context.createGain();
try{
AMEx.result.source.connect(windowAudioNode);}
catch(e){}
var options = {
mimeType : 'audio/wav'
};
var destnode = AMEx.context.createMediaStreamDestination();
windowAudioNode.connect(destnode);
if(!recorder){
recorder = new MediaRecorder(destnode.stream,options,workerOptions);
recorder.start(1);
recorder.ondataavailable = function(e) {
e.data.arrayBuffer().then(buffer => {
if(!GCOverride) {
ipcRenderer.send('writeWAV',buffer,preferences.audio.castingBitDepth);
}
if(!EAOverride) {ipcRenderer.send('writePCM',buffer);}
}
);
}}
} else {queueChromecast = true; selectedGC = device}
},
stopGC : function(){
queueChromecast = false;
try{
recorder.stop();
recorder = null;
} catch(e){}
GCOverride = true;
this.activeCasts = [];
ipcRenderer.send('stopGCast','');
}
};
document.addEventListener('keydown', function (event) {
if (event.ctrlKey || event.metaKey) {
switch (String.fromCharCode(event.which).toLowerCase()) {
case "2":
if (document.getElementById('eq-menu')){
document.getElementById('eq-menu').parentNode.getElementsByTagName('button')[0].click();
}
else{AudioOutputs.ShowEQ();}
break;
case "3":
(EAOverride) ? (EAOverride = false) : (EAOverride = true);
break;
}
}
});
function waitFor(condition, callback) {
if(condition() == null || !condition() ) {
window.setTimeout(waitFor.bind(null, condition, callback), 1000);
} else {
callback();
}
}
function setIntervalX(callback, delay, repetitions) {
var x = 0;
var intervalID = window.setInterval(function () {
callback();
if (++x === repetitions) {
window.clearInterval(intervalID);
}
}, delay);
}
AudioOutputs.init()