gpt4 book ai didi

Android Speex 回声消除问题

转载 作者:行者123 更新时间:2023-11-29 02:05:53 25 4
gpt4 key购买 nike

我有一个基本的 audiorecord-audiotrack,两个 android 设备之间的 udp 数据包语音聊天。它有效,但我的回声不好。我正在尝试使用由 JNI 移植到 android 的 Speex 来删除回声。我导入的 speex 有效,但回声消除无效。 native 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);

正在触发崩溃。

最佳答案

更新:关于崩溃 - 这是由于误用 JNI。您不能将 jshortarray 视为 C 指针。您需要调用相关的JNI函数将其转换为C指针(GetShortArrayElements等)。

关于回声消除器 - 您当前使用的内部缓冲区大约为 2 帧,这对您来说可能不够用。Android 的上下文切换与 PC 不同,您可能会得到 4 个连续的播放帧 -> 4 个连续的记录帧 -> 等等。所以你会以这种方式丢帧。

此外,Android 音频流的延迟比 PC 高,因此 2 帧缓冲区不够用。您需要实现自己的缓冲并使用延迟的帧数进行播放,以便回声消除器在回放后看到其过滤器长度边界内的回声。

除此之外,分析和调试回声消除器的最佳方法是:1. 真正理解它在高层次上是如何工作的以及有什么要求——不是火箭科学,你可以在官方 Speex 文档上找到所有内容。2. 使用提供的 echo_diagnostic 工具,并在 Audacity 等工具中检查流,看看哪里出了问题。

Speex AEC 在正确使用时工作得很好,我已经使用它完成了 3 个不同的项目,所以只需纠正您的实现即可。

关于Android Speex 回声消除问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10080808/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com