Open wkyleg opened 7 years ago
I have the same problem, do you have a solution ? Thx
I did not. I ended up using a Module called ngAudio which is an Angular wrapper for the HTML5 Audio API instead
Hi, i tried ngAudio but it is not working for me. Do you know if it's work on Ionic2/Angular2 project ?
for anyone experiencing this issue heres the temporary solution until the plugin fix : firstly I noticed that the 'id' that we have to pass to the plugin should be in json because plugin needs JSON format while it not handles this by itself. also you have to change the NativeAudio.java file to this : add the following lines in executePlayOrLoop method just in the try section after audioID
if ( !assetMap.containsKey(audioID) ) { this.executePreload( this.dData ); }
and the problem solved
Hi, i tried your solution but i have the same error
Unhandled Promise rejection: A reference does not exist for the specified audio id. ; Zone: <root> ; Task: null ; Value: A reference does not exist for the specified audio id.
It worked once, then nothing !
would you paste your code so I can see that ?
@Tomenbois It's an Angular 1 module so unfortunately not
@ahm-hossein my app.component.ts
import {enableProdMode} from '@angular/core';
import { Component, ViewChild } from '@angular/core';
import { Nav, Events } from 'ionic-angular';
import { Platform } from 'ionic-angular';
import { StatusBar } from 'ionic-native';
import {WelcomePage} from '../pages/welcome/welcome';
import {DbService} from '../providers/db-service';
import {StoreService} from '../providers/store-service';
import {ExamListPage} from '../pages/exam-list/exam-list';
import {DeveloperPage} from '../pages/developer/developer';
import {TrackerListPage} from '../pages/tracker-list/tracker-list';
import {TableauPage} from '../pages/tableau/tableau';
import {TableauDetailsPage} from '../pages/tableau-details/tableau-details';
import {ContactPage} from '../pages/contact/contact';
import {TranslateService} from 'ng2-translate';
import {NativeAudio} from '@ionic-native/native-audio';
declare var window: any;
enableProdMode();
@Component({
selector: 'app',
templateUrl: 'app.html',
providers: [DbService, StoreService],
})
export class MyApp {
@ViewChild(Nav) nav: Nav;
rootPage: any = WelcomePage;
pages: Array<{title: string, component: any, icon: string, paid: number}>;
page_accueil: Array<{title: string, component: any, icon: string, paid: number}>;
page_evaluation: Array<{title: string, component: any, icon: string, paid: number}>;
page_suivi: Array<{title: string, component: any, icon: string, paid: number}>;
page_tableau: Array<{title: string, component: any, icon: string, paid: number}>;
page_temps: Array<{title: string, component: any, icon: string, paid: number}>;
page_contact: Array<{title: string, component: any, icon: string, paid: number}>;
private readySource: any;
dbService: any;
storeService: any;
private ad: any;
public _Paid: any;
private onSuccess:any;
private onError:any;
tracks: any[];
singleTracks: any;
allTracks: any[];
selectedTrack: number;
constructor(
public platform: Platform,
public events: Events,
dbService: DbService,
storeService: StoreService,
public translateService: TranslateService,
private nativeAudio: NativeAudio,
) {
this.initializeApp();
this.dbService = dbService;
this.storeService = storeService;
this.page_accueil = [{title: 'Accueil', component: WelcomePage, icon: 'bookmark', paid: 0}];
this.page_evaluation = [{title: 'Evaluation', component: ExamListPage, icon: 'cube', paid: 0}];
this.page_suivi = [{title: 'Suivi', component: TrackerListPage, icon: "arrow-graph-up-right", paid: 0}];
this.page_tableau = [{title: 'Tableau', component: TableauPage, icon: "arrow-graph-up-right", paid: 0}];
this.page_temps = [{title: 'Temps', component: TableauDetailsPage, icon: "arrow-graph-up-right", paid: 0}];
this.page_contact = [{title: 'Contact', component: ContactPage, icon: "arrow-graph-up-right", paid: 0}];
];
translateService.setDefaultLang('fr');
platform.ready().then(() => {
StatusBar.styleDefault();
});
}
initializeApp() {
this.platform.ready().then((readySource) => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
console.log('Platform ready from', readySource);
this.readySource = readySource;
if (readySource == 'cordova') {
StatusBar.styleDefault();
if (this.platform.is('ios')){
// Copy data.db from Application folder into Document Database folder
window.plugins.sqlDB.copy("Q2data.db",2, success => {
// Initialize database service (DbService)
this.dbService.init();
},error =>{
console.log("Error Code = "+JSON.stringify(error));
});
} else {
// Copy data.db - Android destination
window.plugins.sqlDB.copy("Q2data.db",0, success => {
// Initialize database service (DbService)
this.dbService.init();
},error =>{
console.log("Error Code = "+JSON.stringify(error));
});
}
// Initialize database service (DbService)
this.dbService.init();
// Initialize storeService (IAP)
this.storeService.init();
} else {
this.ad = 0;
// Initialize database service (DbService)
this.dbService.init();
this.storeService.init();
}
//To force Full Version as default comment the line below (149)
//this.dbService.isFullVersion().then(data => this._Paid = data);
// To force Full Version as default uncomment the 3 lines below (152-154)
this.dbService.setProperty('fullversion','true');
this.dbService.setFullVersion();
this.dbService.setProperty('admob','false');
//
//native audio
//musique d'ambiance
console.log("Musique démarre");
this.nativeAudio.preloadComplex('ambiant_1', 'assets/sound/ambiant.mp3', 1, 1, 0);
console.log("Musique Play");
this.nativeAudio.play('ambiant_1', () => console.log('uniqueId1 is done playing'));
});
this.events.subscribe('paid:full',() =>this._Paid=1);
this.events.subscribe('paid:free',() =>this._Paid=0);
//this.nav.setRoot(page.component);
}
openDeveloperPage(){
this.nav.push(DeveloperPage);
}
openPage(page) {
let user = "EVT";
// Reset the content nav to have just this page
// we wouldn't want the back button to show in this scenario
if (page.component == this.rootPage) {
this.nav.setRoot(this.rootPage);
} else {
this.nav.push(page.component, {
readySource: this.readySource,
user: user,
adId: this.ad
});
}
}
translateToEnglish(){
this.translateService.use('en');
}
translateToFrench(){
this.translateService.use('fr');
}
}
and my NativeAudio.java
//
//
// NativeAudio.java
//
// Created by Sidney Bofah on 2014-06-26.
//
package com.rjfun.cordova.plugin.nativeaudio;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.Callable;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.media.AudioManager;
import android.util.Log;
import android.view.KeyEvent;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;
import org.apache.cordova.PluginResult.Status;
import org.json.JSONObject;
public class NativeAudio extends CordovaPlugin implements AudioManager.OnAudioFocusChangeListener {
/* options */
public static final String OPT_FADE_MUSIC = "fadeMusic";
public static final String ERROR_NO_AUDIOID="A reference does not exist for the specified audio id.";
public static final String ERROR_AUDIOID_EXISTS="A reference already exists for the specified audio id.";
public static final String SET_OPTIONS="setOptions";
public static final String PRELOAD_SIMPLE="preloadSimple";
public static final String PRELOAD_COMPLEX="preloadComplex";
public static final String PLAY="play";
public static final String STOP="stop";
public static final String LOOP="loop";
public static final String UNLOAD="unload";
public static final String ADD_COMPLETE_LISTENER="addCompleteListener";
public static final String SET_VOLUME_FOR_COMPLEX_ASSET="setVolumeForComplexAsset";
private static final String LOGTAG = "NativeAudio";
private static HashMap<String, NativeAudioAsset> assetMap;
private static ArrayList<NativeAudioAsset> resumeList;
private static HashMap<String, CallbackContext> completeCallbacks;
private boolean fadeMusic = false;
public void setOptions(JSONObject options) {
if(options != null) {
if(options.has(OPT_FADE_MUSIC)) this.fadeMusic = options.optBoolean(OPT_FADE_MUSIC);
}
}
private PluginResult executePreload(JSONArray data) {
String audioID;
try {
audioID = data.getString(0);
if (!assetMap.containsKey(audioID)) {
String assetPath = data.getString(1);
Log.d(LOGTAG, "preloadComplex - " + audioID + ": " + assetPath);
double volume;
if (data.length() <= 2) {
volume = 1.0;
} else {
volume = data.getDouble(2);
}
int voices;
if (data.length() <= 3) {
voices = 1;
} else {
voices = data.getInt(3);
}
String fullPath = "www/".concat(assetPath);
Context ctx = cordova.getActivity().getApplicationContext();
AssetManager am = ctx.getResources().getAssets();
AssetFileDescriptor afd = am.openFd(fullPath);
NativeAudioAsset asset = new NativeAudioAsset(
afd, voices, (float)volume);
assetMap.put(audioID, asset);
return new PluginResult(Status.OK);
} else {
return new PluginResult(Status.ERROR, ERROR_AUDIOID_EXISTS);
}
} catch (JSONException e) {
return new PluginResult(Status.ERROR, e.toString());
} catch (IOException e) {
return new PluginResult(Status.ERROR, e.toString());
}
}
private PluginResult executePlayOrLoop(String action, JSONArray data) {
final String audioID;
try {
audioID = data.getString(0);
if ( !assetMap.containsKey(audioID) ) { this.executePreload( data ); }
// Log.d( LOGTAG, "play - " + audioID );
if (assetMap.containsKey(audioID)) {
NativeAudioAsset asset = assetMap.get(audioID);
if (LOOP.equals(action))
asset.loop();
else
asset.play(new Callable<Void>() {
public Void call() throws Exception {
if (completeCallbacks != null) {
CallbackContext callbackContext = completeCallbacks.get(audioID);
if (callbackContext != null) {
JSONObject done = new JSONObject();
done.put("id", audioID);
callbackContext.sendPluginResult(new PluginResult(Status.OK, done));
}
}
return null;
}
});
} else {
return new PluginResult(Status.ERROR, ERROR_NO_AUDIOID);
}
} catch (JSONException e) {
return new PluginResult(Status.ERROR, e.toString());
} catch (IOException e) {
return new PluginResult(Status.ERROR, e.toString());
}
return new PluginResult(Status.OK);
}
private PluginResult executeStop(JSONArray data) {
String audioID;
try {
audioID = data.getString(0);
//Log.d( LOGTAG, "stop - " + audioID );
if (assetMap.containsKey(audioID)) {
NativeAudioAsset asset = assetMap.get(audioID);
asset.stop();
} else {
return new PluginResult(Status.ERROR, ERROR_NO_AUDIOID);
}
} catch (JSONException e) {
return new PluginResult(Status.ERROR, e.toString());
}
return new PluginResult(Status.OK);
}
private PluginResult executeUnload(JSONArray data) {
String audioID;
try {
audioID = data.getString(0);
Log.d( LOGTAG, "unload - " + audioID );
if (assetMap.containsKey(audioID)) {
NativeAudioAsset asset = assetMap.get(audioID);
asset.unload();
assetMap.remove(audioID);
} else {
return new PluginResult(Status.ERROR, ERROR_NO_AUDIOID);
}
} catch (JSONException e) {
return new PluginResult(Status.ERROR, e.toString());
} catch (IOException e) {
return new PluginResult(Status.ERROR, e.toString());
}
return new PluginResult(Status.OK);
}
private PluginResult executeSetVolumeForComplexAsset(JSONArray data) {
String audioID;
float volume;
try {
audioID = data.getString(0);
volume = (float) data.getDouble(1);
Log.d( LOGTAG, "setVolume - " + audioID );
if (assetMap.containsKey(audioID)) {
NativeAudioAsset asset = assetMap.get(audioID);
asset.setVolume(volume);
} else {
return new PluginResult(Status.ERROR, ERROR_NO_AUDIOID);
}
} catch (JSONException e) {
return new PluginResult(Status.ERROR, e.toString());
}
return new PluginResult(Status.OK);
}
@Override
protected void pluginInitialize() {
AudioManager am = (AudioManager)cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
int result = am.requestAudioFocus(this,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
// Allow android to receive the volume events
this.webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, false);
this.webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, false);
}
@Override
public boolean execute(final String action, final JSONArray data, final CallbackContext callbackContext) {
Log.d(LOGTAG, "Plugin Called: " + action);
PluginResult result = null;
initSoundPool();
try {
if (SET_OPTIONS.equals(action)) {
JSONObject options = data.optJSONObject(0);
this.setOptions(options);
callbackContext.sendPluginResult( new PluginResult(Status.OK) );
} else if (PRELOAD_SIMPLE.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.sendPluginResult( executePreload(data) );
}
});
} else if (PRELOAD_COMPLEX.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.sendPluginResult( executePreload(data) );
}
});
} else if (PLAY.equals(action) || LOOP.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.sendPluginResult( executePlayOrLoop(action, data) );
}
});
} else if (STOP.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.sendPluginResult( executeStop(data) );
}
});
} else if (UNLOAD.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
executeStop(data);
callbackContext.sendPluginResult( executeUnload(data) );
}
});
} else if (ADD_COMPLETE_LISTENER.equals(action)) {
if (completeCallbacks == null) {
completeCallbacks = new HashMap<String, CallbackContext>();
}
try {
String audioID = data.getString(0);
completeCallbacks.put(audioID, callbackContext);
} catch (JSONException e) {
callbackContext.sendPluginResult(new PluginResult(Status.ERROR, e.toString()));
}
} else if (SET_VOLUME_FOR_COMPLEX_ASSET.equals(action)) {
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.sendPluginResult( executeSetVolumeForComplexAsset(data) );
}
});
}
else {
result = new PluginResult(Status.OK);
}
} catch (Exception ex) {
result = new PluginResult(Status.ERROR, ex.toString());
}
if(result != null) callbackContext.sendPluginResult( result );
return true;
}
private void initSoundPool() {
if (assetMap == null) {
assetMap = new HashMap<String, NativeAudioAsset>();
}
if (resumeList == null) {
resumeList = new ArrayList<NativeAudioAsset>();
}
}
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
// Pause playback
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// Stop playback
}
}
@Override
public void onPause(boolean multitasking) {
super.onPause(multitasking);
for (HashMap.Entry<String, NativeAudioAsset> entry : assetMap.entrySet()) {
NativeAudioAsset asset = entry.getValue();
boolean wasPlaying = asset.pause();
if (wasPlaying) {
resumeList.add(asset);
}
}
}
@Override
public void onResume(boolean multitasking) {
super.onResume(multitasking);
while (!resumeList.isEmpty()) {
NativeAudioAsset asset = resumeList.remove(0);
asset.resume();
}
}
}
Someone found a solution?
same thing with "ionic-angular": "3.5.3"
@di3orlive Here is my code and it worked very well on android and simulator. We have to make sure the sound file loaded and we can play, loop or even stop it. In my case, I'm using the sound when my modal was loaded.
ionViewDidLoad() {
this.nativeAudio.preloadComplex('newOder', 'assets/sounds/beep.mp3', 1, 1, 0).then(() => {
this.nativeAudio.play('newOder');
this.nativeAudio.loop('newOder');
});
}
And on my dismiss event, it should unload the ID to make sure we can load and use for the next time, otherwise you will get the message "A reference does not exist for the specified audio id"
// my dismiss event dismiss() { this.nativeAudio.stop('newOder'); this.nativeAudio.unload('newOder'); this.vc.dismiss({ }); }
@jeff235255
I found better and faster solution.
just html5 audio + "cordova-plugin-music-controls": "~2.0.0"
only cant find same controls for lockscreen, maybe you know something ?
Hello ahm-hossein
What does this mean: " firstly I noticed that the 'id' that we have to pass to the plugin should be in json because plugin needs JSON format while it not handles this by itself. "
I tried the change to the java file ( this.dData -> data ) but still the same problem.
Thanks JC
fixed the issue preloading the audios in the app.components.ts after check if the platform is ready
I came here from ionic-native. I need to some looped alarm in my app. And here some conclusions.
preloadSimple
can't be used with loop
. That says in JSDoc comment
Loads an audio file into memory. Optimized for short clips / single shots (up to five seconds). Cannot be stopped / looped.
But it works on android normal, on ios return an error
Action restricted to assets loaded using preloadComplex()
preloadComplex
+ loop
works on ios, but it launch audio just one time and then stopspreloadComplex
+ 'play' + 'setInterval' works fineplatform.ready()
assets/...
(not ./assets/...
). And file should be placed in project_folder/src/assets/...
Here is solution based on this plugin
public setPhoneAudio(): Promise<void> {
if (this.platform.is('cordova')) {
return this.platform.ready()
.then(() => (<any> window).plugins.NativeAudio
.preloadComplex( 'ring', 'assets/sound/ring.mp3', 0.5, 1, 0,
(msg: string) => this._intervalId = setInterval(() => (<any> window).plugins.NativeAudio.play( 'ring'), 3000),
(msg: string) => alert( 'error: ' + msg )
));
}
return Promise.resolve();
}
private _intervalId;
And solution based on ionic-native
public constructor(
public platform: Platform,
private _nativeAudio: NativeAudio
) {}
public setPhoneAudio(): Promise<any> {
if (this.platform.is('cordova')) {
return this.platform.ready()
.then(() => this._nativeAudio.preloadComplex( 'ring', 'assets/sound/ring.mp3', 0.5, 1, 0))
.then(() => this._intervalId = setInterval(() => this._nativeAudio.play( 'ring'), 3000));
}
return Promise.resolve();
}
private _intervalId;
I got it to work with mp3 files on IOS and ANDROID (and WEB/HTML5) by wrapping the preloadSimple and play methods in cordova ready, AND fixing a bug in the Josh Morony tutorial. He was passing the asset path to the play method, and should be passing the key instead. Here is my solution:
https://github.com/dolthead/topzee/blob/master/src/app/services/audio.service.ts
I call the preload on each page where a sound is played, so it's very modular.
I got it to work with mp3 files on IOS and ANDROID (and WEB/HTML5) by wrapping the preloadSimple and play methods in cordova ready, AND fixing a bug in the Josh Morony tutorial. He was passing the asset path to the play method, and should be passing the key instead. Here is my solution:
https://github.com/dolthead/topzee/blob/master/src/app/services/audio.service.ts
I call the preload on each page where a sound is played, so it's very modular.
And still won't work for me Line 1 - Msg: play error {"key":"tabSwitch","asset":"assets/audio/clickSound.mp3"} A reference does not exist for the specified audio id. on android studio, even tho with your solution its taking all the paths.
I am attempting to use this plugin in an Angular 1/Ionic 1 project but I am receiving the error message that "A reference does not exist for the specified audio id." I checked inside the APK cordova build and all of my audio files were there though.
When I used the "cordovaNativeAudio" module I didn't end up receiving any error at all. I had to to use "window.plugins.NativeAudio" instead to receive that.
Anyone have an idea what could be causing this problem?