Audio onended not functioning - javascript

The audio plays when the user interacts with the window however when the audio ends it doesnt log 'audio ended'
let audio = new Audio();
window.onclick = () => {
if (audio.paused) pickMusic().play();
};
audio.onended = () => {
console.log("audio ended");
};
function pickMusic() {
switch (Math.floor(Math.random() * 1)) {
case 0:
return (audio = new Audio("/music/gorp.mp3"));
break;
case 1:
return (audio = new Audio("/music/gorp.mp3"));
break;
}
}
<p>Click anywhere!</p>

When you call new Audio(), it will override the previous audio which is already attached with onended event.
In this case, whenever you call new Audio(), you also need to initialize onended event once again
let audio = new Audio()
window.onclick = () => {
if (audio.paused) pickMusic().play();
};
function pickMusic() {
switch (Math.floor(Math.random() * 1)) {
case 0:
audio = new Audio("http://commondatastorage.googleapis.com/codeskulptor-assets/jump.ogg")
audio.onended = () => {
console.log("audio ended");
};
return audio;
case 1:
audio = new Audio("http://commondatastorage.googleapis.com/codeskulptor-assets/week7-brrring.m4a")
audio.onended = () => {
console.log("audio ended");
};
return audio;
}
}
<p>Click anywhere!</p>

Related

How can I differentiate the sound level of an audio track in a mediastream in JavaScript? [duplicate]

I'm using the Google Cloud API for Speech-to-text, with a NodeJS back-end.
The app needs to be able to listen for voice commands, and transmit them to the back-end as a buffer. For this, I need to send the buffer of the preceding audio when silence is detected.
Any help would be appreciated. Including the js code below
if (!navigator.getUserMedia)
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
if (navigator.getUserMedia) {
navigator.getUserMedia({audio: true}, success, function (e) {
alert('Error capturing audio.');
});
} else alert('getUserMedia not supported in this browser.');
var recording = false;
window.startRecording = function () {
recording = true;
};
window.stopRecording = function () {
recording = false;
// window.Stream.end();
};
function success(e) {
audioContext = window.AudioContext || window.webkitAudioContext;
context = new audioContext();
// the sample rate is in context.sampleRate
audioInput = context.createMediaStreamSource(e);
var bufferSize = 4096;
recorder = context.createScriptProcessor(bufferSize, 1, 1);
recorder.onaudioprocess = function (e) {
if (!recording) return;
console.log('recording');
var left = e.inputBuffer.getChannelData(0);
console.log(convertoFloat32ToInt16(left));
};
audioInput.connect(recorder);
recorder.connect(context.destination);
}
I'm not too sure as to what exactly is being asked in the question, so this answer is only intended to give a way to detect silences in an AudioStream.
To detect silence in an AudioStream, you can use an AudioAnalyser node, on which you will call the getByteFrequencyData method at regular intervals, and check whether there were sounds higher than than your expected level for a given time.
You can set the threshold level directly with the minDecibels property of the AnalyserNode.
function detectSilence(
stream,
onSoundEnd = _=>{},
onSoundStart = _=>{},
silence_delay = 500,
min_decibels = -80
) {
const ctx = new AudioContext();
const analyser = ctx.createAnalyser();
const streamNode = ctx.createMediaStreamSource(stream);
streamNode.connect(analyser);
analyser.minDecibels = min_decibels;
const data = new Uint8Array(analyser.frequencyBinCount); // will hold our data
let silence_start = performance.now();
let triggered = false; // trigger only once per silence event
function loop(time) {
requestAnimationFrame(loop); // we'll loop every 60th of a second to check
analyser.getByteFrequencyData(data); // get current data
if (data.some(v => v)) { // if there is data above the given db limit
if(triggered){
triggered = false;
onSoundStart();
}
silence_start = time; // set it to now
}
if (!triggered && time - silence_start > silence_delay) {
onSoundEnd();
triggered = true;
}
}
loop();
}
function onSilence() {
console.log('silence');
}
function onSpeak() {
console.log('speaking');
}
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(stream => {
detectSilence(stream, onSilence, onSpeak);
// do something else with the stream
})
.catch(console.error);
And as a fiddle since stackSnippets may block gUM.
You can use SpeechRecognition result event to determine when a word or phrase has been recognized, for example, ls, cd, pwd or other commands, pass the .transcript of SpeechRecognitionAlternative to speechSynthesis.speak() where at attached start and end event of SpeechSynthesisUtterance call .start() or .resume() on MediaRecorder object where MediaStream is passed; convert the Blob at dataavailable event to an ArrayBuffer using FileReader or Response.arrayBuffer().
We could alternatively use audiostart or soundstart with audioend or soundend events of SpeechRecognition to record the users' actual voice, though the ends may not be fired consistently in relation to the actual start and end of audio captured by only a standard system microphone.
<!DOCTYPE html>
<html>
<head>
<title>Speech Recognition Recording</title>
</head>
<body>
<input type="button" value="Stop speech command recognition" id="stop">
<script>
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(stream => {
const recorder = new MediaRecorder(stream);
const recognition = new webkitSpeechRecognition();
const synthesis = new SpeechSynthesisUtterance();
const handleResult = e => {
recognition.onresult = null;
console.log(e.results);
const result = e.results[e.results.length - 1];
if (result.isFinal) {
const [{transcript}] = result;
console.log(transcript);
synthesis.text = transcript;
window.speechSynthesis.speak(synthesis);
}
}
synthesis.onstart = () => {
if (recorder.state === "inactive") {
recorder.start()
} else {
if (recorder.state === "paused") {
recorder.resume();
}
}
}
synthesis.onend = () => {
recorder.pause();
recorder.requestData();
}
recorder.ondataavailable = async(e) => {
if (stream.active) {
try {
const blobURL = URL.createObjectURL(e.data);
const request = await fetch(blobURL);
const ab = await request.arrayBuffer();
console.log(blobURL, ab);
recognition.onresult = handleResult;
// URL.revokeObjectURL(blobURL);
} catch (err) {
throw err
}
}
}
recorder.onpause = e => {
console.log("recorder " + recorder.state);
}
recognition.continuous = true;
recognition.interimResults = false;
recognition.maxAlternatives = 1;
recognition.start();
recognition.onend = e => {
console.log("recognition ended, stream.active", stream.active);
if (stream.active) {
console.log(e);
// the service disconnects after a period of time
recognition.start();
}
}
recognition.onresult = handleResult;
stream.oninactive = () => {
console.log("stream ended");
}
document.getElementById("stop")
.onclick = () => {
console.log("stream.active:", stream.active);
if (stream && stream.active && recognition) {
recognition.abort();
recorder.stop();
for (let track of stream.getTracks()) {
track.stop();
}
console.log("stream.active:", stream.active);
}
}
})
.catch(err => {
console.error(err)
});
</script>
</body>
</html>
plnkr https://plnkr.co/edit/4DVEg6mhFRR94M5gdaIp?p=preview
The simplest approach would be to use .pause() and .resume(), .stop() methods of MediaRecorder() to allow user to start, pause, and stop recording audio captured utilizing navigator.mediaDevices.getUserMedia() and convert the resulting Blob to an ArrayBuffer, if that is what the api is expecting to be POSTed to server
<!DOCTYPE html>
<html>
<head>
<title>User Media Recording</title>
</head>
<body>
<input type="button" value="Start/resume recording audio" id="start">
<input type="button" value="Pause recording audio" id="pause">
<input type="button" value="Stop recording audio" id="stop">
<script>
navigator.mediaDevices.getUserMedia({
audio: true
})
.then(stream => {
const recorder = new MediaRecorder(stream);
recorder.ondataavailable = async(e) => {
if (stream.active) {
try {
const blobURL = URL.createObjectURL(e.data);
const request = await fetch(blobURL);
const ab = await request.arrayBuffer();
// do stuff with `ArrayBuffer` of recorded audio
console.log(blobURL, ab);
// we do not need the `Blob URL`, we can revoke the object
// URL.revokeObjectURL(blobURL);
} catch (err) {
throw err
}
}
}
recorder.onpause = e => {
console.log("recorder " + recorder.state);
recorder.requestData();
}
stream.oninactive = () => {
console.log("stream ended");
}
document.getElementById("start")
.onclick = () => {
if (recorder.state === "inactive") {
recorder.start();
} else {
recorder.resume();
}
console.log("recorder.state:", recorder.state);
}
document.getElementById("pause")
.onclick = () => {
if (recorder.state === "recording") {
recorder.pause();
}
console.log("recorder.state:", recorder.state);
}
document.getElementById("stop")
.onclick = () => {
if (recorder.state === "recording" || recorder.state === "paused") {
recorder.stop();
}
for (let track of stream.getTracks()) {
track.stop();
}
document.getElementById("start").onclick = null;
document.getElementById("pause").onclick = null;
console.log("recorder.state:", recorder.state
, "stream.active", stream.active);
}
})
.catch(err => {
console.error(err)
});
</script>
</body>
</html>
plnkr https://plnkr.co/edit/7caWYMsvub90G6pwDdQp?p=preview

Audio having a weird behaviour while being played

I have a problem with playing audio when I press or click a button.
Drum Machine
It seems like my audio has a delay but I put a audio.currentTime = 0, so I don't know what's going on.
Here is my JS:
const $ = document.querySelectorAll.bind(document);
const sounds = [
{id:'Bass Drum', letter:'Q', src:'https://dight310.byu.edu/media/audio/FreeLoops.com/3/3/Free%20Kick%20Sample%2011-909-Free-Loops.com.mp3'}
];
let volumeVal = 0.5;
$('input[type=range]')[0].addEventListener('input', function(e) {
volumeVal = e.target.value / 100;
});
function main(url, name) {
const audio = new Audio(url);
audio.currentTime = 0;
audio.preload = "auto";
audio.volume = parseFloat(volumeVal);
audio.play();
$('.name')[0].textContent = name;
}
window.onkeydown = function(e) {
function seter(id) {
setTimeout(() => {
$(`.${id}-but`)[0].classList.add('pressed');
setTimeout(() => {
$(`.${id}-but`)[0].classList.remove('pressed');
}, 140);
}, 0);
}
switch(e.key) {
case 'q':
main(sounds[0].src, sounds[0].id);
seter('q');
break;
}
}
$('.q-but')[0].addEventListener('click', () => {
main(sounds[0].src, sounds[0].id);
});
First off, big thanks to #Seblor for providing me with the solution.
I solved my problem by creating audio node for each sound:
<audio id="bass" preload="auto" src="https://dight310.byu.edu/media/audio/FreeLoops.com/3/3/Free%20Kick%20Sample%2011-909-Free-Loops.com.mp3"></audio>
And inside my JS file I added this event listener for every button:
$('.q-but')[0].addEventListener('click', () => {
const audio = $("#bass")[0];
audio.currentTime = 0;
audio.play();
});
Of course, now I could make my code less repetitive because I am doing this for all 9 buttons. And eventually I will.

How can I write this audio playing code nicer?

I want you to play it as many times as I click on it.
(1 sec long audio only.)
function a() {
var elem = document.getElementById('musicplay');
var audio = document.getElementById('music');
var playing = false;
elem.addEventListener('click', function () {
if (playing) {
audio.play();
} else {
audio.play();
}
playing = !playing;
});
}
function playMusic() {
let playBtn= document.getElementById('musicplay');
let audio = document.getElementById('music');
let playing = false;
playBtn.addEventListener('click', ()=> {
if (playing) {
audio.pause();
playing = !playing;
} else {
audio.play();
playing = !playing;
}
});
}

Audio Record Problem in JavaScript some of the recording is missing

Hello Everyone I am trying to Record Audio and then after Recording I play it and when I hit the Cut function it just get the second I am on it in the player and cut to the end and start recording again and then overwrite the chunks array till the end with the new recording to sum up all of that all I need is when I record 5 seconds and then I want to overwrite the last 3 seconds so I remove the last 3 elements in the array and push the new chunks to the original array
when I do that and send the new chunk to the audio player it plays the first time as intended and if I pressed play again it just plays the newly recorded part only without the first 2 seconds that I preserved from the old recording
let audioChunks = [];
let Recorder = null;
const recordAudio = () => {
return new Promise(resolve => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => {
mediaRecorder.start(1000);
};
const stop = () => {
return new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks,{type: 'audio/mpeg-3'});
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
document.getElementById("AudioRecodingPlayer").src = audio.src;
document.getElementById("AudioRecodingPlayer").currentTime = 0;
const play = () => {
audio.play();
};
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
};
resolve({ start, stop });
});
});
};
async function StartRecording(){
Recorder = await recordAudio();
Recorder.start();
}
async function StopRecording(){
const audio = Recorder.stop();
}
function Cut(){
var Audio = document.getElementById("AudioRecodingPlayer");
var CurrenTime = Math.round(Audio.currentTime);
audioChunks.length = CurrenTime;
StartRecording();
}
On the Cut() function, you are not actually removing the elements of the array, nor changing the index or window. You are only changing the value of length.
What you would have to do is overwrite the audio chunks, or remove the chunks from that point forward.
let audioChunks = [];
let Recorder = null;
const recordAudio = () => {
return new Promise(resolve => {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
const mediaRecorder = new MediaRecorder(stream);
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
const start = () => {
mediaRecorder.start(1000);
};
const stop = () => {
return new Promise(resolve => {
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks,{type: 'audio/mpeg-3'});
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
document.getElementById("AudioRecodingPlayer").src = audio.src;
document.getElementById("AudioRecodingPlayer").currentTime = 0;
const play = () => {
audio.play();
};
resolve({ audioBlob, audioUrl, play });
});
mediaRecorder.stop();
});
};
resolve({ start, stop });
});
});
};
async function StartRecording(){
Recorder = await recordAudio();
Recorder.start();
}
async function StopRecording(){
const audio = Recorder.stop();
}
function Cut(){
var Audio = document.getElementById("AudioRecodingPlayer");
var CurrenTime = Math.round(Audio.currentTime);
audioChunks.splice(CurrentTime); // this will actually remove the elements after the "CurrentTime" index
StartRecording();
}

Only change from Play to Pause if the audio link is working

If the value is an incorrect stream link, the 'Play' button still changes to 'Pause.'
This is what I was trying to prevent from happening.
Pressing 'Set' should not cause the Play button to change from 'Play' to 'Pause' if the audio is not working.
Code:
https://jsfiddle.net/vhgL96se/124/
Image
(function iife() {
"use strict";
const player = document.getElementById("player");
const button = document.getElementById("button");
const value = document.getElementById("input");
const sent = document.getElementById("sent");
const input = document.getElementById("clear");
let canPlay = false;
function playPauseIcon(play) {
if (!canPlay) {
return;
}
if (play) {
button.classList.add("is-playing");
} else {
button.classList.remove("is-playing");
}
}
button.addEventListener("click", function () {
if (!canPlay) {
return;
}
if (player.paused) {
player.play();
playPauseIcon(true);
} else {
player.pause();
playPauseIcon(false);
}
});
button.addEventListener("mousedown", function (evt) {
if (evt.detail > 1) {
evt.preventDefault();
}
}, false);
sent.addEventListener("click", function () {
player.src = value.value;
player.volume = 1.0;
playPauseIcon(true);
});
input.addEventListener("click", function () {
value.value = "";
button.classList.remove("is-playing");
player.pause();
canPlay = false;
}, false);
player.onloadstart = function () {
if (value.value !== "") {
canPlay = true;
playPauseIcon(true);
}
};
}());
It should be changed to oncanplay instead, and then it works as it should.
Play button changes from Play to Pause
http://hi5.1980s.fm/;
Play button does not change from Play to Pause
when the stream is not valid.
h://hi5.1980s.fm/;
https://jsfiddle.net/vhgL96se/132/
player.oncanplay = function () {
if (value.value !== "") {
canPlay = true;
playPauseIcon(true);
}
};

Categories