Separate microphone
This commit is contained in:
@@ -8,15 +8,7 @@ class CommunicationsPanel extends BasePanel {
|
|||||||
{text: 'Analyzer', id: 'send-via-analyzer', eventName: 'sendAnalyzerChange'},
|
{text: 'Analyzer', id: 'send-via-analyzer', eventName: 'sendAnalyzerChange'},
|
||||||
{text: 'Speakers', id: 'send-via-speaker', eventName: 'sendSpeakersChange'}
|
{text: 'Speakers', id: 'send-via-speaker', eventName: 'sendSpeakersChange'}
|
||||||
]);
|
]);
|
||||||
this.addSection('Receive');
|
|
||||||
this.addCheckboxes('receive-via', [
|
|
||||||
{text: 'Listening', id: 'is-listening-checkbox', eventName: 'listeningChange'}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
isListeningChecked = () => {
|
|
||||||
return this.getCheckedById('is-listening-checkbox');
|
|
||||||
}
|
|
||||||
setListening = checked => this.setCheckedById('is-listening-checkbox', checked);
|
|
||||||
setSendSpeakers = checked => this.setCheckedById('send-via-speaker', checked);
|
setSendSpeakers = checked => this.setCheckedById('send-via-speaker', checked);
|
||||||
setSendAnalyzer = checked => this.setCheckedById('send-via-analyzer', checked);
|
setSendAnalyzer = checked => this.setCheckedById('send-via-analyzer', checked);
|
||||||
}
|
}
|
||||||
|
|||||||
150
Panels/MicrophonePanel.js
Normal file
150
Panels/MicrophonePanel.js
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
import BasePanel from './BasePanel';
|
||||||
|
|
||||||
|
const media = {
|
||||||
|
audio: {
|
||||||
|
mandatory: {
|
||||||
|
autoGainControl: false,
|
||||||
|
echoCancellation: false,
|
||||||
|
noiseSuppression: false,
|
||||||
|
suppressLocalAudioPlayback: false,
|
||||||
|
voiceIsolation: false
|
||||||
|
},
|
||||||
|
optional: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MicrophonePanel extends BasePanel {
|
||||||
|
constructor() {
|
||||||
|
super('Microphone');
|
||||||
|
|
||||||
|
this.addCheckboxes('receive-via', [
|
||||||
|
{text: 'Listen', id: 'listen', eventName: 'listenChange'}
|
||||||
|
]);
|
||||||
|
this.addCanvas('canvas', 100, 25);
|
||||||
|
|
||||||
|
this.addEventListener('listenChange', (e) => {
|
||||||
|
if(e.checked) {
|
||||||
|
this.error = undefined;
|
||||||
|
navigator.mediaDevices.getUserMedia(media)
|
||||||
|
.then(stream => {
|
||||||
|
this.stream = stream;
|
||||||
|
this.connectStream();
|
||||||
|
this.dispatcher.emit('on', stream);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.error = error;
|
||||||
|
console.log(error);
|
||||||
|
this.setListening(false);
|
||||||
|
this.disconnectStream();
|
||||||
|
this.stopSampling();
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
if(this.stream) {
|
||||||
|
this.error = undefined;
|
||||||
|
this.disconnectStream();
|
||||||
|
this.dispatcher.emit('off');
|
||||||
|
}
|
||||||
|
this.stopSampling();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
setAnalyser = analyser => {
|
||||||
|
if(analyser) {
|
||||||
|
this.analyser = analyser;
|
||||||
|
this.connectStream();
|
||||||
|
} else {
|
||||||
|
this.disconnectStream();
|
||||||
|
this.analyser = analyser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setAudioContext = audioContext => {
|
||||||
|
if(audioContext) {
|
||||||
|
this.audioContext = audioContext;
|
||||||
|
this.connectStream();
|
||||||
|
} else {
|
||||||
|
this.disconnectStream();
|
||||||
|
this.audioContext = audioContext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connectStream = () => {
|
||||||
|
if(this.stream) {
|
||||||
|
if(this.audioContext) {
|
||||||
|
if(!this.streamNode) {
|
||||||
|
this.streamNode = this.audioContext.createMediaStreamSource(this.stream);
|
||||||
|
}
|
||||||
|
if(this.analyser) {
|
||||||
|
this.streamNode.connect(this.analyser);
|
||||||
|
}
|
||||||
|
if(this.getListening()) {
|
||||||
|
this.startSampling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disconnectStream = () => {
|
||||||
|
if(this.streamNode) {
|
||||||
|
if(this.analyser) {
|
||||||
|
this.streamNode.disconnect(this.analyser);
|
||||||
|
this.streamNode = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(this.stream) {
|
||||||
|
this.stream.getTracks().forEach(track => track.stop());
|
||||||
|
this.stream = undefined;
|
||||||
|
}
|
||||||
|
this.stopSampling();
|
||||||
|
}
|
||||||
|
|
||||||
|
isSampling = () => !!this.samplingId;
|
||||||
|
startSampling = () => {
|
||||||
|
if(this.isSampling()) return;
|
||||||
|
// nothing to analyse
|
||||||
|
if(!this.stream) return;
|
||||||
|
// nothing to analyse with
|
||||||
|
if(!this.analyser) return;
|
||||||
|
this.samplingId = window.requestAnimationFrame(this.drawSpectrumAnalyzer);
|
||||||
|
}
|
||||||
|
stopSampling = () => {
|
||||||
|
if(!this.isSampling()) return;
|
||||||
|
window.cancelAnimationFrame(this.samplingId);
|
||||||
|
this.samplingId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
getListening = () => this.getCheckedById('listen');
|
||||||
|
setListening = checked => {
|
||||||
|
if(this.getListening() !== checked) {
|
||||||
|
this.setCheckedById('listen', checked)
|
||||||
|
this.dispatcher.emit('listenChange', {checked});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getStream = () => this.stream;
|
||||||
|
|
||||||
|
drawSpectrumAnalyzer = () => {
|
||||||
|
const canvas = this.getElement('canvas');
|
||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const width = canvas.width;
|
||||||
|
const height = canvas.height;
|
||||||
|
|
||||||
|
let frequencyResolution = this.audioContext.sampleRate / this.analyser.fftSize;
|
||||||
|
let nyquistFrequency = this.audioContext.sampleRate / 2;
|
||||||
|
let buffer = new Uint8Array(this.analyser.frequencyBinCount);
|
||||||
|
this.analyser.getByteFrequencyData(buffer);
|
||||||
|
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
let barWidth = (1/buffer.length) * width;
|
||||||
|
for(let i = 0; i < buffer.length; i++) {
|
||||||
|
let x = i * barWidth;
|
||||||
|
let y = (1 - (buffer[i] / 255)) * height;
|
||||||
|
const hue = Math.floor(((i * frequencyResolution) / nyquistFrequency) * 360)
|
||||||
|
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
||||||
|
ctx.fillRect(x, y, barWidth, height-y);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.samplingId = window.requestAnimationFrame(this.drawSpectrumAnalyzer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default MicrophonePanel;
|
||||||
57
index.js
57
index.js
@@ -29,6 +29,7 @@ import {
|
|||||||
textToBits,
|
textToBits,
|
||||||
textToBytes,
|
textToBytes,
|
||||||
} from './converters';
|
} from './converters';
|
||||||
|
import MicrophonePanel from "./Panels/MicrophonePanel";
|
||||||
var audioContext;
|
var audioContext;
|
||||||
var microphoneStream;
|
var microphoneStream;
|
||||||
var microphoneNode;
|
var microphoneNode;
|
||||||
@@ -68,6 +69,7 @@ const availableFskPairsPanel = new AvailableFskPairsPanel();
|
|||||||
const frequencyGraphPanel = new FrequencyGraphPanel();
|
const frequencyGraphPanel = new FrequencyGraphPanel();
|
||||||
const graphConfigurationPanel = new GraphConfigurationPanel();
|
const graphConfigurationPanel = new GraphConfigurationPanel();
|
||||||
const speedPanel = new SpeedPanel();
|
const speedPanel = new SpeedPanel();
|
||||||
|
const microphonePanel = new MicrophonePanel();
|
||||||
|
|
||||||
function handleWindowLoad() {
|
function handleWindowLoad() {
|
||||||
const panelContainer = document.getElementById('panel-container');
|
const panelContainer = document.getElementById('panel-container');
|
||||||
@@ -82,9 +84,11 @@ function handleWindowLoad() {
|
|||||||
panelContainer.prepend(bitsSentPanel.getDomElement());
|
panelContainer.prepend(bitsSentPanel.getDomElement());
|
||||||
panelContainer.prepend(messagePanel.getDomElement());
|
panelContainer.prepend(messagePanel.getDomElement());
|
||||||
panelContainer.prepend(communicationsPanel.getDomElement());
|
panelContainer.prepend(communicationsPanel.getDomElement());
|
||||||
|
panelContainer.prepend(microphonePanel.getDomElement());
|
||||||
|
|
||||||
// Initialize Values
|
// Initialize Values
|
||||||
communicationsPanel.setListening(false);
|
microphonePanel.setListening(false);
|
||||||
|
|
||||||
communicationsPanel.setSendSpeakers(false);
|
communicationsPanel.setSendSpeakers(false);
|
||||||
communicationsPanel.setSendAnalyzer(true);
|
communicationsPanel.setSendAnalyzer(true);
|
||||||
|
|
||||||
@@ -135,7 +139,6 @@ function handleWindowLoad() {
|
|||||||
|
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
communicationsPanel.addEventListener('listeningChange', handleChangeListening);
|
|
||||||
communicationsPanel.addEventListener('sendSpeakersChange', handleChangeSendSpeakers);
|
communicationsPanel.addEventListener('sendSpeakersChange', handleChangeSendSpeakers);
|
||||||
communicationsPanel.addEventListener('sendAnalyzerChange', handleChangeSendAnalyzer);
|
communicationsPanel.addEventListener('sendAnalyzerChange', handleChangeSendAnalyzer);
|
||||||
|
|
||||||
@@ -554,7 +557,7 @@ function stopGraph() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resumeGraph() {
|
function resumeGraph() {
|
||||||
if(communicationsPanel.isListeningChecked()) {
|
if(microphonePanel.getListening()) {
|
||||||
if(PAUSE) {
|
if(PAUSE) {
|
||||||
PAUSE = false;
|
PAUSE = false;
|
||||||
AudioReceiver.start();
|
AudioReceiver.start();
|
||||||
@@ -830,6 +833,7 @@ function getAudioContext() {
|
|||||||
audioContext = new (window.AudioContext || webkitAudioContext)();
|
audioContext = new (window.AudioContext || webkitAudioContext)();
|
||||||
frequencyPanel.setSampleRate(audioContext.sampleRate);
|
frequencyPanel.setSampleRate(audioContext.sampleRate);
|
||||||
availableFskPairsPanel.setSampleRate(audioContext.sampleRate);
|
availableFskPairsPanel.setSampleRate(audioContext.sampleRate);
|
||||||
|
microphonePanel.setAudioContext(audioContext);
|
||||||
}
|
}
|
||||||
if(audioContext.state === 'suspended') {
|
if(audioContext.state === 'suspended') {
|
||||||
audioContext.resume();
|
audioContext.resume();
|
||||||
@@ -847,6 +851,8 @@ function getAnalyser() {
|
|||||||
if(analyser) return analyser;
|
if(analyser) return analyser;
|
||||||
analyser = audioContext.createAnalyser();
|
analyser = audioContext.createAnalyser();
|
||||||
frequencyGraphPanel.setAnalyser(analyser);
|
frequencyGraphPanel.setAnalyser(analyser);
|
||||||
|
microphonePanel.setAnalyser(analyser);
|
||||||
|
|
||||||
analyser.smoothingTimeConstant = signalPanel.getSmoothingTimeConstant();
|
analyser.smoothingTimeConstant = signalPanel.getSmoothingTimeConstant();
|
||||||
analyser.fftSize = frequencyPanel.getFftSize();
|
analyser.fftSize = frequencyPanel.getFftSize();
|
||||||
return analyser;
|
return analyser;
|
||||||
@@ -859,51 +865,6 @@ function handleChangeSendSpeakers({checked}) {
|
|||||||
SEND_VIA_SPEAKER = checked;
|
SEND_VIA_SPEAKER = checked;
|
||||||
configurationChanged();
|
configurationChanged();
|
||||||
}
|
}
|
||||||
function handleChangeListening({checked}) {
|
|
||||||
stopGraph();
|
|
||||||
var audioContext = getAudioContext();
|
|
||||||
function handleMicrophoneOn(stream) {
|
|
||||||
microphoneStream = stream;
|
|
||||||
microphoneNode = audioContext.createMediaStreamSource(stream);
|
|
||||||
analyser = getAnalyser();
|
|
||||||
microphoneNode.connect(analyser);
|
|
||||||
resumeGraph();
|
|
||||||
}
|
|
||||||
function handleMicrophoneError(error) {
|
|
||||||
console.error('Microphone Error', error);
|
|
||||||
}
|
|
||||||
if(checked) {
|
|
||||||
navigator.mediaDevices
|
|
||||||
.getUserMedia({
|
|
||||||
audio: {
|
|
||||||
mandatory: {
|
|
||||||
autoGainControl: false,
|
|
||||||
echoCancellation: false,
|
|
||||||
noiseSuppression: false,
|
|
||||||
suppressLocalAudioPlayback: false,
|
|
||||||
voiceIsolation: false
|
|
||||||
},
|
|
||||||
optional: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(handleMicrophoneOn)
|
|
||||||
.catch(handleMicrophoneError)
|
|
||||||
} else {
|
|
||||||
if(microphoneStream) {
|
|
||||||
microphoneStream.getTracks().forEach(track => track.stop());
|
|
||||||
microphoneStream = undefined;
|
|
||||||
}
|
|
||||||
if(analyser && microphoneNode) {
|
|
||||||
try {
|
|
||||||
analyser.disconnect(microphoneNode);
|
|
||||||
} catch(e) {
|
|
||||||
|
|
||||||
}
|
|
||||||
microphoneNode = undefined;
|
|
||||||
analyser = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawChannelData() {
|
function drawChannelData() {
|
||||||
// Do/did we have a stream?
|
// Do/did we have a stream?
|
||||||
|
|||||||
Reference in New Issue
Block a user