I've been banging my head against a wall on this for two days now, and I really hope someone can help on this.
I've taken some code for a getUserMedia microphone recorder from https://higuma.github.io/wav-audio-encoder-js/ + https://github.com/higuma/wav-audio-encoder-js here. I've stripped out the components I don't need - and somehow, in the process, I've managed to make it so that there is no audio coming through on the generated file.
It looks like it formats correctly - but is completely silent. I'm getting 0 errors to work from.
// navigator.getUserMedia shim
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
// URL shim
window.URL = window.URL || window.webkitURL;
// audio context + .createScriptProcessor shim
var audioContext = new AudioContext;
if (audioContext.createScriptProcessor == null) {
audioContext.createScriptProcessor = audioContext.createJavaScriptNode;
}
// selectors
var $microphone = $('#microphone');
var $cancel = $('#cancel');
var $recordingList = $('#recording-list');
var $timeDisplay = $('#time-display');
var $microphoneLevel = $('#microphone-level');
var microphone = undefined;
var input = audioContext.createGain();
var mixer = audioContext.createGain();
var microphoneLevel = audioContext.createGain();
microphoneLevel.gain.value = 0;
microphoneLevel.connect(mixer);
var processor = undefined;
var startTime = null;
var encoder = undefined;
// obtaining microphone input
$microphone.click(function() {
navigator.getUserMedia({ audio: true },
function(stream) {
microphone = audioContext.createMediaStreamSource(stream);
microphone.connect(microphoneLevel);
console.log(microphone);
},
function(error) {
window.alert("Could not get audio input");
});
});
// start/stop recording
$microphone.click(function() {
if (startTime != null) {
stopRecording(true);
} else {
startRecording();
}
});
// cancel recording (without saving)
$cancel.click(function() {
stopRecording(false);
});
// microphone level slider
$microphoneLevel.on('input', function() {
var level = $microphoneLevel[0].valueAsNumber / 100;
microphoneLevel.gain.value = level * level;
});
function startRecording() {
startTime = Date.now();
$microphone.html('Stop');
$cancel.removeClass("hidden");
startRecordingProcess();
}
function startRecordingProcess() {
processor = audioContext.createScriptProcessor(1024, 2, 2);
input.connect(processor);
processor.connect(audioContext.destination);
// wav encoder
encoder = new WavAudioEncoder(audioContext.sampleRate, 2);
processor.onaudioprocess = function(event) {
encoder.encode(getBuffers(event));
};
}
function getBuffers(event) {
var buffers = [];
for (var ch = 0; ch < 2; ++ch) {
buffers[ch] = event.inputBuffer.getChannelData(ch);
}
return buffers;
}
function stopRecording(finish) {
startTime = null;
$timeDisplay.html('00:00');
$microphone.html('<i class="start fa fa-microphone fa-5x" aria-hidden="true"></i>');
$cancel.addClass('hidden');
stopRecordingProcess(finish);
}
function stopRecordingProcess(finish) {
input.disconnect();
processor.disconnect();
if (finish) { // if microphone pressed
saveRecording(encoder.finish());
} else { // if cancel pressed
encoder.cancel();
}
}
function saveRecording(blob) {
var url = URL.createObjectURL(blob);
var html = "<p class='recording' recording='" + url + "'><a class='btn btn-default' href='" + url + "' download='recording.wav'>Save Recording</a></p>";
$recordingList.prepend($(html));
// once we have done all the processing, upload the file to beyond verbal
// uploadFile(blob);
}
// update the recording timer
function minuteSeconds(n) { return (n < 10 ? "0" : "") + n; }
function updateDateTime() {
if (startTime !== null) {
var sec = Math.floor((Date.now() - startTime) / 1000);
$timeDisplay.html(minuteSeconds(sec / 60 | 0) + ":" + minuteSeconds(sec % 60));
}
}
window.setInterval(updateDateTime, 200);
If anyone has run into this before, I'd be really appreciative of a fix.
Thank you all for your time, and have a nice day/night
First check your microphone with general recording demo.
If its working you can try passing only Audio Stream & required mime type to media recorder for basic audio recording.
If you want to play with this webaudio context,
Am suspecting issue with microphoneLevel.gain.value = 0;
change it to microphoneLevel.gain.value = 1; //or 2
gain = 0 means we are muting the audio.
gain = 1 default audio level
gain = 0.1 - 0.9 is reducing volume level
gain = above 1.1 increasing the volume level
print the level values in console on
// microphone level slider
$microphoneLevel.on('input', function() {
var level = $microphoneLevel[0].valueAsNumber / 100;
console.log('value: ' + $microphoneLevel[0].valueAsNumber + ' Level: ' + level);
microphoneLevel.gain.value = level * level; // if level is zero, then its silent
// its better if you have a predefined level values based slider position instead of multiplying it
});
See my demo and source
Related
I am using AudioWorkletProcessor, I need to store all the audio stream data into a single file and play it at the end.
Below is my AudioWorkletProcessor code:
class RecorderProcessor extends AudioWorkletProcessor {
// 0. Determine the buffer size (this is the same as the 1st argument of ScriptProcessor)
//bufferSize = 4096
bufferSize = 256
// 1. Track the current buffer fill level
_bytesWritten = 0
// 2. Create a buffer of fixed size
_buffer = new Float32Array(this.bufferSize)
constructor() {
super(); // exception thrown here when not called
this.initBuffer()
}
initBuffer() {
this._bytesWritten = 0
}
isBufferEmpty() {
return this._bytesWritten === 0
}
isBufferFull() {
return this._bytesWritten === this.bufferSize
}
/**
* #param {Float32Array[][]} inputs
* #returns {boolean}
*/
process(inputs) {
// Grabbing the 1st channel similar to ScriptProcessorNode
this.append(inputs[0][0])
// this.append(outputs[0][0])
return true
}
/**
*
* #param {Float32Array} channelData
*/
append(channelData) {
if (this.isBufferFull()) {
this.flush()
}
if (!channelData) return
for (let i = 0; i < channelData.length; i++) {
this._buffer[this._bytesWritten++] = channelData[i]
}
}
flush() {
// trim the buffer if ended prematurely
this.port.postMessage(
this._bytesWritten < this.bufferSize
? this._buffer.slice(0, this._bytesWritten)
: this._buffer
)
this.initBuffer()
}
}
registerProcessor("recorderWorkletProcessor", RecorderProcessor)
which returning 32 bit Float array .
Below is my javascript code :
var recordingNode; //audio worklet node
var micSourceNode; //mic node
const chunks = []; // storing all stream audio chunks
try {
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
microphone = navigator.getUserMedia({
audio: true,
video: false
}, onMicrophoneGranted, onMicrophoneDenied);
} catch (e) {
alert(e)
}
function onMicrophoneDenied() {
console.log('denied')
}
async function onMicrophoneGranted(stream) {
context = new AudioContext({
sampleRate: 48000
});
micSourceNode = context.createMediaStreamSource(stream);
await context.audioWorklet.addModule('/app_resources/recorderWorkletProcessor.js');
recordingNode = new AudioWorkletNode(context, "recorderWorkletProcessor")
recordingNode.port.onmessage = function(e) {
chunks.push(e.data); //storing chunks in arrau
}
micSourceNode
.connect(recordingNode)
.connect(context.destination);
}
function stopstream() {
if (micSourceNode)
micSourceNode.disconnect(recordingNode);
var blob = new Blob(chunks, {
type: "audio/webm;codecs=opus"
});
console.log(blob.size)
const audioUrl = URL.createObjectURL(blob);
document.getElementById('song').innerHTML = '<audio id="audio-player" controls="controls" src="' + audioUrl + '" type="audio/mpeg">';
}
I am unable to convert the float 32 bit array into audio file. i can see the size in the blob but unable to play audio. Please help me understand what can i do here to make it work.
I am using Microsoft Azure Text To Speech with Unity. But there will be broken sounds at the beginning and end of the playing sound. Is this normal, or result.AudioData is broken. Below is the code.
public AudioSource audioSource;
void Start()
{
SynthesisToSpeaker("你好世界");
}
public void SynthesisToSpeaker(string text)
{
var config = SpeechConfig.FromSubscription("[redacted]", "southeastasia");
config.SpeechSynthesisLanguage = "zh-CN";
config.SpeechSynthesisVoiceName = "zh-CN-XiaoxiaoNeural";
// Creates a speech synthesizer.
// Make sure to dispose the synthesizer after use!
SpeechSynthesizer synthesizer = new SpeechSynthesizer(config, null);
Task<SpeechSynthesisResult> task = synthesizer.SpeakTextAsync(text);
StartCoroutine(CheckSynthesizer(task, config, synthesizer));
}
private IEnumerator CheckSynthesizer(Task<SpeechSynthesisResult> task,
SpeechConfig config,
SpeechSynthesizer synthesizer)
{
yield return new WaitUntil(() => task.IsCompleted);
var result = task.Result;
// Checks result.
string newMessage = string.Empty;
if (result.Reason == ResultReason.SynthesizingAudioCompleted)
{
var sampleCount = result.AudioData.Length / 2;
var audioData = new float[sampleCount];
for (var i = 0; i < sampleCount; ++i)
{
audioData[i] = (short)(result.AudioData[i * 2 + 1] << 8
| result.AudioData[i * 2]) / 32768.0F;
}
// The default output audio format is 16K 16bit mono
var audioClip = AudioClip.Create("SynthesizedAudio", sampleCount,
1, 16000, false);
audioClip.SetData(audioData, 0);
audioSource.clip = audioClip;
audioSource.Play();
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = SpeechSynthesisCancellationDetails.FromResult(result);
}
synthesizer.Dispose();
}
The default audio format is Riff16Khz16BitMonoPcm, which has a riff header in the beginning of result.AudioData. If you pass the audioData to audioClip, it will play the header, then you hear some noise.
You can set the format to a raw format without header by speechConfig.SetSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm);, see this sample for details.
I'm trying to broadcast captured mic audio across a websocket. I can see the buffer array is being sent, and the array has actual valid data but the receiving client side cannot hear it. I'm pretty sure my playback function is correct, because I can generate white noise by filling an array with random numbers and using the playback function to hear it. I'm thinking maybe the audio it's broadcasting is too quiet to hear, because the numbers generated in the array are seem to mostly be in the .000### range. Any ideas? Capturing mic audio and broadcasting it seems to be over complicated... :/
//broadcasting side
navigator.mediaDevices.getUserMedia({audio: true,video: false}) // request cam
.then(stream => {
vid.srcObject = stream;
context = new AudioContext();
var source = context.createMediaStreamSource(stream);
var processor = context.createScriptProcessor(1024, 2, 2);
source.connect(processor);
processor.connect(context.destination);
processor.onaudioprocess = function(e) {
audiodata = e.inputBuffer.getChannelData(1);
socket.send(JSON.stringify({sound: audiodata, to: to, from: '$username', text:''}));
};
return vid.play(); // returns a Promise
});
//receiving side object to array
if(typeof (message.sound) != "undefined"){
//$('#video_stream_btn').trigger('click');
var json_sound = message.sound;
var array_sound = [];
for(var i in json_sound){
array_sound.push([i, json_sound [i]]);
}
if(typeof(context) == 'undefined'){
context = new AudioContext();
}
play_sound(array_sound, context);
return;
}
// receiving side play sound function
function play_sound(raw,context){
//alert(raw.length);
var audioBuffer = context.createBuffer(1, raw.length, context.sampleRate);
audioBuffer.getChannelData(0).set(raw);
var source = context.createBufferSource();
source.buffer = audioBuffer;
source.connect(context.destination);
source.start(0);
}
For anyone out there trying to figure this out. I ended up encoding it to an int16array, then sent it across the socket, where the client encoded it back into a float32 array and passed it to the play_sound function. I basically just stole a bunch of stuff off stackoverflow and faked it until I made it, cause I'm not that smart :)
capturing mic and converting to int16array, then sending it across the socket
navigator.mediaDevices.getUserMedia({audio: {sampleSize: 16, channelCount: 2},video: true}) // request cam
.then(stream => {
vid.srcObject = stream; // don't use createObjectURL(MediaStream)
context = new AudioContext();
var source = context.createMediaStreamSource(stream);
var processor = context.createScriptProcessor(1024, 2, 2);
source.connect(processor);
processor.connect(context.destination);
processor.onaudioprocess = function(e) {
// Do something with the data, i.e Convert this to WAV
audiodata = new Int16Array(convertFloat32ToInt16(e.inputBuffer.getChannelData(0)));
console.log(audiodata);
socket.send(JSON.stringify({sound: audiodata, to: to, from: '$username', text:''}));
};
return vid.play(); // returns a Promise
});
relevant function for converting captured mic to int16array:
function convertFloat32ToInt16(buffer){
l = buffer.length;
buf = new Int16Array(l);
while (l--)
{
buf[l] = Math.min(1, buffer[l])*0x7FFF;
}
return buf.buffer;
}
receiving client side json object to int16array, then int16array back to float32array:
if(typeof (message.sound) != "undefined"){
//$('#video_stream_btn').trigger('click');
//var json_sound = message.sound;
if(typeof(context) == 'undefined'){
context = new AudioContext();
}
sound_array = [];
for (i in message.sound)
{
sound_array[i] = (message.sound [i]);
}
//sound_array16 = new Int16Array(sound_array);
sound_array32 = int16ToFloat32(sound_array);
play_sound(sound_array32, context);
return;
}
relevant receiving side int16array to float32array function:
function int16ToFloat32(inputArray) {
let int16arr = new Int16Array(inputArray)
var output = new Float32Array(int16arr.length);
for (var i = 0; i < int16arr.length; i++) {
var int = int16arr[i];
var float = (int >= 0x8000) ? -(0x10000 - int) / 0x8000 : int / 0x7FFF;
output[i] = float;
}
return output;
}
This code brings back an error of
Uncaught TypeError: Cannot read property 'innerHTML' of null
at doLike (<anonymous>:20:21)
at <anonymous>:35:1
doLike # VM1269:20
(anonymous) # VM1269:35
It has worked in the past, I got it from this website : https://blog.joeldare.com/simple-instagram-like-bot/
function getHeartElement() {
var knownHeartElementNames = ["coreSpriteHeartOpen", "coreSpriteLikeHeartOpen"];
var i = 0;
// Loop through the known heart elements until one works
for (i = 0; i < knownHeartElementNames.length; i++) {
var heartElement = document.querySelector("." + knownHeartElementNames[i]);
if (heartElement != undefined) {
break;
}
}
return heartElement;
}
function doLike() {
var likeMax = 100;
var likeElement = getHeartElement();
var nextElement = document.querySelector(".coreSpriteRightPaginationArrow");
likeCount++;
var nextTime = Math.random() * (14000 - 4000) + 4000;
if (likeElement.innerHTML.match("Unlike") == null) {
likeElement.click();
console.log(likeCount + " - liked");
} else {
console.log(likeCount + " - skipped");
}
setTimeout(function() {nextElement.click();}, 1000);
if (likeCount < likeMax) {
setTimeout(doLike, nextTime);
} else {
console.log("Nice! Time for a break.");
}
}
var likeCount = 0;
doLike();
You may want to use a tool such a Keygram - https://www.thekeygram.com
It works really well for me to gain followers
I have some hello world happening with leap motion using nodeJS
I want to know the pitch value for a hand, but is returning "undefined"
var Leap = require('leapjs');
var webSocket = require('ws'),
ws = new webSocket('ws://127.0.0.1:6437');
ws.on('message', function(data, flags) {
frame = JSON.parse(data);
var controller = Leap.loop(function(frame){
if(frame.hands.length == 1)
{
var hand = frame.hands[0];
var position = hand.palmPosition;
var velocity = hand.palmVelocity;
var direction = hand.direction;
var finger = hand.fingers[0];
var pitch = direction.pitch;
var type = hand.type;
if(type == "left"){
console.log("Left hand.");
console.log("pitch ::"+pitch);
}
if(type == "right") {
console.log("Right hand.")
}
}
});
if (frame.hands && frame.hands.length > 1) {
console.log("two");
}
if (frame.hands && frame.hands.length == 0) {
console.log("apaga too");
}
});
So, when i log my left hand i get
Left hand.
pitch ::undefined
I believe pitch is a method belonging to hand not direction, not an attribute belonging to direction docs. Have you tried hand.pitch() ?