How do I sync lottie animation with TTS function (Xam.Plugins.TextToSpeech) - xamarin

I am creating an app with Text-to-Speech functionality.
The TTS function works 100% fine, but when I add audio wave animation using lottie the tts function and animation does not process well.
When a user clicks the button, the text inside my editor will be converted to speech and give a speech output, when tts process starts, animation of the audio wave also starts, and when the application ends, the animation should end as well.
this is my TTS and lottie code:
Indicator.Text = "Start";
if (Indicator.Text == "Start")
{
animationView.Loop = true;
animationView.AutoPlay = true;
animationView.PlayFrameSegment(0, 60);
animationView.Play();
var Text = TTSEditor.Text;
await CrossTextToSpeech.Current.Speak(Text, speakRate: (float)0.9, pitch: (float)1.1f);
Indicator.Text = "End";
}
if (Indicator.Text == "End")
{
//animationView.PlayFrameSegment(0, 0);
animationView.AbortAnimation(animationView.ToString());
animationView.Loop = false;
animationView.AutoPlay = false;
}
The issue here is when my button is clicked the animation plays 1-3 times and then TTS function will follow.

Related

How to immediately stop a lottie animation if button is clicked in xamarin

I have this click event for my button:
private void BtnRecord_Clicked(object sender, EventArgs e)
{
if (BtnRecord.BackgroundColor == Color.Coral)
{
blue_record_lottie.Loop = true;
blue_record_lottie.AutoPlay = true;
blue_record_lottie.Play();
BtnRecord.BackgroundColor = Color.Red;
}
else if (BtnRecord.BackgroundColor == Color.Red)
{
BtnRecord.BackgroundColor = Color.Coral;
blue_record_lottie.AbortAnimation(blue_record_lottie.ToString());
blue_record_lottie.Loop = false;
blue_record_lottie.AutoPlay = false;
}
}
It just stops the animation of the button if the button color is red and when the animation is done playing. The animation will have to finish from the start of the animation to the end, and then it stops. I want it to immediately stop when I click the button.
Thanks in advance
If you want to make your Animation go back immediately to the very first state of the animation, you can do this:
Animation.PlayFrameSegment(0, 0);
This will play the very first Frame of your animation, after that, you can restart the animation like you would normally do.
Since Xamarin Lottie 4.0, the PlayFrameSegment method does not exist anymore. Instead you will have to use either:
Animation.PlayMinAndMaxFrame(0.0f, 0.0f);
or
Animation.PlayMinAndMaxProgress(0.0f, 0.0f);
Animation.playAnimation; to start
Animation.pauseAnimation; to stop

Is it possible to feed MediaStream frames at lower framerate?

I would like to use MediaStream.captureStream() method, but it is either rendered useless due to specification and bugs or I am using it totally wrong.
I know that captureStream gets maximal framerate as the parameter, not constant and it does not even guarantee that, but it is possible to change MediaStream currentTime (currently in Chrome, in Firefox it has no effect but in return there is requestFrame, not available at Chrome), but the idea of manual frame requests or setting the placement of the frame in the MediaStream should override this effect. It doesn't.
In Firefox it smoothly renders the video, frame by frame, but the video result is as long as wall clock time used for processing.
In Chrome there are some dubious black frames or reordered ones (currently I do not care about it until the FPS matches), and the manual setting of currentTime gives nothing, the same result as in FF.
I use modified code from MediaStream Capture Canvas and Audio Simultaneously answer.
const FPS = 30;
var cStream, vid, recorder, chunks = [], go = true,
Q = 61, rec = document.getElementById('rec'),
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
ctx.strokeStyle = 'rgb(255, 0, 0)';
function clickHandler() {
this.textContent = 'stop recording';
//it has no effect no matter if it is empty or set to 30
cStream = canvas.captureStream(FPS);
recorder = new MediaRecorder(cStream);
recorder.ondataavailable = saveChunks;
recorder.onstop = exportStream;
this.onclick = stopRecording;
recorder.start();
draw();
}
function exportStream(e) {
if (chunks.length) {
var blob = new Blob(chunks)
var vidURL = URL.createObjectURL(blob);
var vid2 = document.createElement('video');
vid2.controls = true;
vid2.src = vidURL;
vid2.onend = function() {
URL.revokeObjectURL(vidURL);
}
document.body.insertBefore(vid2, vid);
} else {
document.body.insertBefore(document.createTextNode('no data saved'), canvas);
}
}
function saveChunks(e) {
e.data.size && chunks.push(e.data);
}
function stopRecording() {
go = false;
this.parentNode.removeChild(this);
recorder.stop();
}
var loadVideo = function() {
vid = document.createElement('video');
document.body.insertBefore(vid, canvas);
vid.oncanplay = function() {
rec.onclick = clickHandler;
rec.disabled = false;
canvas.width = vid.videoWidth;
canvas.height = vid.videoHeight;
vid.oncanplay = null;
ctx.drawImage(vid, 0, 0);
}
vid.onseeked = function() {
ctx.drawImage(vid, 0, 0);
/*
Here I want to include additional drawing per each frame,
for sure taking more than 180ms
*/
if(cStream && cStream.requestFrame) cStream.requestFrame();
draw();
}
vid.crossOrigin = 'anonymous';
vid.src = 'https://dl.dropboxusercontent.com/s/bch2j17v6ny4ako/movie720p.mp4';
vid.currentTime = 0;
}
function draw() {
if(go && cStream) {
++Q;
cStream.currentTime = Q / FPS;
vid.currentTime = Q / FPS;
}
};
loadVideo();
<button id="rec" disabled>record</button><br>
<canvas id="canvas" width="500" height="500"></canvas>
Is there a way to make it operational?
The goal is to load video, process every frame (which is time consuming in my case) and return the processed one.
Footnote: I do not want to use ffmpeg.js, external server or other technologies. I can process it by classic ffmpeg without using JavaScript at all, but this is not the point of this question, it is more about MediaStream usability / maturity. The context is Firefox/Chrome here, but it may be node.js or nw.js as well. If this is possible at all or awaiting bug fixes, the next question would be feeding audio to it, but I think it would be good as separate question.

Tracking frames and/or time during an animation

Can I call a function(one that will make another object visible/invisible) on a specific animation frame or time? I would like to have arrows describe the movement of the animation at certain times during the animation. While I can just make them visible when I start the animation and make them invisible when the animation stops, I would like to specify ranges inside the animation to do this
playPatientAnim: function (anim, callback) {
var pending = 1;
var me = this;
var finish = callback ? function () {
if (pending && !--pending) {
callback.call(me, anim);
}
} : null;
me.currentPatient.skinned.forEach(function (mesh) {
mesh.animations.forEach(function(anim){
anim.stop();
});
});
me.currentPatient.skinned.forEach(function (mesh) {
var animation = mesh.animations[anim];
animation.stop();
if (animation) {
pending++;
animation.onComplete = finish;
animation.play();
}
});
if (finish) {
finish();
}
}
You can make a mesh visible or invisible ( mesh.visible = false; //or true ). To change visibility at certain time you could use timestamp:
new Date().getTime() and calculate how you want to do the sequence of your animation.

Switch device camera in Unity 3d

I am using WebCamTexture class to open camera from device. I want to switch camera from front to back and vice versa in between the game play. Can anybody help?
WebCamDevice[] devices = WebCamTexture.devices;
WebCamTexture webCamTexture = new WebCamTexture(devices[1].name);
renderer.material.mainTexture = webCamTexture;
webCamTexture.filterMode = FilterMode.Trilinear;
webCamTexture.Play();
This opens my front camera. But I can't switch the camera.
I got the solution. You just need to set the name of the device. By default back camera is "Camera 0" and "Camera 1" front camera. First Stop your camera , change the set device.
if (devices.Length > 1) {
webCamTexture.Stop();
if (frontCamera == true)
{
webCamTexture.deviceName = devices[0].name;
frontCamera = false;
}
else
{
webCamTexture.deviceName = devices[1].name;
frontCamera = true;
}
webCamTexture.Play ();
}
I have used the following code for switching the front and back camera of the iPhone Device using C# script
WebCamDevice[] devices;
public WebCamTexture mCamera = null;
public GameObject plane; // this is the object where i am going to show the camera
// Use this for initialization
void Start ()
{
devices = WebCamTexture.devices;
plane = GameObject.FindWithTag ("Player");
mCamera = new WebCamTexture (Screen.width,Screen.height);
mCamera.deviceName = devices[0].name; // Front camera is at Index 1 and Back Camera is at Index 0
plane.renderer.material.mainTexture = mCamera;
mCamera.Play ();
}
the above code will show the back Camera when the Scene is loaded ,for accessing the front camera on the click of front button defined below
if (GUI.Button (new Rect (100, 1000, 120, 40), "Front"))
{
if(devices.Length>1)
{
mCamera.Stop();
mCamera.deviceName = (mCamera.deviceName == devices[0].name) ? devices[1].name : devices[0].name;
mCamera.Play();
}
}
This is button, by clicking it will redirect to front camera and again clicking it will redirect to back camera alternatively
if (devices.Length > 1) {
webCamTexture.Stop();
if (frontCamera == true)
{
webCamTexture.deviceName = devices[0].name;
frontCamera = WebCamTexture.devices[0].isFrontFacing;
}
else
{
webCamTexture.deviceName = devices[1].name;
frontCamera = WebCamTexture.devices[1].isFrontFacing;
}
webCamTexture.Play ();
}
We've used Camera Capture Kit for a social image sharing game/app to do a similar functionality to what you are describing, The other solution presented here is not 100% solid, since there might potentially be devices with different order of front and back-facing devices.

Initialize youtube api to target ajax loaded iframe

it's my first time to work with Youtube API and after I got most things done, I'm having one last issue with it which i can't seem to solve, so I would appreciate if you could take a few minutes to help.
Here it goes in simple words:
I have a video slider (carousel). Under the main video, are thumbnails of other related videos (this is the site: kushtube.com)
when I click on a related video thumbnail, the main player content is loaded via AJAX
What I wanted to ultimately achieve is that when the current video ends,the next vicdo starts playing automatically.
Now after I did some work, I managed to make it work like that, BUT
it only work on page load...
If i click on one of the related video thumbnails, the content of the player loads via AJAX and I cant seem to be able to re-initialize Youtube API to target the new ajax-loaded player.
This is my current code:
<script>
// global variable for the player
var player;
function ytInit(){
// create the global player from the specific iframe (#video)
player = new YT.Player('test', {
events: {
// call this function when player is ready to use
'onReady': onPlayerReady,
'onStateChange': onPlayerStatusChange
}
});
}
// this function gets called when API is ready to use
function onYouTubeIframeAPIReady() {
ytInit();
}
jQuery(document).ready( function($){
jQuery('ul.carousel-list li').click( function(){
ytInit();
jQuery('ul.carousel-list li.active').removeClass('active');
});
});
function onPlayerStatusChange(event) {
/* video status
----------------
-1 (unstarted)
0 (ended)
1 (playing)
2 (paused)
3 (buffering)
5 (video cued)
*/
console.log( event );
if( event.data == 0 ){
var thisLI = jQuery('ul.carousel-list li.active'); //get active slider item
var thisLINext = jQuery('ul.carousel-list li.active').next(); //get next slider item
var nextVideoCode = thisLINext.attr('video_code'); //get next video code
var nextVideoTitle = thisLINext.attr('video_title'); //get next video code
var nextVideoUrl = thisLINext.attr('video_url'); //get next video code
thisLINext.addClass('active'); //activate next slider item
thisLI.removeClass('active'); //deactivate current cslider item
//load next video
player.loadVideoById(nextVideoCode);
//update video title in header
jQuery('.entry-header h1.entry-title a').attr('href', decodeURIComponent(nextVideoUrl)).text(decodeURIComponent(nextVideoTitle));
}
}
function onPlayerReady(event) {
// bind events
var playButton = document.getElementById("play-button");
playButton.addEventListener("click", function() {
player.playVideo();
});
var pauseButton = document.getElementById("pause-button");
pauseButton.addEventListener("click", function() {
player.pauseVideo();
});
}
// Inject YouTube API script
var tag = document.createElement('script');
tag.src = "//www.youtube.com/player_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
</script>
Do you have any helpful suggestions? I would really appreciate.
Calling ytInit() when you click the <li> creates a new player object. I think that's not quite what you want.
Have you tried:
jQuery('ul.carousel-list li').click( function(){
// ytInit();
// just load the video from the "video_code" attr
player.loadVideoById(this.getAttribute('video_code'));
jQuery('ul.carousel-list li.active').removeClass('active');
});
If that doesn't autoplay, then you may have to wait for the YT.PlayerState.CUED (i.e. 5) event in your player status change event listener to start the player.

Resources