In my Launchpad Simulator there are a lot of Buttons on the main activity. I want to play sound on each button press. If the buttons are pressed simultaneously, I want to play the sound simultaneously. From what I made uptil now, it seems that Whenever I press buttons it does play the sound, but since the first sound is 3 seconds long (eg. a clap sound file is 3 seconds long in its playtime).
Before any other sound is produced through buttons, it waits 3 seconds and the plays the next sound if any button is pressed after 3 seconds and same with other 32 buttons.
Just like a launchpad as soon as I press buttons I want a sound to be played. In this case the clap sound.
Here is the Java Code:
MainActivity.java
package com.oniichansblog.launchpadify;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.media.MediaPlayer;
public class MainActivity extends AppCompatActivity {
private MediaPlayer clap1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
clap1 = MediaPlayer.create(this, R.raw.clap1);
}
public void clap1(View v){
clap1.start();
}
}
I only have this one function clap1() that is being called over and over everytime I press a button.
if you want to check it for yourself I have a repository:
GitHub Repository for LaunchPadify
I'd recommend to not define the MediaPlayer above.
Just create a new temporary MediaPlayer every ButtonClick
new MediaPlayer.create(this, R.raw.clap).start();
and it will play sounds simultaneously
UPDATE
If you want to destroy a MediaPlayer after it finishes playing the sound, you can do this by using this code
new MediaPlayer.create(getApplicationContext(), R.raw.test)
.start()
.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.release();
}
});
To play several tracks simultaneously you should use AsyncTask and execute it on THREAD_POOL_EXECUTOR.
How to do this:
Thread class:
class PlayThread extends AsyncTask<MediaPlayer, Void, Void>
{
#Override
protected Void doInBackground(MediaPlayer... player) {
player[0].start();
return null;
}
}
Media Players:
MediaPlayer[] players = new MediaPlayer[5];
//Then set source to each player...
//...
//Create objects of Thread class
PlayThread[] playThreads = new PlayThread[5];
for (int i = 0; i < 5; i++)
playThreads[i] = new PlayThread();
//Set Media Players there and start play
for (int i = 0; i < 5; i++)
playThreads[i].executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, players[i]);
AsyncTask can execute only 5 Threads simultaneously, other are going to queue.
SoundPool buffer limit is 1Mb, so it is not a solution at all.
It is very important:
To play simultaneously more audio tracks you should mix them into 1 buffer.
The way to do this: decode mp3 to pcm (to small buffers in stream mode), mix it into one byte buffer and put it to AudioTrack for playing.
Related
I have a project where I added a MediaPlayer in a custom Array Adapter and I make it take the Resource ID of the audio file from the Array List the problem is that I want to release the media player after the audio completes but every time I try to add release in OnCompletion and try to press a view while the media player plays a sound the app crashes .. I searched online and found a code but it gives me an error and I can't find the actual reason for such a crash.
that's the code for the on Click Listener and the Media player:
final MediaPlayer mediaPlayer = MediaPlayer.create(getContext(), currentWord.getVoiceResourceID());
// Wait for user's input by his click on the list's item to play the sound.
View.OnClickListener listener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Storing the return value of getVoiceResourceID() into mVoice.
// I think this line can be ignored so we call the method inside the creation of the Media Player.
// Creating our media player and put our track on it.
mediaPlayer.selectTrack(currentWord.getVoiceResourceID());
mediaPlayer.start();
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mediaPlayer.release();
}
});
}
};
I hope I could fix that problem.
Trying something pretty out of the box... I have a simple app with a button that when pushed, plays music out of the audio jack of my android tablet.
public void btn1 (View view) {
MediaPlayer mp = MediaPlayer.create(this, R.raw.xxx);
mp.start();
}
I've now added a usb audio interface (through a micro usb adapter) and I can hear audio out of it.
I'm able to list the sound cards with this
AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
for (AudioDeviceInfo device : devices) {
int b = device.getId();
int d = device.getType();
CharSequence productName = device.getProductName();
}
How do I route music so that I can play 2 different music at once, one through usb and the other through the headphone jack?
According to the MediaPlayer documentation, you can set the audio device using setPreferredDevice which receive an AudioDeviceInfo as a parameter, see https://developer.android.com/reference/android/media/MediaPlayer.html#setPreferredDevice(android.media.AudioDeviceInfo).
You will then have to create one MediaPlayer to play on each device.
it works about like this:
protected void playAudio() {
this.playByDeviceIdx(0, R.raw.xxx);
this.playByDeviceIdx(1, R.raw.yyy);
}
protected void playByDeviceIdx(int deviceIndex, #IdRes int resId) {
/* obtain audio-output device-infos */
deviceInfos[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
/* check, if the desired index is even within bounds */
if(deviceInfos.length < deviceIndex) {
/* create an instance of MediaPlayer */
MediaPlayer mp = MediaPlayer.create(this, resId);
/* assign a preferred device to the MediaPlayer instance */
mp.setPreferredDevice(deviceInfos[deviceIndex]);
/* start the playback (only if a device exists at the index) */
mp.start();
}
}
you could also filter for the headset jack plug/unplug event:
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
Intent intent = context.registerReceiver(null, intentFilter);
boolean isConnected = intent.getIntExtra("state", 0) == 1;
sources: me, based upon the SDK documentation for the MediaPlayer.
Ok, somewhat similar questions have been asked, but there is no answer that has made any difference in solving my problem. I've tried Thread.sleep, and also the delayed runnable. Using a Handler, etc.
I want to display a sequence of frame animations(AnimationDrawable) using the same imageview and changing the background animations as needed. The user inputs a series of 5 animations and can then play them back(if my program worked). Once the animations are selected I use a for loop that contains if statements and a switch statement to select that whatever animations were chosen and play them back.
As you might imagine, this doesn't work correctly, as the program whips through the for loop and only the first and last animation actually play. Here is the jist of the code:
for(i=0;i<5;i++){
if(conditions){
view.setBackground(chosenAnimation);
((AnimationDrawable)view.getBackground().start();
}
}
So as I said, I have tried Thread.sleep(), that doesn't do what I'm looking for. I have tried using the Handler class and putting a delay on the runnable. I have tried this:
view.postDelayed(new Runnable() {
#Override
public void run() {
//works the same without this line
((AnimationDrawable)view.getBackground()).stop();
((AnimationDrawable)view.getBackground()).start();
}
}, 1000);
None of these things do anything at all except add pauses before it does the exact same thing it did before I added this stuff. I have meticulously debugged the code and everything is working correctly. The animations have all been individually tested.
As I said, similar questions have been asked and answered and nothing offered does what I want, which is for the program to wait until one animation is finished before it runs through the for loop again.
I'd like to state again that this is a series of frame animations using AnimationDrawable and the same imageview each time. Thanks in advance!
All your animations will be started with the same delay, you should increase this delay by multiplying it by i for example. You also can count duration of every animation programmatically and increase delay as you need it.
I just tried to achieve what you want and had no problems, although my example uses 3rd party library, it's not necessary.
package com.example.masktest.app;
import android.animation.Animator;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicReference;
import static com.dtx12.android_animations_actions.actions.Actions.*;
public class MainActivity extends AppCompatActivity {
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playAnimation();
}
});
}
private void playAnimation() {
final AnimationDrawable firstDrawable = (AnimationDrawable) ContextCompat.getDrawable(MainActivity.this, R.anim.anim_android);
final AnimationDrawable secondDrawable = (AnimationDrawable) ContextCompat.getDrawable(MainActivity.this, R.anim.anim_android_2);
final AtomicReference<Integer> cpt = new AtomicReference<>(0);
Animator sequence = repeat(6, sequence(run(new Runnable() {
#Override
public void run() {
if (imageView.getDrawable() instanceof AnimationDrawable) {
((AnimationDrawable) imageView.getDrawable()).stop();
}
imageView.setImageDrawable(cpt.get() % 2 == 0 ? secondDrawable : firstDrawable);
((AnimationDrawable) imageView.getDrawable()).start();
cpt.set(cpt.get() + 1);
}
}), delay(countAnimationDuration(secondDrawable))));
play(sequence, imageView);
}
private float countAnimationDuration(AnimationDrawable drawable) {
int duration = 0;
for (int i = 0; i < drawable.getNumberOfFrames(); i++) {
duration += drawable.getDuration(i);
}
return duration / 1000f;
}
}
You can simply using for loop and you dont need 3rd party library.
Lets say you have a LinearLayout(or any viewgroup) that you want to add these buttons dynamically. You can do something like this:
for (int i = 0; i < buttons.size(); i++) {
Button button = new Button(context);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
linearLayout.addView(button);
animateButton(button);
}
}, BUTTON_DELAY_DURATION * i);
}
Thanks to some assistance from dtx12 here, I have realized my problem, and I thought I'd leave some code here since I am not good enough to completely understand their example.
I didn't quite understand what I was doing making new threads, and it turns out I was just making 5 threads that all went off at the same time, as dtx12 explained and I eventually understood. So here's a basic way of doing this:
public void playAnimation(){
// in dtx12's answer they demonstrate how to get the
// exact duration of any give animation using
//getNumberOfFrames() and getDuration() methods of
// AnimationDrawable but I am just hardcoding it for simplicity
int duration=2000;
// This is to keep track of the animations which are in an array, I use this variable in run()
order=1;
for(int i=0;i<5;i++){
//play the first animation
if(i==0){
view.setBackground(animation[0]);
animation[i].stop();
animation[i].start();
}
else{
//Now set up the next animation to play after 2000ms, the next after 4000ms, etc.
//You are supposed to use a handler if you want to change the view in the main thread.
//it is very easy: Handler handler=new Handler():
handler.postDelayed(new Runnable(){
#Override
public void run() {
view.setBackground(animation[order]);
animation[order].stop();
animation[order].start();
order++;
}
}, duration);
//dtx12 suggestion of multiplying by i is probably smarter
duration+=2000;
}
}
}
So there you have it. Not the most elegant display of coding, but it gets the basic job done. You can't use 'i' in the run() method because it is an anonymous inner class, and if you assign the value of 'i' to another variable in the loop, it will be '4', by the time the other threads execute. So, I just did some counting inside of run to make sure everything fired in order.
What i am trying to acomplish is for me to click on a button and then play a sound based on a substring.
This is my current code for pressing the button :
display.setText("start");
thefull = thef.getText().toString();
for(int a=0;a<thefull.length();a++)
{
letter=thefull.substring(a,a+1);
if(letter=="m")
{
oursong = MediaPlayer.create(MainActivity.this, R.raw.m);
oursong.start();
while(oursong.isPlaying())
{
display.setText("start");
}
oursong.release();
}
else if(letter=="a")
{
oursong = MediaPlayer.create(MainActivity.this, R.raw.a);
oursong.start();
while(oursong.isPlaying())
{
display.setText("start");
}
oursong.release();
}
display.setText("done");
but for some reason when i click my button no sound is played.
I am also new to android and java programming, so am i going right about doing this?
Because what i really want is for the program to keep checking if a sound is playing(in this case "oursound) when the button is clicked and if the sound is playing i want the program to wait for it to finish to start another sound right after it.
But for now my code doesn´t play any sounds
First of all you need to stop the media player before releasing it(its safer.)
Second, instead of using while(oursong.isPlaying())
{
display.setText("start");
} ; you have to register OnCompletionListener using setOnCompletionListener method. When the song is played endCompletion will be called. Like so;
MediaPlayer mp = new MediaPlayer();
mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener(){
public void onCompletion(MediaPlayer player) {
player.release();
}});
and inside the onComplete should reset the text "done"
I'm wondering if there is any way to get an audio effect like the one here
in Java and/or Objective C. It is normally done in After Effects, but I want to create it on a smartphone. I know that the x axis is frequency and the y axis is volume. Can anyone help?
I'm not sure about iOS, but if you don't mind the extra space, Eclipse can handle a .mp3 file, and you can play it using the MediaPlayer class. As for creating the file, I'm not sure .raw has enough distinct sounds.
I played an mp3 in Android using a separate Music.java class and then calling it with mp3s in res/raw/*.mp3
import android.content.Context;
import android.media.MediaPlayer;
public class Music
{
private static MediaPlayer mp = null;
//Stop old song, play new one
public static void play(Context context, int resource)
{
stop(context);
if (Prefs.isMusic(context)) //is music on?
{
mp = MediaPlayer.create(context, resource);
mp.setLooping(true);
mp.start();
}//end if
}//end play method
//Stop the music
public static void stop(Context context)
{
if(mp != null)
{
mp.stop();
mp.release();
mp = null;
}//end if
}//end stop method
}//end class Music
and the calls in my Game.java file:
Music.play(this, R.raw.game);
Music.stop(this);