Why is phonegap app not making sound on Samsung with Android 4? - audio

I have a phonegap app, which is now in alpha testing of PlayStore.
The sound works on most devices, but not on Samsung Devices with Android 4.x.
I didn't find an issue or similar questions. Did I do something wrong?
Code with https://github.com/goldfire/howler.js/tree/2.0
var sound = new Howl({
src: ['res/raw/warning.mp3']
}).play();
My code with Cordova-plugin-media:
var src = "res/raw/warning.mp3";
// HTML5 Audio
if (typeof Audio != "undefined") {
new Audio(src).play() ;
// Phonegap media
} else if (typeof device != "undefined") {
// Android needs the search path explicitly specified
if (device.platform == 'Android') {
src = '/android_asset/www/' + src;
}
var mediaRes = new Media(src,
function onSuccess() {
// release the media resource once finished playing
mediaRes.release();
},
function onError(e){
alert("error playing sound: " + JSON.stringify(e));
});
mediaRes.play();
} else {
alert("no sound API to play: " + src);
}
Anyone an idea what is the source of the problem?

There could be a couple of things here; ideally, you should wait for the sound be to loaded before trying to play it (Howler tries to handle this itself, but it doesn't always work)
From their examples
var sound = new Howl({
src: ['sound.webm', 'sound.mp3']
});
// Clear listener after first call.
sound.once('load', function(){
sound.play();
});
The second issue may be because it's an older android device, and thus WebAudio support may be sketchy. Try adding html5 into the options
var sound = new Howl({
src: ['sound.webm', 'sound.mp3', 'sound.wav'],
html5 : true
});
The last issue could be because a lot of mobile devices require a user interaction before they can play a sound (think of browser makers not wanting people to browse to websites and having music automatically start playing!). So once the sound has loaded, request the user to touch the screen once, and then play a sound on that touch event. This should 'unlock' the audio from now on. Again, Howler does try to do this, but it's something you may want to look at as well.

Related

How could I change the audio output device of node module "say"

I'm making a program that will output text to speech, the user will need to be able to change the output device (for example to virtual audio cable). At the moment I'm using https://www.npmjs.com/package/say to produce the speech
e.g.
const say = require("say");
say.speak("Hello World");
but I've no clue on how I could go about choosing the audio output.
What I've found so far has been pretty useless, I'm guessing largely because I don't know the proper terminology to search for answers, regardless this is it:
I first found out about navigator.MediaDevices, then I found how I could make an audio element and change the audio device of that element via setSinkId, then I realized these things are probably(?) irrelevant since the say module seems to play sounds using powershell commands. I've even gone as far as trying to change powershell's output device in app volume device preferences(img), but that seems to do nothing.
I'm pretty much stumped right now, so I'd appreciate any help please. I'm not set on using Say as a module, it just seemed easy to use at first.
Edit:
A workaround I've settled with is making my own TTS class and using SpVoice.
I have something similar to this:
const childProcess = require('child_process');
class TTS {
constructor(channel, speed) {
this.speed = speed;
this.channel = channel;
this.baseCommand = "$speak = New-Object -ComObject SAPI.SPVoice;"
}
speak(text){
var command = this.baseCommand +
`$speak.AudioOutput = foreach ($o in $speak.GetAudioOutputs()) {if ($o.getDescription() -eq '${this.channel}') {$o; break;}}; `
+ "$speak.Speak([Console]::In.ReadToEnd());"
this.child = childProcess.spawn('powershell', [command], {shell: true})
this.child.stdin.setEncoding('ascii')
this.child.stdin.end(text);
this.child.addListener('exit', (code, signal) => {
if (code === null || signal !== null) {
console.log(new Error(`error [code: ${code}] [signal: ${signal}]`))
}
this.child = null
})
}
}
Then I can pass in an audio channel like
tts = new TTS("CABLE Input (VB-Audio Virtual Cable)", 0);
tts.speak("Hello there");
and it will output TTS in my desired channel
Some of the browsers support the built in speechSynthesis API.
Save the following code in 'test.html' file and open the file in chrome web browser for testing the speech api.
<script>
//---------------- SpeechAPI Start ------------------------
function speak(message) {
try {
var utterance= new SpeechSynthesisUtterance("");
utterance.lang='en-GB';
utterance.text=message;
window.speechSynthesis.speak(utterance);
}catch(e){
console.log('Exception in speak : ' + e)
}
}
//---------------- SpeechAPI End ------------------------
</script>
<button onclick="speak('Hello, how are you doing?');">Press to speak</button>
Is this what you are looking for ?

Alexa - Perform background tasks during audioPlayer.Play

How is it possible to carry out any background tasks while Alexa is playing something with audioPlayer.Play?
The below plays an audio stream, but I need to perform other tasks in the background without intervention while the stream is playing. I know it is possible because other Skills can do it.
var handlers = {
'LaunchRequest': function() {
this.emit('Play');
},
'Play': function() {
this.response.speak('Sure.').
audioPlayerPlay(
'REPLACE_ALL',
stream.url,
stream.url,
null,
0);
this.emit(':responseReady');
}
}
Does anyone know or have any suggestions? From what I can see, once it starts playing the stream, I cannot get it to do anything unless I interrupt the stream to command another intent?
Alexa has a few built in requests that she sends to your skill throughout the lifecycle of an Audio stream for just this purpose! They are as follows:
AudioPlayer.PlaybackStarted - Sent when a new audio item begins playing.
AudioPlayer.PlaybackNearlyFinished - Sent when an audio item is almost over (most commonly used by a skill service to handle queuing the next item.)
AudioPlayer.PlaybackFinished - Sent when an audio item ends.
There are a couple of other ones too that you can read about here, but my guess is these will do just fine for what you need.
To use them, just set up handlers in your code for any of these requests and perform any tasks you need to there!
I don't know node.js at all, but my guess is the end result will look relatively close to this:
var handlers = {
'LaunchRequest': function() { /* your launch request code from above */ }
'Play': function() { /* your play code from above */ }
'AudioPlayer.PlaybackNearlyFinished': function() {
// Perform any background tasks here
}
}

VIDEOJS: playing a pre-roll add before each playlist item

I'm using the videojs-playlist plugin along with Google's videojs-ima plugin. Everything works swimmingly except I am only getting a preload ad before the first video. I want one before each video in the playlist.
Basic setup is boilerplate, but for reference:
this.player = videojs('currentvideo', { autoplay : true, fluid : true });
this.player.playlist(this.playlist);
this.player.playlist.autoadvance(5);
const skippable_linear = {google's test ad};
const options = {
id: 'currentvideo',
adTagUrl: skippable_linear,
debug : true
};
this.player.ima(
options
);
this.player.ima.requestAds();
I have tried various ways of manually calling ads from inside an 'ended' event handler, such as calling requestAds again:
const _this = this;
this.player.on( 'ended', function(){
/* some other stuff */
_this.player.ima.requestAds();
});
This does play an ad where I want it, but
this breaks playlist's 'autoadvance' setting (next video doesn't start playing when the ad is finished), and
this puts the player into "ad display" mode (scrubber is unavailable, etc).
Is there a simple way to just say, "play an ad now" programmatically? I've tried, without joy, to use all of the seemingly applicable methods exposed by both the ima plugin and the contrib-ads plugin it relies on. I'll admit here that this is the first time I've ever had to deal with videos that run ads, so I'm kind of a noob.
I am trying to do the same thing. Just like you I failed when calling player.ima.requestAds() on events. I dug deeper and the best I could come up with is what I share bellow.
According to the videojs-ima API you have to use the setContentWithAdTag method instead of whatever you are using to switch the player content. In our case it is the player.playlist.next method.
I combined the code found in the videojs-ima examples with the original playlist.next to write my own next.
Then quite brutally I overrode the original plugin method.
Here's the code:
player.playlist(myPlayilst);
player.playlist.autoadvance(2);
player.playlistUi(); //videojs-playlist-ui
player.ima({
id: 'video5',
adTagUrl: 'thy adserver request'
});
//override playlist.next
player.playlist.next = function(){
var nextIndex = 0,
playlist = this.player_.playlist,
list = this.player_.playlist();
//everything below is copied directly from the original `next` (except for the "//load with ad")
// Repeat
if (playlist.repeat_) {
nextIndex = playlist.currentIndex_ + 1;
if (nextIndex > list.length - 1) {
nextIndex = 0;
}
} else {
// Don't go past the end of the playlist.
nextIndex = Math.min(playlist.currentIndex_ + 1, list.length - 1);
}
// Make the change
if (nextIndex !== playlist.currentIndex_) {
//load with ad
this.player_.playlist.currentItem(nextIndex);
this.player_.ima.setContentWithAdTag(
this.player_.playlist.currentItem(),
null,
true);
this.player_.ima.requestAds();
/////
return list[playlist.currentItem()];
}
}
You will probably need to override other methods that change the current playback, like playlist.previous.
I use videojs-playlist-ui so in my case it was neccessary to change the onclick handler called switchPlaylistItem_. I used some good old brute force to do that like this:
videojs.getComponent('PlaylistMenuItem').prototype.switchPlaylistItem_ = function(e){
this.player_.playlist.currentItem(this.player_.playlist().indexOf(this.item));
this.player_.ima.setContentWithAdTag(
this.player_.playlist.currentItem(),
null,
true);
this.player_.ima.requestAds();
};
PlaylistMenuItem's prototype should be changed before initializing the player.
This solution works, but it feels hacky, so if anyone can come up with something cleaner, please share!
I ended up forking videojs-playlist, and adding the option to override the player.src method. Feel free to use it:
fw-videojs-playlist
Details on how to use it are all in the github readme (including an example with ima.setContentWithAdTag)

Default Audio Output - Getting Device Changed Notification? (CoreAudio, Mac OS X, AudioHardwareAddPropertyListener)

I am trying to write a listener using the CoreAudio API for when the default audio output is changed (e.g.: a headphone jack is plugged in). I found sample code, although a bit old and using deprecated functions (http://developer.apple.com/mac/library/samplecode/AudioDeviceNotify/Introduction/Intro.html, but it didn't work. Re-wrote the code in the 'correct' way using AudioHardwareAddPropertyListener method, but still it doesn't seem to work. When I plug in a headphone the function that I registered is not triggered. I'm a bit of a loss here... I suspect the problem may lay some where else, but I can't figure out where...
The Listener Registration Code:
OSStatus err = noErr;
AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDefaultOutputDevice, KAudioObjectPropertyScopeGlobal, KAudioObjectPropertyElementMaster };
err = AudioObjectAddPropertyListener ( KAudioObjectAudioSystemObject, &AudioDevicesAddress, coreaudio_property_listener, NULL);
if (err) trace ("error on AudioObjectAddPropertyListener");
After a search in sourceforge for projects that used the CoreAudio API, I found the rtaudio project, and more importantly these lines:
// This is a largely undocumented but absolutely necessary
// requirement starting with OS-X 10.6. If not called, queries and
// updates to various audio device properties are not handled
// correctly.
CFRunLoopRef theRunLoop = NULL;
AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
OSStatus result = AudioObjectSetPropertyData( kAudioObjectSystemObject, &property, 0, NULL, sizeof(CFRunLoopRef), &theRunLoop);
if ( result != noErr ) {
errorText_ = "RtApiCore::RtApiCore: error setting run loop property!";
error( RtError::WARNING );
}
After adding this code I didn't even need to register a listener myself.
Try CFRunLoopRun() - it has the same effect. i.e. making sure the event loop that is calling your listener is running.

load sound dynamically in as2 having problem

I want to load sound placed in sounds folder.
Code is
var my_sound = new Sound();
my_sound.loadSound("sounds/sound1.mp3");
my_sound.onLoad = function(success:boolean){
if(success){
my_sound.start();
}
}
This plays sound when flash is open and pressing CTRL+ENTER(Test Movie).
but when we plays the swf it won't play sound.
for this problem i found one solution.
i made off onLoad function. and the Test Movie. Now the opposite things happend.
It dosen't play when press CTRL+ENTER (TestMovie);
but it plays when swf is playing.
Is there any other way of loading sound.
Try:
var my_sound:Sound = new Sound();
my_sound.onLoad = function(success:Boolean)
{
if (success)
{
my_sound.stop();
}
};
my_sound.loadSound("sounds/sound1.mp3", true);
This will stop the sound as soon as it's loaded.
Whenever you want to start the sound, just call this function:
my_sound.start();

Resources