6

The Android documentation for SoundPool says "the application can also alter the pitch by adjusting the playback rate in real-time for doppler or synthesis effects". So I tried to do this, using the setRate method to vary smoothly from one note to another, but the result is awful: the sound variation is very ragged. Here is the code I tried. Please tell me if there is a better way.

int streamId = soundPool.play (soundId, 1, 1, 1, 0, 1.0f);
for (float x = 0; x < 1; x += 0.005) {
  SystemClock.sleep (10);
  soundPool.setRate (streamId, 1 + x);
}
Patrick
  • 3,578
  • 5
  • 31
  • 53
  • change `1 + x` to `1.0f + x` and (also change `0.005` to `0.005f` – Sherif elKhatib Nov 17 '11 at 12:58
  • How are you testing your code. If you are using a real device, it may be possible that your device is too slow for dynamically changing the playback rate. If you are using the simulator, maybe the problem is your host's sound card. Changing the playback rate is not a trivial task and may require complex interpolation especially when run with non-standard conversion rates. – MBober Nov 18 '11 at 07:54
  • I tested both on device (Galaxy S) and emulator. It sounds worse on the device, but in the emulator the sound is also "chopped". – Patrick Nov 18 '11 at 09:53
  • Does it get better when you increase the time between rate increments? Say `SystemClock.sleep(100);`? – MBober Nov 18 '11 at 12:10
  • The defect is less evident if the increment is slower, but this does not help because I need to change the pitch fast enough, to simulate a musical instrument (e.g. guitar, violin). – Patrick Nov 18 '11 at 12:32
  • According to my experience SoundPool provides functionalities only for short sounds, please correct me if I am wrong. – Baimyrza Shamyr Aug 30 '16 at 16:05

2 Answers2

13

Even though the Androids documentation suggests that you can use setRate() to produce doppler effects, I would not rely on a fast or good enough implementation on every android device.

Affordable audio ICs require a fixed sample rate of 44kHz or 48kHz. Sample rate conversion (i.e. pitch modification) therefore has to be done in software. Because this includes low-level number crunching I am sure that this is implemented by the device manufacturer and not by Google. Given the wide variety of Android device manufacturers and the often low-cost hardware used, there are certainly many sample rate conversion implementations out there. Some faster, some slower. Some smooth, some ragged.

What happens when changing the conversion rate during playback? If the algorithm doesn't match the pieces precisely together, there might be a short crack in your audio playback. You change the conversion rate every 10 milliseconds. If there is a crack every 10ms, that corresponds to a frequency of 100Hz which is a tone itself. This is likely to be what you hear as "ragged".

All in all I don't think that it is possible to emulate musical instruments in real-time on a wide range of Android devices using high-level Android API. A native application might be fast enough though.

MBober
  • 1,095
  • 9
  • 25
0

After trying everything out, i decided to go with SoundPool to change the Pitch, A playback rate of 2.0 causes the sound to play at a freq double its original, and a playback rate of 0.5 causes it to play at half its original frequency. The playback rate range is 0.5 to 2.0. But it did work with freq lower and higher than 0.5 and 2.0.

I am posting my working code,

but as its just for demo purpose, Here you need to Manually change the "playback rate", everytime you install the application for eg: "sp.play(explosion, 1,1,0,0,1.5f)" here "1.5f" is the playback rate. One can easily create an EditView,or something similar to set the value of playback rate at run time.

In this app, you just need to tap on the app's screen to play the music at the set playback rate.

import java.io.IOException;

import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class SoundPoolActivity extends Activity implements OnClickListener {

    SoundPool sp;
    int explosion = 0;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

       View v = new View(this);
       v.setOnClickListener(this);
       setContentView(v);
       sp = new SoundPool(1,AudioManager.STREAM_MUSIC,0);
       //explosion = sp.load(this, R.raw.hh,0);
       explosion = sp.load("/sdcard/hh.m4a",0);


    }


    public void onClick(View v){
         if (explosion!=0){

             sp.play(explosion, 1,1,0,0,2.3f);
         }

    }
}
Kumar Vivek Mitra
  • 33,294
  • 6
  • 48
  • 75