Skip to content

Commit

Permalink
Add WebAudio Part I
Browse files Browse the repository at this point in the history
  • Loading branch information
YeonV committed Sep 29, 2021
1 parent 12426dd commit 9848a2e
Show file tree
Hide file tree
Showing 11 changed files with 598 additions and 30 deletions.
9 changes: 9 additions & 0 deletions main/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { app,
} from 'electron';
import serve from 'electron-serve';
import { createWindow } from './helpers';
import {DgramAsPromised} from "dgram-as-promised"

const path = require('path');
const isProd: boolean = process.env.NODE_ENV === 'production';


if (isProd) {
serve({ directory: 'app' });
} else {
Expand Down Expand Up @@ -37,6 +39,13 @@ let tray = null;
mainWindow.setSize(arg[0],arg[1])
})

ipcMain.on('UDP', async(event, arg) => {
const socket = DgramAsPromised.createSocket("udp4")
const PORT = 21324
const message = Buffer.from(arg[1])
await socket.send(message, 0, message.length, PORT, arg[0].ip)
})

// tray = new Tray(nativeImage.createFromDataURL('data:image/x-icon;base64,AAABAAEAEBAAAAEAGACGAAAAFgAAAIlQTkcNChoKAAAADUlIRFIAAAAQAAAAEAgGAAAAH/P/YQAAAE1JREFUOI1j/P//PwOxgNGeAUMxE9G6cQCKDWAhpADZ2f8PMjBS3QW08QK20KaZC2gfC9hCnqouoNgARgY7zMxAyNlUdQHlXiAlO2MDAD63EVqNHAe0AAAAAElFTkSuQmCC'))


Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"private": true,
"name": "wled-manager",
"description": "WLED Manager by Blade",
"version": "0.0.2",
"version": "0.0.3",
"author": "YeonV aka Blade <https://github.com/YeonV>",
"main": "app/background.js",
"scripts": {
Expand All @@ -14,8 +14,10 @@
"@types/bonjour": "^3.5.9",
"bonjour": "^3.5.0",
"custom-electron-titlebar": "^3.2.7",
"dgram-as-promised": "^5.0.1",
"electron-serve": "^1.1.0",
"electron-store": "^8.0.0"
"electron-store": "^8.0.0",
"react-colorful": "^5.5.0"
},
"devDependencies": {
"@material-ui/core": "^4.12.2",
Expand Down
Binary file added renderer/audio/GummyBearz.mp3
Binary file not shown.
86 changes: 86 additions & 0 deletions renderer/components/AudioContainer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from 'react';
import VisualDemo from './Visualizer';
import { Button } from '@material-ui/core';

var theStream
var theSource
var theAnalyser
var theGain

class AudioDataContainer extends React.Component {

constructor(props) {
super(props);
this.state = {}
this.frequencyBandArray = [...Array(48).keys()]
}

initializeAudioAnalyser = () => {
const audioContext = new AudioContext();
this.getMedia().then(stream => {
theStream = stream
if (!audioContext || audioContext.state === 'closed') {
return
}
const source = audioContext.createMediaStreamSource(stream);
theSource = source
const analyser = audioContext.createAnalyser();
theAnalyser = analyser
analyser.fftSize = 4096;
// source.connect(audioContext.destination);
const gain = audioContext.createGain()
theGain = gain.gain
source.connect(gain)
gain.connect(analyser)
this.setState({
audioData: analyser
})
})
}

getFrequencyData = (styleAdjuster) => {
const bufferLength = this.state.audioData && this.state.audioData.frequencyBinCount;
const amplitudeArray = new Uint8Array(bufferLength);
this.state.audioData && this.state.audioData.getByteFrequencyData(amplitudeArray)
styleAdjuster(amplitudeArray)
}
getMedia = async () => {
try {
return await navigator.mediaDevices.getUserMedia({
// waiting for state-management
// audio: navigator.mediaDevices.enumerateDevices()
// .then(function (devices) {
// (clientDevice === null || devices.indexOf(clientDevice === -1)) ? true : { deviceId: { exact: clientDevice } }
// }),
audio: true,
video: false,
})
} catch (err) {
console.log('Error:', err)
}
}
render() {

return (
<div style={{ height: 255 }}>
<VisualDemo
initializeAudioAnalyser={this.initializeAudioAnalyser}
frequencyBandArray={this.frequencyBandArray}
getFrequencyData={this.getFrequencyData}
stop={() => {
theGain.value = 0
setTimeout(() => {
theStream.getTracks().forEach(track => track.stop())
this.setState({
audioData: null
})
}, 1000);

}}
/>
</div>
);
}
}

export default AudioDataContainer;
122 changes: 122 additions & 0 deletions renderer/components/AudioContainerYZ.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import React, { useState, useEffect, useRef } from 'react';
import VisualDemo from './Visualizer';
// import soundFile from '../audio/GummyBearz.mp3'
import { Button } from '@material-ui/core';




const AudioDataContainer = (props) => {

const frequencyBandArray = [...Array(48).keys()]
const [state, setState] = useState({})
const [dataPlaying, setDataPlaying] = useState(false);
const audioContextRef = useRef();

let s

useEffect(() => {
const audioContext = new AudioContext();

getMedia().then(stream => {
if (!audioContext || audioContext.state === 'closed') {
return
}
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 4096;
source.connect(analyser);
setState({
audioData: analyser
})
audioContextRef.current = audioContext;
audioContext.suspend();
})


return () => source.disconnect(analyser);
}, []);

const toggleAnalyser = () => {
if (dataPlaying) {
audioContextRef.current.suspend();
} else {
audioContextRef.current.resume();
}
setDataPlaying((play) => !play);
};

const initializeAudioAnalyser = () => {
if (audioContext) {

getMedia().then(stream => {

s = stream
if (!audioContext || audioContext.state === 'closed') {
return
}
setActiveAudio(true)
console.log(audioContext)
const source = audioContext.createMediaStreamSource(stream);
const analyser = audioContext.createAnalyser();
analyser.fftSize = 4096;
// source.connect(audioContext.destination);
source.connect(analyser);
console.log(analyser)
setState({
audioData: analyser
})
})
}

}

const getFrequencyData = (styleAdjuster) => {
const bufferLength = state.audioData && state.audioData.frequencyBinCount;
const amplitudeArray = new Uint8Array(bufferLength);
state.audioData && state.audioData.getByteFrequencyData(amplitudeArray)
styleAdjuster(amplitudeArray)
}
const getMedia = async () => {
try {
return await navigator.mediaDevices.getUserMedia({
audio: true,
video: false,
})
} catch (err) {
console.log('Error:', err)
}
}


return (
<div style={{ height: 255 }}>
<button onClick={toggleAnalyser} data-playing={dataPlaying}>
<span>{dataPlaying ? "Pause" : "Play"}</span>
</button>
<VisualDemo
initializeAudioAnalyser={initializeAudioAnalyser}
frequencyBandArray={frequencyBandArray}
getFrequencyData={getFrequencyData}
audioData={state.audioData}
stop={() => {
console.log("Stopping")
if (audioContext) {
console.log("Stopping2")
s.getTracks().forEach(track => track.stop())
console.log("Stopping3")
audioContext.close()
console.log("Stopping4")
}
console.log("Stopping5")
setState({
audioData: null
})
}}
/>
</div>
);

}

export default AudioDataContainer;
43 changes: 43 additions & 0 deletions renderer/components/ColorPicker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { useCallback, useRef, useState } from "react";
import { RgbColorPicker } from "react-colorful";

import useClickOutside from "../lib/useClickOutside";

const ColorPicker = ({ color, onChange }) => {
const popover = useRef();
const [isOpen, toggle] = useState(false);

const close = useCallback(() => toggle(false), []);
useClickOutside(popover, close);

return (
<div style={{ position: 'relative' }}>
<div
style={{
width: '56px',
height: '56px',
borderRadius: '8px',
border: '2px solid #555',
boxShadow: '0 0 0 1px rgba(0, 0, 0, 0.1), inset 0 0 0 1px rgba(0, 0, 0, 0.1)',
cursor: 'pointer',
backgroundColor: `rgb(${color.r}, ${color.g}, ${color.b})`
}}
onClick={() => toggle(true)}
/>

{isOpen && (
<div style={{
position: 'absolute',
top: '50%',
right: '100%',
borderRadius: '9px',
boxShadow: '0 6px 12px rgba(0, 0, 0, 0.15)',
}
} ref={popover}>
<RgbColorPicker color={color} onChange={onChange} />
</div>
)}
</div>
);
};
export default ColorPicker
Loading

0 comments on commit 9848a2e

Please sign in to comment.