UNPKG

cordova-plugin-audioinput

Version:

This cordova plugin enables audio capture from the device microphone, by in (near) real-time forwarding raw audio data to the web layer of your application.

277 lines (222 loc) 7.96 kB
/* This demo shows how to collect the raw microphone data, create a AudioBufferSourceNode from it and play it using the Web Audio API. */ // Web Audio API var audioContext, micGainNode; // Capture configuration object var captureCfg = {}; // Queue var audioDataQueue = []; // How many data chunks should be joined before playing them var concatenateMaxChunks = 10; // Timers var timerGetNextAudio, timerInterVal; // Info/Debug var totalReceivedData = 0, totalPlayedData = 0; /** * Called continuously while AudioInput capture is running. */ function onAudioInputCapture(evt) { try { if (evt && evt.data) { // Increase the debug counter for received data totalReceivedData += evt.data.length; // Push the data to the audio queue (array) audioDataQueue.push(evt.data); } } catch (ex) { alert("onAudioInputCapture ex: " + ex); } } /** * Called when a plugin error happens. */ function onAudioInputError(error) { alert("onAudioInputError event recieved: " + JSON.stringify(error)); } /** * Consume data from the audioinput queue and calls the playAudio method */ var getNextToPlay = function () { var duration = 50; // Check if there is any data in the queue if (audioDataQueue.length > 0) { // Concatenate up to concatenateMaxChunks data arrays from the queue var concatenatedData = []; for (var i = 0; i < concatenateMaxChunks; i++) { if (audioDataQueue.length === 0) { break; } concatenatedData = concatenatedData.concat(audioDataQueue.shift()); } // Play the audio duration = playAudio(concatenatedData) * 1000; } // Still capturing? Then call myself to continue consuming incoming data. if (window.audioinput && audioinput.isCapturing()) { timerGetNextAudio = setTimeout(getNextToPlay, duration); } }; /** * Play audio from data using the Web Audio API */ var playAudio = function (data) { try { // Create an audio buffer to hold the data var audioBuffer = audioContext.createBuffer(captureCfg.channels, (data.length / captureCfg.channels), captureCfg.sampleRate); // Initialize the audio buffer with the data if (captureCfg.channels > 1) { // For multiple channels (stereo) we assume that the data is interleaved for (var i = 0; i < captureCfg.channels; i++) { var chdata = [], index = 0; while (index < data.length) { chdata.push(data[index + i]); index += parseInt(captureCfg.channels); } audioBuffer.getChannelData(i).set(chdata); } } else { // For just one channels (mono) audioBuffer.getChannelData(0).set(data); } // Create a buffer source based on the audio buffer var source = audioContext.createBufferSource(); source.buffer = audioBuffer; // Connect the buffer source to the gain node source.connect(micGainNode); // Play the audio immediately source.start(0); // Increase the debug counter for played data totalPlayedData += data.length; // Return the duration of the sound so that we can play the next sound when ended. return audioBuffer.duration; } catch(e) { alert("playAudio exception: " + e); return 100; } }; /** * Creates the Web Audio Context and audio nodes for output. */ var initWebAudio = function () { try { window.AudioContext = window.AudioContext || window.webkitAudioContext; audioContext = new window.AudioContext(); consoleMessage("Web Audio Context is ready"); } catch (e) { consoleMessage('Web Audio API is not supported in this browser: ' + e); return false; } // Create a gain node for volume control micGainNode = audioContext.createGain(); // Connect the gain node to the speaker micGainNode.connect(audioContext.destination); return true; }; /** * */ var startCapture = function () { try { if (window.audioinput && !audioinput.isCapturing()) { var audioSourceElement = document.getElementById("audioSource"), audioSourceType = audioSourceElement.options[audioSourceElement.selectedIndex].value; // Get the audio capture configuration from the UI elements // captureCfg = { sampleRate: parseInt(document.getElementById('sampleRate').value), bufferSize: parseInt(document.getElementById('bufferSize').value), channels: parseInt(document.querySelector('input[name="channels"]:checked').value), format: document.querySelector('input[name="format"]:checked').value, audioSourceType: parseInt(audioSourceType) }; audioinput.start(captureCfg); consoleMessage("Microphone input started!"); // Start the Interval that outputs time and debug data while capturing // timerInterVal = setInterval(function () { if (audioinput.isCapturing()) { document.getElementById("infoTimer").innerHTML = "" + new Date().toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, "$1") + "|Received:" + totalReceivedData + "|Played:" + totalPlayedData; } }, 1000); // Start the audio queue consumer // getNextToPlay(); disableStartButton(); } } catch (e) { alert("startCapture exception: " + e); } }; /** * */ var stopCapture = function () { try { if (window.audioinput && audioinput.isCapturing()) { if (timerInterVal) { clearInterval(timerInterVal); } if (window.audioinput) { audioinput.stop(); } audioDataQueue = []; totalReceivedData = 0; totalPlayedData = 0; document.getElementById("infoTimer").innerHTML = ""; consoleMessage("Stopped"); disableStopButton(); } } catch (e) { alert("stopCapture exception: " + e); } }; /** * */ var initUIEvents = function () { document.getElementById("startCapture").addEventListener("click", startCapture); document.getElementById("stopCapture").addEventListener("click", stopCapture); }; /** * When cordova fires the deviceready event, we initialize everything needed for audio input. */ var onDeviceReady = function () { if (window.cordova && window.audioinput) { consoleMessage("Use 'Start Capture' to begin..."); initUIEvents(); if (initWebAudio()) { consoleMessage("Use 'Start Capture' to begin..."); } // Subscribe to audioinput events // window.addEventListener('audioinput', onAudioInputCapture, false); window.addEventListener('audioinputerror', onAudioInputError, false); } else { consoleMessage("cordova-plugin-audioinput not found!"); disableAllButtons(); } }; // Make it possible to run the demo on desktop if (!window.cordova) { // Make it possible to run the demo on desktop console.log("Running on desktop!"); onDeviceReady(); } else { // For Cordova apps document.addEventListener('deviceready', onDeviceReady, false); }