Android ile rastgele bir ton çalma


93

Android'in rastgele bir frekansta ses çıkarmasını sağlamanın bir yolu var mı (yani, önceden kaydedilmiş ses dosyalarına sahip olmak istemiyorum)?

Etrafa baktım ve ToneGenerator bunu daha da yakın bulabildiğim tek şeydi, ancak yalnızca standart DTMF tonlarını çıkarabiliyor gibi görünüyor.

Herhangi bir fikir?


2
Herhangi bir gerçek çözüm buldunuz mu?
o0 '.

21
Hayır, ama sonunda projeyi yapmadım.
Jeremy Logan

1
@JeremyLogan Ve olumlu olumsuz geri bildirimler aldınız. lol.
TheRealChx101

Yanıtlar:


111

Başlangıçta bu örnek kodu bir blogda buldum , ancak içinde bazı korkunç sesler üreten bazı hatalar vardı. Hataları düzelttim ve ortaya çıkan kodu buraya gönderdim. Benim için iyi çalışıyor gibi görünüyor!

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();
    }
}

2
Bu satır doğru mu? audioTrack.write (createdSnd, 0, numSamples); veya numSamples * 2 mi olmalı, çünkü örnek başına 2 bayt var. Ayrıca yazma yöntemi de bir dizi kısa devre alır, öyleyse bir ara bayt dizisi oluşturmanın avantajı nedir?
Damian Kennedy,

2
Bu gerçekten harika bir örnek, çok teşekkürler. Ancak başka bir kötü hata buldum (kodu genişletirseniz): audioTrack.write (createdSnd, 0, numSamples) audioTrack.write (generatedSnd, 0, 2 * numSamples) veya daha iyi audioTrack.write (createdSnd, 0 , oluşturulanSnd.length);
AudioDroid

6
AudioTrack yapıcısında "numSamples" kullanmak yerine, createdSnd.length kullanmalısınız çünkü Beşinci parametre "bayt cinsinden arabellek boyutu" dur. Örnek, tonun yalnızca ilk yarısını çalar.
Torben

5
Black27 @ numuneler bir genlik aralığı noktaları yüzer oluşturulan 0.0için 1.0. Çarpma işlemi 32767, 16 bitlik sabit nokta aralığına dönüştürür. AudioTrack tampon biraz olmasını beklediğini endian biçimi. Dolayısıyla, sonraki iki satır bayt sırasını büyük endian'dan küçük endian'a dönüştürür.
ains

2
özel statik final int sampleRate = 192000 kullanarak; Ultra-sonic oynayabildim
user3505444

26

Yukarıdaki kod üzerinde iyileştirme:

Tıklamaları önlemek için genlik artırma ve azaltma ekleyin.

Raptiyenin ne zaman biteceğini belirlemek için kod ekleyin.

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.

1
Birincil değişiklik, genliğin yükselmesi ve azalmasıydı. Orijinal kod maksimum genlikle başladı ve bitti. Bu, tonun başında ve sonunda tıklamalar oluşturur. Bu kod, numunelerin ilk% 20'si üzerinde genliği 0'dan tam genliğe yükseltir. Daha sonra, numunelerin son% 20'si üzerinde tam genlikten sıfıra düşer. Tonlar daha pürüzsüz ve çok daha hoş. Diğer değişiklik tonun çalınmasını izlemek ve tonun çalınması bitene kadar devam etmemekti.
Xarph

Çalıştırmasını sağlayamıyorum .. İlkini çalıştırabiliyorum .. ama bunu yaptığınız şeye nasıl değiştireceğimi gerçekten anlayamıyorum ... Tıklama sesini ortadan kaldırmaya çalıştığım için gerçekten yardımcı olur. .
Coder

3
+1, ancak bu yanıttaki kod derlemeye yaklaşmıyor. Bunu burada doğru bir şekilde uyguladım: gist.github.com/SuspendedPhan/7596139 Steve'in genTone () yöntemini benimkiyle değiştirin ve rampa etkisini elde edin.
Dylan P

MODE_STATIC'te bir bellek sızıntısı olduğundan, kodu aşağıdaki MODE_STREAM'i kullanacak şekilde değiştirdim
aşırı

API ile başlayarak, setVolume () kullanarak rampa yapmak mümkündür. Bu, çok küçük bir örneğin döngüye alınmasını ve hatta dinamik uzunlukta bir sesin çalınmasını sağlar (örneğin, kullanıcı bir düğmeyi tutarken). Kod örneği: github.com/stefanhaustein/android-tone-generator/blob/master/…
Stefan Haustein

8

Yukarıdaki harika çözümleri, basit, yapılandırılabilir bir zil olarak kutudan çıkar çıkmaz daha kullanışlı olan küçük, temiz bir pakete sardım. Bir arka plan iş parçacığında çalıştırır ve durdurma ve oynatma yöntemlerine ve ayarlayabileceğiniz birkaç seçeneğe sahiptir.

JCenter'da var, böylece onu bağımlılıklar listenize şöyle ekleyebilirsiniz

compile 'net.mabboud:android-tone-player:0.2'

ve bunu sürekli bir zil sesi için böyle kullanırsın

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

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

veya yalnızca bir kez çalınan bir zil ve bunun gibi frekansı ve ses seviyesini ayarlayabilirsiniz

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

// volume values are from 0-100
buzzer.setVolume(50);
buzzer.setToneFreqInHz(110);

Burada bununla ilgili genişletilmiş blog yayını burada GitHub burada


@Melchester, şimdi düzeltildi.
Uyarılar

4

Bazı eski android sürümlerinde MODE_STATIC kullanırken bellek sızıntısına neden olan bir hata olduğundan, Xarph'ın yukarıdaki yanıtını MODE_STREAM kullanacak şekilde değiştirdim. Umarım bazılarına yardımcı olur.

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.
}


3

Singhaks'ın cevabına dayalı değiştirilmiş kod

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();
    }
}

2
    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);
    } 

// Değişiklik sesi almak için synth_frequency'de keyfi bir değer ekleyebilir, örneğin ses elde etmek için rastgele değişken ekleyebilirsiniz


Sonunda hepsini kısaca çeviriyorsun. Şamandıra olarak açı yapmak için bir neden yok. çift ​​matematik aynı hızdır ve çok fazla döküm gerektirmez.
Tatarize

2

Büyük yapın (16 nota)

 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();
  }

}

2

bu yararlı kitaplığa bakın

https://github.com/karlotoy/perfectTune

kullanımı kolay

bunu bağımlılıklarınıza ekleyin

 compile 'com.github.karlotoy:perfectTune:1.0.2'

Ve bunu şu şekilde kullanıyorsun:

PerfectTune perfectTune = new PerfectTune();
perfectTune.setTuneFreq(desire_freq);
perfectTune.playTune();

melodiyi durdurmak için:

perfectTune.stopTune();

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.