使用Android播放任意音
有没有什么办法让Android发出任意频率的声音(意思是,我不想有预先录制的声音文件)?
我环顾四周, ToneGenerator是我能够发现的唯一一个接近的东西,但它似乎只能输出标准的DTMF音调。
有任何想法吗?
我最初在博客上发现了这个示例代码 ,但是它有一些错误,会产生一些可怕的声音。 我已经修复了错误,并在此处发布了代码。 似乎为我工作得很好!
public class PlaySound extends Activity { // originally from http://marblemice.blogspot.com/2010/04/generate-and-play-tone-in-android.html // and modified by Steve Pomeroy <steve@staticfree.info> private final int duration = 3; // seconds private final int sampleRate = 8000; private final int numSamples = duration * sampleRate; private final double sample[] = new double[numSamples]; private final double freqOfTone = 440; // hz private final byte generatedSnd[] = new byte[2 * numSamples]; Handler handler = new Handler(); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } @Override protected void onResume() { super.onResume(); // Use a new tread as this can take a while final Thread thread = new Thread(new Runnable() { public void run() { genTone(); handler.post(new Runnable() { public void run() { playSound(); } }); } }); thread.start(); } void genTone(){ // fill out the array for (int i = 0; i < numSamples; ++i) { sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone)); } // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. int idx = 0; for (final double dVal : sample) { // scale to maximum amplitude final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } } void playSound(){ final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, generatedSnd.length, AudioTrack.MODE_STATIC); audioTrack.write(generatedSnd, 0, generatedSnd.length); audioTrack.play(); } }
改进上面的代码:
增加幅度上升和下降以避免点击。
添加代码以确定粘性何时完成播放。
double duration = 1; // seconds double freqOfTone = 1000; // hz int sampleRate = 8000; // a number double dnumSamples = duration * sampleRate; dnumSamples = Math.ceil(dnumSamples); int numSamples = (int) dnumSamples; double sample[] = new double[numSamples]; byte generatedSnd[] = new byte[2 * numSamples]; for (int i = 0; i < numSamples; ++i) { // Fill the sample array sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate)); } // convert to 16 bit pcm sound array // assumes the sample buffer is normalized. // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. int idx = 0; int i = 0 ; int ramp = numSamples / 20 ; // Amplitude ramp as a percent of sample count for (i = 0; i< ramp; ++i) { // Ramp amplitude up (to avoid clicks) double dVal = sample[i]; // Ramp up to maximum final short val = (short) ((dVal * 32767 * i/ramp)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } for (i = i; i< numSamples - ramp; ++i) { // Max amplitude for most of the samples double dVal = sample[i]; // scale to maximum amplitude final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } for (i = i; i< numSamples; ++i) { // Ramp amplitude down double dVal = sample[i]; // Ramp down to zero final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp )); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } AudioTrack audioTrack = null; // Get audio track try { audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, (int)numSamples*2, AudioTrack.MODE_STATIC); audioTrack.write(generatedSnd, 0, generatedSnd.length); // Load the track audioTrack.play(); // Play the track } catch (Exception e){ RunTimeError("Error: " + e); return false; } int x =0; do{ // Montior playback to find when done if (audioTrack != null) x = audioTrack.getPlaybackHeadPosition(); else x = numSamples; }while (x<numSamples); if (audioTrack != null) audioTrack.release(); // Track play done. Release track.
这是另一个博客演示一个简单的合成器加上一些用户界面
您可能还对android的csound或pdlib(纯数据库)感兴趣。
由于在使用MODE_STATIC时会导致内存泄漏的旧版本的android版本中存在一个bug,所以我修改了上面的Xarph的回答以使用MODE_STREAM。 希望这将有助于一些。
public void playTone(double freqOfTone, double duration) { //double duration = 1000; // seconds // double freqOfTone = 1000; // hz int sampleRate = 8000; // a number double dnumSamples = duration * sampleRate; dnumSamples = Math.ceil(dnumSamples); int numSamples = (int) dnumSamples; double sample[] = new double[numSamples]; byte generatedSnd[] = new byte[2 * numSamples]; for (int i = 0; i < numSamples; ++i) { // Fill the sample array sample[i] = Math.sin(freqOfTone * 2 * Math.PI * i / (sampleRate)); } // convert to 16 bit pcm sound array // assumes the sample buffer is normalized. // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. int idx = 0; int i = 0 ; int ramp = numSamples / 20 ; // Amplitude ramp as a percent of sample count for (i = 0; i< ramp; ++i) { // Ramp amplitude up (to avoid clicks) double dVal = sample[i]; // Ramp up to maximum final short val = (short) ((dVal * 32767 * i/ramp)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } for (i = i; i< numSamples - ramp; ++i) { // Max amplitude for most of the samples double dVal = sample[i]; // scale to maximum amplitude final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } for (i = i; i< numSamples; ++i) { // Ramp amplitude down double dVal = sample[i]; // Ramp down to zero final short val = (short) ((dVal * 32767 * (numSamples-i)/ramp )); // in 16 bit wav PCM, first byte is the low order byte generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } AudioTrack audioTrack = null; // Get audio track try { int bufferSize = AudioTrack.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize, AudioTrack.MODE_STREAM); audioTrack.play(); // Play the track audioTrack.write(generatedSnd, 0, generatedSnd.length); // Load the track } catch (Exception e){ } if (audioTrack != null) audioTrack.release(); // Track play done. Release track. }
修改后的代码基于Singhaks的答案
public class MainActivity extends Activity { private final int duration = 30; // seconds private final int sampleRate = 8000; private final int numSamples = duration * sampleRate; private final double sample[] = new double[numSamples]; private final double freqOfTone = 440; // hz private final byte generatedSnd[] = new byte[2 * numSamples]; Handler handler = new Handler(); private AudioTrack audioTrack; private boolean play = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, numSamples, AudioTrack.MODE_STREAM); } @Override protected void onResume() { super.onResume(); // Use a new tread as this can take a while Thread thread = new Thread(new Runnable() { public void run() { handler.post(new Runnable() { public void run() { playSound(); genTone(); } }); } }); thread.start(); } void genTone(){ // fill out the array while(play){ for (int i = 0; i < numSamples; ++i) { // float angular_frequency = sample[i] = Math.sin(2 * Math.PI * i / (sampleRate/freqOfTone)); } int idx = 0; // convert to 16 bit pcm sound array // assumes the sample buffer is normalised. for (double dVal : sample) { short val = (short) (dVal * 32767); generatedSnd[idx++] = (byte) (val & 0x00ff); generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8); } audioTrack.write(generatedSnd, 0, numSamples); } } void playSound(){ play = true; audioTrack.play(); } }
我将上面的精彩解决方案整合到一个整洁的小包装中,作为一个简单的可配置蜂鸣器,它更易于使用。 它运行在一个后台线程,并有停止和播放的方法,你可以设置一些选项。
它在JCenter上,所以你可以像这样把它添加到你的依赖列表中
compile 'net.mabboud:android-tone-player:0.2'
你用这个来连续蜂鸣器
ContinuousBuzzer tonePlayer = new ContinuousBuzzer(); tonePlayer.play(); // just an example don't actually use Thread.sleep in your app Thread.sleep(1000); tonePlayer.stop();
或蜂鸣器只播放一次,你可以像这样设置频率和音量
OneTimeBuzzer buzzer = new OneTimeBuzzer(); buzzer.setDuration(5); // volume values are from 0-100 buzzer.setVolume(50); buzzer.setToneFreqInHz(110);
在这里扩展的博客文章在这里
float synth_frequency = 440; int minSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT); AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); audioTrack.play(); short[] buffer = new short[minSize]; float angle = 0; while (true) { if (play) { for (int i = 0; i < buffer.length; i++) { float angular_frequency = (float)(2*Math.PI) * synth_frequency / SAMPLE_RATE; buffer[i] = (short)(Short.MAX_VALUE * ((float) Math.sin(angle))); angle += angular_frequency; } audioTrack.write(buffer, 0, buffer.length); }
//你可以在synth_frequency中添加任意值来获得改变声音,例如你可以添加随机变量来获得声音
重大(16注)
public class MainActivity extends AppCompatActivity { private double mInterval = 0.125; private int mSampleRate = 8000; private byte[] generatedSnd; private final double mStandardFreq = 440; Handler handler = new Handler(); private AudioTrack audioTrack; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onResume() { super.onResume(); // Use a new tread as this can take a while final Thread thread = new Thread(new Runnable() { public void run() { byte[] tempByte = new byte[0]; for (int i = 0; i < 16 ; i++ ){ double note = getNoteFrequencies(i); byte[] tonByteNote = getTone(mInterval, mSampleRate, note); tempByte = concat(tonByteNote, tempByte); } generatedSnd = tempByte; handler.post(new Runnable() { public void run() { playTrack(generatedSnd); } }); } }); thread.start(); } public byte[] concat(byte[] a, byte[] b) { int aLen = a.length; int bLen = b.length; byte[] c= new byte[aLen+bLen]; System.arraycopy(a, 0, c, 0, aLen); System.arraycopy(b, 0, c, aLen, bLen); return c; } private double getNoteFrequencies(int index){ return mStandardFreq * Math.pow(2, (double) index/12.0d); } private byte[] getTone(double duration, int rate, double frequencies){ int maxLength = (int)(duration * rate); byte generatedTone[] = new byte[2 * maxLength]; double[] sample = new double[maxLength]; int idx = 0; for (int x = 0; x < maxLength; x++){ sample[x] = sine(x, frequencies / rate); } for (final double dVal : sample) { final short val = (short) ((dVal * 32767)); // in 16 bit wav PCM, first byte is the low order byte generatedTone[idx++] = (byte) (val & 0x00ff); generatedTone[idx++] = (byte) ((val & 0xff00) >>> 8); } return generatedTone; } private AudioTrack getAudioTrack(int length){ if (audioTrack == null) audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, length, AudioTrack.MODE_STATIC); return audioTrack; } private double sine(int x, double frequencies){ return Math.sin( 2*Math.PI * x * frequencies); } void playTrack(byte[] generatedSnd){ getAudioTrack(generatedSnd.length) .write(generatedSnd, 0, generatedSnd.length); audioTrack.play(); } }
看到这个有用的库
https://github.com/karlotoy/perfectTune
它很容易使用
将此添加到您的依赖项
compile 'com.github.karlotoy:perfectTune:1.0.2'
你这样使用它:
PerfectTune perfectTune = new PerfectTune(); perfectTune.setTuneFreq(desire_freq); perfectTune.playTune();
停止调谐:
perfectTune.stopTune();
有几个这样的程序,但他们吸。 我测了几个:
http://www.endolith.com/wordpress/2009/11/24/android-audio-applications/
所以不要做任何事情 :d