我有一个基本的录音-audiotrack,两个android设备之间的udp数据包语音聊天。它起作用了,但我的回声不好。我正在尝试使用JNI移植到android的Speex来删除回声。我导入的speex可以工作,但是回声消除不能。本机C代码是:
#include <jni.h>
#include <speex/speex_echo.h>
#define FRAME_SIZE 256
#define FILTER_LENGTH 800
SpeexEchoState *echoState;
// Initialization of echo cancelation
void Java_telefonie_voip_VoIPActivity_InitEcho(JNIEnv * env, jobject jobj)
{
echoState = speex_echo_state_init(FRAME_SIZE, FILTER_LENGTH);
}
// Queue the frame to soundcard for playing (receiving)
void Java_telefonie_voip_VoIPActivity_Playback(JNIEnv * env, jobject jobj, jshortArray inputFrame)
{
speex_echo_playback(echoState, inputFrame);
}
jshortArray Java_telefonie_voip_VoIPActivity_Capture(JNIEnv * env, jobject jobj, jshortArray inputFrame)
{
jshortArray outputFrame;
speex_echo_capture(echoState, inputFrame, *&outputFrame);
return outputFrame;
}
// Destroing echo cancelation
void Java_telefonie_voip_VoIPActivity_DestroyEcho(JNIEnv * env, jobject jobj)
{
speex_echo_state_destroy(echoState);
}以及一些重要的java代码:
native void InitEcho();
native void DestroyEcho();
native void Playback(short[] inputData); // listener/receiving
native short[] Capture(short[] inputData); // recorder/sender
static {
System.loadLibrary("speex");
}
public void Initialize ()
{
minBufferSize = 4096;
try
{
InitEcho();
}
catch (Exception e){Log.e(TAG, "jni " + e.getMessage());}
}
public void startRecording ()
{
isStreaming = true;
streamingThread = new Thread(new Runnable(){
@Override
public void run ()
{
try
{
audioRecorder = new AudioRecord(
MediaRecorder.AudioSource.MIC,
sampleRate,
channelConfig,
audioFormat,
minBufferSize*10
);
buffer = new short[minBufferSize];
audioRecorder.startRecording();
while(isStreaming)
{
sendPackets++;
minBufferSize = audioRecorder.read(buffer, 0, buffer.length);
// Echo cancelling
buffer = Capture(buffer);
// Send
dataPacket = new DatagramPacket(ShortToByteArray(buffer), buffer.length*2, receiverAddressIA, serverPort1);
dataSocket.send(dataPacket);
}
}
catch(Exception e)
{
Log.e(TAG, "2" + e.getMessage());
}
}
});
streamingThread.start();
}
public void startListen ()
{
isListening = true;
receivingThread = new Thread(new Runnable(){
@Override
public void run ()
{
try
{
audioTrack = new AudioTrack(
AudioManager.STREAM_VOICE_CALL,
sampleRate,
channelConfig,
audioFormat,
minBufferSize*10,
AudioTrack.MODE_STREAM
);
audioTrack.play();
buffer2 = new short[minBufferSize];
while (isListening)
{
receivedPackets++;
dataPacket2 = new DatagramPacket(ShortToByteArray(buffer2), buffer2.length*2);
dataSocket2.receive(dataPacket2);
// Cancel echo
Playback(buffer2);
// Queue to soundcard
audioTrack.write(buffer2, 0, minBufferSize);
}
}
catch(Exception e)
{
Log.e(TAG, "3" + e.getMessage());
}
}
});
receivingThread.start();
}
public short[] ByteToShortArray(byte[] byteArray)
{
short[] shortArray = new short[byteArray.length/2];
ByteBuffer.wrap(byteArray).order(ByteOrder.BIG_ENDIAN).asShortBuffer().get(shortArray);
return shortArray;
}问题是,当我启动记录器/流线程时,它显示它发送了一个包,然后应用程序崩溃,没有消息。你有什么建议或建议吗?请帮助我,因为我需要尽快做这个项目,我已经努力工作,并记录了自己,但它仍然不想很好地工作。谢谢!
编辑:我刚刚发现
// Echo cancelling
buffer = Capture(buffer);引发了撞车事故。
发布于 2012-04-10 20:33:51
更新:关于崩溃-这是由于JNI的误用。您不能将jshortarray视为C指针。您需要调用相关的JNI函数将其转换为C指针(GetShortArrayElements等)。
关于回声消除器-你目前使用的内部缓冲区大约是2帧,这对你来说可能还不够。安卓的上下文切换不同于PC,你可能会得到4个连续的播放帧和4个连续的录制帧->,以此类推。因此,这样会丢失帧。
此外,Android音频流的延迟比PC更高,因此2帧缓冲是不够的。您需要实现自己的缓冲,并使用延迟的帧数进行播放,以便回声消除器在回放后在其滤波器长度的范围内看到回声。
除此之外,分析和调试回声消除器的最好方法是: 1.真正了解它在高层次上是如何工作的,以及有什么要求-不是火箭科学,你可以在官方Speex文档中找到一切。2.使用提供的echo_diagnostic工具,检查Audacity等工具中的流,看看哪里出了问题。
当正确使用Speex AEC时,它工作得很好,我已经用它做了3个不同的项目,所以只需要纠正你的实现。
https://stackoverflow.com/questions/10080808
复制相似问题