I am having problems with playing sound on android with the SoundPool, this works nicely with the MediaPlayer class. Why I can't use the MediaPlayer class is because it's not supporting the change of the sound pitch or speed.
The use case: Sound is played on background and it will be changed based on the user input.
The current problem: Does not play the sound correctly and freezes the application.
Question: What would be correct way to handle this situation, should the background sound played in separate thread and the communication of changing the sound volume should be done using Handler and messages ?
#Override
public void surfaceCreated(SurfaceHolder holder) {
soundPool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
RAIN_SOUND_ID = soundPool.load(getContext(), R.raw.rain, 1);
new Thread(new Runnable() {
#Override
public void run() {
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
#Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (sampleId == RAIN_SOUND_ID) {
soundPool.play(RAIN_SOUND_ID, 1f, 1f, 10, -1, 1);
}
}
});
}
}).start();
}
The SoundPool class actually creates and manages it's own thread pool behind the scenes, for playing it's sounds. So you can simplify your code to remove your additional threading as a potential issue.
Related
I am creating a basic camera app as a small project I'm doing to get started with Android development.
When I click on the button to take a picture, there is about a 1-second delay in which the preview freezes before unfreezing again. There is no issue with crashing - just the freezing issue. Why is this happening and how can I fix it?
Below is the method where the camera is instantiated, as well as my SurfaceView class.
private void startCamera() {
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
cameraPreviewLayout = (FrameLayout) findViewById(R.id.camera_preview);
camera = checkDeviceCamera();
camera.setDisplayOrientation(90);
mImageSurfaceView = new ImageSurfaceView(MainActivity.this, camera);
cameraPreviewLayout.addView(mImageSurfaceView);
ImageButton captureButton = (ImageButton)findViewById(R.id.imageButton);
captureButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
camera.takePicture(null, null, pictureCallback);
camera.stopPreview();
camera.startPreview();
}
});
}
public class ImageSurfaceView extends SurfaceView implements
SurfaceHolder.Callback {
private Camera camera;
private SurfaceHolder surfaceHolder;
public ImageSurfaceView(Context context, Camera camera) {
super(context);
this.camera = camera;
this.surfaceHolder = getHolder();
this.surfaceHolder.addCallback(this);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
this.camera.setPreviewDisplay(holder);
this.camera.startPreview();
this.camera.setDisplayOrientation(90);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
EDIT: There is currently nothing in the pictureCallback.
Camera.PictureCallback pictureCallback = new Camera.PictureCallback() {
}
You don't need to call stopPreview() after takePicture(). And you don't need startPreview() on the next line. You do need startPreview() inside your onPictureTaken() callback (not in onClick() as in the posted code!!) if you want live preview to restart after the picture is captured into a Jpeg stream.
To keep your UI responsive while using camera, you should do all work with the camera on a background thread. But it is not enough to call Camera.open() or Camera.close() on some background thread. You must create a Handler thread and use it for Camera.open(). The same Looper will be used for all camera callbacks, including PictureCallback.onPictureTaken(). See my detailed walkthrough about the use of HandlerThread.
As I explained elsewhere, you can achieve even better performance if you use the new camera2 API on devices that fully support this API (but better use the old API with devices that provide only LEGACY level of camera2 support).
But if you want to get maximum from the camera ISP, this kind of freeze may be inevitable (this depends on many hardware and firmware design choices, made by the manufacturer). Some clever UI tweaks may help to conceal this effect.
You’ll need to enable access to the hidden “Developer options” menu on
your Android phone. To do that, simply tap the “About phone” option in
Settings. Then tap “Build number” seven times and you’re done. Now you
can just back out to the main Settings menu and you’ll find Developer
options somewhere near the bottom of the list.
==>Now that you’re done with that part, let the real fun begins. Tap the new Developer options menu you just enabled and scroll until you
see the following three settings (note that they may be located within
an “Advanced” subsection):
Window animation scale Transition animation scale Animator animation
scale
==>Did you see them? By default, each of those three options is set to “1x” but tapping them and changing them to “.5x” will dramatically
speed up your phone. This harmless tweak forces the device to speed up
all transition animations, and the entire user experience is faster
and smoother as a result
Please read the question before marking this as a duplicate. I'm trying to access the LED/Flashlight WITHOUT using the Camera methods shown in other code on this site. Thank you.
I'm trying to use the flashlight/torch in Android. I have found and implemented code that works for doing this. The problem I have is that I'm using an image recognition API that uses the camera as an image scanner and they don't have a light on/off function. When I try to override their methods and use the Camera methods to turn the torch on/off, this works, however, the Camera methods now control the window and their Scanner no longer has priority on the screen.
So what I'm trying to determine is... Is there another way to turn on the flashlight/torch without using the Camera methods and preview functions. Anyone have an idea how to bypass the Camera to use the flashlight/torch? Any information would be greatly appreciated.
Here is the code that I currently use, which is working to turn the flashlight on/off, but like I said...this overrides the scanner, and I need the camera/scanner to operate at the same time to recognize my images.
public class Flashlight extends AutoScannerSession {
Camera cam;
private CameraManager mCameraManager;
private Context context;
private Scanner scanner;
private AutoScannerSession.Listener listener;
private boolean advancedListener = false;
public Flashlight(Activity parent, Scanner scanner, Listener listener, SurfaceView preview) {
super(parent, scanner, listener, preview);
cam = Camera.open();
cam.setErrorCallback(new Camera.ErrorCallback() {
#Override
public void onError(int error, Camera camera) {
Log.e("erro", error +"");
}
});
this.context = parent;
this.mCameraManager = new CameraManager(parent, this, preview);
this.scanner = scanner;
this.listener = listener;
if(listener instanceof AutoScannerSession.AdvancedListener) {
this.advancedListener = true;
}
}
#Override
public void start() {
super.start();
//flashOn();
}
public void flashOn() {
android.hardware.Camera.Parameters p = cam.getParameters();
p.setFlashMode(android.hardware.Camera.Parameters.FLASH_MODE_TORCH);
cam.setParameters(p);
//cam.startPreview();
}
public void flashOff() {
cam.stopPreview();
cam.release();
}
#Override
public void stop() {
super.stop();
flashOff();
}
No there is no alternative way to work with flash. But probably you can "share" the camera object with the Scanner.
At any rate, Camera.open() in Activity.onCreate() and turning on flashlight in Activity.onStart() do not look correct. To be a good citizen among other apps, your app should not obtain camera before onResume() and release it no later than onPause().
Code Snippet to turn on camera flash light.
Camera cam = Camera.open();
Parameters p = cam.getParameters();
p.setFlashMode(Parameters.FLASH_MODE_TORCH);
cam.setParameters(p);
cam.startPreview();
Code snippet to turn off camera led light.
cam.stopPreview();
cam.release();
I have a Symphony H20 smartphone officially running Android 4.4.2, Kernel 3.4.67, MT6582. Setting Parameters.FLASH_MODE_TORCH as a camera parameter did not work. After trying a few hours I decided to decompile its pre-installed FlashLight app. There, I found that they write 1 in file /sys/class/torch/torch/torch_level to turn on the Camera LED and 0 to turn it off.
Then I tried the same thing and voilla! It worked.
But the same technique did not work in my Winmax W800 Plus with Android 4.4.2, Kernel 3.4.67, MT6572. It even does not have a file like /sys/class/torch/torch/torch_level at all.
i'm having trouble playing a sound in android.
My first attempt was all inside my onButtonClick function which worked really well for about 20 clicks. then the sound stopped. and i believe it used the entire sound pool of android because all my other apps stopped having sounds.
public void onButtonClick(View view)
{
MediaPlayer clickSound = MediaPlayer.create(this, R.raw.click);
clickSound.start();
//... other onButtonCode
}
i have since changed this up a little bit so that this is now a global variable
MediaPlayer clickSound;
and it is instantiated inside my onCreate() method. the onbuttonClick now has the clickSound.start()
but this doesn't work the way i thought it should. the sound is seemingly random. sometimes the click will be there. sometimes there will be no sound. or sometimes the sound is there for a little blip and ends before the sound is complete. On the good side it doesn't seem to completely fill up the sound pool so i can keep getting 'some' sounds while i am testing.
What am i missing to make the sound function properly?
I tried to add a clickButton.stop() but that made my sound not function at all, probably because it stops before it is noticeable.
You should release the ressource after use.
clickSound.release();
clickSound = null;
I would suggest following:
Initialize the Player in onResume:
clickSound = MediaPlayer.create(this, R.raw.click);
Release ressources in onPause:
clickSound.release();
clickSound = null;
Use button to play sound:
clickSound.start();
Use SoundPool instead.
soundPool = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
resId = soundPool.load(context, R.raw.click, 1);
for play :
private void playSpecSound(int resId)
{
if(soundPool != null)
{
if(0 == soundPool.play(resId, 1f, 1f, 0, 0, 1.0f))
{
Log.e(TAG, "Play specified sound failed !");
}
}
}
In my project I have a lot of asynctask, which all follow this pattern:
#Override
protected void onPreExecute() {
super.onPreExecute();
crossfade(progressBar, contentView);//hide content, show progress bar
}
#Override
protected Void doInBackground(String... arg0) {
//some work
}
protected void onPostExecute(Void unused) {
crossfade(contentView, progressBar);
}
Code for crossfade:
void crossfade(View contentView, View loadingView){
Runnable r = new Runnable(){
#Override
public void run() {
if(contentView != null){
contentView.setAlpha(0f);
contentView.setVisibility(View.VISIBLE);
contentView.animate()
.alpha(1f)
.setDuration(CROSSFADE_TIME)
.setListener(null);
}
if(loadingView != null){
loadingView.animate()
.alpha(0f)
.setDuration(CROSSFADE_TIME)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if(loadingView != null){
loadingView.setVisibility(View.GONE);
}
}
});
}
}
};
runOnUiThread(r);
}
The problem happens when asynctask executes faster than animation time, causing second crossfade call before the first one is finished, resulting in both views being invisible.
I tried queueing runnables to execute them sequentially, but the problem is if user clicks a lot of buttons or many fragments are being loaded(they use crossfade method too), UI thread becomes overloaded and it may crash my app. The only solution I see so far is to add extra delay to all my asynctasks, using Thread.sleep(CROSSFADE_TIME), however it looks like a really dirty hack and I'm not sure if it's a good user experience.
In case someone needs it in future, adding loadingView.animate().cancel() and contentView.animate().cancel() before animation cancels previous animations and everything works ok.
Why are you running the crossfade animation in a separate thread? Try to do the animation without
Runnable r = new Runnable(){ and runOnUiThread(r);
I am working on an app, wherein when a new activity is started, it should start playing a sound.
So I used mediaplayer to play the sound in oncreate and It worked fine. But when I tried to use soundpool instead, by loading and playing it in oncreate of the activity.
Its is not playing.
I choose soundpool, since its better than mediaplayer.
what might be the issue? doesn't soundpool work in oncreate?
Maybe you just need to put a sleep in the onCreate method.
I had pretty much same problem trying to write an app that would sometimes need to play a sound as soon as it woke up. Eventually after much trial and error I discovered that it was putting the error "sample NOT READY" in the log output. The problem was that loading a sound file happens asynchronously, and if you try to play the sound before it has loaded it fails.
There is supposedly a mechanism you can use called setOnLoadCompleteListener, but I've yet to see an example of how this is actually useful. In the example shown above from mirroredAbstraction (assuming it works as advertised) all that would happen if the sound is not loaded yet is that it would not play the sound, which is pretty much the same as what you have now.
If that example somehow magically "fixed" your problem then I would suggest that it was only because all the extra overhead in the two method calls basically give your sound time to load before it was played. You could probably have achieved the same outcome with a simple SystemClock.sleep(100) in your onCreate between the load the play.
Depending on the size of your sound you might need to lengthen the delay, but a bit of experimentation with differing delays should tell you how much you need.
Alternatively , you could override OnWindowFocusChanged to play the file when the Activity starts ...
Something like this ...
public class YourActivity extends Activity {
.
.
.
private Handler mHandler;
public void onCreate(Bundle savedInstanceState) {
.
.
.
this.mHandler = new Handler();
.
.
.
}
public void onWindowFocusChanged(boolean paramBoolean)
{
if (paramBoolean)
{
this.mHandler.postDelayed(new Runnable()
{
public void run()
{
// Refer to the answer above to create the play function for your soundfile ...
YourActivity.this.play("The sound from the sound pool");
}
}
, 1000L);
}
}
You can play it anywhere,
Ill demonstrate with a simple example
Create a method initializeSoundPool
private void initializeSoundPool(){
mSoundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
mSoundPool.setOnLoadCompleteListener(new OnLoadCompleteListener() {
public void onLoadComplete(SoundPool soundPool, int sampleId,
int status) {
loaded = true;
}
});
soundID = mSoundPool.load(this, R.raw.glassbreak, 1);
}
Then create a method playFile
private void playFile(){
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
float actualVolume = (float) audioManager
.getStreamVolume(AudioManager.STREAM_MUSIC);
float maxVolume = (float) audioManager
.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
float volume = actualVolume / maxVolume;
if (loaded) {
mSoundPool.play(soundID, volume, volume, 1, 0, 1f);
Log.e("Test", "Played sound");
}
}
Then in onCreate call them like this
private SoundPool mSoundPool;
private int soundID;
boolean loaded = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.spxml);
initializeSoundPool();
playFile();
}
Or even better call initializeSoundPool in onCreate and then call playFile in onResume.