I'm working with the android source, and want to add an additional method to the AUDIO_SERVICE which is implemented in AudioManager.java. I have added the additional method to the AudioManager class, I now what to call that from and application.
I'm assuming that after adding the method I need to create/update and aidl file so the app knows about the interface
In the app I want to use it like:
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
mAudioManager.myNewFunction();
Any help would be greatly appreciated
You shouldn't update the android source code. I would do something like this:
public class MyAudioManager {
AudioManager myAudioManager;
public MyAudioManager(Context context){
this.myAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
public void myAudioManagerMethod(){
//Do your audio manager stuff here
}
}
Then you can call your method like so:
//Assuming you call this from an Activity
MyAudioManager mManager = new MyAudioManager(this);
mManager.myAudioManagerMethod();
This allows you to get an instance of the AudioManager and allows you to interact with it as needed.
AudioManager as far as I know runs as part of the app process, not the system_process. So you will need to modify an AIDL file if you want to add a method to AudioService and call it from AudioManager. But not if you are adding it only to AudioManager.
Just add the method in AudioManager, and compile.
To have it "appear" when developing an app in Android Studio, you have to generate the SDK from your ASOP env..
Be sure to run "make update-api" in order to confirm a new public API method.
Another option to SDK generation would be to use reflection in APP to access the new method. Not recommended
Related
I want to implement a cool effect, when there is an explosion the music gets slightly slower for a moment.
The Music class of libgdx doesn't allow changing the pitch of the sound, I tried using the Sound class instead of the Music to play my music but its very slow at loading the files ( 5 sec for a 5 mb file, on Desktop! ).
So, the question is if there is a way to workaround this, or if there is an external library, that works both on desktop and android and can work along with libgdx.
After some searching I found a way by editing the libgdx source code.
You need to use the AudioDevice object to play your music sample by sample ,to do that you need to include the audio-extension of libgdx.
We gonna edit the libgdx source code so you need to download it and replace the gdx.jar , gdx-backend-android.jar and gdx-backend-lwjgl.jar with the correct libgdx projects(they have the same name without the jar extension)
1)Edit the AudioDevice.java inside com.badlogic.gdx.audio package of the gdx project
add the following code inside the interface
public void setSpeed(float val);
2)Edit the AndroidAudioDevice.java inside com.badlogic.gdx.backends.android package of the gdx-backend-android project
The Android side of the AudioDevice class relies on the AudioTrack class of the Android sdk ,this class has a setPlaybackRate(..) method.
add the following code inside the class
#Override
public void setSpeed (float speed) {
track.setPlaybackRate((int)(track.getSampleRate()*speed));
}
3)Edit the OpenALAudioDevice.java inside com.badlogic.gdx.backends.lwjgl.audio package of the gdx-backend-lwjgl project
The Desktop side of the AudioDevice relies on OpenAL (the opengl of audio) which has a handy set pitch method
add the following inside the class
#Override
public void setSpeed (float speed) {
alSourcef(sourceID, AL_PITCH, speed);
}
4)Play the audio
Here is the code for loading and playing the sound file
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.AudioDevice;
import com.badlogic.gdx.audio.io.Mpg123Decoder;
import com.badlogic.gdx.files.FileHandle;
public class MusicBeat {
static short[] samples = new short[2048];
Mpg123Decoder decoder;
AudioDevice device;
static FileHandle externalFile;
public boolean playing=true;
public MusicBeat(String name )
{
FileHandle file=Gdx.files.internal(name);
FileHandle external=Gdx.files.external("myappname/"+name);
if(!external.exists())file.copyTo(external); //copy the file to the external storage only if it doesnt exists yet
decoder = new Mpg123Decoder(external);
device = Gdx.audio.newAudioDevice(decoder.getRate(),decoder.getChannels() == 1 ? true : false);
playing=false;
externalFile=file;
}
void play()
{
playing=true;
Thread playbackThread = new Thread(new Runnable() {
#Override
public synchronized void run() {
int readSamples = 0;
while ( playing) {
if(decoder!=null){
if((readSamples = decoder.readSamples(samples, 0,samples.length))<=0){
decoder.dispose();
decoder = new Mpg123Decoder(externalFile);
playing=false;
}
device.writeSamples(samples, 0, readSamples);
}
}
}
});
playbackThread.setDaemon(true);
playbackThread.start();
}
public void stop(){
playing=false;
decoder.dispose();
}
public void setVolume(float vol){
device.setVolume(vol);
}
public void setSpeed(float speed){
device.setSpeed(speed);
}
}
Where to get the audio extension?(required for the AudioDevice)
The audio extension seems to have been deprecated and the jars cant be found easily, I have uploaded them here, its an old version but should work just fine.
Easier way?
If your game is only intent to run on desktop ,the Music class of libgdx on desktop relies again on OpenAL which gives as the power to play with the pitch ,so you just need to edit the Music interface(OpenALMusic on desktop) instead of the AudioDevice and get out the whole play sample by sample thing out of the equation,unfortunately as dawez said the Music class on android relies on MediaPlayer which is not giving us the pitch change option.
Conclusion :This method doesnt seem nice to me ,If your game really needs the pitch thing and it doesn't makes sense without it then go with it ,otherwise its just too much effort for such a small detail .
It seems that you cannot change the pitch of Music in Android.
To play music in libgdx you refer to the Interface Music:
public interface Music extends Disposable {
This is implemented by
package com.badlogic.gdx.backends.android;
public class AndroidMusic implements Music, MediaPlayer.OnCompletionListener {
private MediaPlayer player;
You are interested in the player object. That is of type MediaPlayer
package android.media;
public class MediaPlayer {
So now it boils down to the question if android is able to support pitching on the MediaPlayer class. Short answer no, long answer:Speed Control of MediaPlayer in Android
A workaround that you can do is to use a SoundPool even if that is slow, the loading can be started while the user is the loading screen. Otherwise you can try can split the music files in chunks and load them as you go still using a SoundPool. So you would do something like a lazy loading when the current part is coming to its end.
If you manage to find a suitable a solution, please post the relative code!
With the release of Android 6.0 (API level 23), "PlaybackParams" can be added to the MediaPlayer. These include playback speed and pitch among others.
While searching about internal details of video player, I came across a pdf where MediaPlayer class internally uses android_media_Mediaplayer for every message(i.e, setDataSource(), prepare(), start() etc.) and android_media_MediaPlayer calls libmedia::MediaPlayer() with same message. My question is why can't MediaPlayer class directly call libmedia::MediaPlayer instead calling through android_media_MediaPlayer?
Thank you!
The link of image is given below...
http://img600.imageshack.us/img600/2005/capturejij.png
The diagram you linked to wasn't crystal clear, but I assume that the blue MediaPlayer box refers to the MediaPlayer Java class.
The libmedia MediaPlayer is a native class. Calls between Java and C/C++ need to go through the Java Native Interface (JNI), so android_media_MediaPlayer contains the necessary JNI code to communicate with the MediaPlayer Java class, thereby acting as a sort of proxy between the Java class and the native libmedia class.
For example, in MediaPlayer.java you'll find this delcaration:
public native void prepareAsync() throws IllegalStateException;
Which is listed in android_media_MediaPlayer as a JNINativeMethod:
{"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
This says that the method which Java knows as "prepareAsync" has the signature "()V" (no arguments, returns void) and corresponds to the native function android_media_MediaPlayer_prepareAsync. When android_media_MediaPlayer_prepareAsync is called, it in turn calls the native MediaPlayer's prepareAsync method.
I want to know, how to run a song in a service, I also want to use .aidl file to expose clients the interface.
Sound basic for Android..
I recommended you for go through this tutorial MusicDroid - Audio Player Part.
There are three parts of these tutorial. Its nicely describe for how to implement Audio player for android using service and AIDL.
Also look at this android developer tutorial Media Playback.
I think this will help you a lot..!
I assume you know how to create a service, I did something before similar
import android.media.MediaPlayer;
private MediaPlayer mMediaPlayer;
private void play() {
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setDataSource(getSongUrl());
mMediaPlayer.prepare();
mMediaPlayer.start();
}
when interact with UI, send Intent from the UI to the service so that you can do let pause:
mMediaPlayer.pause();
or seek to certain time:
mMediaPlayer.seekTo((int) (to * mMediaPlayer.getDuration()));
and remember make sure call the release()
Please check the class here: http://developer.android.com/reference/android/media/MediaPlayer.html
In the documentation, it is said:
The mute command is protected against client process death: if a process with an active mute request on a stream dies, this stream will be unmuted automatically.
The mute requests for a given stream are cumulative: the AudioManager can receive several mute requests from one or more clients and the stream will be unmuted only when the same number of unmute requests are received.
Well, the first paragraph is true; Whenever my process dies, all of the streams I muted are automatically unmuted.
However, no matter how many time I call setStreamMute(someStream, false) it NEVER EVER unmutes.
Last time I tried calling it over 1 million times after muting only ONCE and NOTHING happens!
Just to mention - If i unmute it in the same method I mute it - it stays unmuted. But on the next calls to the same method - it never unmutes.
I am muting in a Broadcast Receiver onReceive method, which I start using an alarm manager. So maybe it because my app was killed during the time between the muting call and the unmuting call? (But my app still stays in the RAM)
Can this problem be because I am not keeping a reference to the AlarmManager (Getting different instances each time?)
Did anyone encounter this problem?
Apperantly, there is a bug in these Android versions; Tested for versions 2.2 and 2.3.3 and the bug exists.
Looks like, if you call setStreamMute on an AudioManager object:
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
am.setStreamMute(...., true);
and you lose your reference, then get a new reference:
am = null;
am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
No matter how many times you call am.setStreamMute(..., false) now, it will never unmutes.
I think ill report this bug now..
Lesson: Keep a static reference to your AudioManager.
#Michell Bak, thanks for giving me the idea to check whether its the Android software bug :) I've been stuck on this thing for way too much time, and I never had the idea to see if its not my fault.
I've never used that API before, but a quick Google search, returned a few results with it not working. It seems to be a bug that's still present in Android 2.3:
http://code.google.com/p/android/issues/detail?id=4235
I solve the problem by putting the audio manager variable in the application
public class myApplication extends Application {
static AudioManager am;
public void onCreate() {
super.onCreate();
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
this.setMute(false);
}
}
Then in my activity class add this function:
private AudioManager getAM() {
return ((elpApplication)getApplication()).am;
}
and here is how I use the getAM();
private void toogleMediaPlayerMute() {
//defaultVolumn = am.getStreamVolume(AudioManager.STREAM_MUSIC);
elpApplication app = getElpApp();
Log.d("appmute", String.valueOf(app.isMute()));
if (!app.isMute()) {
getAM().setStreamVolume(AudioManager.STREAM_MUSIC, 0,
AudioManager.FLAG_PLAY_SOUND);
getAM().setStreamMute(AudioManager.STREAM_MUSIC, true);
ismute = true;
} else {
int maxVolume = getAM().getStreamMaxVolume(AudioManager.STREAM_MUSIC);
Log.d("maxvol", String.valueOf(maxVolume));
getAM().setStreamMute(AudioManager.STREAM_MUSIC, false);
getAM().setStreamVolume(AudioManager.STREAM_MUSIC, maxVolume,
AudioManager.FLAG_SHOW_UI);
ismute = false;
// app.setMute(ismute);
}
app.setMute(ismute);
}
I have been having the same problem w/ newer versions of the API. So, to work [around] this issue, I've implemented a bit of 'old-school' solution. If your design is such that you handle getting the byte stream / sending the byte stream directly - send a byte array filled with zeros if mute is selected:
*
...
byte[] whatToSend = realByteData;
byte [] mutedOutput = new byte[recBuffSize];
Array.setByte( mutedOutput, 0, (byte) 0);
...
if ( muteSet )
whatToSend = mutedOutput;
amountWritten = audioTrack.write(whatToSend, 0, amountRead);
*
How can I check if headphones are currently plugged in. I don't want a broadcastreceiver which informs me when they have been connected to the device. I need something like:
if(/*headphone is connected*/)
...
It looks like you'll be interested in the isWiredHeadsetOn() method and isBluetoothA2dpOn() method of the AudioManager class.
However, the isWiredHeadsetOn() method is only available in Android 2.0 or later. (The isBluetoothA2dpOn() method has been available since Android 1.5.)
Use this code snippet
AudioManager am1 = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
Log.i("am1.isWiredHeadsetOn()", am1.isWiredHeadsetOn()+"");
Log.i("am1.isMusicActive()", am1.isMusicActive()+"");
Log.i("am1.isSpeakerphoneOn()", am1.isSpeakerphoneOn()+"");
This seems to do the job at least on 1.6; not sure whether it's supported in later versions (a is an instance of AudioManager)
boolean headphones = (a.getRouting(a.getMode()) & AudioManager.ROUTE_HEADSET) == AudioManager.ROUTE_HEADSET;