有什么方法可以使Android发出任意频率的声音(意味着,我不想预先录制声音文件)?
我环顾四周,ToneGenerator是我唯一能找到的东西,它甚至很近,但是它似乎只能输出标准DTMF音调。
有任何想法吗?
有什么方法可以使Android发出任意频率的声音(意味着,我不想预先录制声音文件)?
我环顾四周,ToneGenerator是我唯一能找到的东西,它甚至很近,但是它似乎只能输出标准DTMF音调。
有任何想法吗?
Answers:
我最初是在博客上找到此示例代码的,但其中包含一些错误,这些错误会产生一些可怕的声音。我已经修复了这些错误,并将结果代码发布到了这里。似乎对我来说很好!
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();
}
}
0.0
到1.0
。乘以32767
将其转换为16位定点范围。该AudioTrack预计缓冲区要小尾数格式。因此,接下来的两行只是将字节顺序从大字节序转换为小字节序。
改进上面的代码:
增加幅度的上升和下降,以避免咔嗒声。
添加代码以确定平头钉何时完成演奏。
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.
我将上述奇妙的解决方案包装在一个整洁的小包装中,它作为简单的可配置蜂鸣器开箱即用。它在后台线程中运行,并具有停止和播放方法以及可设置的少数选项。
它位于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);
由于使用MODE_STATIC时,某些较旧的android版本中存在一个导致内存泄漏的错误,因此我修改了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.
}
这是另一个博客,演示了一个简单的合成器以及一些用户界面
您可能也对android的csound或pdlib(纯数据库)感兴趣。
基于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();
}
}
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