Setting the volume back to what it was originally? - java

I am giving the user the choice to mute my app using AudioManager. I want to be able to have the user unmute the app to the same volume that the user was originally on. So, I created a variable that can be accessed from all of my activities by creating a separate java class. This variable gets the original volume:
public class userVolumeOnStart {
static AudioManager mAudioManager;
public static int userVolume = setVolume(); //Setting it equal to the return value of method setVolume
public static int setVolume() {
userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
return userVolume;
}
}
I call this from a different activity to unmute it like so:
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, userVolumeOnStart.userVolume, 0);
Toast.makeText(Main_Menu.this, "UNMUTED", Toast.LENGTH_SHORT).show();
For some reason, I am getting exceptions when I try to unmute it to the users original volume:
ExceptionInInitializationError
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, userVolumeOnStart.userVolume, 0);
NullPointerException
userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
public static int userVolume = setVolume(); //Setting it equal to the return value of method setVolume
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, userVolumeOnStart.userVolume, 0);
I have been stuck trying to fix this for many hours now, and I have looked through the internet with no luck. Please help me unmute the app to the same volume that the user was originally on. I would appreciate your help and feedback (positive or negative!)
Thanks,
{Rich}

You are doing lot of redundant things in your code for userVolumeOnStart(), you shouldn't need to set the variable userVolume all you need to do is use the function, userVolumeOnStart.setVolume() I dont know why you decided to call a function that actually returns the volume as setVolume you should probably rename to getVolume() anyways try changing this line
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, userVolumeOnStart.userVolume, 0);
to this
audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, userVolumeOnStart.setVolume(), 0);

Related

How to set wave height of WaveView according to SeekBar's progress?

Above GIF is current look of my music player.
The center layout is WaveView and front of that is a simple musical note vector-drawable.
I first tried to set Waveview progress according to SeekBar's progress without any song playing to test it. And it worked perfectly fine (Seekbars current progress/100 which is 0.01 to 1.00).
Now what I want to do is set WaveView's Water Level (height of water) according to song progress.
When song will play, the water level will be set from 0.0f to 1.0f and revealing the White Music note icon.
Note that the WaveView's water level can only be set from 0.0f to 1.0f.
I tried multiple ways but could not solve this problem.
Here is my code which should return 0.01, 0.02 and so on ... :
this.runOnUiThread(new Runnable() {
#Override
public void run() {
if (mainMediaPlayer != null) {
currentDurationPosition = mainMediaPlayer.getCurrentPosition()/1000;
songSeekBar.setProgress(currentDurationPosition+1);
topSongTitle.setText(songsList.get(currentSongPosition).getName());
songCircleProgress.setWaterLevelRatio(currentDurationPosition/mainMediaPLayer.getDuration());
} else {
Toast.makeText(MainActivity.this, "No song is playing now", Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, 1000);
}
});
After rereading your code, I think I see two issues here:
first, your staticFloat initialisation:
private static float staticFloat = 1/100;
By doing this, you are actually doing an integer division, then converting it to a float. 1/100 in integer division equals to 0, so your float will always be 0.0f.
To have the 0.01 value, you need to do
private static float staticFloat = 1/100f; // or 1f/100
This way you are dividing an int by a float, or the opposite, and it will have a floating result.
Secondly, your getWaterPosition function. You are returning 0 when progress is 0. In every other case, you are returning, well... 0 too.
Why? You are returning on each case
returnPosition = minusPosition - (minusPosition - staticFloat);
which is equivalent to:
returnPosition = minusPosition - minusPosition + staticFloat;
and again equivalent to:
returnPosition = staticFloat;
and as we've seen before, this would always be 0, or even 0.01f when you will fix it.
I guess you made some errors when coming with that formula. But won't it work by doing only something like return progress / 100f; ?
You can also add in your xml a max value to your seekbar, so it's never above 100 and you'll have your return between 0.0f and 1.0f
<SeekBar
// initialisation
android:max="100"/>

OnSensorChanged Being Called More Than Intended

I'm trying to develop an app that detects a fall and so far the accuracy of a fall is in an acceptable range but the problem is the "OnSensorChanged" gets invoked way too frequently causing the toast to appear on my screen constantly.
I think the problem is that during a free fall, the "OnSensorChanged" keeps on getting called for the duration of the fall (since the values correspond to a fall) which is whats causing the toast spam on my screen.
A solution i tried is to unregister the listener after a fall so that it only appears once but the problem is that it wont detect a fall again unless i restart the app (because obviously the listener stopped working).
Is there any solution to this issue?
Thanks
private void checkFall(SensorEvent xyzValues) {
float[] values = xyzValues.values;
double rootSquare = Math.sqrt(Math.pow(values[0],2)+Math.pow(values[1],2)+Math.pow(values[2],2));
if (rootSquare < 1.5){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
sensorManager.unregisterListener(this);
sensorManager = null;
}
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
checkFall(event);
}
}
You can make use of a member boolean variable, mFallen that keeps track of when a fall has just occurred. You might also want to define some threshold value to indicate when the phone is back to a normal "standing" state. Try the following:
if (rootSquare < 1.5 && !mFallen){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
mFallen = true;
} else if (rootSquare > STANDING_THRESHOLD) {
mFallen = false;
}
Better Standing Detection Algorithm
Once the state of mFallen has changed from false to true, the magnitude of the device's acceleration vector (as you've calculated and stored in rootSquare) should be very close to 0. This is because when the device is in free-fall (falling all by itself), its acceleration will be that of gravity from the reference frame of a standing person. But in theory, the accelerometer would only be measuring values relative to free-fall, and therefore it would be exactly 0.
Since the acceleration of gravity is approximately 9.81 m/s^2, to detect when the device is not moving again, you'd need to check when the magnitude of acceleration is close to that. This is because while still, the sensor will be detecting the force that is preventing it from falling (canceling the acceleration due to gravity) which are the forces of whatever is keeping it still. The overall magnitude of the accelerations due to these forces must sum up exactly to the magnitude of the acceleration due to gravity in this case.
You could define a constant GRAVITY to be 9.81, and GRAVITY_THRESHOLD to be a tolerance value, which you can try setting to 0.1 and then refining later on. Try replacing your code with the following:
if (rootSquare < 1.5 && !mFallen){
Toast toast = Toast.makeText(this, "The Phone Fell!", Toast.LENGTH_LONG);
toast.show();
mFallen = true;
} else if (Math.abs(rootSquare - GRAVITY) < GRAVITY_THRESHOLD) {
mFallen = false;
}
Just to emphasize, the standing state here is defined as only when the device has stopped moving, so it won't be able to detect when the phone has reached a certain height with respect to the ground.
Please see https://developer.android.com/reference/android/hardware/SensorEvent.html#values, as it may help you understand the problem better.

How to play lot of SoundPool files and a radio online in the same time

I just created a app in Android Studio for two reasons:
Make an instrumental-sampler, each pad will play a sound thanks to SoundPool.
And playing a radio online (using a service to play in the background).
The radio works fine and the sampler works also, but I can't use a different sampler (I have 3 different ones, with 6 pads in each one) without any errors such as:
AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 4, track 44100 Hz,
output 48000 Hz
and when I use lot of sound:
AudioTrack: AudioFlinger could not create track, status: -12
SoundPool: Error creating AudioTrack
This is my first problem, and after that the sound is playing when I touch the button maybe 1/10.
My second problem is when this problem occurs, the radio can't work anymore with this message appearing in logcat:
AudioTrack: AudioFlinger could not create track, status: -12
AudioTrack-JNI: Error -12 initializing AudioTrack
android.media.AudioTrack: Error code -20 when initializing AudioTrack.
ExoPlayerImplInternal: Playback error.
When I just use 1 sampler, or just the radio, it's OK. I just got this message in the sampler:
AUDIO_OUTPUT_FLAG_FAST denied by client; transfer 4, track 44100 Hz, output 48000 Hz
...but if I use different samplers or playing fast a lot of sound, or with the radio, it's not working
I think using a thread is OK; I don't know how it's exactly works, but in lot of posts SoundPool with a thread isn't working.
I tried to change my .mp3 files to .ogg or change the track Hz, but it's not working.
I still have these permissions in my manifest:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="RECORD_AUDIO" />
My Sampler's files are like this:
public class ActivityDrumsLofi extends AppCompatActivity {
SoundPool Soundpool;
private int sound_g, sound_h, sound_i, sound_j, sound_k, sound_l;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Soundpool = new SoundPool(32, AudioManager.STREAM_MUSIC, 0);
sound_g= Soundpool.load(this, R.raw.kick6lofi, 1);
sound_h= Soundpool.load(this, R.raw.kicklofi, 1);
sound_i= Soundpool.load(this, R.raw.hatlofi, 1);
sound_j= Soundpool.load(this, R.raw.hithatlofi, 1);
sound_k= Soundpool.load(this, R.raw.tambilofi, 1);
sound_l= Soundpool.load(this, R.raw.snare2lofi, 1);
}
public void playsample1 (View view) {
SoundpoolLofi.play(sound_g, 1, 1, 1,0, 1);
}
public void playsample2 (View view) {
SoundpoolLofi.play(sound_i, 1, 1, 1,0, 1);
}
public void playsnare (View view) {
SoundpoolLofi.play(sound_k, 1, 1, 1,0, 1);
}
public void playkick (View view) {
SoundpoolLofi.play(sound_h, 1, 1, 1,0, 1);
}
public void playhat (View view) {
SoundpoolLofi.play(sound_j, 1, 1, 1,0, 1);
}
public void platclap (View view) {
SoundpoolLofi.play(sound_l, 1, 1, 1,0, 1);
}
}
Sorry in advance for a short answer. I would comment, but I can't because I'm new to SO :)
Status -12 says, that you are basically running out of memory. This means, that either your sound-files are too big or the more likely case: you are exceeding the limit of simultaneously loaded sounds.
Soundpool can only "hold" 32 sound objects. Problem is, that every time you play a sound, it adds one to that. You have to Soundpool.release() at some point to clear up the memory.
Second problem is, that you can't find out, when Soundpool has finished playing your sound, to know when you could release it. Since you're using it over and over in the same Activity etc. it's probably not gonna work to release in onPause() or something.
A "hacky" solution would be, that you release it after a specified amount of time through a handler for example, because you know, how long your sounds are. That's definitely not an elegant solution, so maybe someone else has a better one.
I hope this helps a little (my first answer :P )

Asynchronous audio player with java for Android

I have created an Java application that runs in Android. Sounds are prepared and played only with a synchronous MediaPlayer class, involving a latency from 50 to 80 ms, that is too big for a real time product.
So, for improving performance of a sound player in Java for Android (by minimizing its latency), I am looking for an asynchronous audio player or media player.
Asynchronous because that avoids latency when loading (preparing) or playing a sound
Do you know an Android native library or something else that can be imported in a java application?
For instance, I have seen that URL but I don't know how to do for "plugging" it in a Java application?
https://developer.android.com/reference/android/media/AsyncPlayer.html
Thanks
I wrote a pretty complere chapter about Android Audio in my book. Here is the flowchart I use to decide which API to use. The old AsyncPlayer you referenced is deprecatated and would not really solve your latency issue in my opinion.
Media Player is the worse for start up latency. SoundPool is probably the best choice based on the info you have provided.
AudioTrack gives you the most flexibility.
Hope this helps. Here is a code excerpt for playing sounds using the soundPool API:
private void playSoundPool(int soundID) {
int MAX_STREAMS = 20;
int REPEAT = 0;
SoundPool soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, REPEAT);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int soundId, int status) {
int priority = 0;
int repeat = 0;
float rate = 1.f; // Frequency Rate can be from .5 to 2.0
// Set volume
AudioManager mgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
float streamVolumeCurrent =
mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
float streamVolumeMax =
mgr.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = streamVolumeCurrent / streamVolumeMax;
// Play it
soundPool.play(soundId, volume, volume, priority, repeat, rate);
}
});
soundPool.load(this, soundID, 1);
}

Sound not working? ( still needs an answer )

My audio isn't playing...I've been stuck on this for around 7 hours (ALL DAY!), and have cannot figure out the issue. I am trying to have the volume mute when user clicks mute and unmute when user clicks it again.
I want to be able to have the user unmute the app to the same volume that the user was originally on. userVolumeOnStart.userVolume is where the users original volume is stored in. I use this value in the unMute, to set their volume to what it used to be.
For example:
If your volume was 7 and you used my mute button:
Open app->Mute (Volume=0)->Unmute(Volume = 7 again) etc.
Here is the listener to the mute button:
public void mute(View view) {
mutebutton = (ImageButton) findViewById(R.id.mutebutton);
if ((variableForMute.x % 2) != 0) { //If it's odd UNMUTE
Log.v(TAG, "Use this volume to unmute " +userVolumeOnStart.userVolume +"");
Toast.makeText(Main_Menu.this, "UNMUTED", Toast.LENGTH_SHORT).show();
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, userVolumeOnStart.userVolume, 0);
variableForMute.x++;
} else { //If its even MUTE
userVolumeOnStart.userVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
Log.v(TAG, "Right Before Mute " +userVolumeOnStart.userVolume +"");
mAudioManager.setStreamVolume(AudioManager.STREAM_RING, 0, 0);
Toast.makeText(Main_Menu.this, "MUTED", Toast.LENGTH_SHORT).show();
variableForMute.x++;
}
}
userVolumeOnStart.userVolume is a variable that I defined in a seperate class, to use it globally. Here is where the audio actually runs. I can assure you the issue is not here (as I have not messed with the code in this class today) , but I will still provide the code anyway:
cdt = new CountDownTimer(20000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.v(TAG, millisUntilFinished + "left");
millisUntilFinishedRounded = ((500 + millisUntilFinished) / 1000) * 1000;
if (millisUntilFinishedRounded == 10000) { //TEN SECONDS
ten = MediaPlayer.create(TwentySeconds.this, R.raw.tenmoreseconds);
ten.start();
}
if (millisUntilFinishedRounded == 3000) {
three = MediaPlayer.create(TwentySeconds.this, R.raw.threetwoone);
three.start();
}
}
This is a service that runs in the background when the app starts.
Here is my log after changing ringer volume on my phone while app is running and pressing the mute button. It shows that userVolumeOnStart.userVolume is doing what it's supposed to be doing:
I am desperate now, so I would really appreciate any feedback (negative or positive!)
Thanks so much,
{Rich}
By the way, my question is not a duplicate of The audio volume is zero?. I have changed the audio stream since that question, and have figured out the problem there. There, there was an issue with AudioManager.STREAM_MUSIC where userVolumeOnStart.userVolume would always be 0. I have changed STRAM_MUSIC to STREAM_RING. That is not the issue now, as userVolumeOnStart.userVolume successfully changes based on user volume. The problem now is that the Media Players are not effectively working for an unknown reason.
I found the answer after 2 days!
MediaPlayer mp = MediaPlayer.create(this, R.raw.MYAUDIO);
I shouldn't break it up into declaration and instantiation:
Mp.create will call the static method which is an instance that won't get stored. When I call start() I am starting the original MediaPlayer, not the instance! So basically, I am satrting something that I never even stored, and that's why the audio wasn't playing!

Categories