我要求我需要显示一个像屏幕一样的拨号盘,每当用户按下拨号盘按钮时,我必须产生1khz音(不是DTMF音)。
我使用了以下链接中的代码生成1 khz音:
Playing an arbitrary tone with Android
当我开始拨打我的拨号盘屏幕上的按钮直到21次按下它成功产生音调但是在第22次尝试后我得到应用程序无响应(ANR)错误,我需要关闭应用程序。
以下是我的代码:
final float duration = 0.3f; // seconds
final int sampleRate = 4000;
final int numSamples = (int)duration * sampleRate + 100;
final double sample[] = new double[numSamples];
final double freqOfTone = 1000; // hz
final byte generatedSnd[] = new byte[2 * numSamples];
final Handler handler = new Handler();
public void onClick(View v) {
// TODO Auto-generated method stub
int id = v.getId();
playSound();
}
private void playSound()
{
final Thread thread = new Thread(new Runnable() {
public void run() {
genTone();
handler.post(new Runnable() {
public void run() {
playSound1();
}
});
}
});
thread.start();
}
void playSound1(){
final AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRate, AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, numSamples,
AudioTrack.MODE_STATIC);
audioTrack.write(generatedSnd, 0, generatedSnd.length);
audioTrack.play();
}
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);
}
}
每次按下拨号盘上的按钮时,都会调用onClick()方法。
使用上面的代码我在Logcat中得到了这个输出:
05.500 E/AudioFlinger( 85): no more track names available
09-21 05:20:05.500 E/AudioTrack( 133): AudioFlinger could not create track, status: -12
09-21 05:20:05.503 E/SoundPool( 133): Error creating AudioTrack
09-21 05:20:05.535 E/AudioFlinger( 85): no more track names available
09-21 05:20:05.535 E/AudioTrack( 6080): AudioFlinger could not create track, status: -12
09-21 05:20:05.535 E/AudioTrack-JNI( 6080): Error initializing AudioTrack
09-21 05:20:05.535 E/AudioTrack-Java( 6080): [ android.media.AudioTrack ] Error code -20 when initializing AudioTrack.
09-21 05:20:05.535 D/AndroidRuntime( 6080): Shutting down VM
09-21 05:20:05.535 W/dalvikvm( 6080): threadid=1: thread exiting with uncaught exception (group=0x40015578)
09-21 05:20:05.539 E/AndroidRuntime( 6080): FATAL EXCEPTION: main
09-21 05:20:05.539 E/AndroidRuntime( 6080): java.lang.IllegalStateException: play() called on uninitialized AudioTrack.
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.media.AudioTrack.play(AudioTrack.java:824)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.dial.DialPadScreen.playSound1(DialPadScreen.java:274)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.dial.DialPadScreen$1$1.run(DialPadScreen.java:248)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Handler.handleCallback(Handler.java:587)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Handler.dispatchMessage(Handler.java:92)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.os.Looper.loop(Looper.java:123)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at android.app.ActivityThread.main(ActivityThread.java:3687)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at java.lang.reflect.Method.invokeNative(Native Method)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at java.lang.reflect.Method.invoke(Method.java:507)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:842)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
09-21 05:20:05.539 E/AndroidRuntime( 6080): at dalvik.system.NativeStart.main(Native Method)
09-21 05:20:05.542 E/ ( 133): Dumpstate > /data/log/dumpstate_app_error
09-21 05:20:05.542 W/ActivityManager( 133): Force finishing activity com.android.dial/.DialPadScreen
我打印“audioTrack”的状态直到第21次我得到值1(STATE_INITIALIZED)之后我得到值'0'(STATE_UNINITIALIZED)。不知道为什么国家正在改变。
请帮助我,我需要做些什么更改,以防止我的应用中出现这个问题。 或者请建议是否有任何替代方法。
答案 0 :(得分:4)
关于21个成功生成音调的次数,然后失败,我认为你必须释放以前创建的实例,调用audioTrack.release()
方法。
为了避免IllegalStateException
:
创建AudioTrack
的实例后,您必须测试它是否已初始化
使用MODE_STATIC
模式,如果未正确初始化,状态将为:STATE_UNINITIALIZED
,
相反,如果正确初始化:STATE_NO_STATIC_DATA
,当您调用write方法时,状态将更改为STATE_INITIALIZED
。
使用MODE_STREAM
模式,如果未正确初始化,状态将为:STATE_UNINITIALIZED
,
相反,如果正确初始化:STATE_INITIALIZED
。
阅读AudioTrack
的来源,我看到了这一点。我认为这很奇怪,因为构造函数返回的对象不可用,因为在构造之后无法初始化。
答案 1 :(得分:2)
您可以在应用的AudioTrack
中创建一个onCreate
,然后在应用开始时调用audioTrack.play()
,然后在按下按钮时将数据写入其中。
另外,根据我对AudioTrack
的体验,如果我尝试同时播放多个AudioTrack,它会产生一个非常糟糕,不稳定的声音并且存在延迟。
您可以考虑使用Sound Pool预录制声音,因为它可以同时播放多个声音。
或者,从API级别9开始,您可以将会话ID分配给AudioTrack,如果我理解正确,您可以使用SoundPool播放音频曲目,然后您理想地想要创建STATIC AudioTracks
答案 2 :(得分:1)
将AudioTrack.MODE_STATIC更改为AudioTrack.MODE_STREAM。应该这样做:))
答案 3 :(得分:1)
我遇到了类似的问题,我通过将此权限添加到清单文件来解决它:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>