How do I get the current volume/amplitude in a MediaPlayer? - android

I'm working on an app that will both record an audio file, and then have the option to play back that file once it's been recorded. The UI has an EQ component that animates relative to the current amplitude of the recording. I've got the animation working via the MediaRecorder.getMaxAmplitude() method, but can't find any means to do this with MediaPlayer. I know it must be possible since there are music visualization Live Wallpapers by default that perform this functionality but I can't see any way that it's pulling that information when combing through AOSP. Does anybody know how to make this work?

You can get the current volume of media player with the help of Audiomanager class.The code is as follows:-
AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
int volume_level= am.getStreamVolume(AudioManager.STREAM_MUSIC);
Similarly,if you want to set the default volume of media player.You can do that like as:-
am.setStreamVolume(
AudioManager.STREAM_MUSIC,
volume_level,
0);
That's all..Happy coding :)

You are in luck. There is a class called Visualizer which will do what you want I think.
import android.app.Activity;
import android.media.audiofx.Visualizer;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends Activity {
private Visualizer audioOutput = null;
public float intensity = 0; //intensity is a value between 0 and 1. The intensity in this case is the system output volume
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createVisualizer();
}
private void createVisualizer(){
int rate = Visualizer.getMaxCaptureRate();
audioOutput = new Visualizer(0); // get output audio stream
audioOutput.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
intensity = ((float) waveform[0] + 128f) / 256;
Log.d("vis", String.valueOf(intensity));
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
}
},rate , true, false); // waveform not freq data
Log.d("rate", String.valueOf(Visualizer.getMaxCaptureRate()));
audioOutput.setEnabled(true);
}
}
you will need these permissions:
<uses-permission android:name="android.permission.RECORD_AUDIO"></uses-permission>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"></uses-permission>

I think you have to use AudioManager. As the API states it can be used for volume control:
AudioManager provides access to volume
and ringer mode control.
Use
Context.getSystemService(Context.AUDIO_SERVICE)
to get an instance of this class.
Then I think this method would be useful.

As follow:
audioManager = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolumeLevel = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
int volumePercent = (int) (((float) volumeLevel / maxVolumeLevel) * 100);

You have to implement getLevel() in DataLine. This is as close to the bus as it gets in Java.
This involves calculating a running average of amplitude (waveform) data from the sound stream buffer.
This causes a lot of bus traffic to access the buffer, so left as abstract method.
Root Mean Square (RMS) is one approach:
https://community.oracle.com/message/5391003
DSP with FFT (eg. Visualizer class) gives a complete frequency spectrum, but consumes much more CPU resources.
Don't slam on full DSP if all you need is RMS (eg. simple beat detection). Limit the quality of your samples to improve performance.

I've been looking for a way to do something similar for a while. I really want to be able to see the volume/amplitude of anything being played over the media stream, but I'll settle for being able to do it for something I'm currently playing.
So far, the best solution I've found is implemented in RingDroid. I haven't looked into the code too deeply, but it looks like the way that RingDroid creates its soundmap is by actually analyzing the sound file bit by bit.
I've considered using a similar approach and then displaying a visualizer that runs separate from the audio file, but runs at a synchronized pace. However, this seems like too much work for something that should be way simpler.

I got this solution:
final int volume_level = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
int maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = (float) volume_level / maxVolume;

Related

Libgdx Sound | setPitch causes Sound object to stop playing/making noise

I have a Sound object that calls setPitch(id, 1.0f). For some reason, on most devices this seems to work and sound perfect (I realize that 1.0f is normal). But on the galaxy note 3, it makes an ugly deep sound, then the Sound object no longer makes any sounds after calling play method again (I play the Sound object every time player gets point). Setting pitch is necessary because I use the same Sound object for other parts of my game. I will link my code below, but I feel as though this may be a libgdx glitch/hardware problem. Thanks!
if (contact.getFixtureA().getFilterData().categoryBits == Config.CATEGORYBIT_BONUS) {
final BonusFixtureUserData userData = (BonusFixtureUserData) contact.getFixtureA().getUserData();
if (userData.bonus.scorable) {
long id = AudioManager.getAudioManager().playSound(AudioManager.getAudioManager().bonusSound);
AudioManager.getAudioManager().bonusSound.setPitch(id, Config.pointSoundPitch * 2);
}
}
AudioManager class (below)
public class AudioManager {
private static AudioManager audioManager;
public Sound jumpSound, deathSound, pointSound, bonusSound;
public static AudioManager getAudioManager() {
if (audioManager == null) {
audioManager = new AudioManager();
}
return audioManager;
}
private float sound_volume, music_volume;
private AudioManager() {
sound_volume = 1f;
}
public void setAudio() {
deathSound = (Sound) Game.assetManager.get("sound/death.mp3");
jumpSound = (Sound) Game.assetManager.get("sound/tap.mp3");
pointSound = (Sound) Game.assetManager.get("sound/point.mp3");
bonusSound = (Sound) Game.assetManager.get("sound/point.mp3");
}
public long playSound(Sound sound) { //Returns soundID (Sound can play multiple times, soundID refers to which instance is playing)
long id = sound.play();
sound.setLooping(id, false);
sound.setVolume(id, sound_volume);
return id;
}
public static void dispose() {
audioManager = null;
}
}
Config.pointPitchSound is actually equal to between 1.0f and 1.07f throughout the game. When it is 1.0f, then it does the glitch. I haven't tried the other values, but I would asume they'd do the same.
LibGDX audio implementation depends completely on Android. Sound.setPitch actually calls SoundPool.setRate Android documentation of setRate
Try to convert sounds to OGG format, it is known that SoundPool has issues with playing MP3 files on some devices.

SoundPool is loading sounds but every time I play it it says "sample ___ is not ready"?

I am having a bit of difficulty with my SoundPool. I have an ArrayList<Integer> of my soundIDs located in my res/raw folder. I'm creating the SoundPool like so in my ViewHolder class for my RecyclerView:
//create the SoundPool
sp = new SoundPool(20, AudioManager.STREAM_MUSIC, 0);
//create the AudioManager for managing volume
audioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
//store the streamVolume in an int
streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
//set the load listener for my soundfiles
sp.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
loaded = true;
}
});
I am playing the sound on the click of an ImageView like so:
#Override
public void onClick(View v) {
if (v.equals(cardImg)) {
sp.load(context, cardData.getSoundId(), 1);
if (loaded) {
sp.play(cardData.getSoundId(), streamVolume, streamVolume, 1, 0, 1f);
}
}
}
And every time I click on the cardImg it gives me this error:
W/SoundPool﹕ sample 2130968609 not READY
Where the sample int changes depending on which card I click, like it should.
So I know that it's GETTING the proper soundID from my ArrayList (stored in CardData class), and it definitely sees that it's loaded, because it wouldn't try to play otherwise because loaded would never be true. My question is: Why are my sounds never ready?
My sounds are .m4a format which Android says is supported in the documentation. I've tried on an Android 5.0 device and an Android 4.4 device but nothing changes.
I have tried using a MediaPlayer but I am running into memory errors as I have many short clips that need to be able to be spammed and I read that a SoundPool was a much better way to manage this (?).
Any help would be appreciated! I am super vexxed here.
You're using the wrong id. SoundPool.load returns an id that is suppose to be passed in to SoundPool.play.

Game Sound effects in Android

I am developing a simple game in Android. I want to add sound effects for each of the touch
events. However I have add background sound effect that runs throughout the game. But
how can add tiny sound effect for touching any character of the game. For better understanding
following is my class design :
I have a main activity from where as view I'm calling my GameView class that extends
surfaceView. For the bacground sound I just created the sound at mainActivity and then called
that GameView class as bellow:
public class MainActivity extends Activity {
MediaPlayer backgroundMusic;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
backgroundMusic = MediaPlayer.create(MainActivity.this, R.raw.bg);
backgroundMusic.setLooping(true);
backgroundMusic.setVolume(10.0f, 3.0f);
backgroundMusic.start();
setContentView(new GameView(this));
}
}
And following is my GameView class. I want to add sound effect here in this class onTouchEvent as bellow:
public class GameView extends SurfaceView {
#Override
public boolean onTouchEvent(MotionEvent event) {
//checking condition I want to give different sound here.
}
}
I tried to do it as mainActivity (that is using MediaPlayer.creat() ), but it shows error.
Anybody knows how to add such sound effect on the basis of my class design ?
For short sound effect like explosions, coin collections etc, it is better to use SoundPool.
You just need to create a sound pool :
SoundPool sp = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
In Lollipop and later:
AudioAttributes attrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_GAME)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build();
SoundPool sp = new SoundPool.Builder()
.setMaxStreams(10)
.setAudioAttributes(attrs)
.build();
This creates sound pool for max. of 10 sound streams (i.e. how many simultaneous sound effects can be played at any one time) and uses AudioManager.STREAM_MUSIC as sounds stream.
Be sure to also set the volume control in your Activity, so the user is able to change the volume of the proper stream:
setVolumeControlStream(AudioManager.STREAM_MUSIC);
Than, you need to load sound effects into pool and give them their identifiers:
int soundIds[] = new int[10];
soundIds[0] = sp.load(context, R.raw.your_sound, 1);
//rest of sounds goes here
You need to pass a context to load method, so either you do this inside your activity, or get is from somwhere else.
And final step to play sound is to call play method:
sp.play(soundIds[0], 1, 1, 1, 0, 1.0);
parameters are:
soundID a soundID returned by the load() function
leftVolume left volume value (range = 0.0 to 1.0)
rightVolume right volume value (range = 0.0 to 1.0)
priority stream priority (0 = lowest priority)
loop loop mode (0 = no loop, -1 = loop forever)
rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
You need to remember, that SoundPool should not use media files over 1MB, the smaller the files, the better effect and performance you have.
Be sure to release the SoundPool when you are done, or in Activity.onDestroy.
sp.release();
Hope this helps
GameView is not a subclass of Context. Pass the Activity or the ApplicationContext to the Mediaplayer
Just do the following ...
MediaPlayer mp = MediaPlayer.create(getApplicationContext(), R.raw.combo);
mp.start();
I found it at How to play a Sound Effect in Android

Android Mediaplayer volume fluctuates

I'm currently working on an app that has to play audio. I have a method that plays a series of .mp3's in order. Here is the method:
/**
* Plays a series of sounds in order without delay.
*
* #param audioResourceIds
* An in-order array of the audio asset resources to play.
* */
public void playQueuedPlaylist(int[] audioResourceIds)
{
float lastPlayedSoundDuration;
for(int i = 0; i<audioResourceIds.length; i++)
{
lastPlayedSoundDuration = playMedia(audioResourceIds[i], null);
try
{
Thread.sleep((long)lastPlayedSoundDuration);
}
catch(Exception eh)
{
Log.v("BulacsAlmighty","Exception caught at playQueuedPlaylist()");
}
}
}
/**
* Used to play voice overs
*
* #param index
* id of the sound
* #param listener
* on completion listener
*/
public int playMedia(int index, OnCompletionListener listener)
{
MediaPlayer mp = MediaPlayer.create(context, index);
mp.setVolume(streamVolume, streamVolume);
stopMedia(index); // Stops the sound if it still is playing.
mp.start();
return mp.getDuration();
}
The problem is that the sounds sometimes do not play. And when they play, the timing is correct, the order is correct, but the last audio resource played is played at a significantly lower volume than the other sounds played before it.
Tell me, where did I go wrong!?
How are you stopping the previous tracks if you are not storing and releasing the associated MediaPlayer for that track? How does stopMedia work?
setVolume will fail if the MediaPlayer is on the Error state. Please set On<Event>Listeners so that you can really find out what is happening with the previously played MediaPlayers. Particularly, you might be interested in implementing OnInfoListener and OnPreparedListener.
Also, how is streamVolume modified? You might have a race condition somewhere and that code might be setting streamVolume preemptively. Please check all code that are modifying streamVolume.
Just curious why do you call
mp.setVolume(streamVolume, streamVolume);
at all? Is it really necessary? You don't mention why the volume needs to be adjusted from code.

SoundPool slowing down app

I have a class called Sound.java that consists of 3 functions (initSound, addSound, and playSound).
public static void initSound(Context con) {
mContext = con;
soundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
soundPoolMap = new HashMap<Integer, Integer>();
audioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
streamVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
streamVolume = streamVolume / audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
}
public static void addSound(int index, int SoundID) {
soundPoolMap.put(index, soundPool.load(mContext, SoundID, 1));
}
public static void playSound(int index) {
soundPool.play(soundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
}
I called the initSound and addSound in the MainGame.java constructor.
Sound.initSound(getContext());
Sound.addSound(1, R.raw.machine_gun);
and called playSound inside a Thread (looping) in MainGame.java. PlaySound called every second when an event is triggered (for example, when an enemy is in sight, troops (more than one) will shoot (play the sound) continuously until the enemy is dead).
Sound.playSound(1);
The problem is when the sound plays, the app is slowing down.
I'm using soundpool because as far as I know for sound effects, soundpool is better than mediaplayer. (I've tried mediaplayer and the lag is even more.)
The sound file that I use is .wav (unsigned 8bit PCM, 1 Channel, 8000hz) with size 5.44KB.
Is there a better way to play effect sounds without slowing the game performance, or is mine wrong? I really appreciate any ideas and responses.
According to the SoundPool docs, it decodes to 16-bit PCM, so you could change to that and see if you get some performance out of that. Other than that, your code seems pretty similar (at least as far as I can remember) to stuff I've done before and I didn't see any significant perf problems (I believe I wasn't even using WAV, I was just using OGGs, but I don't remember for sure). Are you trying this on an actual device, or just the emulator?
I had the same problem. Then I've created a new thread for the soundpool and added 20ms sleep into the while loop. The problem is gone.

Categories

Resources