Angular Play Button not update on audio complete - javascript

Strange issue
<button ng-show="scene.audio" class="button icon {{scene.audioIcon}}"
ng-click="playAudio(scene)"/>
$scope.playAudio = function ($scene){
if($scene.audioIcon == "ion-ios-play-outline") {
$scene.audioIcon = "ion-ios-pause-outline";
media = new Media($scene.audio.src,function(){
**$scene.audioIcon = "ion-ios-play-outline";**
media.stop();
media.release();
},null);
media.scene = $scene;
media.play();
}
else if(media){
media.stop();
media.release();
$scene.audioIcon = "ion-ios-play-outline";
}
I can update the $scene.audioIcon on the 2 click functions, which updates the button in the UI. However in the onComplete function of new Media, this function is called when the audio is finished, and the $scene audio icon changes, however it doesn't get updated in the UI.
I assume because it comes later?
Is there a way I can trigger an update of the button?

angularjs doesn't know that it should check for changes because the completion event is called from native code. You should wrap your code in $scope.$apply().
Something like that:
media = new Media($scene.audio.src,function() {
$scope.$apply(function() {
$scene.audioIcon = "ion-ios-play-outline";
media.stop();
media.release();
});
},null);

Related

Microsoft Azure Text to Speech - Stop Recording On Button Press - StopRecognitionOnceAsync() Not Working?

Apologies in advance for any terminology mistakes, I'm a student and trying my hardest to be as clear as possible! and thanks in advance for any help!
I'm trying to use Azure Speech-To-Text services. I'd like the user to be able to press a start and stop button to record themselves and print out the transcription. My app will eventually be a React Frontend and Rails backend, but right now I am just trying to understand and work through the demo.
I'm confused by the documentation but was able to get things half working. However, right now it just continuously listens to the speaker and never stops.
I want to use stopContinuousRecognitionAsync() or recognizer.close() once a button is pressed, but I cannot seem to get it working. The farthest I've gotten is the result is logged only once the stop button is pressed, but it continues to listen and print out results. I've also tried using recognizer.close() -> recognizer = undefined but to no avail. I am guessing that due to the asynchronous behavior, it closes out the recognizer before logging a result.
The latest code I've tried is below. This result starts listening on start click and prints speech on stop, but continues to listen and log results.
// subscription key and region for speech services.
var subscriptionKey, serviceRegion;
var authorizationToken;
var SpeechSDK;
var recognizer;
document.addEventListener("DOMContentLoaded", function () {
startRecognizeOnceAsyncButton = document.getElementById("startRecognizeOnceAsyncButton");
subscriptionKey = document.getElementById("subscriptionKey");
serviceRegion = document.getElementById("serviceRegion");
phraseDiv = document.getElementById("phraseDiv");
startRecognizeOnceAsyncButton.addEventListener("click", function () {
startRecognizeOnceAsyncButton.disabled = true;
phraseDiv.innerHTML = "";
// if we got an authorization token, use the token. Otherwise use the provided subscription key
var speechConfig;
if (authorizationToken) {
speechConfig = SpeechSDK.SpeechConfig.fromAuthorizationToken(authorizationToken, serviceRegion.value);
} else {
speechConfig = SpeechSDK.SpeechConfig.fromSubscription(“API_KEY”, serviceRegion.value);
}
speechConfig.speechRecognitionLanguage = "en-US";
var audioConfig = SpeechSDK.AudioConfig.fromDefaultMicrophoneInput();
recognizer = new SpeechSDK.SpeechRecognizer(speechConfig, audioConfig);
recognizer.startContinuousRecognitionAsync(function () {}, function (err) {
console.trace("err - " + err);});
stopButton = document.querySelector(".stopButton")
stopButton.addEventListener("click", () =>{
console.log("clicked")
recognizer.recognized = function(s,e) {
console.log("recognized text", e.result.text)
}
})
});
Assuming the recognizer is conjured correctly outside of the code, there's a few things to change to get the result you want.
The events should be hooked to the recognizer before calling startContinuousRecognition().
In the stop button handler, call stop. I'd also hook the stop event outside of the start button click handler.
Quick typed changes, didn't compile. :-)
startRecognizeOnceAsyncButton.addEventListener("click", function () {
startRecognizeOnceAsyncButton.disabled = true;
//div where text is being shown
phraseDiv.innerHTML = "";
// The event recognized signals that a final recognition result is received.
recognizer.recognized = function(s,e) {
console.log("recognized text", e.result.text)
}
//start listening to speaker
recognizer.startContinuousRecognitionAsync(function () {}, function (err) {
console.trace("err - " + err);});
});
stopButton = document.querySelector(".stopButton")
stopButton.addEventListener("click", () =>{
console.log("clicked");
recognizer.stopContinuousRecongition();
};

Javascript Audio in Angular 2

I've been trying to play an audio linked to a toast notification . I'm currently using toastr.js .
toastr.warning(eventData.type);
self.eventAudio[eventData.id] = {};
self.eventAudio[eventData.id].audio = new Audio();
self.eventAudio[eventData.id].audio.src = './images/someaudio.mp3';
self.eventAudio[eventData.id].audio.loop = true;
self.eventAudio[eventData.id].audio.play();
eventData has a unique Id in id key : eventData.id.
what i wanted was for a particular toastr notification , audio is played and for toastr.options.onHidden, i've bound a function with the contents :
var self = this;
toastr.options.onHidden = function() {
self.eventAudio[eventData.id].audio.pause();
}
toastr.options.onclose = function() {
self.eventAudio[eventData.id].audio.pause();
}
toastr.options.onCloseClick = function() {
self.eventAudio[eventData.id].audio.pause();
}
The issue i'm facing is that , the audio is always in loop and never paused , not even after the toast notification is hidden .This issue is when the toast notification has more than one instance ,like i have 2 or more notifications.
What am i doing wrong ?
Please help me out
Thanks in advance
This is difficult because the toastr.js library doesn't pass any identifiable event information to the onHidden/onClose/onCloseClick callbacks. If you look at the source code, it's calling the same options.onHidden() function for all toast events.
if (options.onHidden && response.state !== 'hidden') {
options.onHidden();
}
https://github.com/CodeSeven/toastr/blob/master/toastr.js
Right now what you're doing is overwriting the callback function for every new event. (If your last event.id is abc, every time any toast is closed it will try to pause the audio associated with abc.)
The easiest (and probably most user friendly) way to do this is to create a single Audio element that gets restarted every time a new toast appears.
var audio = new Audio();
audio.src = './audio/test.mp3';
audio.play();
toastr.options.onHidden = function() {
audio.pause();
}
toastr.options.onclose = function() {
audio.pause();
}
toastr.options.onCloseClick = function() {
audio.pause();
}
If you need it to keep playing continuously until all toasts are closed, you could have a counter that pauses the audio when there are no more active toasts
I used Jquery to access the uniqueId in the eventData.id .
toastr.warning() , toastr.success() ,toastr.error(), toastr.info() takes input: message , title for notification .
var message = '<input type="hidden" id="eventId" value="'+ eventData.id +'"/>';
toastr.warning(message,eventData.type);
$('.toast-warning').on('mouseleave',function(){
var eventDataId = $(this).find('#eventId').val();
self.eventAudio[eventDataId].pause();
self.eventAudio[eventDataId].src ='';
});
I bound the onHidden() ,onclose() , onCloseclick() with :
$('.toast-warning').trigger('mouseleave');
Since the <input> has its type as hidden , it was not visible in the toast notification , and i accessed the element easily using:
$(this).find() .
Used this to reach the answer: Jquery toastr onHidden function

Javascript & Soundcloud Widget: How to load new track into SC Widget iFrame (via URL)

I’ve seen different web apps like Playmoss, Whyd, and Songdrop etc. that, I believe, HAVE to utilize the Soundcloud Embedded Widget in order to produce the functionality of playing multiple tracks, in sucession, not apart of a set/(playlist). Currently I am having issues reproducing this functionality with the following library, so I decided to attempt to write my own:
https://github.com/eric-robinson/SCLPlayer
I am very new to writing javascript, but my below code, will load a first track, and play it once hitting the “ready” bind. Once hitting the “finish” bind, It will then jump to the loadNextTrack() function and load the next tracks URL, into the src of the widget’s iFrame. After that, it doesn’t ever hit the original “ready” bind, which would then begin playback.
So to clear things up, playback doesn’t begin for the second track.
<script type = "text/javascript">
var SCLPlayer = {
isPlayerLoaded : false,
isPlayerFullLoaded : false,
needsFirstTrackSkip : true,
isPaused: true,
scPlayer : function() {
widgetContainer = document.getElementById('sc');
widget = SC.Widget(widgetContainer);
return widget;
},
loadNextTrack : function() {
var ifr = document.getElementById('sc');
ifr.src = 'http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/231758952';
console.log ('Loading Next Track');
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
console.log ('Player is Ready, next Track');
SCLPlayer.scPlayer().play();
});
}
};
$( '#sc' ).ready(function() {
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
SCLPlayer.isPlayerLoaded = true;
//window.location = 'sclplayer://didLoad';
console.log ('Player is Ready');
SCLPlayer.scPlayer().play();
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PLAY, function() {
SCLPlayer.isPaused = false;
//window.location = 'sclplayer://didPlay';
console.log ('Player did Play');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PAUSE, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didPause';
console.log ('Player did Pause');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.FINISH, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didFinish';
console.log ('Player did Finish');
SCLPlayer.loadNextTrack();
});
});
</script>
</head>
<body>
<iframe id = "sc" width="100%" height="100%" scrolling="no" frameborder="no" src="http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/226183306"></iframe>
</body>
The whole point of me writing this Javascript is so that I can then use a Swift to Javascript bridge in my iOS app to then control the loading of tracks into the embedded players. For some reason over a slower connection, the next track doesn't always load into the player, using the "bridge". I hope to provide the nextTrackURL to the javascript side of things before the currentTrack finishes, so that the bridge conveys nothing and the Javascript handles new track loading, solely on its own.
I think you want to use the load function to specify the url for the new track
From the soundcloud Widget API docs:
load(url, options) — reloads the iframe element with a new widget specified by the url. All previously added event listeners will continue working. options is an object which allows you to define all possible widget parameters as well as a callback function which will be executed as soon as new widget is ready. See below for detailed list of widget parameters.
var url = "https://api.soundcloud.com/";
var options = [];
// if a track
url += "tracks/";
// if a playlist
url += "playlists/"
// append the id of the track / playlist to the url
url += id;
// set any options you want for the player
options.show_artwork = false;
options.liking = false;
options.auto_play = true;
widget.load(url, options, OPTIONAL_CALLBACK_FUNCTION);
Edited to show binding...
The bind code is called once, after the widget is initially loaded.
The ready event is only called once, when the widget is initially loaded, it is not called for each subsequent call using load().
try {
widget.bind(SC.Widget.Events.FINISH,
function finishedPlaying() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PAUSE,
function paused() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PLAY,
function playing() {
// your code / function call
widget.getCurrentSound(function scCurrentSound(sound) {
// this also binds getCurrent sound which is called
// each time a new sound is loaded
});
}
);
widget.bind(SC.Widget.Events.PLAY_PROGRESS,
function position(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.SEEK,
function seek(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.READY,
function ready() {
// your code / function call
}
);
} catch(e) {
// exception handler code
}

Call Javascript After Saving Calendar Item

I am having a script as below:-
function getColorValue(aId,atitle) {
try{
var clientContext = new SP.ClientContext();
var oWebsite = clientContext.get_web();
var oList = oWebsite.get_lists().getByTitle('Item type');
var oListItem = oList.getItemById(parseInt(aId));
clientContext.load(oListItem);
clientContext.executeQueryAsync(function () {
var listItem = oListItem;
var colorname = listItem.get_item('Color_x0020_Name');
if (typeof colorname != 'undefined') {
if (colorname != null) {
$("div[title$='" + atitle + "']").css("background-color", colorname);
}
}
}, onColorQueryFail);
}
catch(e){
}
}
I need to call this script each time after a SharePoint Calendar Item is created.
Can anyone help?
The following JavaScript example demonstrates how to register event that will be triggered after Calendar item is created:
//custom handler that will be triggered after Calendar item is created
function onEventCreated(){
alert('Event has been created...');
}
function registerCalendarEventOnItemCreated(event)
{
var currentCtx = getCurrentContextInfo();
var calContainer = SP.UI.ApplicationPages.CalendarInstanceRepository.lookupInstance(currentCtx.ctxId);
for(var name in calContainer) {
if(calContainer.hasOwnProperty(name)) {
var p = calContainer[name];
if(p instanceof SP.UI.ApplicationPages.CalendarNewFormDialog) {
p.get_events().addHandler("newitemcreated",event);
}
}
}
}
//get current context info
function getCurrentContextInfo()
{
var currentListId = new SP.Guid(_spPageContextInfo.pageListId);
for(var ctxKey in g_ctxDict){
var curCtx = g_ctxDict[ctxKey];
if(curCtx.listName == currentListId.toString()){
return curCtx;
}
}
return null;
}
//register Calendar events
$('body').on('click', 'div#AsynchronousViewDefault_CalendarView', function() {
registerCalendarEventOnItemCreated(onEventCreated);
});
Has been tested against SharePoint 2013/Online
In your case the function getColorValue could be invoked from onEventCreated, for example:
function onEventCreated(){
getColorValue (id,title);
}
How to apply changes
Switch the page into Edit mode
Add Script Editor webpart into page.
Put the specified code by wrapping it using script tag code into the Script Editor, for example: <script type="text/javascript">{JavaScipt code goes here}</script>
Save the page
Results
Create an Event Receiver with List Item Events for type and Calendar for Source then check 'An item is being added' in handling the event.
Then in the code behind of your Event Receiver:
public override void ItemAdding(SPItemEventProperties properties)
{
base.ItemAdding(properties);
//Call your function through this
Page.ClientScript.RegisterStartupScript(this.GetType(), "CallMyFunction", "MyFunction()", true);
}
Hope that helps :)
I know this is an old question, but there is an issue with the solution given.
I had a requirement to implement an workaround to the missing Resource Reservation feature in Sharepoint online. It was not possible to use an approach more suitable to the Modern Experience, due its dependency of Azure (no Azure subscription available for it), so I use Sharepoint API calls to perform the the same functionality of Resource Reservation.
To use the Sharepoint API, some Ajax calls were needed. But I observed that two calls were executed for each request.
The point is when you register calendar events, it is needed to attach the click event using one, as seen below, to prevent the click event to be fired more than once.
//register Calendar events
$('body').one('click', 'div#AsynchronousViewDefault_CalendarView', function() {
registerCalendarEventOnItemCreated(onEventCreated);
});

imagesLoaded Plugin not working with Angular JS

So i'm learning AngularJS and i'm building a small web app that allows you to click through images randomly. Basically you click the next button and an image is downloaded and shown, when you click the back button it goes to the previous image in the stack.
I'd like to show a loading spinner and disable the back/forward buttons until the ajax request for the new image is complete, AND the image is completely loaded
My image controller is structured like so:
app.controller('ImageController', ['imageService', function(imageService) {
var that = this;
that.position = 0;
that.images = [];
that.loading = false;
that.isLoading = function() {
return that.loading;
}
that.setLoading = function(isLoading) {
that.loading = isLoading;
}
that.currentImage = function() {
if (that.images.length > 0) {
return that.images[that.position];
} else {
return {};
}
};
that.fetchSkin = function() {
that.setLoading(true);
imageService.fetchRandomSkin().success(function(data) {
// data is just a js object that contains, among other things, the URL for the image I want to display.
that.images.push(data);
that.imagesLoaded = imagesLoaded('.skin-preview-wrapper', function() {
console.log('images loaded');
that.setLoading(false);
});
});
};
that.nextImage = function() {
that.position++;
if (that.position === that.images.length) {
that.fetchSkin();
}
};
that.previousImage = function() {
if (that.position > 0) {
that.position--;
}
};
that.fetchSkin();
}]);
If you notice inside of the that.fetchSkin() function, i'm calling the imagesLoaded plugin then when the images are loaded I am setting that.loading to false. In my template I am using ng-show to show the images when the loading variable is set to false.
If I set loading to false outside of the imagesLoaded callback (like when the ajax request is complete) then everything works as expected, when I set it inside of the imagesLoaded function the template doesn't update with the new loading value. Note that the console.log('images loaded'); does print to the console once the images have loaded so I know the imagesLoaded plugin is working correctly.
As your imagesLoaded callback is invoked asynchronously once images are loaded, Angular does not know that values of that.isLoading() method calls changed. It is because of dirty checking that Angular uses to provide you with easy to use 2 way data binding.
If you have a template like so:
<div ng-show="isLoading()"></div>
it won't update after you change the values.
You need to manually tell angular about data changes and that can be done by invoking $digest manually.
$scope.$digest();
just after you do
console.log('images loaded');
that.setLoading(false);
Pseudo code that can work (copied and pasted from my directive):
//inside your controller
$scope.isLoading = false;
// just another way of using imagesLoaded. Yours is ok.
$element.imagesLoaded(function() {
$scope.isLoading = true;
$scope.$digest();
});
As long as you only change your controller $scope within async callback, there's no need to call $apply() to run $digest on $rootScope because your model changes are only local.

Categories