使用Android播放任意音调

Jer*_*gan 87 audio android

有没有办法让Android发出任意频率的声音(意思是,我不想有预先录制的声音文件)?

我环顾四周,ToneGenerator是我唯一能找到甚至接近的东西,但它似乎只能输出标准的DTMF音调.

有任何想法吗?

Ste*_*roy 105

我最初在博客上发现了这个示例代码,但它有一些错误,它产生了一些可怕的声音.我修复了错误并在此处发布了生成的代码.似乎对我有用!

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 您应该使用generatedSnd.length而不是在AudioTrack构造函数中使用"numSamples",因为第五个参数是"缓冲区大小(以字节为单位)".该示例仅播放音调的前半部分. (6认同)
  • @ Black27样本在浮点中创建,幅度范围从"0.0"到"1.0".乘以`32767`将其转换为16位定点范围.[AudioTrack](http://developer.android.com/reference/android/media/AudioTrack.html)期望缓冲区很少[endian](http://en.wikipedia.org/wiki/Endianness)格式.因此,接下来的两行只是将字节顺序从大端转换为小端. (5认同)
  • 这条线是否正确?audioTrack.write(generatedSnd,0,numSamples); 或者它应该是numSamples*2,因为每个样本有2个字节.write方法也采用了一个short数组,那么创建一个中间字节数组有什么好处呢? (2认同)
  • 这确实是一个很好的例子,非常感谢.但是我发现了另一个令人讨厌的错误(如果你扩展代码),它是:audioTrack.write(generatedSnd,0,numSamples)应该是audioTrack.write(generatedSnd,0,2*numSamples)或更好的audioTrack.write(generatedSnd,0 ,generatedSnd.length); (2认同)
  • 使用私有静态最终 int sampleRate = 192000; 我能够演奏超声波 (2认同)

Xar*_*rph 25

改进上述代码:

添加振幅上升和下降以避免咔嗒声.

添加代码以确定大头钉何时播放完毕.

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{                                                              // Monitor 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.
Run Code Online (Sandbox Code Playgroud)

  • +1,但这个答案中的代码并没有接近编译.我在这里正确实现了它:https://gist.github.com/SuspendedPhan/7596139只需用我的替换史蒂夫的genTone()方法,你就会得到渐变效果. (3认同)
  • 从 API 开始,可以使用 setVolume() 进行渐变。这使得仅循环非常小的样本,甚至可以播放动态长度的声音(例如,当用户按住按钮时)。代码示例:https://github.com/stefanhaustein/android-tone-generator/blob/master/core/src/main/java/org/kobjects/atg/ToneGenerator.java (2认同)

mee*_*ese 7

我将上述精彩的解决方案包装成一个整洁的小包装,开箱即用,作为一个简单的可配置蜂鸣器.它在后台线程中运行它,并具有停止和播放方法以及您可以设置的一些选项.

它取决于JCenter,因此您可以将其添加到依赖项列表中

compile 'net.mabboud:android-tone-player:0.2'
Run Code Online (Sandbox Code Playgroud)

你就像这样用它来连续蜂鸣器

ContinuousBuzzer tonePlayer = new ContinuousBuzzer();
tonePlayer.play();

// just an example don't actually use Thread.sleep in your app
Thread.sleep(1000); 
tonePlayer.stop();
Run Code Online (Sandbox Code Playgroud)

或者蜂鸣器只播放一次,您可以像这样设置频率和音量

OneTimeBuzzer buzzer = new OneTimeBuzzer();
buzzer.setDuration(5);

// volume values are from 0-100
buzzer.setVolume(50);
buzzer.setToneFreqInHz(110);
Run Code Online (Sandbox Code Playgroud)

这里有关于它的扩展博客文章 GitHub


小智 5

由于一些较旧的android版本中存在一个错误,导致使用MODE_STATIC时出现内存泄漏,因此我修改了上面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.
}
Run Code Online (Sandbox Code Playgroud)