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
Related
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.
I have a class that extends Service, where i have a MediaPlayer that manages the background music of my app.
MediaPlayer player = MediaPlayer.create(this, R.raw.background_music);
player.setLooping(true);
The problem is that it doens't "loop" well: I mean, when the mp3 file ends, there's a second of silence before it starts again.
But, in the mp3 file, actually, there's not any second of silence.
How can i solve this problem?
You can't. This bug has been there for ages now, but they still didn't fix it and it does not look like they are going to do it in the close future!
http://code.google.com/p/android/issues/detail?id=18756
You can try to use SoundPool to play your audio file (it loops very well and without delay, however might be inappropriate for some audio resources).
Here is some kind of implementation (deprecation removal and appropriate closing of resource is left as a homework :))
SoundPool pool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
afd = getResources().openRawResourceFd(audioResource);
pool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (0 == status) {
soundPool.play(sampleId, 1f, 1f, 10, -1 /* looping parameter */, 1f);
}
}
});
pool.load(afd, 10);
afd.close();
From other side if MediaPlayer is a must have you might look into method setNextMediaPlayer. So the idea is to initialize several MediaPlayers for the same file (what a mess) and set them in a smart way.
I would like to play sound after touching the button. MediaPlayer works fine, but I read somewhere that this library is for long .wav (like music).
Is there any better way to play short .wav(2-3 sec.)?
The SoundPool is the correct class for this. The below code is an example of how to use it. It is also the code I use in several apps of mine to manage the sounds. You can have as may sounds as you like (or as memory permits).
public class SoundPoolPlayer {
private SoundPool mShortPlayer= null;
private HashMap mSounds = new HashMap();
public SoundPoolPlayer(Context pContext)
{
// setup Soundpool
this.mShortPlayer = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
mSounds.put(R.raw.<sound_1_name>, this.mShortPlayer.load(pContext, R.raw.<sound_1_name>, 1));
mSounds.put(R.raw.<sound_2_name>, this.mShortPlayer.load(pContext, R.raw.<sound_2_name>, 1));
}
public void playShortResource(int piResource) {
int iSoundId = (Integer) mSounds.get(piResource);
this.mShortPlayer.play(iSoundId, 0.99f, 0.99f, 0, 0, 1);
}
// Cleanup
public void release() {
// Cleanup
this.mShortPlayer.release();
this.mShortPlayer = null;
}
}
You would use this by calling:
SoundPoolPlayer sound = new SoundPoolPlayer(this);
in your Activity's onCreate() (or anytime after it). After that, to play a sound simple call:
sound.playShortResource(R.raw.<sound_name>);
Finally, once you're done with the sounds, call:
sound.release();
to free up resources.
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.
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;