有没有办法让Android发出任意频率的声音(意思是,我不想预先录制声音文件)?
我环顾四周,ToneGenerator是我唯一能找到甚至接近的东西,但它似乎只能输出标准的DTMF音色。
有什么想法吗?
答案 0 :(得分:103)
我最初在博客上找到this example code,但它有一些错误,产生了一些可怕的声音。我修复了错误并在此处发布了生成的代码。似乎对我有用!
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();
}
}
答案 1 :(得分:24)
改进上述代码:
添加振幅上升和斜降以避免咔嗒声。
添加代码以确定大头钉何时播放完毕。
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.
答案 2 :(得分:7)
我将上述精彩的解决方案整理成一个整洁的小包装,开箱即用,是一个简单的可配置蜂鸣器。它在后台线程中运行它,并具有停止和播放方法以及您可以设置的一些选项。
这取决于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);
答案 3 :(得分:3)
这是另一个演示简单合成器和一些UI的博客
您可能也对csound或pdlib(纯数据库)感兴趣。
答案 4 :(得分:3)
由于某些较旧的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.
}
答案 5 :(得分:3)
基于Singhaks的修改代码&#39;回答
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();
}
}
答案 6 :(得分: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);
}
//您可以在synth_frequency中添加任意值以获得更改声音,例如您可以添加随机变量来获取声音
答案 7 :(得分:2)
做专业(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();
}
}
答案 8 :(得分:2)
查看这个有用的库
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();
答案 9 :(得分:1)
有几个程序,但他们很糟糕。我测量了一些:
http://www.endolith.com/wordpress/2009/11/24/android-audio-applications/
所以不要做他们做的事情。 :d