import './speaker.css';
import AudioPeer from './audioPeer';
import AudioCalibrator from './audioCalibrator';
const QRCode = require('qrcode');
// TODO: some of these methods were preimplmented, but appear to be unused, cleanup
/**
* @class Handles the speaker's side of the connection. Responsible for initiating the connection,
* rendering the QRCode, and answering the call.
* @extends AudioPeer
*/
class Speaker extends AudioPeer {
/**
* Takes the url of the current site and a target element where html elements will be appended.
* @param {initParameters} params - see type definition for initParameters
*/
constructor(params) {
super(params);
this.siteUrl += '/listener?';
this.ac = new AudioCalibrator();
/* Set up callbacks that handle any events related to our peer object. */
this.peer.on('open', this.onPeerOpen);
this.peer.on('connection', this.onPeerConnection);
this.peer.on('call', this.onPeerCall);
this.peer.on('close', this.onPeerClose);
this.peer.on('disconnected', this.onPeerDisconnected);
this.peer.on('error', this.onPeerError);
}
/**
* Called after the peer conncection has been opened.
* Generates a QR code for the connection and displays it.
*/
showQRCode = () => {
// this.ac.test();
// Get query string, the URL parameters to specify a Listener
const queryStringParameters = {
speakerPeerId: this.peer.id,
};
const queryString = this.queryStringFromObject(queryStringParameters);
const uri = this.siteUrl + queryString;
// Display QR code for the participant to scan
const qrCanvas = document.createElement('canvas');
qrCanvas.setAttribute('id', 'qrCanvas');
console.log(uri);
QRCode.toCanvas(qrCanvas, uri, error => {
if (error) console.error(error);
});
// If specified HTML Id is available, show QR code there
if (document.getElementById(this.targetElement)) {
document.getElementById(this.targetElement).appendChild(qrCanvas);
} else {
// or just print it to console
console.log('TEST: Peer reachable at: ', uri);
}
};
/**
* Called when the peer connection is opened.
* Saves the peer id and calls the QR code generator.
* @param {object} peerId - The peer id of the peer connection
*/
onPeerOpen = id => {
// Workaround for peer.reconnect deleting previous id
if (id === null) {
console.error('Received null id from peer open');
this.peer.id = this.lastPeerId;
} else {
this.lastPeerId = this.peer.id;
}
if (id !== this.peer.id) {
console.warn('DEBUG Check you assumption that id === this.peer.id');
}
this.showQRCode();
};
/**
* Called when the peer connection is established.
* Enforces a single connection.
* @param {*} connection - The connection object
*/
onPeerConnection = connection => {
console.log('Speaker - onPeerConnection');
// Allow only a single connection
if (this.conn && this.conn.open) {
connection.on('open', () => {
connection.send('Already connected to another client');
setTimeout(() => {
connection.close();
}, 500);
});
return;
}
this.conn = connection;
console.log('Connected to: ', this.conn.peer);
this.ready();
};
/**
* Called after a call is established and data is flowing.
* Sets up the local audio stream and starts the calibration process.
* @param {MediaStream} stream - The stream of audio from the Listener.
*/
onReceiveStream = stream => {
window.localStream = stream;
window.localAudio.srcObject = stream;
window.localAudio.autoplay = true;
// Start calibration
if (!this.ac.getCalibrationStatus()) {
this.ac.startCalibration(stream);
}
};
/**
* Called when a call is made by the Listener.
* Answers the call in a one-way manner, and sets up a stream listener.
* @param {*} call
*/
onPeerCall = call => {
call.answer(); // Answer the call (one way)
this.ac.createLocalAudio(document.getElementById(this.targetElement));
call.on('stream', this.onReceiveStream);
};
/**
* Called when the peer connection is closed.
*/
onPeerClose = () => {
this.conn = null;
console.log('Connection destroyed');
};
/**
* Called when the peer connection is disconnected.
* Attempts to reconnect.
*/
onPeerDisconnected = () => {
console.log('Connection lost. Please reconnect');
// Workaround for peer.reconnect deleting previous id
this.peer.id = this.lastPeerId;
// eslint-disable-next-line no-underscore-dangle
this.peer._lastServerId = this.lastPeerId;
this.peer.reconnect();
};
/**
* Called when the peer connection encounters an error.
* @param {*} error
*/
onPeerError = error => {
// TODO: check if this function is needed or not
console.error(error);
};
/**
* Called when data is received from the peer connection.
* @param {*} data
*/
onIncomingData = data => {
// TODO: check if this function is needed or not
console.log({data});
};
/**
* Called when the peer connection is ready.
*/
ready = () => {
// Perform callback with data
this.conn.on('data', this.onIncomingData);
this.conn.on('close', () => {
console.log('Connection reset<br>Awaiting connection...');
this.conn = null;
});
};
}
/*
Referenced links:
https://stackoverflow.com/questions/28016664/when-you-pass-this-as-an-argument/28016676#28016676
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
https://stackoverflow.com/questions/879152/how-do-i-make-javascript-beep [3]
*/
export default Speaker;