Android soundpool loop not working: Alternative - android

I want to play a short sound (.ogg) on Android and tried soundpool.
The sound should be played several times so I used sound pool loop parameter. On my Nexus 4 (JB4.3), the loop parameter in soundpool gets ignored and the sound will only be played once.
It seems to be a bug in soundpool:
Soundpool not looping in android 4.3
What is the best alternative for soundpool to play a short sound and repeating that sound?

This issue was being discussed in the android issue tracker (http://code.google.com/p/android/issues/detail?id=58113) .
There is apparently no straight forward workaround.
In audioTrack, sound looping can be achieved with setLoopPoints() API call.
audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRateInHz, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, totalNumOfSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(pcmdata, 0, pcmdata.length);
//the end frame is the length/4 if it is 16bites
audioTrack.setLoopPoints(0,pcmdata.length/4,-1);
audioTrack.play();

I have find the Solution to fix the problem of looping.
I don't know how but is works with this limitation.
soundpool that can Loop the sound only file that size is < 1mb.

Related

audioTrack.stop doesn't stop playing audio

I am creating AudioTrack with following definition.
audioTrack = new AudioTrack(
AudioManager.STREAM_MUSIC,
44100,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT,
buffer.length * 2,
AudioTrack.MODE_STATIC);
audioTrack.write(buffer, 0, buffer.length);
audioTrack.setPositionNotificationPeriod(500);
audioTrack.setNotificationMarkerPosition(buffer.length);
progressListener = new PlaybackProgress(buffer.length);
audioTrack.setPlaybackPositionUpdateListener(progressListener);
When the audioTrack finishes, the following is called to stop the audio and reset the head position.
private void resetAudioPlayback() {
ViewGroup.LayoutParams params = playbackView.getLayoutParams();
params.width = 0;
playbackView.setLayoutParams(params);
audioTrack.stop();
audioTrack.reloadStaticData();
playImage.animate().alpha(100).setDuration(500).start();
}
The above code works perfectly fine with Android 5.1. But I having issues with 4.4.4. audioTrack.stop() is called but the audio is not stopped, since the reloadStaticData rewinds the audio back to the start position, it replays the audio. but with 5.1, it correctly stops and resets the buffer back to the start of the playback and when play button is pressed, plays from beginning.
Can someone help me how can I this issue with Android 4.4.4?
I'm not absolutely certain if this will solve your problem, but consider using pause() instead of stop(). By documentation, stop() for MODE_STREAM will actually keep playing the remainder of the last buffer that was written. You're using MODE_STATIC, but it might be worth trying.
Also (possibly unrelated), consider that write() returns the number of bytes written, so you shouldn't depend on a single write filling the entire buffer of the AudioTrack every time. write() should be treated like an OutputStream write in that it may not write the entire contents of the buffer it was given, so it's better to write a loop and check how much has been written with each call to write(), then continue to write from a new index in the buffer array until the sum of all the writes equals the length of the buffer.

Way to fade out Android AudioTrack on .pause()?

I've been delving into Android AudioTrack against my better interest. I am trying to seamlessly transition between two AudioTrack's playback, that is, one should pause and the other should start and there should be no gap between the two.
This works okay, but I have noticed that when calling the .pause() method on AudioTrack, it will 'pop' or 'crackle' when stopping playback of the sound. This is unsurprising, as suddenly stopping the playback of a sound in this manner (especially if it is at a high point) is bound to create these kinds of artifacts.
However, if I could fade out the playback of the AudioTrack when pause is called, this would be a non-issue. This is easier said than done, however, because it appears Android AudioTracks cannot be modified in place. I also can't use .setVolume() because I am targeting API 17 as my minimum so Android 4.0 users can still use my app.
Is there any way of doing this? My immediate thoughts were to create a new pause(AudioTrack at) method that would modify the AudioTrack buffer and allow it to quickly fade out, and then calling pause once it had faded. It isn't a huge deal for me if the pause occurs a few frames late if it means the popping sound will be gone. Unfortunately I don't see an easy way to do this.
Here's what I have so far:
if(event.getAction() == MotionEvent.ACTION_DOWN) {
audioTracks[noteToPlay].release();
audioTracks[noteToPlay] = new AudioTrack(AudioManager.STREAM_MUSIC,
sr, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, BUFFSIZE,
AudioTrack.MODE_STATIC);
writeSample(noteToPlay);
audioTracks[noteToPlay].play();
}
else if (event.getAction() == MotionEvent.ACTION_UP) {
short[] release = makeReleaseSample(noteToPlay);
AudioTrack releaseTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sr, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, BUFFSIZE,
AudioTrack.MODE_STATIC);
releaseTrack.write(release, 0, release.length);
audioTracks[noteToPlay].pause();
releaseTrack.play();
}
As you can see, within the ACTION_UP handler I pause the audioTracks[noteToPlay] track and play the release track right after. The pop occurs on the pause, because the audioTracks[noteToPlay] contains a sine wave and pause is not pausing at the low point so it is creating artifacts.
Something to note is that the last frame of audioTrack and the first frame of release contain the same frame, so I know it's not a case of jumping from the point in the first audioTrack to the point in the second -- I am fairly certain it is due to the sudden cut-off of the first audioTrack.
Any ideas?

Android: why MediaPlayer plays the same sound louder than SoundPool?

You can play a sound with MediaPlayer or SoundPool on Android.
Two of our Android devices play the same sound much louder when MediaPlayer is used.
On the third device, the volume sounds the same.
Do you know why?
Can I make SoundPool to play sounds as loud as MediaPlayer?
Devices in question:
Tablet LG-V400 (Android 4.4 Kit Kat)
Phone Sony Xperia L (Android 4.2.2)
Code: play mp3 sound with SoundPool
private void playSoundPool() {
int MAX_STREAMS = 2;
SoundPool soundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
soundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int soundId, int status) {
int loop = 0;
int priority = 0;
float rate = 1.f;
soundPool.play(soundId, 1, 1, priority, loop, rate);
}
});
soundPool.load(this, R.raw.test, 1);
}
Code: play mp3 sound with MediaPlayer
private void playMediaPlayer() {
MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.test);
mediaPlayer.setVolume(1, 1);
mediaPlayer.start();
}
You are welcome to download test project.
Possible Solution:
You need to create AudioManager for getting, changing the global media volume set by user in phone and changing the volume for our own app independently by changing stream volume in soundPool.
AudioManager mgr = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
int streamVolume = mgr.getStreamVolume(AudioManager.STREAM_MUSIC);
streamVolume = streamVolume / AudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
mSoundPool.play(mSoundPoolMap.get(index), streamVolume, streamVolume, 1, 0, 1f);
Note:
if I set 0.5 for volume in soundpool, the actual volume will be always half of the global one. Very easy to reproduce:
set global media volume in phone settings to max
set volume in activity using soundpool.play to 0.5 - play sound
set volume in soundpool.play to 1 - play sound, it will be two times
louder
So volume passed to SoundPool.play method really a multiplier to the global volume!
**So If you want to play a sound at the current volume setting just pass "1" as the volume or change it as per the requirement **
may be media player class using global media volume for playing the audio file. you are using hard-coded soundPool.play(soundId, 1, 1, priority, loop, rate); values !
we need try with
MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.test);
mediaPlayer.setVolume(streamVolume,streamVolume);
mediaPlayer.start();
Some more Texts :
SoundPool:
SoundPool is designed for short clips which can be kept in memory
decompressed for quick access, this is best suited for sound effects in apps or games. Using this method with soundboards is a bad
idea as you will be loading lots of “medium” sized sounds into the
memory and you may exceed your limit (16Mb) and get an
OutOfMemoryException. SoundPool load music files using a separate
thread, the operation of the main thread does not block the UI.
it can be used to play short sound clips say like gun shots during
a game (something like less than 3 seconds long). A nice feature that
sound pool comes with is that you can play many sounds simultaneously
which happens a lot when you think of a game. So you first build a
Sound Pool object to load all media files.
MediaPlayer:"
MediaPlayer is designed for longer sound files or streams, this is
best suited for music files or larger files. The files will be loaded
from disk each time create is called, this will save on memory space
but introduce a small delay (not really noticeable).
Ref: SO
Mechanism difference is there, as you can say that due to compression and decompression the sound quality and volume could go below the actual one, and when you compare it with MediaPlayer. So it is better to use MediaPlayer for better sound quality and volume in your case.
SoundPool
SoundPool is designed for short clips which can be kept in memory decompressed for quick access, this is best suited for sound effects in apps or games. Using this method with soundboards is a bad idea as you will be loading lots of “medium” sized sounds into the memory and you may exceed your limit (16Mb) and get an OutOfMemoryException.
MediaPlayer
MediaPlayer is designed for longer sound files or streams, this is best suited for music files or larger files. The files will be loaded from disk each time create is called, this will save on memory space but introduce a small delay (not really noticeable).

Android AudioTrack stream cuts out early

I'm trying to play some looping sound in Android, and I have that going pretty well for me. All good things must come to an end, though, and I would like for that to include my audio loop. However, if I call AudioTrack.release() after this loop, as I should, the end of my audio stream gets cut off - there is extra data that I know I'm supposed to hear, but don't.
I've verified this by putting in a Thread.sleep(2000) before the release - the sound plays correctly with that in there. My code looks something like this:
// Initialize Audiotrack
int minBufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, 2 * minBufferSize, AudioTrack.MODE_STREAM);
mAudioTrack.play();
// Play looping sound
while (stuff) {
mAudioTrack.write(stuff);
}
// Play one last bit of sound before returning
mAudioTrack.write(lastSound);
// Block until the AudioTrack has played everything we've given it
Thread.sleep(2000);
// Get rid of the Audiotrack
mAudioTrack.release();
I suppose I could leave the Thread.sleep(2000) in there and call it a day, but that sounds messy and irresponsible to me. I'd like to either have a while() loop block for the most appropriate amount of time, or use AudioTrack.setPlaybackPositionUpdateListener() and put the release() in there.
If I go the first route, I need something to pend on, and AudioTrack.getPlayState() appears to always report the track as playing. So I'm stuck there.
If I go the second route, I need a way of getting the position in the AudioTrack buffer that was written to last, so I can tell the AudioTrack what position I'm waiting for it to play up to. I don't have any ideas as to how to get that information, though.
I guess I don't really care which way I do it, so any help towards solving the problem one way or the other would be much appreciated.
The problem is related to the buffer size in the AudioTrack.
Imagine the minBufferSize is 8k. This means that the AudioTrack will play sound when the buffer is full.
mAudioTrack.write(stuff);
If stuff is only 4K, the AudioTrack will wait until the next call to write until it has enough data to play.
Conclusion: You need to keep track on how much data you have written, and at the end of your playback feed the AudioTrack with some dummy bytes to complete minBufferSize. To make thing easier you could just feed a whole minBufferSize amount of silence bytes.
By the way, to feed dummy or silence just fill the data with zeroes.

Playing back sound coming from microphone in real-time

I've been trying to get my application recording the sound coming from the microphone and playing it back in (approximately) real-time, however without success.
I'm using AudioRecord and AudioTrack classes for record and playback, respectively. I've tried different approaches, I've tried to record the incoming sound and write it to a file and it worked fine. I've also tried to playback sound from that file AFTER with AudioTrack and it worked fine too. The problem is when I try to play the sound in real-time, instead of reading a file after it's written.
Here is the code:
//variables
private int audioSource = MediaRecorder.AudioSource.MIC;
private int samplingRate = 44100; /* in Hz*/
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private int bufferSize = AudioRecord.getMinBufferSize(samplingRate, channelConfig, audioFormat);
private int sampleNumBits = 16;
private int numChannels = 1;
// …
AudioRecord recorder = new AudioRecord(audioSource, samplingRate, channelConfig, audioFormat, bufferSize);
recorder.startRecording();
isRecording = true;
AudioTrack audioPlayer = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM);
if(audioPlayer.getPlayState() != AudioTrack.PLAYSTATE_PLAYING)
audioPlayer.play();
//capture data and record to file
int readBytes=0, writtenBytes=0;
do{
readBytes = recorder.read(data, 0, bufferSize);
if(AudioRecord.ERROR_INVALID_OPERATION != readBytes){
writtenBytes += audioPlayer.write(data, 0, readBytes);
}
}
while(isRecording);
It is thrown a java.lang.IllegalStateException with the reason being caused by "play() called on a uninitialized AudioTrack".
However, if I change the AudioTrack initialization for example to use sampling rate 8000Hz and sample format 8 bits (instead of 16), it doesn't throw the exception anymore and the application runs, although it produces horrible noise.
When I play AudioTrack from a file, there is no problem with the initialization of the AudioTrack, I tried 44100 and 16 bits and it worked properly, producing the correct sound.
Any help ?
All native Android audio is encoded. You can only play out PCM formats in real time, or use a special streaming codec, which I don't think is trivial on Android.
The point is that if you want to record/play out audio simultaneously, you would have to create your own audio buffer and store raw PCM-encoded audio samples in there (I'm not sure if you're thinking duh! or whether this is all over your head, so I'll try to be clear but not to chew your own gum).
PCM is a digital representation of an analog signal in which your audio samples are a set of "snapshots" of the original acoustic wave. Because all kinds of clever mathematicians and engineers saw the potential in trying to reduce the number of bits you represent this data with, they came up with all sorts of encoders. The encoded (compressed) signal is represented very differently from the raw PCM signal and has to be decoded (en-cod-er+dec-oder = codec). Unless you're using special algorithms and media streaming codecs, it's impossible to play back an encoded signal like you're trying to, because it's not encoded sample by sample, but rather frame by frame, where you need the whole frame of samples, if not the complete signal, to decode this frame.
The way to do it is to manually store audio samples coming from the microphone buffer and manually feeding them to the output buffer. You will have to do some coding for that, but I believe there are some open-source apps that you can look at and take a peak at their source (unless you're willing to sell your app later on, of course, but that's a whole different discussion).
If you're developing for Android 2.3 or later and are not too scared of programming in native code, you can try using OpenSL ES. The Android-specific features of OpenSL ES are listed here. This platform allows you somewhat more flexible audio manipulation and you might find just what you need, if your app will be highly reliant on audio processing.
It is thrown a java.lang.IllegalStateException with the reason being
caused by "play() called on a uninitialized AudioTrack".
It is because the buffer size too small. I tried "bufferSize += 2048;", it's ok then.
I had a similar problem and I solved it by adding this permission to the manifest file:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
make sure that your var data is enough for samplingRate
Ex: if you use samplingRate as 44100 your data bytearrays's length should be 44101 or more

Categories

Resources