Czy istnieje sposób, aby Android emitował dźwięk o dowolnej częstotliwości (co oznacza, że nie chcę mieć wcześniej nagranych plików dźwiękowych)?
Rozejrzałem się i ToneGenerator był jedyną rzeczą, jaką udało mi się znaleźć, która była nawet blisko, ale wydaje się, że jest w stanie wyprowadzić tylko standardowe tony DTMF.
Jakieś pomysły?
Odpowiedzi:
Pierwotnie znalazłem ten przykładowy kod na blogu, ale zawierał on kilka błędów, które generowały przerażające dźwięki. Naprawiłem błędy i opublikowałem tutaj wynikowy kod. Wydaje mi się, że działa dobrze dla mnie!
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 <[email protected]> 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(); } }
źródło
0.0
do1.0
. Pomnożenie przez32767
spowoduje przekształcenie go w 16-bitowy zakres stałego punktu. AudioTrack spodziewa bufor być trochę endian Format. Stąd następne dwie linie po prostu konwertują kolejność bajtów z big endian na little endian.Poprawa powyższego kodu:
Dodaj zwiększanie i zmniejszanie amplitudy, aby uniknąć kliknięć.
Dodaj kod, aby określić, kiedy hals się skończył.
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.
źródło
Zapakowałem powyższe wspaniałe rozwiązania w zgrabne małe opakowanie, które jest bardziej użyteczne po wyjęciu z pudełka jako prosty konfigurowalny brzęczyk. Uruchamia go w wątku w tle i ma metody zatrzymywania i odtwarzania oraz kilka opcji, które można ustawić.
Jest w JCenter, więc możesz dodać go do swojej listy zależności w ten sposób
compile 'net.mabboud:android-tone-player:0.2'
i używasz go w ten sposób jako ciągłego brzęczyka
ContinuousBuzzer tonePlayer = new ContinuousBuzzer(); tonePlayer.play(); // just an example don't actually use Thread.sleep in your app Thread.sleep(1000); tonePlayer.stop();
lub brzęczyk odtwarzany tylko raz i możesz ustawić częstotliwość i głośność w ten sposób
OneTimeBuzzer buzzer = new OneTimeBuzzer(); buzzer.setDuration(5); // volume values are from 0-100 buzzer.setVolume(50); buzzer.setToneFreqInHz(110);
Rozszerzony wpis na blogu tutaj o tym tutaj GitHub tutaj
źródło
Ponieważ w niektórych starszych wersjach Androida występuje błąd, który powoduje wyciek pamięci podczas korzystania z MODE_STATIC, zmodyfikowałem powyższą odpowiedź Xarpha, aby użyć MODE_STREAM. Mam nadzieję, że niektórym to pomoże.
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. }
źródło
Oto kolejny blog demonstrujący prosty syntezator i trochę interfejsu użytkownika
http://audioprograming.wordpress.com/2012/10/18/a-simple-synth-in-android-step-by-step-guide-using-the-java-sdk/
Możesz być także zainteresowany csound lub pdlib (czysta biblioteka danych) dla Androida.
źródło
Zmodyfikowany kod oparty na odpowiedzi Singhaksa
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(); } }
źródło
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); }
// Możesz dodać dowolną wartość w synth_frequency, aby uzyskać dźwięk zmiany, na przykład możesz dodać zmienną losową, aby uzyskać dźwięk
źródło
Wykonaj dur (16 nut)
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(); } }
źródło
zobacz tę pomocną bibliotekę
https://github.com/karlotoy/perfectTune
jest łatwy w użyciu
dodaj to do swoich zależności
compile 'com.github.karlotoy:perfectTune:1.0.2'
I używasz tego w ten sposób:
PerfectTune perfectTune = new PerfectTune(); perfectTune.setTuneFreq(desire_freq); perfectTune.playTune();
zatrzymać melodię:
perfectTune.stopTune();
źródło
Jest do tego kilka programów, ale są do niczego. Zmierzyłem kilka:
http://www.endolith.com/wordpress/2009/11/24/android-audio-applications/
Więc nie rób tego, co robią. :RE
źródło