how to play a video backwards with js? - web-audio-api

<video id="vid" controls>
<source src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/twitter_cat.mp4" type="video/mp4">
</video>
<script>
window.onload=()=>{
const vid= document.getElementById("vid");
vid.currentTime=4;
vid.playbackRate=-0.5;
vid.play();
}
</script>
I tried that but it didn't work, is there a way to play backward videos?

My ugly go at it, simply look out for seeked event after triggering it in initial canplay event, seeking backwards from that point by a few frames.
(function() {
var playbackSpeed = .05
var video = document.getElementById('vid')
video.addEventListener('canplay', function() {
if (!this.started) {
this.started = true
this.play()
this.currentTime = this.duration - .01
}
})
video.addEventListener('seeked', function() {
this.currentTime = this.currentTime <= 0.01 ?
this.duration - .01 : this.currentTime - playbackSpeed
})
}())
<video id="vid" controls>
<source
src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/222579/twitter_cat.mp4"
type="video/mp4"
/>
</video>

This is complicated, but most video formats and compression types are meant to be played forward, like this:
Raw footage is uploaded
A few Key frames are selected, that most of the video is based off of.
The compression software keeps track of the changes between the last frame and the current frame. It could be something like "This region changed from black to grey" or "this blob of blue pixels moved up"
The non-Key frames are discarded and only the changes are kept
As you can see, this would cause a massive problem when trying to play backward, because the changes that make up a frame are based on a frame that is based of changes of a frame that is based off changes....of a Key frame. Playing forward isn't an issue, because you don't have to go back to the last Key frame and keep track of the changes, you already have the last frame right in front of you. Although there are brute force ways of doing it backward (see here), these are normally slow and memory heavy. The best answer for you question is unfortunately, you can't.

Many video formats are Streaming Media formats that are designed to be played forward.
Playing it backwards would require decoding the whole stream, storing each raw frame on the disk to avoid clobbering memory, then rendering the frames backwards.
TMLVideoElement.prototype.playBackwards = function() {
this.pause();
var video = this;
var fps = 25;
var intervalRewind = setInterval(function() {
if(video.currentTime == 0){
clearInterval(intervalRewind);
video.pause();
} else {
video.currentTime += -(1/fps);
}
}, 1000 / fps);
};

Related

Web audio playback contains clicks

I am trying to build a midi player using web audio API. I used tonejs to parse midi file into JSON. I am using mp3 files to play notes. Following are the relevant parts of the code:
//create audio samples
static async setupSample(audioContext, filepath) {
const response = await fetch(filepath);
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
return audioBuffer;
}
//play a single sample
static playSample(audioContext, audioBuffer, time) {
const sampleSource = new AudioBufferSourceNode(audioContext, {
buffer: audioBuffer,
playbackRate: 1,
});
sampleSource.connect(audioContext.destination);
sampleSource.start(time);
return sampleSource;
}
Scheduling samples:
async start() {
this.startTime = this.audioCtx.currentTime;
this.play();
}
play() {
let nextNote = this.notes[this.noteIndex];
//schedule samples
while ((nextNote.time + this.startTime) - this.audioCtx.currentTime <= 0.250) {
let s = Audio.playSample(this.audioCtx, this.samples[nextNote.midi], this.startTime + nextNote.time);
s.stop(this.startTime + nextNote.time + nextNote.duration);
this.noteIndex++;
if (this.noteIndex == this.notes.length) {
break;
}
nextNote = this.notes[this.noteIndex];
}
if (this.noteIndex == this.notes.length) {
return;
}
requestAnimationFrame(() => {
this.play();
});
}
I am testing code with a midi file which contains C major scale. I have tested the midi file using timidity and it is fine.
The code does play the midi file correctly execpet a small problem: I hear some clicking sounds during playback. The clicking increases with increasing tempo but does not completely go away even with tempo as small as 50bpm. Any ideas what could be going wrong?
Full code can be viewed at : https://test.meedee.in/
Nothing is "wrong". You are observing a phenomenon intrinsic to the physics of audio.
Chopping audio samples arbitrarily like this creates clicks at the transitions. Any instantaneous change in level is heard as a click. To get rid of the clicks, apply an envelope to the sample, blend adjacent notes, or apply a low-pass filter.

Unity jagged mouse input detection

I wrote a script that writes Input.mousePosition into a file on every frame. The idea is that I want to identify which button on screen the player is trying to click before actually clicking, from the speed of the mouse basically. However, I ran into data like this:
(1113.0, 835.0, 0.0)
(1113.0, 835.0, 0.0)
(1113.0, 835.0, 0.0)
(1126.0, 835.0, 0.0)
Basically on one frame the x position is one value, a couple of frames later it's changed, but in the middle there is no gradation. While my mouse movement was continuous, if I'm to believe Unity, in the example above I hovered on 1 pixel for 3 frames then jumped 13 pixels to the right in one frame. Why is this? Is there any code to get the actual frame by frame position of the mouse?
EDIT:
Vector2 _lastPosition;
StreamWriter _mouseData;
// Start is called before the first frame update
void Start()
{
_mouseData = new StreamWriter(File.Open("sdata.txt", FileMode.Create));
}
// Update is called once per frame
void FixedUpdate()
{
_mouseData.WriteLine(Input.mousePosition.ToString());
if (Input.GetMouseButtonDown(0))
{
_mouseData.WriteLine("CLICK\n\n");
}
_lastPosition = Input.mousePosition;
}
void OnDestroy()
{
_mouseData.Close();
}
EDIT 2:
I changed the code to the following:
void FixedUpdate()
{
_mouseData.WriteLine(Vector2.SqrMagnitude(new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"))));
if (Input.GetMouseButtonDown(0))
{
_mouseData.WriteLine("CLICK\n\n");
}
}
Now I'm still getting output that's 50% 0-es and non-0 values are sprinkled in on every second row. Exceptions: a few rows where actual values are supposed to be still contain random 0-es. Now, I'm not super concerned about getting less frequent than 1/frame data, but there's no way to distinguish between these false 0-es and actual 0-es when the mouse is not moving, which is an issue.
I cannot find out from your question but I am guessing that you use the FixedUpdate() method which is unreliable in this situation. Update() is advised to use for calls that you want to execute once per frame.
EDIT:
Also, note that it is recommended that you set your application's framerate to a realistic number since it is unlimited by default (on desktop) and your app could be running with so many FPS that it is faster than how often you can sample your mouse input.
You can set the framerate using: Application.targetFrameRate = 60;
Aside from this problem it is generally a good idea to set your framerate to save yourself some headaches. (This is specifically true if you develop for mobile platforms and test on your desktop.)

using WebAudio AnalyserNode.getFloatFrequencyData() to shift pitch of a BufferSource

I have a BufferSource, which I create thusly:
const proxyUrl = location.origin == 'file://' ? 'https://cors-anywhere.herokuapp.com/' : '';
const request = new XMLHttpRequest();
request.open('GET', proxyUrl + 'http://heliosophiclabs.com/~mad/projects/mad-music/non.mp3', true);
// request.open('GET', 'non.mp3', true);
request.responseType = 'arraybuffer';
request.onload = () => {
audioCtx.decodeAudioData(request.response, buffer => {
buff = buffer;
}, err => {
console.error(err);
});
}
request.send();
Yes, the CORS workaround is pathetic, but this is the way I found to be able to work locally without needing to run a HTTP server. Anyway...
I would like to shift the pitch of this buffer. I've tried various different forms of this:
const source = audioCtx.createBufferSource();
source.buffer = buff;
const analyser = audioCtx.createAnalyser();
analyser.connect(audioCtx.destination);
analyser.minDecibels = -140;
analyser.maxDecibels = 0;
analyser.smoothingTimeConstant = 0.8;
analyser.fftSize = 2048;
const dataArray = new Float32Array(analyser.frequencyBinCount);
source.connect(analyser);
analyser.connect(audioCtx.destination);
source.start(0);
analyser.getFloatFrequencyData(dataArray);
console.log('dataArray', dataArray);
All to no avail. dataArray is always filled with -Infinity values, no matter what I try.
My idea is to get this frequency domain data and then to move all the frequencies up/down by some amount and create a new Oscillator node out of these, like this:
const wave = audioCtx.createPeriodicWave(real, waveCompnents);
oscillator.setPeriodicWave(wave);
Anyway. If anyone has a better idea of how to shift pitch, I'd love to hear it. Sadly, detune and playbackRate both seem to do basically the same thing (why are there two ways of doing the same thing?), namely just to speed up or slow down the playback, so that's not it.
First, there's a small issue with the code: you connect the analyser to the destination twice. You don't actually need to connect it at all.
Second, I think the reason you're getting all -infinity values is because you call getFloatFrequencyData right after you start the source. There's a good chance that no samples have been played so the analyser only has buffers of all zeros.
You need to call getFloatFrequencyData after a bit of time to see non-zero values.
Third, I don't think this will work at all, even for shifting the pitch of an oscillator. getFloatFrequencyData only returns the magnitude information. You will need the phase information for the harmonics to get everything shifted correctly. Currently there's no way to get the phase information.
Fourth, if you have an AudioBuffer with the data you need, consider using the playbackRate to change the pitch. Not sure if this will produce the shift you want.

Webaudio :: Play Recorded Audio

I want to play the recorded audio using microphone.
After recording it as 32 bit arrays
let left = e.inputBuffer.getChannelData(0);
let tempLeftChannel = this.state.leftChannel;
tempLeftChannel.push(new Float32Array(left));
this.setState({ leftChannel: tempLeftChannel });
Now In the leftChannel array, I had chunk of audio data. Now, I want to play them in the browser. How can I do that?
You leave quite a bit out from your snippet, but perhaps the following will give you an idea of one way to play out the float array that you have. Let context be the AudioContext that you probably have.
let buffer = new AudioBuffer({length: leftChannel.length,
sampleRate: context.sampleRate});
buffer.copyToChannel(leftChannel, 0);
let source = new AudioBufferSourceNode(context, {buffer: buffer});
source.connect(context.destination);
source.start();

easeljs create painting tools updateCache('destination-out')

I am working on a painting tools with eraser.
I don't know why when i click eraser and start to clear up.
The whole stage will become lighter.After i set updateCache('destination-out').
wrapper.addEventListener("pressup", function(event) {
console.log("up");
dragging = false;
// real_wrapper.updateCache();
if (erase == true) {
drawStroke(real_draw);
real_wrapper.updateCache('destination-out');
} else {
drawStroke(real_draw);
real_wrapper.updateCache();
tmp_draw.graphics.clear();
wrapper.updateCache();
}
ppts = [];
});
My Code: https://jsfiddle.net/steven_wong/mnfupy5o/6/
Sorry for my bad english.
You need to clear your graphics between draws. Right now your code is redrawing all of the old strokes each time, so when you're using the pencil, all the old shapes are made darker, when you're using the eraser, they become lighter.
graphics.clear();
Here's a fixed version:
https://jsfiddle.net/mnfupy5o/7/