android - SuperpoweredAndroidAudioIO和UDP socket音频传输

标签 android c++ sockets audio superpowered

我正在尝试制作将 SuperpoweredAndroidAudioIO 缓冲区从一台安卓设备传输到另一台安卓设备的应用。使用以下代码,我已经设法从音频回调中发送和接收 short int 缓冲区。然而,在接收端,声音在播放过程中会变得非常失真。

注意:为简洁起见,我没有包含一些似乎与问题无关的代码,包括与套接字初始化相关的函数。如果需要,我可以添加代码。

发送方:

麦克风.cpp

static bool SendBuffer(
    int sd,
    sockaddr_in address,
    short int *buffer,
    size_t bufferSize) {

// Send data buffer to the socket
ssize_t sentSize = sendto(sd, 
    buffer, 
    bufferSize,
    0,  
    (struct sockaddr*)&address,
    sizeof address);

    // If send is failed
    if (sentSize == -1){
        __android_log_print(ANDROID_LOG_INFO, "Sent size ", "%i error %i", 
        sentSize , errno);
    }
    else if (sentSize > 0) {
    __android_log_print(ANDROID_LOG_INFO, "DatagramSent : ", "%hi size: %hi", 
    buffer, sentSize);
    }
    return true;
}

// audio callback
static bool micProcessing(
    void *clientdata,
    short int *audioInputOutput, 
    int numberOfSamples, 
    int __unused samplerate) {

    return SendBuffer(micSocket, dsocket, audioInputOutput, numberOfSamples);

}

// Constructor
Mic::Mic(JNIEnv *env,
    unsigned int samplerate,
    unsigned int buffersize,
    unsigned int port){

    micSocket = NewUdpSocket(env);
    dsocket = initDestinationSocket(port); // where to send
    __android_log_write(ANDROID_LOG_DEBUG, "Sockets", "initialised");

    // init IO
    microphone =  new SuperpoweredAndroidAudioIO(samplerate, 
                                                 buffersize, 
                                                 true, 
                                                 false, 
                                                 micProcessing,
                                                 this, 
                                                 -1, 
                                                 SL_ANDROID_STREAM_MEDIA, 
                                                 buffersize*2);

    __android_log_write(ANDROID_LOG_DEBUG, "Mic", "initialised");
}

接收端由两部分组成:MixerChannel

混合器.cpp

//audio callback
static bool mainprocess(
    void *clientdata, 
    short int *audioInputOutput, 
    int numberOfSamples, 
    int __unused samplerate) {

    return ((Mixer*)clientdata)->processMain(audioInputOutput, numberOfSamples);
}
// Setting up Mixer
Mixer::Mixer(JNIEnv *env,unsigned int samplerate, unsigned int buffersize) {
    //Short int buffers for recieving
    channel1 = new Channel(samplerate,buffersize);
    //output buffer
    outputFloat = ((float *)memalign(16, (buffersize + 16) * sizeof(float) * 2));

    //volumes
    outputLevel = 0.5f;

    channel1level = 0.2f;
    channel2level = 0.2f;
    channel3level = 0.2f;
    channel4level = 0.2f;

    mainmixer = new SuperpoweredMonoMixer();
    __android_log_print(ANDROID_LOG_INFO, "Mixer", " Created");

    main = new SuperpoweredAndroidAudioIO(
        samplerate,
        buffersize, 
        false, 
        true, 
        mainprocess,
        this,
        -1, 
        SL_ANDROID_STREAM_MEDIA, 
        buffersize*2);

    __android_log_write(ANDROID_LOG_INFO, "Main AudioIO created", " ");
        main->stop();

        SuperpoweredCPU::setSustainedPerformanceMode(true); // Prevents CPU drops
}
// processing.
bool Mixer::processMain(short int *outputbuffer, unsigned int numberOfSamples{
    // a bit awkward
    channel1->returnFloatBuffer();
    inputs[0] = channel1->floatBuffer;

    inputs[1] = NULL;
    inputs[2] = NULL;
    inputs[3] = NULL;

    __android_log_print(ANDROID_LOG_INFO, "Channels are inside", " of mixer");
    inputLevels[0] = channel1level;
    inputLevels[1] = channel2level;
    inputLevels[2] = channel3level;
    inputLevels[3] = channel4level;

    mainmixer->process(inputs,
                       outputFloat,
                       inputLevels, 
                       outputLevel,
                       numberOfSamples);

    SuperpoweredFloatToShortInt(outputFloat, outputbuffer, numberOfSamples);
    return true;
}

Channel.cpp

//receiving buffer

static bool  ReceiveDatagramFromSocket(int sd, short int *buffer, size_t bufferSize) {

    ssize_t  recvSize = recvfrom(sd, buffer, bufferSize, 0, NULL, NULL);

    if (-1 == recvSize){ // If failed
        __android_log_print(ANDROID_LOG_INFO, "receive failed", " %i  ", errno);
    }
    else {
        // If data is received
        if (recvSize > 0) {
        __android_log_print(ANDROID_LOG_INFO, "Received"," %i bytes: %hi", recvSize, buffer);
        }
    }
    return true;
}
// init channel
Channel::Channel(unsigned int samplerate, unsigned int buffersize){
    socketIn = NewUdpSocket();
    BindSocketToPort(socketIn);

    samplerRate = samplerate;
    bufferSize = buffersize;

    shortIntBuffer = (short int *)malloc((buffersize + 16) * sizeof(short int)*4);
    floatBuffer = (float *)memalign(16, (buffersize + 16) * sizeof(float) * 2);

    bandEQ = new Superpowered3BandEQ(samplerate);
    bandEQ->enable(true);
    __android_log_print(ANDROID_LOG_INFO, "Channel ", "created");
}

// this function is called from Mixer.cpp
void Channel::returnFloatBuffer(){
    ReceiveDatagramFromSocket(socketIn, shortIntBuffer, bufferSize);
    Converting the 16-bit integer samples to 32-bit floating point.
    SuperpoweredShortIntToFloat(shortIntBuffer, floatBuffer, bufferSize, 1);
    bandEQ->process(floatBuffer, floatBuffer, bufferSize );
    __android_log_print(ANDROID_LOG_INFO, "EQ", " processing");
}

起初我以为是因为两侧的 AudioIO 使用不同的缓冲区大小(不同的设备 240 和 512)初始化,但后来我尝试将 512 硬编码到它们中,但没有帮助。

我还尝试将 sendto() 和 recvfrom() 中的缓冲区大小增加到 4096,这使声音更容易辨认,但仍然失真。

我还应该补充一点,我是 C++ 的新手,我坚持使用“天真”“一切可行” 方法,这让我走到了这一步。

我想了解我是否走在正确的轨道上,以及我应该采取什么方法来传输无失真的音频。

最佳答案

您的方法有两个主要问题:

  • 应避免在音频处理回调中使用网络等阻塞功能。您需要从不同的线程执行网络(在两侧),并且您需要在音频处理回调和网络线程之间进行一些缓冲以传递音频。

  • 您需要将传输“打包”,您需要处理双方的网络数据包。网络传输速度不快也不可靠,因此您需要巧妙的机制来处理这个问题。

一般来说,这种音频传输的实现对于您当前的代码来说要复杂得多。

关于android - SuperpoweredAndroidAudioIO和UDP socket音频传输,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46506848/

相关文章:

android - Firebase 存储在下载过程中处理网络中断

java - Lint 将 Kotlin MutableMap.forEach() 视为 java.util.Map.forEach()

android - 如何将参数放入 Android 中的本地化字符串中?

c++ - 在 MSVC 16.6.0 中找不到 std::popcount 标识符

c++ - firebreath npplugin 中的文件写入权限被拒绝

java - 文件压缩服务器在不重启的情况下只能压缩一个文件

c++ - 带有 "Accept=true"的 systemd 套接字在服务启动之前有很长的延迟

android - 从 Android 重新启动整个设备

c++ - pthread_key_create 析构函数未被调用

c++ - 通过套接字从C服务器发送数据到qt客户端