How can I play Sound more than once in Haxe/Heaps? - haxe

I'm using Heaps.io Game Engine for Haxe and currently trying to play sound WAV file from my resources every time a specific event happens. But it always plays only first time, then nothing. Code example:
import hxd.Event;
import hxd.System;
import hxd.Window;
import hxd.res.Sound;
class Main extends hxd.App {
var sound: Sound = null;
var window: Window;
function onEvent(event: Event) {
if (event.kind == EKeyDown) {
if (event.keyCode == hxd.Key.P) {
playSound();
}
if (event.keyCode == hxd.Key.Q) {
System.exit();
}
}
}
function playSound() {
trace(sound);
if (sound != null) {
sound.play();
}
}
override function init() {
hxd.Res.initEmbed();
window = Window.getInstance();
window.addEventTarget(onEvent);
if (Sound.supportedFormat(Wav)) {
sound = hxd.Res.pong;
}
}
override function update(dt: Float) {
}
public static function main() {
new Main();
}
}
After pressing 'P' for 7 times, pong.wav would play only once. After quitting by pressing 'Q', debug console shows this:
(7) src/Main.hx:22: pong.wav
AL lib: (EE) alc_cleanup: 1 device not closed
So it does see my sound file but can't play it for whatever reason. I've tried to write sound.stop(); before sound.play();, but to no avail. Could it be something about the last debug message?
Edit1: I've tried to use Channels, but result was still the same.
And in the code above it seems like no other sound file could be played after the first one as well.
P.S. There's even no such tag as "Heaps" on Stack Overflow.

Found solution: https://github.com/HeapsIO/heaps/issues/1100, and seems like it is a bug!
A simple fix is to put this line in your App.init
#:privateAccess haxe.MainLoop.add(() -> {});
E.g.
override function init() {
hxd.Res.initEmbed();
#:privateAccess haxe.MainLoop.add(() -> {});
window = Window.getInstance();
window.addEventTarget(onEvent);
if (Sound.supportedFormat(Wav)) {
sound = hxd.Res.pong;
}
}
Now sound is always working properly and problem solved.
As a side note, I still don't know what to think about the last waning message, it still occures but does nothing.

Related

How to get Microsoft Azure Speech To Text to start transcribing when program is run? (Unity, C#)

I am trying to build a simple app using Microsoft Azure's Cognitive Services Speech To Text SDK in Unity3D. I've following this tutorial, and it worked quite well. The only problem with this tutorial is that the Speech-To-Text is activated by a button. When you press the button, it'll transcribe for the duration of a sentence, and you'll have to press the button again for it to transcribe again. My problem is I'd like it to start transcribing as soon as the program is run in Unity, rather than having to press a button each time I want to transcribe a sentence.
Here is the code.
public async void ButtonClick()
{
// Creates an instance of a speech config with specified subscription key and service region.
// Replace with your own subscription key and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("[My API Key]", "westus");
// Make sure to dispose the recognizer after use!
using (var recognizer = new SpeechRecognizer(config))
{
lock (threadLocker)
{
waitingForReco = true;
}
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
var result = await recognizer.RecognizeOnceAsync().ConfigureAwait(false);
// Checks result.
string newMessage = string.Empty;
if (result.Reason == ResultReason.RecognizedSpeech)
{
newMessage = result.Text;
}
else if (result.Reason == ResultReason.NoMatch)
{
newMessage = "NOMATCH: Speech could not be recognized.";
}
else if (result.Reason == ResultReason.Canceled)
{
var cancellation = CancellationDetails.FromResult(result);
newMessage = $"CANCELED: Reason={cancellation.Reason} ErrorDetails={cancellation.ErrorDetails}";
}
lock (threadLocker)
{
message = newMessage;
waitingForReco = false;
}
}
}
void Start()
{
if (outputText == null)
{
UnityEngine.Debug.LogError("outputText property is null! Assign a UI Text element to it.");
}
else if (startRecoButton == null)
{
message = "startRecoButton property is null! Assign a UI Button to it.";
UnityEngine.Debug.LogError(message);
}
else
{
// Continue with normal initialization, Text and Button objects are present.
}
}
void Update()
{
lock (threadLocker)
{
if (startRecoButton != null)
{
startRecoButton.interactable = !waitingForReco && micPermissionGranted;
}
}
}
I've tried removing the Button object, but then the speech-to-text won't run.
Any tips or advice would be amazing. Thank you.
Per the comments in the script of the tutorial your referenced:
// Starts speech recognition, and returns after a single utterance is recognized. The end of a
// single utterance is determined by listening for silence at the end or until a maximum of 15
// seconds of audio is processed. The task returns the recognition text as result.
// Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
// shot recognition like command or query.
// For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
But it's not as simple as replacing 'RecognizeOnceAsync' with 'StartContinuousRecognitionAsync', because the behaviours are different. RecognizeOnceAsync will basically turn on your mic for a maximum of 15 seconds, and then stop listening.
Instead, make the button into 'should I listen continuously or not?' using StartContinuousRecognitionAsync and StopContinuousRecognitionAsync, and then change your Start function to simply start up a new recognizer and have it waiting for the Speech Recognizer event to come through. Below is the script I used to enable this functionality:
using UnityEngine;
using UnityEngine.UI;
using Microsoft.CognitiveServices.Speech;
public class HelloWorld : MonoBehaviour
{
public Text outputText;
public Button startRecordButton;
// PULLED OUT OF BUTTON CLICK
SpeechRecognizer recognizer;
SpeechConfig config;
private object threadLocker = new object();
private bool speechStarted = false; //checking to see if you've started listening for speech
private string message;
private bool micPermissionGranted = false;
private void RecognizingHandler(object sender, SpeechRecognitionEventArgs e)
{
lock (threadLocker)
{
message = e.Result.Text;
}
}
public async void ButtonClick()
{
if (speechStarted)
{
await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false); // this stops the listening when you click the button, if it's already on
lock(threadLocker)
{
speechStarted = false;
}
}
else
{
await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false); // this will start the listening when you click the button, if it's already off
lock (threadLocker)
{
speechStarted = true;
}
}
}
void Start()
{
startRecordButton.onClick.AddListener(ButtonClick);
config = SpeechConfig.FromSubscription("KEY", "REGION");
recognizer = new SpeechRecognizer(config);
recognizer.Recognizing += RecognizingHandler;
}
void Update()
{
lock (threadLocker)
{
if (outputText != null)
{
outputText.text = message;
}
}
}
}
And below is a gif of me using this functionality. You'll not that I don't click the button at all (and it was only clicked once, prior to the gif being recorded)(also, sorry for the strange sentences, my coworkers kept interrupting asking who I was talking to)

Could I Intercept Exit FullScreen event from octane.xam.VideoPlayer plugin (XAMARIN FORMS)?

My application works portait, ma i want fullscreen video playback even in landscape mode using the plugin mentionend above.
For this purpose I create a customrenderer to take access to native AVPlayerViewController Ios Control.
I tried in many many ways, but seems to be impossible to handle exit fullscreen event. In that method i want to force layout portrait. I have the code for reset orientation already implemented but the problem is to put the code in the right place.
Any other that faced the same issue??
I tried to search for something useful in AVPlayerView(not accessible), AVPlayerVideoController, AVPlayerCurrentItem etc
Any ideas?
Thanks you in advance.
I have translated the OC code to C# in this link for you, see the following codes:
using Foundation;
using CoreGraphics;
playerViewController = new AVPlayerViewController();
playerViewController.ContentOverlayView.AddObserver(this, new NSString("bounds"), NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Old , IntPtr.Zero);
public override void ObserveValue(NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
{
base.ObserveValue(keyPath, ofObject, change, context);
if(ofObject == playerViewController.ContentOverlayView)
{
if(keyPath == "bounds")
{
NSValue oldRect = change.ValueForKey(new NSString("NSKeyValueChangeOldKey")) as NSValue;
NSValue newRect = change.ValueForKey(new NSString("NSKeyValueChangeNewKey")) as NSValue;
CGRect oldBounds = oldRect.CGRectValue;
CGRect newBounds = newRect.CGRectValue;
bool wasFullscreen = CGRect.Equals(oldBounds, UIScreen.MainScreen.Bounds);
bool isFullscreen = CGRect.Equals(newBounds, UIScreen.MainScreen.Bounds);
if(isFullscreen && !wasFullscreen)
{
if(CGRect.Equals(oldBounds,new CGRect(0,0,newBounds.Size.Height, newBounds.Size.Width)))
{
Console.WriteLine("rotated fullscreen");
}
else
{
Console.WriteLine("entered fullscreen");
}
}
else if(!isFullscreen && wasFullscreen)
{
Console.WriteLine("exited fullscreen");
}
}
}
}

Xamarin.Forms Warning: Attempt to present * on * whose view is not in the window hierarchy with iOS image/gesture recogniser

I have a modal Navigation page with an image which acts like a button;
<Image Source ="share.png" HeightRequest="32" WidthRequest="32">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="On_Share" />
</Image.GestureRecognizers>
</Image>
And the method behind;
async void On_Share(object sender, EventArgs e)
{
if (CrossConnectivity.Current.IsConnected)
{
var message = "Share this";
var title = "Share";
await CrossShare.Current.Share(new ShareMessage { Text = message, Title = title}, new ShareOptions { ExcludedUIActivityTypes = new[] { ShareUIActivityType.PostToFacebook } });
}
else
{
NoInternetLabel.IsVisible = true;
}
}
I'm getting the error when I try to click on the share image/button. I've put breakpoints into the first line of the On_Share method & they're not being hit.
Warning: Attempt to present <UIActivityViewController: 0x141b60f70> on <Xamarin_Forms_Platform_iOS_ModalWrapper: 0x1419a0920> whose view is not in the window hierarchy!
Please note this works fine in Android, I'm only seeing issues in iOS. I'm not sure what is going on - I'm not trying to present any other windows or anything when I click the image. Regardless, the error appears before the process reaches the beginning of the On_Share method. What am I missing here?
EDIT: The method does get hit now, and I'm still getting the error. It must be trying to send up the share sheet and failing...
There was a problem with the Share plugin in the end - we resolved it by making part of the code recursive.
the GetVisibleViewController used to look like this;
UIViewController GetVisibleViewController()
{
var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;
if (rootController.PresentedViewController == null)
return rootController;
if (rootController.PresentedViewController is UINavigationController)
{
return ((UINavigationController)rootController.PresentedViewController).VisibleViewController;
}
if (rootController.PresentedViewController is UITabBarController)
{
return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
}
return rootController.PresentedViewController;
}
whereas it needed to cycle through to find the top UIViewController;
UIViewController GetVisibleViewController(UIViewController controller = null)
{
controller = controller ?? UIApplication.SharedApplication.KeyWindow.RootViewController;
if (controller.PresentedViewController == null)
return controller;
if (controller.PresentedViewController is UINavigationController)
{
return ((UINavigationController)controller.PresentedViewController).VisibleViewController;
}
if (controller.PresentedViewController is UITabBarController)
{
return ((UITabBarController)controller.PresentedViewController).SelectedViewController;
}
return GetVisibleViewController(controller.PresentedViewController);
}
I've raised the issue and submitted a pull request on the github

Unity Vuforia Google VR - Can't make onPointerEnter to GameObject change material for itself

I have two 3d buttons in my scene and when I gaze into any of the buttons it will invoke OnPointerEnter callback and saving the object the pointer gazed to.
Upon pressing Fire1 on the Gamepad I apply materials taken from Resources folder.
My problem started when I gazed into the second button, and pressing Fire1 button will awkwardly changed both buttons at the same time.
This is the script I attached to both of the buttons
using UnityEngine;
using UnityEngine.EventSystems;
using Vuforia;
using System.Collections;
public class TriggerMethods : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler
{
Material _mat;
GameObject targetObject;
Renderer rend;
int i = 0;
// Update is called once per frame
void Update () {
if (Input.GetButtonDown("Fire1"))
TukarMat();
}
public void OnPointerEnter(PointerEventData eventData)
{
targetObject = ExecuteEvents.GetEventHandler<IPointerEnterHandler>(eventData.pointerEnter);
}
public void OnPointerExit(PointerEventData eventData)
{
targetObject = null;
}
public void TukarMat()
{
Debug.Log("Value i = " + i);
if (i == 0)
{
ApplyTexture(i);
i++;
}
else if (i == 1)
{
ApplyTexture(i);
i++;
}
else if (i == 2)
{
ApplyTexture(i);
i = 0;
}
}
void ApplyTexture(int i)
{
rend = targetObject.GetComponent<Renderer>();
rend.enabled = true;
switch (i)
{
case 0:
_mat = Resources.Load("Balut", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 1:
_mat = Resources.Load("Khasiat", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
case 2:
_mat = Resources.Load("Alma", typeof(Material)) as Material;
rend.sharedMaterial = _mat;
break;
default:
break;
}
}
I sensed some logic error and tried making another class to only manage object the pointer gazed to but I was getting more confused.
Hope getting some helps
Thank you
TukarMat() is beeing called on both buttons when you press Fire1. If targetObject is really becoming null this should give an error on first button since it's trying to get component from a null object. Else, it'll change both as you said. Make sure OnPointerExit is beeing called.
Also, it seems you are changing the shared material.
The documentation suggests:
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
So, try changing the material property instead of sharedMaterial since it'll change the material for that object only.

Wait() in Haxe?

I am getting started with Haxe and OpenFl, and have some experience with Javascript and Lua.
It was going pretty well, till I got to a point where I needed a function similar to wait() in Lua, etc, which stops the script until the number of seconds you set is over.
How would I go about doing this?
EDIT: To clarify, I am building to Flash.
Although this is old, I wanted to add another point for reference. The OP mentioned in a comment this was for a game. One method I often use is (and could probably be put in a library):
var timerCount:Float = 0;
var maxTimerCounter:Float = 5;
function update () {
timerCounter += elapsedTime;
if (timerCounter > maxTimerCounter){
onTimerComplete();
timerCount = 0;
}
}
In SYS you are looking for:
static function sleep( seconds : Float ) : Void
Suspend the current execution for the given time (in seconds).
Example: Sys.sleep(.5);
http://haxe.org/api/sys/
Edit: User is porting to flash.
So the suggestion is to use Timer
http://haxe.org/api/haxe/timer
In Timer the suggestion is to use
static function delay( f : Void -> Void, time_ms : Int ) : Timer
Someone on stack overflow has an example that looks like this: haxe.Timer.delay(callback(someFunction,"abc"), 10); located here... Pass arguments to a delayed function with Haxe
For the Flash compile target, the best you can do is use a timer, and something like this setTimeout() function.
This means slicing your function into two - everything before the setTimeout(), and everything after that, which is in a separate function that the timeout can call.
so somethine like, eg:
tooltipTimerId = GlobalTimer.setTimeout(
Tooltip.TOOLTIP_DELAY_MS,
handleTooltipAppear,
tootipParams
);
[...]
class GlobalTimer {
private static var timerList:Array<Timer>;
public static function setTimeout(milliseconds:Int, func:Dynamic, args:Array<Dynamic>=null):Int {
var timer:Timer = new Timer(milliseconds);
var id = addTimer(timer, timerList);
timer.run = function() {
Reflect.callMethod(null, func, args);
clearTimeout(id);
}
return id;
}
private static function addTimer(timer:Timer, arr:Array<Timer>):Int {
for (i in 0...arr.length) {
if (null == arr[i]) {
arr[i] = timer;
return i;
}
}
arr.push(timer);
return arr.length -1;
}
public static function clearTimeout(id:Int) {
var timers:Array<Timer> = GlobalTimer.getInstance().timerList;
try {
timers[id].stop();
timers[id] = null;
} catch(e:Error) {/* Nothing we can do if it fails, really. */}
}
}

Resources