<|start_filename|>AudioStreamingLib/Demos/BroadcastServer/waveformwidget.cpp<|end_filename|>
#include "waveformwidget.h"

WaveFormWidget::WaveFormWidget(QWidget *parent) : QWidget(parent)
{
    m_size = 0;

    int threads = QThreadPool::globalInstance()->maxThreadCount() + 1;
    QThreadPool::globalInstance()->setMaxThreadCount(threads);

    qRegisterMetaType<QVector<QPoint>>("QVector<QPoint>");

    clear();
}

WaveFormWidget::~WaveFormWidget()
{
    m_thread.waitForFinished();

    int threads = QThreadPool::globalInstance()->maxThreadCount() - 1;
    QThreadPool::globalInstance()->setMaxThreadCount(threads);
}

void WaveFormWidget::start(const QAudioFormat &format)
{
    if (m_initialized)
        return;

    if (!format.isValid())
        return;

    m_initialized = true;
    m_format = format;

    m_size = int(AudioStreamingLibCore::timeToSize(50, m_format));
}

void WaveFormWidget::clear()
{
    m_initialized = false;
    m_point_list.clear();
    repaint();
}

void WaveFormWidget::calculateWaveForm(const QByteArray &data)
{
    if (!m_initialized)
        return;

    if (m_thread.isRunning())
        return;

    m_thread = QtConcurrent::run(this, &WaveFormWidget::calculateWaveFormThread, data, width(), height());
}

void WaveFormWidget::calculateWaveFormThread(const QByteArray &data, int window_width, int window_height)
{
    m_wave_form_buffer.append(data);

    window_height -= 1;
    window_height /= 2;

    while (m_wave_form_buffer.size() >= m_size)
    {
        QByteArray buffer = m_wave_form_buffer.mid(0, m_size);
        m_wave_form_buffer.remove(0, m_size);

        int numSamples = buffer.size() / int(sizeof(float));

        QVector<float> wave_form_vector;
        wave_form_vector.resize(numSamples);

        const float *samples = reinterpret_cast<const float*>(buffer.constData());

        memcpy(wave_form_vector.data(), samples, uint(buffer.size()));

        QVector<QPoint> point_list;

        QPoint initial(0, window_height);
        QPoint final(window_width - 1, window_height);

        int density = numSamples / window_width;

        if (density < 1)
            density = 1;

        int iter = numSamples - density;

        int i;

        for (i = 0; i < iter; i += density)
        {
            float level = wave_form_vector[i];

            for (int j = 1; j < density; j++)
            {
                float sample = wave_form_vector[i + j];

                if (qAbs(sample) > qAbs(level))
                    level = sample;
            }

            QPoint point = QPoint(qRound(i * (float(window_width - 1) / numSamples)),
                                  int(window_height - level * qRound(window_height * 0.75)));

            point_list.append(point);
        }

        iter = numSamples - i;
        //Process remaining data
        {
            float level = wave_form_vector[i];

            for (int j = 1; j < iter; j++)
            {
                float sample = wave_form_vector[i + j];

                if (qAbs(sample) > qAbs(level))
                    level = sample;
            }

            QPoint point = QPoint(qRound(i * (float(window_width - 1) / numSamples)),
                                  int(window_height - level * qRound(window_height * 0.75)));

            point_list.append(point);
        }

        if (point_list.size() >= 2)
        {
            point_list.first() = initial;
            point_list.last() = final;
        }

        if (!point_list.isEmpty())
            QMetaObject::invokeMethod(this, "prepaint", Qt::QueuedConnection, Q_ARG(QVector<QPoint>, point_list));
    }
}

void WaveFormWidget::prepaint(const QVector<QPoint> &point_list)
{
    if (!m_initialized)
        return;

    m_point_list = point_list;
    repaint();
}

void WaveFormWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    QPainter painter(this);
    painter.fillRect(rect(), Qt::black);

    QPen pen;

    pen.setWidth(1);
    pen.setBrush(Qt::green);
    pen.setStyle(Qt::SolidLine);

    painter.setPen(pen);

    for (int i = 0; i < m_point_list.size() - 1; i++)
        painter.drawLine(m_point_list[i], m_point_list[i + 1]);
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/opusdecoderclass.cpp<|end_filename|>
#include "opusdecoderclass.h"

//Opus decoder class

OpusDecoderClass::OpusDecoderClass(QObject *parent) : QObject(parent)
{
    m_initialized = false;

    START_THREAD
}

OpusDecoderClass::~OpusDecoderClass()
{
    if (m_initialized)
    {
        m_initialized = false;

        /*Destroy the encoder state*/
        opus_decoder_destroy(m_decoder);
    }

    STOP_THREAD
}

void OpusDecoderClass::stopPrivate()
{
    deleteLater();
}

void OpusDecoderClass::stop()
{
    QTimer::singleShot(0, this, &OpusDecoderClass::stopPrivate);
}

void OpusDecoderClass::startPrivate(int sample_rate, int channels, int frame_size, int max_frame_size)
{
    if (m_initialized)
        return;

    /* Create a new decoder state. */
    int err;
    m_decoder = opus_decoder_create(sample_rate, channels, &err);
    if (err < 0)
    {
        emit error(QString("Failed to create Opus decoder: %0").arg(opus_strerror(err)));
        return;
    }

    m_channels = channels;
    m_frame_size = frame_size;
    m_max_frame_size = max_frame_size;

    m_initialized = true;
}

void OpusDecoderClass::start(int sample_rate, int channels, int frame_size, int max_frame_size)
{
    QMetaObject::invokeMethod(this, "startPrivate", Qt::QueuedConnection,
                              Q_ARG(int, sample_rate),
                              Q_ARG(int, channels),
                              Q_ARG(int, frame_size),
                              Q_ARG(int, max_frame_size));
}

void OpusDecoderClass::writePrivate(const QByteArray &data)
{
    if (!m_initialized)
        return;

    int frame_size;
    /* Decode the data. In this example, frame_size will be constant because
         the encoder is using a constant frame size. However, that may not
         be the case for all encoders, so the decoder must always check
         the frame size returned. */

    QByteArray output = QByteArray(int(sizeof(float)) * m_channels * m_frame_size, char(0));

    frame_size = opus_decode_float(m_decoder, reinterpret_cast<const uchar*>(data.constData()), data.size(), reinterpret_cast<float*>(output.data()), m_max_frame_size, 0);

    if (frame_size < 0)
    {
        emit error(QString("Opus decoder failed: %0").arg(opus_strerror(frame_size)));
        return;
    }

    int size = int(sizeof(float)) * m_channels * frame_size;

    /* Write the decoded audio to output. */
    output.resize(size);

    emit decoded(output);
}

void OpusDecoderClass::write(const QByteArray &data)
{
    QMetaObject::invokeMethod(this, "writePrivate", Qt::QueuedConnection,
                              Q_ARG(QByteArray, data));
}


<|start_filename|>AudioStreamingLib/WebServer/openssllib.h<|end_filename|>
#ifndef OPENSSLLIB_H
#define OPENSSLLIB_H

#include <QtCore>

#include <memory>

#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/sha.h>
#include <openssl/rand.h>
#include <openssl/err.h>

class OpenSslLib : public QObject
{
    Q_OBJECT
public:
    explicit OpenSslLib(QObject *parent = nullptr);

    //Functions
    bool isLoaded();
    static QByteArray RANDbytes(int size);
    static QByteArray SHA256(const QByteArray &data, const QByteArray &salt);
    QByteArray encrypt(QByteArray data);
    QByteArray decrypt(QByteArray data);
    void setPassword(const QByteArray &password);

private:
    //Functions
    void loadFunctions();

    void startEncrypt(const QByteArray &salt);
    QByteArray encryptPrivate(const QByteArray &input);
    void stopEncrypt();

    void startDecrypt(const QByteArray &salt);
    QByteArray decryptPrivate(const QByteArray &input);
    void stopDecrypt();

    //Variables
    EVP_CIPHER_CTX *enc_ctx;
    EVP_CIPHER_CTX *dec_ctx;
    QByteArray m_password;

    bool m_loaded;

    //Imports
    struct BIGNUMDeleter
    {
        static inline void cleanup(BIGNUM *pointer)
        {
            OpenSslLib openssl;
            if (openssl.isLoaded())
                openssl.pBN_free(pointer);
        }
    };

    struct BIODeleter
    {
        static inline void cleanup(BIO *pointer)
        {
            OpenSslLib openssl;
            if (openssl.isLoaded())
                openssl.pBIO_free_all(pointer);
        }
    };

    typedef int(*tRAND_bytes)(unsigned char *buf, int num);
    tRAND_bytes pRAND_bytes;

    typedef int (*tSHA256_Init)(SHA256_CTX *c);
    tSHA256_Init pSHA256_Init;

    typedef int (*tSHA256_Update)(SHA256_CTX *c, const void *data, size_t len);
    tSHA256_Update pSHA256_Update;

    typedef int (*tSHA256_Final)(unsigned char *md, SHA256_CTX *c);
    tSHA256_Final pSHA256_Final;

    typedef BIGNUM*(*tBN_new)(void);
    tBN_new pBN_new;

    typedef void(*tBN_free)(BIGNUM *a);
    tBN_free pBN_free;

    typedef int(*tBN_set_word)(BIGNUM *a, BN_ULONG w);
    tBN_set_word pBN_set_word;

    typedef BIO*(*tBIO_new)(BIO_METHOD *type);
    tBIO_new pBIO_new;

    typedef BIO_METHOD*(*tBIO_s_mem)(void);
    tBIO_s_mem pBIO_s_mem;

    typedef void(*tBIO_free_all)(BIO *a);
    tBIO_free_all pBIO_free_all;

    typedef int(*tBIO_read)(BIO *b, void *data, int len);
    tBIO_read pBIO_read;

    typedef int(*tEVP_BytesToKey)(const EVP_CIPHER *type,const EVP_MD *md,
                                  const unsigned char *salt,
                                  const unsigned char *data, int datal, int count,
                                  unsigned char *key,unsigned char *iv);
    tEVP_BytesToKey pEVP_BytesToKey;

    typedef const EVP_CIPHER*(*tEVP_aes_256_cbc)(void);
    tEVP_aes_256_cbc pEVP_aes_256_cbc;

    typedef const EVP_MD*(*tEVP_sha256)(void);
    tEVP_sha256 pEVP_sha256;

    typedef EVP_CIPHER_CTX *(*tEVP_CIPHER_CTX_new)(void);
    tEVP_CIPHER_CTX_new pEVP_CIPHER_CTX_new;

    typedef int(*tEVP_EncryptInit_ex)(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
                                      ENGINE *impl, const unsigned char *key,
                                      const unsigned char *iv);
    tEVP_EncryptInit_ex pEVP_EncryptInit_ex;

    typedef int(*tEVP_EncryptUpdate)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
                                     const unsigned char *in, int inl);
    tEVP_EncryptUpdate pEVP_EncryptUpdate;

    typedef int(*tEVP_EncryptFinal_ex)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl);
    tEVP_EncryptFinal_ex pEVP_EncryptFinal_ex;

    typedef int(*tEVP_CIPHER_CTX_free)(EVP_CIPHER_CTX *ctx);
    tEVP_CIPHER_CTX_free pEVP_CIPHER_CTX_free;

    typedef int(*tEVP_DecryptInit_ex)(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
                                      ENGINE *impl, const unsigned char *key,
                                      const unsigned char *iv);
    tEVP_DecryptInit_ex pEVP_DecryptInit_ex;

    typedef int(*tEVP_DecryptUpdate)(EVP_CIPHER_CTX *ctx, unsigned char *out, int *outl,
                                     const unsigned char *in, int inl);
    tEVP_DecryptUpdate pEVP_DecryptUpdate;

    typedef int(*tEVP_DecryptFinal_ex)(EVP_CIPHER_CTX *ctx, unsigned char *outm, int *outl);
    tEVP_DecryptFinal_ex pEVP_DecryptFinal_ex;
};

#endif // OPENSSLLIB_H


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/flowcontrol.cpp<|end_filename|>
#include "flowcontrol.h"

//Realtime flow control, used with the CustomInputDevice

#define interval 10

#define errorstr "Format not supported by the fake input device"

FlowControl::FlowControl(QObject *parent) : QObject(parent)
{
    m_elapsed_time = 0;

    m_sample_rate = 0;
    m_channels = 0;
    m_bits_per_sample = 0;

    m_timer = new QTimer(this);
    m_timer->setTimerType(Qt::PreciseTimer);
    connect(m_timer, &QTimer::timeout, this, &FlowControl::askForBytes);
    m_timer->setInterval(interval);
}

void FlowControl::stop()
{
    deleteLater();
}

void FlowControl::start(int sample_rate, int channels, int bits_per_sample)
{
    if (sample_rate < 8000 || sample_rate > 192000)
    {
        emit error(errorstr);
        return;
    }

    switch (channels)
    {
    case 1:
    case 2:
    case 4:
    case 6:
    case 8:
        break;
    default:
        emit error(errorstr);
        return;
    }

    switch (bits_per_sample)
    {
    case 32:
        break;
    default:
        emit error(errorstr);
        return;
    }

    m_sample_rate = 8000; //Generate at 8KHz and up-sample
    m_channels = channels;
    m_bits_per_sample = bits_per_sample;

    m_resampler = new r8brain(this);

    connect(m_resampler, &r8brain::error, this, &FlowControl::error);
    connect(m_resampler, &r8brain::resampled, this, &FlowControl::readyRead);

    m_resampler->start(m_sample_rate, sample_rate, channels, channels, bits_per_sample);

    m_timer->start();
    m_time.start();
}

void FlowControl::askForBytes() //Compute the size in bytes of the elapsed time
{
    qint64 elapsed_time = m_time.elapsed();

    qint64 elapsed = elapsed_time - m_elapsed_time;
    m_elapsed_time = elapsed_time;

    int bytes = int(timeToSize(elapsed, m_channels, m_bits_per_sample, m_sample_rate));

    QByteArray data;
    data.resize(bytes);

    if (!data.isEmpty())
    {
        Eigen::Ref<Eigen::VectorXf> samples = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(data.data()), data.size() / int(sizeof(float)));
        samples.fill(0);
    }

    if (m_resampler)
        m_resampler->write(data);
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/audiostreamingworker.cpp<|end_filename|>
﻿#include "audiostreamingworker.h"

#if defined (IS_TO_DEBUG_VERBOSE_1) || defined (IS_TO_DEBUG_VERBOSE_2)
QMutex record_mutex;
qint64 record_count = 0;
#endif

#define INVALID_FORMAT\
    (m_streaming_info.inputAudioFormat().isValid() &&\
    (m_streaming_info.inputAudioFormat().sampleType() != QAudioFormat::Float ||\
    m_streaming_info.inputAudioFormat().sampleSize() != 32 ||\
    m_streaming_info.inputAudioFormat().byteOrder() != QAudioFormat::LittleEndian ||\
    m_streaming_info.inputAudioFormat().sampleRate() < 8000 ||\
    m_streaming_info.inputAudioFormat().sampleRate() > 192000))\
    ||\
    (m_streaming_info.audioFormat().isValid() &&\
    (m_streaming_info.audioFormat().sampleType() != QAudioFormat::Float ||\
    m_streaming_info.audioFormat().sampleSize() != 32 ||\
    m_streaming_info.audioFormat().byteOrder() != QAudioFormat::LittleEndian ||\
    m_streaming_info.audioFormat().sampleRate() < 8000 ||\
    m_streaming_info.audioFormat().sampleRate() > 192000))\

AudioStreamingWorker::AudioStreamingWorker(QObject *parent) : QObject(parent)
{
    m_is_walkie_talkie = false;
    m_has_error = false;
    m_input_muted = false;
    m_volume = 0;
    m_ready_to_write_extra_data = false;
    m_extra_data_peers = 0;
    m_callback_enabled = false;
    m_input_device_running = false;
    m_output_device_running = false;
    m_workers_started_later = false;
    m_input_device_changing = false;
    m_output_device_changing = false;

#ifdef OPUS
    m_frame_size = 0;
    m_max_frame_size = 0;
    m_bitrate = 0;
#endif

    START_THREAD
}

AudioStreamingWorker::~AudioStreamingWorker()
{
    STOP_THREAD
}

void AudioStreamingWorker::stopPrivate()
{
    deleteLater();
}

void AudioStreamingWorker::stop()
{
    QTimer::singleShot(0, this, &AudioStreamingWorker::stopPrivate);
}

void AudioStreamingWorker::start(const AudioStreamingLibInfo &streaming_info)
{
    m_streaming_info = streaming_info;

    if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::Undefined)
    {
        errorPrivate("Mode of work not defined!");
        return;
    }

    if (m_streaming_info.workMode() != AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer &&
            m_streaming_info.isListenAudioInputEnabled())
    {
        errorPrivate("Listen input is allowed only on broadcast mode!");
        return;
    }

    if (INVALID_FORMAT)
    {
        errorPrivate("Invalid format!");
        return;
    }

    switch (m_streaming_info.workMode())
    {
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient:
    {
        if (m_streaming_info.isEncryptionEnabled())
            m_client = new EncryptedClient(this);
        else
            m_client = new Client(this);

        connect(m_client, &AbstractClient::error, this, &AudioStreamingWorker::errorPrivate);
        connect(m_client, &AbstractClient::connected, this, &AudioStreamingWorker::clientConencted);
        connect(m_client, &AbstractClient::disconnected, this, &AudioStreamingWorker::clientDisconencted);
        connect(m_client, &AbstractClient::readyRead, this, &AudioStreamingWorker::processClientInput);

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();

        m_server_discover = new DiscoverServer(this);

        if (m_streaming_info.isEncryptionEnabled())
            m_server = new EncryptedServer(this);
        else
            m_server = new Server(this);

        connect(m_server, &AbstractServer::error, this, &AudioStreamingWorker::errorPrivate);
        connect(m_server, &AbstractServer::connected, this, &AudioStreamingWorker::serverClientConencted);
        connect(m_server, &AbstractServer::disconnected, this, &AudioStreamingWorker::serverClientDisconencted);
        connect(m_server, &AbstractServer::pending, this, &AudioStreamingWorker::pending);
        connect(m_server, &AbstractServer::readyRead, this, &AudioStreamingWorker::processServerInput);

        adjustSettingsPrivate(false);

        startAudioWorkers();

        restartActiveWorkers();

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer:
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient:
    {
        m_is_walkie_talkie = true;

        if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient)
        {
            if (m_streaming_info.isEncryptionEnabled())
                m_client = new EncryptedClient(this);
            else
                m_client = new Client(this);

            connect(m_client, &AbstractClient::error, this, &AudioStreamingWorker::errorPrivate);
            connect(m_client, &AbstractClient::connected, this, &AudioStreamingWorker::clientConencted);
            connect(m_client, &AbstractClient::disconnected, this, &AudioStreamingWorker::clientDisconencted);
            connect(m_client, &AbstractClient::readyRead, this, &AudioStreamingWorker::processClientInput);
        }
        else if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer)
        {
            m_server_discover = new DiscoverServer(this);

            if (m_streaming_info.isEncryptionEnabled())
                m_server = new EncryptedServer(this);
            else
                m_server = new Server(this);

            connect(m_server, &AbstractServer::error, this, &AudioStreamingWorker::errorPrivate);
            connect(m_server, &AbstractServer::connected, this, &AudioStreamingWorker::serverClientConencted);
            connect(m_server, &AbstractServer::disconnected, this, &AudioStreamingWorker::serverClientDisconencted);
            connect(m_server, &AbstractServer::pending, this, &AudioStreamingWorker::pending);
            connect(m_server, &AbstractServer::readyRead, this, &AudioStreamingWorker::processServerInput);

            adjustSettingsPrivate(false);

            startAudioWorkers();

            restartActiveWorkers();
        }

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WebClient:
    {
        if (m_streaming_info.isEncryptionEnabled())
        {
            m_client = new WebClient(this);
        }
        else
        {
            errorPrivate("WebClient requires encryption enabled!");
            return;
        }

        m_is_walkie_talkie = true;

        connect(m_client, &AbstractClient::error, this, &AudioStreamingWorker::errorPrivate);
        connect(m_client, &AbstractClient::connectedToServer, this, &AudioStreamingWorker::webClientConencted);
        connect(m_client, &AbstractClient::connectedToPeer, this, &AudioStreamingWorker::webClientConnectedToPeer);
        connect(m_client, &AbstractClient::disconnectedFromPeer, this, &AudioStreamingWorker::webClientDisconnected);
        connect(m_client, &AbstractClient::pending, this, &AudioStreamingWorker::pending);
        connect(m_client, &AbstractClient::webClientLoggedIn, this, &AudioStreamingWorker::webClientLoggedIn);
        connect(m_client, &AbstractClient::webClientWarning, this, &AudioStreamingWorker::webClientWarning);
        connect(m_client, &AbstractClient::readyRead, this, &AudioStreamingWorker::processWebClientInput);
        connect(m_client, &AbstractClient::commandXML, this, &AudioStreamingWorker::commandXML);

        adjustSettingsPrivate(false);

        break;
    }
    default:
        break;
    }
}

void AudioStreamingWorker::startInputAudioWorkers()
{
    switch (m_streaming_info.workMode())
    {
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient:
    {
        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();
#ifdef OPUS
        m_resampler = new r8brain(this);
        m_opus_enc = new OpusEncoderClass(this);

        connect(m_resampler, &r8brain::error, this, &AudioStreamingWorker::warningInputPrivate);
        connect(m_resampler, &r8brain::resampled, m_opus_enc, &OpusEncoderClass::write);

        if (m_streaming_info.isGetAudioEnabled() && !m_streaming_info.isListenAudioInputEnabled())
            connect(m_resampler, &r8brain::resampled, this, &AudioStreamingWorker::veryInputData);

        connect(m_opus_enc, &OpusEncoderClass::encoded, this, &AudioStreamingWorker::posProcessedInput);

        connect(m_opus_enc, &OpusEncoderClass::error, this, &AudioStreamingWorker::warningInputPrivate);
#endif
        if (m_streaming_info.inputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_input = new AudioInput(this);

            connect(m_audio_input, &AudioInput::error, this, &AudioStreamingWorker::warningInputPrivate);

            if (m_callback_enabled)
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputData);
            else
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputDataBack);
        }
        else
        {
            QAudioFormat inputFormat = m_streaming_info.inputAudioFormat();

            m_flow_control = new FlowControl(this);

            connect(m_flow_control, &FlowControl::readyRead, this, &AudioStreamingWorker::flowControl);
            connect(m_flow_control, &FlowControl::error, this, &AudioStreamingWorker::error);
            m_flow_control->start(inputFormat.sampleRate(), inputFormat.channelCount(), inputFormat.sampleSize());
        }

        m_level_meter_input = new LevelMeter(this);

        connect(m_level_meter_input, &LevelMeter::currentlevel, this, &AudioStreamingWorker::inputLevel);

        if (m_streaming_info.isListenAudioInputEnabled())
        {
            if (!m_audio_output)
            {
                m_audio_output = new AudioOutput(this);

                connect(m_audio_output, &AudioOutput::error, this, &AudioStreamingWorker::warningOutputPrivate);
                connect(m_audio_output, &AudioOutput::currentlevel, this, &AudioStreamingWorker::outputLevel);
                if (m_streaming_info.isGetAudioEnabled())
                    connect(m_audio_output, &AudioOutput::veryOutputData, this, &AudioStreamingWorker::veryInputData);
                connect(m_audio_output, &AudioOutput::veryOutputData, m_level_meter_input, &LevelMeter::write);
#ifdef OPUS
                connect(m_resampler, &r8brain::resampled, m_audio_output, &AudioOutput::write);
#endif
                m_audio_output->setVolume(m_volume);

                m_audio_output->start(m_streaming_info.outputDeviceInfo(),
                                      m_streaming_info.audioFormat(),
                                      m_streaming_info.timeToBuffer(),
                                      true /*is_very_output_enabled*/);
            }
            else
            {
                connect(m_audio_output, &AudioOutput::veryOutputData, m_level_meter_input, &LevelMeter::write);
#ifdef OPUS
                connect(m_resampler, &r8brain::resampled, m_audio_output, &AudioOutput::write);
#endif
            }
        }

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer:
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();

#ifdef OPUS
        m_resampler = new r8brain(this);
        m_opus_enc = new OpusEncoderClass(this);

        connect(m_opus_enc, &OpusEncoderClass::error, this, &AudioStreamingWorker::warningInputPrivate);

        connect(m_resampler, &r8brain::error, this, &AudioStreamingWorker::warningInputPrivate);
        connect(m_resampler, &r8brain::resampled, m_opus_enc, &OpusEncoderClass::write);

        if (m_streaming_info.isGetAudioEnabled())
            connect(m_resampler, &r8brain::resampled, this, &AudioStreamingWorker::veryInputData);

        connect(m_opus_enc, &OpusEncoderClass::encoded, this, &AudioStreamingWorker::posProcessedInput);
#endif
        if (m_streaming_info.inputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_input = new AudioInput(this);

            connect(m_audio_input, &AudioInput::error, this, &AudioStreamingWorker::warningInputPrivate);

            if (m_callback_enabled)
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputData);
            else
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputDataBack);

            m_level_meter_input = new LevelMeter(this);

            connect(m_level_meter_input, &LevelMeter::currentlevel, this, &AudioStreamingWorker::inputLevel);
        }
        else //used to compute level if not using the library input device
        {
            m_flow_control = new FlowControl(this);

            connect(m_flow_control, &FlowControl::readyRead, this, &AudioStreamingWorker::flowControl);
            connect(m_flow_control, &FlowControl::error, this, &AudioStreamingWorker::error);

            m_level_meter_input = new LevelMeter(this);

            connect(m_level_meter_input, &LevelMeter::currentlevel, this, &AudioStreamingWorker::inputLevel);

            if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer)
            {
                QAudioFormat inputFormat = m_streaming_info.inputAudioFormat();

                m_flow_control->start(inputFormat.sampleRate(),
                                      inputFormat.channelCount(),
                                      inputFormat.sampleSize());
            }
        }

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WebClient:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();

#ifdef OPUS
        m_resampler = new r8brain(this);
        m_opus_enc = new OpusEncoderClass(this);
        connect(m_opus_enc, &OpusEncoderClass::error, this, &AudioStreamingWorker::warningInputPrivate);

        connect(m_resampler, &r8brain::error, this, &AudioStreamingWorker::warningInputPrivate);
        connect(m_resampler, &r8brain::resampled, m_opus_enc, &OpusEncoderClass::write);

        if (m_streaming_info.isGetAudioEnabled())
            connect(m_resampler, &r8brain::resampled, this, &AudioStreamingWorker::veryInputData);

        connect(m_opus_enc, &OpusEncoderClass::encoded, this, &AudioStreamingWorker::posProcessedInput);
#endif
        if (m_streaming_info.inputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_input = new AudioInput(this);

            connect(m_audio_input, &AudioInput::error, this, &AudioStreamingWorker::warningInputPrivate);

            if (m_callback_enabled)
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputData);
            else
                connect(m_audio_input, &AudioInput::readyRead, this, &AudioStreamingWorker::inputDataBack);

            m_level_meter_input = new LevelMeter(this);

            connect(m_level_meter_input, &LevelMeter::currentlevel, this, &AudioStreamingWorker::inputLevel);
        }
        else //used to compute level if not using the library input device
        {
            m_flow_control = new FlowControl(this);

            connect(m_flow_control, &FlowControl::readyRead, this, &AudioStreamingWorker::flowControl);
            connect(m_flow_control, &FlowControl::error, this, &AudioStreamingWorker::error);

            QAudioFormat inputFormat = m_streaming_info.inputAudioFormat();

            m_level_meter_input = new LevelMeter(this);

            connect(m_level_meter_input, &LevelMeter::currentlevel, this, &AudioStreamingWorker::inputLevel);

            m_flow_control->start(inputFormat.sampleRate(),
                                  inputFormat.channelCount(),
                                  inputFormat.sampleSize());
        }

        break;
    }
    default:
        break;
    }

    m_input_device_running = true;
}

void AudioStreamingWorker::startOutputAudioWorkers()
{
    switch (m_streaming_info.workMode())
    {
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();
#ifdef OPUS
        m_opus_dec = new OpusDecoderClass(this);

        connect(m_opus_dec, &OpusDecoderClass::decoded, this, &AudioStreamingWorker::posProcessedOutput);

        connect(m_opus_dec, &OpusDecoderClass::error, this, &AudioStreamingWorker::warningOutputPrivate);
#endif
        if (m_streaming_info.outputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_output = new AudioOutput(this);

            if (m_streaming_info.isGetAudioEnabled())
                connect(m_audio_output, &AudioOutput::veryOutputData, this, &AudioStreamingWorker::veryOutputData);

            connect(m_audio_output, &AudioOutput::error, this, &AudioStreamingWorker::warningOutputPrivate);
            connect(m_audio_output, &AudioOutput::currentlevel, this, &AudioStreamingWorker::outputLevel);

            m_audio_output->setVolume(m_volume);
        }
        else //used to compute level if not using the library output device
        {
            m_level_meter_output = new LevelMeter(this);

            connect(m_level_meter_output, &LevelMeter::currentlevel, this, &AudioStreamingWorker::outputLevel);
        }

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer:
    {
        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer:
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();

#ifdef OPUS
        m_opus_dec = new OpusDecoderClass(this);

        connect(m_opus_dec, &OpusDecoderClass::error, this, &AudioStreamingWorker::warningOutputPrivate);

        connect(m_opus_dec, &OpusDecoderClass::decoded, this, &AudioStreamingWorker::posProcessedOutput);
#endif

        if (m_streaming_info.outputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_output = new AudioOutput(this);

            if (m_streaming_info.isGetAudioEnabled())
                connect(m_audio_output, &AudioOutput::veryOutputData, this, &AudioStreamingWorker::veryOutputData);

            connect(m_audio_output, &AudioOutput::error, this, &AudioStreamingWorker::warningOutputPrivate);
            connect(m_audio_output, &AudioOutput::currentlevel, this, &AudioStreamingWorker::outputLevel);

            m_audio_output->setVolume(m_volume);
        }
        else //used to compute level if not using the library output device
        {
            m_level_meter_output = new LevelMeter(this);

            connect(m_level_meter_output, &LevelMeter::currentlevel, this, &AudioStreamingWorker::outputLevel);
        }

        break;
    }
    case AudioStreamingLibInfo::StreamingWorkMode::WebClient:
    {
        m_callback_enabled = m_streaming_info.isCallBackEnabled();

#ifdef OPUS
        m_opus_dec = new OpusDecoderClass(this);

        connect(m_opus_dec, &OpusDecoderClass::error, this, &AudioStreamingWorker::warningOutputPrivate);

        connect(m_opus_dec, &OpusDecoderClass::decoded, this, &AudioStreamingWorker::posProcessedOutput);
#endif

        if (m_streaming_info.outputDeviceType() == AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
        {
            m_audio_output = new AudioOutput(this);

            if (m_streaming_info.isGetAudioEnabled())
                connect(m_audio_output, &AudioOutput::veryOutputData, this, &AudioStreamingWorker::veryOutputData);

            connect(m_audio_output, &AudioOutput::error, this, &AudioStreamingWorker::warningOutputPrivate);
            connect(m_audio_output, &AudioOutput::currentlevel, this, &AudioStreamingWorker::outputLevel);

            m_audio_output->setVolume(m_volume);
        }
        else //used to compute level if not using the library output device
        {
            m_level_meter_output = new LevelMeter(this);

            connect(m_level_meter_output, &LevelMeter::currentlevel, this, &AudioStreamingWorker::outputLevel);
        }

        break;
    }
    default:
        break;
    }

    m_output_device_running = true;
}

void AudioStreamingWorker::startAudioWorkers()
{
    startInputAudioWorkers();
    startOutputAudioWorkers();
}

void AudioStreamingWorker::restartActiveWorkers()
{
    if (m_audio_input)
        m_audio_input->start(m_streaming_info.inputDeviceInfo(), m_streaming_info.inputAudioFormat());

    if (m_level_meter_input)
        m_level_meter_input->start(m_streaming_info.inputAudioFormat());

    if (m_streaming_info.workMode() != AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer)
    {
        if (m_audio_output)
            m_audio_output->start(m_streaming_info.outputDeviceInfo(),
                                  m_streaming_info.audioFormat(),
                                  m_streaming_info.timeToBuffer(),
                                  m_streaming_info.isGetAudioEnabled());
    }

    if (m_level_meter_output)
        m_level_meter_output->start(m_streaming_info.audioFormat());

#ifdef OPUS
    startOpusEncoder();
    startOpusDecoder();
#endif

    m_workers_started_later = true;
}

void AudioStreamingWorker::stopInputAudioWorkers()
{
    if (m_audio_input)
        m_audio_input->stop();
    if (m_flow_control)
        m_flow_control->stop();
#ifdef OPUS
    if (m_resampler)
        m_resampler->stop();
    if (m_opus_enc)
        m_opus_enc->stop();
#endif
    if (m_level_meter_input)
        m_level_meter_input->stop();
}

void AudioStreamingWorker::stopOutputAudioWorkers()
{
    if (m_audio_output)
        m_audio_output->stop();
#ifdef OPUS
    if (m_opus_dec)
        m_opus_dec->stop();
#endif
    if (m_level_meter_output)
        m_level_meter_output->stop();
}

void AudioStreamingWorker::stopAudioWorkers()
{
    stopInputAudioWorkers();
    stopOutputAudioWorkers();

    m_input_device_running = false;
    m_output_device_running = false;

    m_workers_started_later = false;
}

void AudioStreamingWorker::changeInputDevice(const QAudioDeviceInfo &dev_info)
{
    if (m_streaming_info.inputDeviceType() != AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
    {
        emit warning("The input device type isn't 'LibraryAudioDevice'");
        return;
    }

    if (!m_input_device_running)
    {
        emit warning("No input device running, nothing to change!'");
        return;
    }

    if (m_input_device_changing)
    {
        m_input_device_pending = dev_info;
        return;
    }

    m_input_device_changing = true;

    if (m_audio_input)
        connect(m_audio_input, &QObject::destroyed, this, &AudioStreamingWorker::restartInputLater, Qt::UniqueConnection);
    if (m_flow_control)
        connect(m_flow_control, &QObject::destroyed, this, &AudioStreamingWorker::restartInputLater, Qt::UniqueConnection);
#ifdef OPUS
    if (m_resampler)
        connect(m_resampler, &QObject::destroyed, this, &AudioStreamingWorker::restartInputLater, Qt::UniqueConnection);
    if (m_opus_enc)
        connect(m_opus_enc, &QObject::destroyed, this, &AudioStreamingWorker::restartInputLater, Qt::UniqueConnection);
#endif
    if (m_level_meter_input)
        connect(m_level_meter_input, &QObject::destroyed, this, &AudioStreamingWorker::restartInputLater, Qt::UniqueConnection);

    stopInputAudioWorkers();

    m_streaming_info.setInputDeviceInfo(dev_info);

    restartInputLater();
}

void AudioStreamingWorker::restartInputLater()
{
    if (m_audio_input)
        return;
    if (m_flow_control)
        return;
#ifdef OPUS
    if (m_resampler)
        return;
    if (m_opus_enc)
        return;
#endif
    if (m_level_meter_input)
        return;

    startInputAudioWorkers();

    if (m_workers_started_later)
        restartActiveWorkers();

    m_input_device_changing = false;

    if (!m_input_device_pending.isNull())
    {
        QAudioDeviceInfo dev_info = m_input_device_pending;
        m_input_device_pending = QAudioDeviceInfo();
        changeInputDevice(dev_info);
    }
}

void AudioStreamingWorker::changeOutputDevice(const QAudioDeviceInfo &dev_info)
{
    if (m_streaming_info.outputDeviceType() != AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice)
    {
        emit warning("The output device type isn't 'LibraryAudioDevice'");
        return;
    }

    if (!m_output_device_running)
    {
        emit warning("No output device running, nothing to change!");
        return;
    }

    if (m_output_device_changing)
    {
        m_output_device_pending = dev_info;
        return;
    }

    m_output_device_changing = true;

    if (m_audio_output)
        connect(m_audio_output, &QObject::destroyed, this, &AudioStreamingWorker::restartOutputLater, Qt::UniqueConnection);
#ifdef OPUS
    if (m_opus_dec)
        connect(m_opus_dec, &QObject::destroyed, this, &AudioStreamingWorker::restartOutputLater, Qt::UniqueConnection);
#endif
    if (m_level_meter_output)
        connect(m_level_meter_output, &QObject::destroyed, this, &AudioStreamingWorker::restartOutputLater, Qt::UniqueConnection);

    stopOutputAudioWorkers();

    m_streaming_info.setOutputDeviceInfo(dev_info);

    restartOutputLater();
}

void AudioStreamingWorker::restartOutputLater()
{
    if (m_audio_output)
        return;
#ifdef OPUS
    if (m_opus_dec)
        return;
#endif
    if (m_level_meter_output)
        return;

    startOutputAudioWorkers();

    if (m_workers_started_later)
        restartActiveWorkers();

    m_output_device_changing = false;

    if (!m_output_device_pending.isNull())
    {
        QAudioDeviceInfo dev_info = m_output_device_pending;
        m_output_device_pending = QAudioDeviceInfo();
        changeOutputDevice(dev_info);
    }
}

void AudioStreamingWorker::listen(quint16 port, bool auto_accept, const QByteArray &password, int max_connections)
{
    if (m_server_discover)
    {
        m_server_discover->listen(port, m_streaming_info.negotiationString(), m_streaming_info.ID());
    }
    else
    {
        errorPrivate("Not started in server mode!");
        return;
    }

    if (m_server)
    {
        m_server->listen(port, auto_accept, !m_is_walkie_talkie ? max_connections : 1,
                         m_streaming_info.negotiationString(), m_streaming_info.ID(), password);
    }
    else
    {
        errorPrivate("Not started in server mode!");
        return;
    }
}

void AudioStreamingWorker::connectToHost(const QString &host, quint16 port, const QByteArray &password)
{
    if (m_client)
    {
        m_client->connectToHost(host, port, m_streaming_info.negotiationString(), m_streaming_info.ID(), password);
    }
    else
    {
        errorPrivate("Not started in client mode!");
        return;
    }
}

void AudioStreamingWorker::writeCommandXML(const QByteArray &XML)
{
    if (m_client)
        m_client->writeCommandXML(XML);
}

void AudioStreamingWorker::connectToPeer(const QString &ID)
{
    if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WebClient)
    {
        m_client->connectToPeer(ID);
    }
}

void AudioStreamingWorker::disconnectFromPeer()
{
    if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WebClient)
    {
        m_client->disconnectFromPeer();
    }
}

void AudioStreamingWorker::acceptSslCertificate()
{
    if (m_client)
        m_client->acceptSslCertificate();
}

void AudioStreamingWorker::acceptConnection()
{
    if (m_server)
        m_server->acceptNewConnection();
    else if (m_client)
        m_client->acceptConnection();
}

void AudioStreamingWorker::rejectConnection()
{
    if (m_server)
        m_server->rejectNewConnection();
    else if (m_client)
        m_client->rejectConnection();
}

void AudioStreamingWorker::warningInputPrivate(const QString &warning_description)
{
    stopInputAudioWorkers();

    emit warning(warning_description);
}

void AudioStreamingWorker::warningOutputPrivate(const QString &warning_description)
{
    stopOutputAudioWorkers();

    emit warning(warning_description);
}

void AudioStreamingWorker::errorPrivate(const QString &error_description)
{
    if (m_has_error)
        return;

    m_has_error = true;

    emit error(error_description);
}

void AudioStreamingWorker::startOpusEncoder()
{
#ifdef OPUS
    int application = 0;

    switch (m_streaming_info.workMode())
    {
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer:
        application = OPUS_APPLICATION_AUDIO;
        break;
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient:
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer:
    case AudioStreamingLibInfo::StreamingWorkMode::WebClient:
        application = OPUS_APPLICATION_VOIP;
        break;
    default:
        break;
    }

    QAudioFormat inputAudioFormat = m_streaming_info.inputAudioFormat();
    QAudioFormat audioFormat = m_streaming_info.audioFormat();

    if (m_resampler)
        m_resampler->start(inputAudioFormat.sampleRate(),
                           audioFormat.sampleRate(),
                           inputAudioFormat.channelCount(),
                           audioFormat.channelCount(),
                           inputAudioFormat.sampleSize());

    if (m_opus_enc)
        m_opus_enc->start(audioFormat.sampleRate(),
                          audioFormat.channelCount(),
                          m_bitrate,
                          m_frame_size,
                          application);
#endif
}

void AudioStreamingWorker::startOpusDecoder()
{
#ifdef OPUS
    QAudioFormat format = m_streaming_info.audioFormat();

    if (m_opus_dec)
        m_opus_dec->start(format.sampleRate(),
                          format.channelCount(),
                          m_frame_size,
                          m_max_frame_size);
#endif
}

void AudioStreamingWorker::adjustSettingsPrivate(bool client_mode)
{
    QAudioFormat format = m_streaming_info.inputAudioFormat();

#ifdef OPUS
    //Change the settings to Opus compatible settings
    if (!client_mode)
    {
        int input_sample_rate = format.sampleRate();

        int opus_sample_rate = 0;

        int frame_size = 0;

        if (input_sample_rate <= 8000)
        {
            opus_sample_rate = 8000;
            frame_size = 80; //10 ms
        }
        else if (input_sample_rate <= 12000)
        {
            opus_sample_rate = 12000;
            frame_size = 120; //10 ms
        }
        else if (input_sample_rate <= 16000)
        {
            opus_sample_rate = 16000;
            frame_size = 160; //10 ms
        }
        else if (input_sample_rate <= 24000)
        {
            opus_sample_rate = 24000;
            frame_size = 240; //10 ms
        }
        else
        {
            opus_sample_rate = 48000;
            frame_size = 480; //10 ms
        }

        m_frame_size = frame_size;

        m_max_frame_size = 6 * frame_size;

        format.setSampleRate(opus_sample_rate);
        format.setChannelCount(qMin(format.channelCount(), 2));

        m_bitrate = m_streaming_info.OpusBitrate();

        m_streaming_info.setAudioFormat(format);
    }
#else
    Q_UNUSED(client_mode)

    m_streaming_info.setAudioFormat(format);
#endif

    emit adjustSettings();
}

//Client connected to the app, currently a server
void AudioStreamingWorker::serverClientConencted(const PeerData &pd, const QString &id)
{
    //Send the audio format to the peer
    QByteArray data;
    data.append(getBytes<quint8>(Command::AudioHeader));
    data.append(createHeader());

    m_server->writeToHost(data, pd.descriptor);

    m_ready_to_write_extra_data = true;

    m_id_connections_list.append(pd.descriptor);
    m_host_connections_list.append(pd.host);

    m_hash_heart_beat[pd.descriptor] = 0;
    m_hash_heart_beat_time[pd.descriptor].start();

    emit connected(pd.host, id);
}

//Client disconnected from the app, currently a server
void AudioStreamingWorker::serverClientDisconencted(const PeerData &pd)
{
    if (m_is_walkie_talkie)
    {
        m_ready_to_write_extra_data = false;
    }
    else
    {
        m_extra_data_peers--;
        m_extra_data_peers = qMax(m_extra_data_peers, 0);

        if (m_extra_data_peers == 0)
            m_ready_to_write_extra_data = false;
    }

    int index = m_id_connections_list.indexOf(pd.descriptor);

    if (index >= 0)
    {
        m_id_connections_list.removeAt(index);
        m_host_connections_list.removeAt(index);
    }

    m_hash_heart_beat.remove(pd.descriptor);
    m_hash_heart_beat_time.remove(pd.descriptor);

    emit disconnected(pd.host);
}

//The app is currently connected to the server
void AudioStreamingWorker::clientConencted(const PeerData &pd, const QString &id)
{
    m_ready_to_write_extra_data = true;

    m_id_connections_list.append(pd.descriptor);
    m_host_connections_list.append(pd.host);

    m_hash_heart_beat[pd.descriptor] = 0;
    m_hash_heart_beat_time[pd.descriptor].start();

    emit connected(pd.host, id);
}

//The app currently disconnected from the server
void AudioStreamingWorker::clientDisconencted(const PeerData &pd)
{
    if (m_is_walkie_talkie)
    {
        m_ready_to_write_extra_data = false;
    }
    else
    {
        m_extra_data_peers--;
        m_extra_data_peers = qMax(m_extra_data_peers, 0);

        if (m_extra_data_peers == 0)
            m_ready_to_write_extra_data = false;
    }

    int index = m_id_connections_list.indexOf(pd.descriptor);

    if (index >= 0)
    {
        m_id_connections_list.removeAt(index);
        m_host_connections_list.removeAt(index);
    }

    m_hash_heart_beat.remove(pd.descriptor);
    m_hash_heart_beat_time.remove(pd.descriptor);

    emit disconnected(pd.host);
}

void AudioStreamingWorker::webClientConencted(const QByteArray &hash)
{
    emit connectedToServer(hash);
}

//The app is currently connected to another peer
void AudioStreamingWorker::webClientConnectedToPeer(const QString &id)
{
    //Send the audio format to the peer
    QByteArray data;
    data.append(getBytes<quint8>(Command::AudioHeader));
    data.append(createHeader());

    m_client->write(data);

    m_ready_to_write_extra_data = true;

    m_id_connections_list.append(0);

    m_hash_heart_beat[0] = 0;
    m_hash_heart_beat_time[0].start();

    emit connected(QHostAddress(), id);
}

//The app currently disconnected from another peer
void AudioStreamingWorker::webClientDisconnected()
{
    stopAudioWorkers();

    m_ready_to_write_extra_data = false;

    int index = m_id_connections_list.indexOf(0);

    if (index >= 0)
        m_id_connections_list.removeAt(index);

    m_hash_heart_beat.remove(0);
    m_hash_heart_beat_time.remove(0);

    emit disconnected(QHostAddress());
}

//Write data that can be or not relacted to audio streamed
void AudioStreamingWorker::writeExtraData(const QByteArray &data)
{
    QByteArray result;
    result.append(getBytes<quint8>(Command::ExtraData));
    result.append(data);

    if (m_server)
        m_extra_data_peers = m_server->writeToAll(result);
    else if (m_client)
        m_extra_data_peers = m_client->write(result);

    if (m_extra_data_peers > 0)
        m_ready_to_write_extra_data = false;
}

//Tell to the peer that the extra data was sent and now it's ready to more
void AudioStreamingWorker::writeExtraDataResult()
{
    QByteArray result;
    result.append(getBytes<quint8>(Command::ExtraDataReceived));

    if (m_server)
        m_server->writeToAll(result);
    else if (m_client)
        m_client->write(result);
}

//Receive the input callback data back
//and encode audio if using with Opus codec
void AudioStreamingWorker::inputDataBack(const QByteArray &data)
{
    if (data.size() % int(sizeof(float)) != 0)
    {
        errorPrivate("Corrupted audio data!");
        return;
    }

    QByteArray middle = data;

    if (m_input_muted && !middle.isEmpty())
    {
        Eigen::Ref<Eigen::VectorXf> samples = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(middle.data()), middle.size() / int(sizeof(float)));
        samples.fill(0);
    }
    else if (!middle.isEmpty())
    {
        int middle_size = middle.size() / int(sizeof(float));

        float *middle_data = reinterpret_cast<float*>(middle.data());

        for (int i = 0; i < middle_size; i++)
            middle_data[i] = qBound(float(-1), middle_data[i], float(1));
    }

    if (!m_streaming_info.isListenAudioInputEnabled() && m_level_meter_input)
        m_level_meter_input->write(middle);

#ifdef OPUS
    m_resampler->write(middle);
#else
    if (m_streaming_info.isGetAudioEnabled() && !m_streaming_info.isListenAudioInputEnabled())
        emit veryInputData(middle);

    if (m_streaming_info.isListenAudioInputEnabled() && m_audio_output)
        m_audio_output->write(middle);

    posProcessedInput(middle);
#endif
}

void AudioStreamingWorker::posProcessedInput(const QByteArray &data)
{
    QByteArray result;
    result.append(getBytes<quint8>(Command::AudioData));
    result.append(data);

    if (m_server)
    {
        for (qintptr descriptor : m_id_connections_list)
        {
            if (!m_hash_heart_beat.contains(descriptor))
                continue;

            qint32 &beats = m_hash_heart_beat[descriptor];
            QElapsedTimer &time = m_hash_heart_beat_time[descriptor];

            if (time.elapsed() < MAX_BUFFERED_TIME || beats <= MAX_PENDING_BEATS)
            {
                LIB_DEBUG_LEVEL_2("Sent Audio Data - Size:" << result.size() << "bytes.");

                beats++;

                m_server->writeToHost(result, descriptor);
            }
            else
            {
                LIB_DEBUG_LEVEL_2("Not Sent Audio Data - Size:" << result.size() << "bytes.");
            }
        }
    }
    else if (m_client)
    {
        for (qintptr descriptor : m_id_connections_list)
        {
            if (!m_hash_heart_beat.contains(descriptor))
                continue;

            qint32 &beats = m_hash_heart_beat[descriptor];
            QElapsedTimer &time = m_hash_heart_beat_time[descriptor];

            if (time.elapsed() < MAX_BUFFERED_TIME || beats <= MAX_PENDING_BEATS)
            {
                LIB_DEBUG_LEVEL_2("Sent Audio Data - Size:" << result.size() << "bytes.");

                beats++;

                m_client->write(result);
            }
            else
            {
                LIB_DEBUG_LEVEL_2("Not Sent Audio Data - Size:" << result.size() << "bytes.");
            }
        }
    }
}

//Decode audio if using Opus codec
void AudioStreamingWorker::preProcessOutput(const QByteArray &data)
{
#ifdef OPUS
    m_opus_dec->write(data);
#else
    posProcessedOutput(data);
#endif
}

void AudioStreamingWorker::posProcessedOutput(const QByteArray &data)
{
    if (m_level_meter_output)
        m_level_meter_output->write(data);

    if (m_callback_enabled)
        emit outputData(data);
    else
        outputDataBack(data);
}

//Send callback output data to the output device
void AudioStreamingWorker::outputDataBack(const QByteArray &data)
{
    if (data.size() % int(sizeof(float)) != 0)
    {
        errorPrivate("Corrupted audio data!");
        return;
    }

    if (m_audio_output)
        m_audio_output->write(data);
}

//Convert flow control bytes to pseudo audio data
void AudioStreamingWorker::flowControl(const QByteArray &data)
{
    if (m_callback_enabled)
        emit inputData(data);
    else
        inputDataBack(data);
}

bool AudioStreamingWorker::isInputMuted() const
{
    return m_input_muted;
}

void AudioStreamingWorker::setInputMuted(bool mute)
{
    m_input_muted = mute;
}

int AudioStreamingWorker::volume() const
{
    return m_volume;
}

void AudioStreamingWorker::setVolume(int volume)
{
    m_volume = volume;

    if (m_audio_output)
        m_audio_output->setVolume(m_volume);
}

AudioStreamingLibInfo AudioStreamingWorker::audioStreamingLibInfo() const
{
    return m_streaming_info;
}

QList<QHostAddress> AudioStreamingWorker::connectionsList() const
{
    return m_host_connections_list;
}

bool AudioStreamingWorker::isReadyToWriteExtraData() const
{
    return m_ready_to_write_extra_data;
}

void AudioStreamingWorker::processServerInput(const PeerData &pd)
{
    QByteArray data = QByteArray(pd.data);

    if (data.isEmpty())
    {
        m_server->abort(pd.descriptor);
        return;
    }

    quint8 command = getValue<quint8>(data.mid(0, 1));
    data.remove(0, 1);

    switch (command)
    {
    case Command::AudioHeader:
    {
        //Server should not receive header
        m_server->abort(pd.descriptor);
        return;
    }
    case Command::AudioData:
    {
        //Audio data comes to the server,
        //if necesary decode it and send it to the callback signal

        LIB_DEBUG_LEVEL_2("Got Audio Data - Size:" << data.size() << "bytes.");

        preProcessOutput(data);

        QByteArray result;
        result.append(getBytes<quint8>(Command::HeartBeat));

        if (m_server)
            m_server->writeToHost(result, pd.descriptor);

        break;
    }
    case Command::HeartBeat:
    {
        if (!data.isEmpty())
        {
            m_server->abort(pd.descriptor);
            return;
        }

        qint32 &beats = m_hash_heart_beat[pd.descriptor];
        QElapsedTimer &time = m_hash_heart_beat_time[pd.descriptor];

        if (beats > 0)
            beats--;
        else
            time.restart();

        break;
    }
    case Command::ExtraData:
    {
        //Extra data received
        emit extraData(data);

        break;
    }
    case Command::ExtraDataReceived:
    {
        if (!data.isEmpty())
        {
            m_server->abort(pd.descriptor);
            return;
        }

        m_extra_data_peers--;

        m_extra_data_peers = qMax(m_extra_data_peers, 0);

        if (m_extra_data_peers == 0)
        {
            m_ready_to_write_extra_data = true;
            emit extraDataWritten();
        }

        break;
    }
    default:
    {
        m_server->abort(pd.descriptor);
        return;
    }
    }
}

void AudioStreamingWorker::processClientInput(const PeerData &pd)
{
    QByteArray data = QByteArray(pd.data);

    if (data.isEmpty())
    {
        m_client->abort();
        return;
    }

    quint8 command = getValue<quint8>(data.mid(0, 1));
    data.remove(0, 1);

    switch (command)
    {
    case Command::AudioHeader:
    {
        //Header got by the server

        if (data.size() != 56)
        {
            m_client->abort();
            return;
        }

        QAudioFormat inputFormat;
        QAudioFormat audioFormat;
        qint32 timeToBuffer;

        header(data, &inputFormat, &audioFormat, &timeToBuffer);

        m_streaming_info.setInputAudioFormat(inputFormat);
        m_streaming_info.setAudioFormat(audioFormat);

        if (m_is_walkie_talkie)
            m_streaming_info.setTimeToBuffer(timeToBuffer);

        if (INVALID_FORMAT)
        {
            errorPrivate("Invalid format!");
            return;
        }

        adjustSettingsPrivate(true);

        startAudioWorkers();

        restartActiveWorkers();

        break;
    }
    case Command::AudioData:
    {
        LIB_DEBUG_LEVEL_2("Got Audio Data - Size:" << data.size() << "bytes.");

        preProcessOutput(data);

        QByteArray result;
        result.append(getBytes<quint8>(Command::HeartBeat));

        if (m_client)
            m_client->write(result);

        break;
    }
    case Command::HeartBeat:
    {
        if (!data.isEmpty())
        {
            m_client->abort();
            return;
        }

        qint32 &beats = m_hash_heart_beat[pd.descriptor];
        QElapsedTimer &time = m_hash_heart_beat_time[pd.descriptor];

        if (beats > 0)
            beats--;

        if (beats <= MAX_PENDING_BEATS)
            time.restart();

        break;
    }
    case Command::ExtraData:
    {
        emit extraData(data);

        break;
    }
    case Command::ExtraDataReceived:
    {
        if (!data.isEmpty())
        {
            m_client->abort();
            return;
        }

        m_extra_data_peers--;

        m_extra_data_peers = qMax(m_extra_data_peers, 0);

        if (m_extra_data_peers == 0)
        {
            m_ready_to_write_extra_data = true;
            emit extraDataWritten();
        }

        break;
    }
    default:
    {
        m_client->abort();
        return;
    }
    }
}

void AudioStreamingWorker::processWebClientInput(const PeerData &pd)
{
    QByteArray data = QByteArray(pd.data);

    if (data.isEmpty())
    {
        m_client->abort();
        return;
    }

    quint8 command = getValue<quint8>(data.mid(0, 1));
    data.remove(0, 1);

    switch (command)
    {
    case Command::AudioHeader:
    {
        //Header got by the server

        if (data.size() != 56)
        {
            m_client->abort();
            return;
        }

        QAudioFormat inputFormat;
        QAudioFormat audioFormat;
        qint32 timeToBuffer;

        header(data, &inputFormat, &audioFormat, &timeToBuffer);

        if (INVALID_FORMAT)
        {
            errorPrivate("Invalid format!");
            return;
        }

        if (m_streaming_info.inputAudioFormat() != inputFormat ||
                m_streaming_info.audioFormat() != audioFormat ||
                m_streaming_info.timeToBuffer() != timeToBuffer)
        {
            errorPrivate("Incompatible audio formats!");
            return;
        }

        startAudioWorkers();

        restartActiveWorkers();

        break;
    }
    case Command::AudioData:
    {
        LIB_DEBUG_LEVEL_2("Got Audio Data - Size:" << data.size() << "bytes.");

        preProcessOutput(data);

        QByteArray result;
        result.append(getBytes<quint8>(Command::HeartBeat));

        if (m_client)
            m_client->write(result);

        break;
    }
    case Command::HeartBeat:
    {
        if (!data.isEmpty())
        {
            m_server->abort(pd.descriptor);
            return;
        }

        qint32 &beats = m_hash_heart_beat[pd.descriptor];
        QElapsedTimer &time = m_hash_heart_beat_time[pd.descriptor];

        if (beats > 0)
            beats--;

        if (beats <= MAX_PENDING_BEATS)
            time.restart();

        break;
    }
    case Command::ExtraData:
    {
        emit extraData(data);

        break;
    }
    case Command::ExtraDataReceived:
    {
        if (!data.isEmpty())
        {
            m_client->abort();
            return;
        }

        m_extra_data_peers--;

        m_extra_data_peers = qMax(m_extra_data_peers, 0);

        if (m_extra_data_peers == 0)
        {
            m_ready_to_write_extra_data = true;
            emit extraDataWritten();
        }

        break;
    }
    default:
    {
        m_client->abort();
        return;
    }
    }
}

QByteArray AudioStreamingWorker::createHeader()
{
    QByteArray data;

    QAudioFormat inputFormat = m_streaming_info.inputAudioFormat();
    QAudioFormat format = m_streaming_info.audioFormat();

    QDataStream stream(&data, QIODevice::WriteOnly);

    stream << qint32(format.sampleSize());
    stream << qint32(format.sampleRate());
    stream << qint32(format.channelCount());
    stream << qint32(format.sampleType());
    stream << qint32(format.byteOrder());

    stream << qint32(inputFormat.sampleSize());
    stream << qint32(inputFormat.sampleRate());
    stream << qint32(inputFormat.channelCount());
    stream << qint32(inputFormat.sampleType());
    stream << qint32(inputFormat.byteOrder());

    if (m_is_walkie_talkie)
        stream << qint32(m_streaming_info.timeToBuffer());
    else
        stream << qint32(0);

#ifdef OPUS
    stream << m_frame_size;
    stream << m_max_frame_size;
    stream << m_bitrate;
#else
    stream << qint32(0);
    stream << qint32(0);
    stream << qint32(0);
#endif

    return data;
}

void AudioStreamingWorker::header(QByteArray data,
                    QAudioFormat *refInputAudioFormat,
                    QAudioFormat *refAudioFormat,
                    qint32 *refTimeToBuffer)
{
    qint32 sampleSize;
    qint32 sampleRate;
    qint32 channelCount;
    qint32 sampleType;
    qint32 byteOrder;

    qint32 inputSampleSize;
    qint32 inputSampleRate;
    qint32 inputChannelCount;
    qint32 inputSampleType;
    qint32 inputByteOrder;

    qint32 timeToBuffer;

#ifdef OPUS
    qint32 frame_size;
    qint32 max_frame_size;
    qint32 bitrate;
#endif

    QDataStream stream(&data, QIODevice::ReadOnly);

    stream >> sampleSize;
    stream >> sampleRate;
    stream >> channelCount;
    stream >> sampleType;
    stream >> byteOrder;

    stream >> inputSampleSize;
    stream >> inputSampleRate;
    stream >> inputChannelCount;
    stream >> inputSampleType;
    stream >> inputByteOrder;

    stream >> timeToBuffer;

#ifdef OPUS
    stream >> frame_size;
    stream >> max_frame_size;
    stream >> bitrate;

    if (m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient
            || m_streaming_info.workMode() == AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient)
    {
        m_frame_size = frame_size;
        m_max_frame_size = max_frame_size;
        m_bitrate = bitrate;
    }

    m_streaming_info.setOpusBitrate(m_bitrate);
#endif

    if (refTimeToBuffer)
        *refTimeToBuffer = timeToBuffer;

    QAudioFormat format;
    format.setCodec("audio/pcm");
    format.setSampleSize(sampleSize);
    format.setSampleRate(sampleRate);
    format.setChannelCount(channelCount);
    format.setSampleType(QAudioFormat::SampleType(sampleType));
    format.setByteOrder(QAudioFormat::Endian(byteOrder));

    QAudioFormat inputFormat;
    inputFormat.setCodec("audio/pcm");
    inputFormat.setSampleSize(inputSampleSize);
    inputFormat.setSampleRate(inputSampleRate);
    inputFormat.setChannelCount(inputChannelCount);
    inputFormat.setSampleType(QAudioFormat::SampleType(inputSampleType));
    inputFormat.setByteOrder(QAudioFormat::Endian(inputByteOrder));

    *refInputAudioFormat = inputFormat;
    *refAudioFormat = format;
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/audiocommon.h<|end_filename|>
#ifndef AUDIOCOMMON_H
#define AUDIOCOMMON_H

#include <QtCore>
#include <QtMultimedia>
#include <Eigen/Eigen>

static inline qint64 timeToSize(qint64 ms_time, int channel_count, int sample_size, int sample_rate)
{
    qint64 value = qint64(qCeil(channel_count * (sample_size / 8) * sample_rate / qreal(1000) * ms_time));

    if (value % (channel_count * (sample_size / 8)) != 0)
        value += (channel_count * (sample_size / 8) - value % (channel_count * (sample_size / 8)));

    return value;
}

static inline qint64 timeToSize(qint64 ms_time, const QAudioFormat &format)
{
    return timeToSize(ms_time, format.channelCount(), format.sampleSize(), format.sampleRate());
}

static inline qint64 sizeToTime(qint64 bytes, int channel_count, int sample_size, int sample_rate)
{
    return qint64(qFloor(bytes / (channel_count * (sample_size / 8) * sample_rate / qreal(1000))));
}

static inline qint64 sizeToTime(qint64 bytes, const QAudioFormat &format)
{
    return sizeToTime(bytes, format.channelCount(), format.sampleSize(), format.sampleRate());
}

typedef Eigen::Matrix<quint8, Eigen::Dynamic, 1> VectorXuint8;
typedef Eigen::Matrix<qint8, Eigen::Dynamic, 1> VectorXint8;
typedef Eigen::Matrix<qint16, Eigen::Dynamic, 1> VectorXint16;
typedef Eigen::Matrix<qint32, Eigen::Dynamic, 1> VectorXint32;

static inline QByteArray convertSamplesToInt(const QByteArray &data, const QAudioFormat &supported_format)
{
    QByteArray input = data;

    switch (supported_format.sampleSize())
    {
    case 8:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::UnSignedInt:
        {
            Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));

            Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<quint8>::max());

            VectorXuint8 samples_int = samples_int_tmp.cast<quint8>();

            QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(quint8)));

            return raw;
        }
        case QAudioFormat::SignedInt:
        {
            Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));

            Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint8>::max());

            VectorXint8 samples_int = samples_int_tmp.cast<qint8>();

            QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint8)));

            return raw;
        }
        default:
            break;
        }

        break;
    }
    case 16:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::SignedInt:
        {
            Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));

            Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint16>::max());

            VectorXint16 samples_int = samples_int_tmp.cast<qint16>();

            QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint16)));

            return raw;
        }
        default:
            break;
        }

        break;
    }
    case 32:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::SignedInt:
        {
            Eigen::Ref<Eigen::VectorXf> samples_float = Eigen::Map<Eigen::VectorXf>(reinterpret_cast<float*>(input.data()), input.size() / int(sizeof(float)));

            Eigen::VectorXf samples_int_tmp = samples_float * float(std::numeric_limits<qint32>::max());

            VectorXint32 samples_int = samples_int_tmp.cast<qint32>();

            QByteArray raw = QByteArray(reinterpret_cast<char*>(samples_int.data()), int(samples_int.size()) * int(sizeof(qint32)));

            return raw;
        }
        default:
            break;
        }

        break;
    }
    default:
        break;
    }

    return QByteArray();
}

static inline QByteArray convertSamplesToFloat(const QByteArray &data, const QAudioFormat &supported_format)
{
    QByteArray input = data;

    switch (supported_format.sampleSize())
    {
    case 8:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::UnSignedInt:
        {
            QByteArray raw = input;

            Eigen::Ref<VectorXuint8> samples_int = Eigen::Map<VectorXuint8>(reinterpret_cast<quint8*>(raw.data()), raw.size() / int(sizeof(quint8)));

            Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<quint8>::max());

            return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
        }
        case QAudioFormat::SignedInt:
        {
            QByteArray raw = input;

            Eigen::Ref<VectorXint8> samples_int = Eigen::Map<VectorXint8>(reinterpret_cast<qint8*>(raw.data()), raw.size() / int(sizeof(qint8)));

            Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint8>::max());

            return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
        }
        default:
            break;
        }

        break;
    }
    case 16:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::SignedInt:
        {
            QByteArray raw = input;

            Eigen::Ref<VectorXint16> samples_int = Eigen::Map<VectorXint16>(reinterpret_cast<qint16*>(raw.data()), raw.size() / int(sizeof(qint16)));

            Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint16>::max());

            return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
        }
        default:
            break;
        }

        break;
    }
    case 32:
    {
        switch (supported_format.sampleType())
        {
        case QAudioFormat::SignedInt:
        {
            QByteArray raw = input;

            Eigen::Ref<VectorXint32> samples_int = Eigen::Map<VectorXint32>(reinterpret_cast<qint32*>(raw.data()), raw.size() / int(sizeof(qint32)));

            Eigen::VectorXf samples_float = samples_int.cast<float>() / float(std::numeric_limits<qint32>::max());

            return QByteArray(reinterpret_cast<char*>(samples_float.data()), int(samples_float.size()) * int(sizeof(float)));
        }
        default:
            break;
        }

        break;
    }
    default:
        break;
    }

    return QByteArray();
}

#endif // AUDIOCOMMON_H


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/audioinput.h<|end_filename|>
#ifndef AUDIOINPUT_H
#define AUDIOINPUT_H

#include <QtCore>
#include <QtMultimedia>
#include "common.h"
#include "threadcommon.h"

//\cond HIDDEN_SYMBOLS
class AudioInput : public QObject
{
    Q_OBJECT
public:
    explicit AudioInput(QObject *parent = nullptr);
    ~AudioInput();

    CLASS_MEMBER_HEADER

signals:
    void error(QString);
    void readyRead(QByteArray);

public slots:
    void start(const QAudioDeviceInfo &devinfo,
               const QAudioFormat &format);

private slots:
    void startPrivate(const QAudioDeviceInfo &devinfo, const QAudioFormat &format);
    void readyReadPrivate();
    void resampledData(const QByteArray &data);

private:
    QPointer<QAudioInput> m_audio_input;
    QPointer<QIODevice> m_device;
    QPointer<r8brain> m_resampler;
    QAudioFormat m_format;
    QAudioFormat m_supported_format;
};
//\endcond

#endif // AUDIOINPUT_H


<|start_filename|>AudioStreamingLib/Demos/BroadcastClientConsole/main.cpp<|end_filename|>
#include <QtCore>
#include <AudioStreamingLibCore>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QCoreApplication::setApplicationName("BroadcastClientDemoConsole");

    QCommandLineParser parser;

    QCommandLineOption address = QCommandLineOption(QStringList() << "a" << "address",
                                                    "Set the address to connect to <addr>",
                                                    "addr");

    QCommandLineOption port = QCommandLineOption(QStringList() << "p" << "port",
                                                 "Set the address port to connect to <port>, "
                                                 "default is 1024",
                                                 "port", "1024");

    QCommandLineOption time_to_buffer = QCommandLineOption(QStringList() << "time-to-buffer",
                                                           "Set the time to buffer in milliseconds to <time>, "
                                                           "default is 0 (Smart buffer)",
                                                           "time", "0");

    QCommandLineOption password = QCommandLineOption(QStringList() << "password",
                                                     "Enable encryption with the password <pass> "
                                                     "(Not secure in console), default is no password",
                                                     "pass");

    QCommandLineOption help = parser.addHelpOption();
    parser.setApplicationDescription("\nBroadcast client demo console is a simple example\n"
                                     "on how to use the AudioStreamingLib with minimal code,\n"
                                     "on envinronments with no graphical interface and/or in automated mode!");

    parser.addOption(address);
    parser.addOption(port);
    parser.addOption(time_to_buffer);
    parser.addOption(password);

    if (!parser.parse(QCoreApplication::arguments()))
    {
        qDebug() << qPrintable(parser.errorText());
        return 1;
    }

    if (parser.isSet(help))
        parser.showHelp(0);

    if (!parser.isSet(address))
        parser.showHelp(1);

    AudioStreamingLibCore audio_lib;

    AudioStreamingLibInfo info;

    info.setWorkMode(AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient);
    info.setTimeToBuffer(parser.value(time_to_buffer).toInt());
    info.setEncryptionEnabled(!parser.value(password).isEmpty());
    info.setNegotiationString("BroadcastTCPDemo");

    QObject::connect(&audio_lib, &AudioStreamingLibCore::error, &a, [&](const QString &error){
        qDebug() << "Error:" << qPrintable(error) << "\n";
        QCoreApplication::exit(1);
    });

    QObject::connect(&audio_lib, &AudioStreamingLibCore::connected, &a, [&](const QHostAddress &host_address){
        qDebug() << "Connected to:" << qPrintable(host_address.toString()) << "\n";
    });

    QObject::connect(&audio_lib, &AudioStreamingLibCore::disconnected, &a, [&](const QHostAddress &host_address){
        qDebug() << "Disconnected from:" << qPrintable(host_address.toString()) << "\n";
    });

    QObject::connect(&audio_lib, &AudioStreamingLibCore::finished, &a, [&]{
        qDebug() << "AudioStreamingLib finished, exiting..." << "\n";
        QCoreApplication::exit(0);
    });

    audio_lib.start(info);

    audio_lib.setVolume(100);

    qDebug() << "Connecting to:" << qPrintable(parser.value(address))
             << "on port:" << parser.value(port).toInt() << "..." << "\n";

    audio_lib.connectToHost(parser.value(address),
                            quint16(parser.value(port).toInt()),
                            parser.value(password).toLatin1());

    return a.exec();
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/r8brain.h<|end_filename|>
#ifndef R8BRAIN_H
#define R8BRAIN_H

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wpragmas"
#pragma GCC diagnostic ignored "-Wunused-private-field"
#endif

#include <QtCore>
#include <limits>
#include <r8bbase.h>
#include <CDSPResampler.h>
#include "threadcommon.h"

//\cond HIDDEN_SYMBOLS
using namespace r8b;
//\endcond

//\cond HIDDEN_SYMBOLS
class r8brain : public QObject
{
    Q_OBJECT
public:
    explicit r8brain(QObject *parent = nullptr);
    ~r8brain();

    CLASS_MEMBER_HEADER

signals:
    void resampled(QByteArray);
    void error(QString);

public slots:
    void start(int in_sample_rate, int out_sample_rate, int in_channels, int out_channels, int sample_size);
    void write(const QByteArray &input);

private slots:
    void startPrivate(int in_sample_rate, int out_sample_rate, int in_channels, int out_channels, int sample_size);
    void writePrivate(const QByteArray &input);

private:
    int m_bits;
    int m_channels_input;
    int m_channels_output;
    CFixedBuffer<double> *m_in_bufs;
    CPtrKeeper<CDSPResampler24*> *m_resamps;
    double **m_opp;
    bool m_initialized;
};
//\endcond

#endif // R8BRAIN_H


<|start_filename|>AudioStreamingLib/WebServer/certificate.h<|end_filename|>
#ifndef CERTIFICATE_H
#define CERTIFICATE_H

#include <QtCore>

#include <cstdio>
#include <cstdlib>
#include <stdio.h>

#include <memory>

#include <openssl/pem.h>
#include <openssl/x509.h>

#define pEVP_PKEY_assign_RSA(pkey,rsa) \
pEVP_PKEY_assign((pkey),EVP_PKEY_RSA,reinterpret_cast<char*>(rsa))

class Certificate : public QObject
{
    Q_OBJECT
public:
    explicit Certificate(QObject *parent = nullptr);

public slots:
    bool generate(const QString &country, const QString &commonname, const QString &domain, const QByteArray &key_password = QByteArray());

    bool hasError();
    QString errorString();

private:
    //Functions
    void loadFunctions();

    EVP_PKEY *generate_key();
    X509 *generate_x509(EVP_PKEY *pkey);

    //Variables
    bool m_has_error;
    QString m_error_str;

    char m_country[255];
    char m_common_name[255];
    char m_domain[255];

    bool m_loaded;

    //Imports
    typedef EVP_PKEY *(*tEVP_PKEY_new)();
    tEVP_PKEY_new pEVP_PKEY_new;

    typedef const EVP_CIPHER *(*tEVP_des_ede3_cbc)();
    tEVP_des_ede3_cbc pEVP_des_ede3_cbc;

    typedef int (*tSSL_library_init)();
    tSSL_library_init pSSL_library_init;

    typedef RSA *(*tRSA_generate_key)(int bits, unsigned long e, void (*callback) (int, int, void *), void *cb_arg);
    tRSA_generate_key pRSA_generate_key;

    typedef int (*tEVP_PKEY_assign)(EVP_PKEY *pkey, int type, void *key);
    tEVP_PKEY_assign pEVP_PKEY_assign;

    typedef void (*tEVP_PKEY_free)(EVP_PKEY *pkey);
    tEVP_PKEY_free pEVP_PKEY_free;

    typedef X509 *(*tX509_new)();
    tX509_new pX509_new;

    typedef int (*tASN1_INTEGER_set)(ASN1_INTEGER *a, long v);
    tASN1_INTEGER_set pASN1_INTEGER_set;

    typedef ASN1_INTEGER *(*tX509_get_serialNumber)(X509 *x);
    tX509_get_serialNumber pX509_get_serialNumber;

    typedef ASN1_TIME *(*tX509_gmtime_adj)(ASN1_TIME *s, long adj);
    tX509_gmtime_adj pX509_gmtime_adj;

    typedef int (*tX509_set_pubkey)(X509 *x, EVP_PKEY *pkey);
    tX509_set_pubkey pX509_set_pubkey;

    typedef X509_NAME *(*tX509_get_subject_name)(X509 *a);
    tX509_get_subject_name pX509_get_subject_name;

    typedef int (*tX509_NAME_add_entry_by_txt)(X509_NAME *name, const char *field, int type,
                                               const unsigned char *bytes, int len, int loc,
                                               int set);
    tX509_NAME_add_entry_by_txt pX509_NAME_add_entry_by_txt;

    typedef int (*tX509_set_issuer_name)(X509 *x, X509_NAME *name);
    tX509_set_issuer_name pX509_set_issuer_name;

    typedef const EVP_MD *(*tEVP_sha1)(void);
    tEVP_sha1 pEVP_sha1;

    typedef int (*tX509_sign)(X509 *x, EVP_PKEY *pkey, const EVP_MD *md);
    tX509_sign pX509_sign;

    typedef void (*tX509_free)(X509 *a);
    tX509_free pX509_free;

    typedef int (*tPEM_write_PrivateKey)(FILE *fp, EVP_PKEY *x,
                                         const EVP_CIPHER *enc, unsigned char *kstr, int klen,
                                         pem_password_cb *cb, void *u);
    tPEM_write_PrivateKey pPEM_write_PrivateKey;

    typedef int (*tPEM_write_X509)(FILE *fp, X509 *x);
    tPEM_write_X509 pPEM_write_X509;

    typedef BIO*(*tBIO_new)(BIO_METHOD *type);
    tBIO_new pBIO_new;

    typedef BIO_METHOD*(*tBIO_s_mem)(void);
    tBIO_s_mem pBIO_s_mem;

    typedef void(*tBIO_free_all)(BIO *a);
    tBIO_free_all pBIO_free_all;

    typedef int(*tBIO_read)(BIO *b, void *data, int len);
    tBIO_read pBIO_read;

    typedef int(*tPEM_write_bio_PrivateKey)(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc,
                                            unsigned char *kstr, int klen,
                                            pem_password_cb *cb, void *u);
    tPEM_write_bio_PrivateKey pPEM_write_bio_PrivateKey;

    typedef int(*tPEM_write_bio_X509)(BIO *bp, X509 *x);
    tPEM_write_bio_X509 pPEM_write_bio_X509;

    typedef long(*tBIO_ctrl)(BIO *bp, int cmd, long larg, void *parg);
    tBIO_ctrl pBIO_ctrl;

    typedef ASN1_TIME *(*tX509_getm_notBefore)(const X509 *x);
    tX509_getm_notBefore pX509_getm_notBefore;

    typedef ASN1_TIME *(*tX509_getm_notAfter)(const X509 *x);
    tX509_getm_notAfter pX509_getm_notAfter;
};

#endif // CERTIFICATE_H


<|start_filename|>AudioStreamingLib/Demos/BroadcastServer/mainwindow.cpp<|end_filename|>
#include "mainwindow.h"

#define TITLE "Broadcast Server Demo"

static QPlainTextEdit *debug_edit = nullptr;
static QMutex debug_mutex;

void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(type)
    Q_UNUSED(context)

    QMutexLocker locker(&debug_mutex);

    if (debug_edit)
        QMetaObject::invokeMethod(debug_edit, "appendPlainText", Q_ARG(QString, msg));
}

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    qRegisterMetaType<QVector<SpectrumStruct>>("QVector<SpectrumStruct>");

    setWindowTitle(TITLE);

    m_total_size = 0;

    m_paused = true;

    initWidgets();
}

MainWindow::~MainWindow()
{
    QMutexLocker locker(&debug_mutex);

    debug_edit = nullptr;
}

void MainWindow::initWidgets()
{
    tabwidget = new QTabWidget(this);

    QWidget *widget = new QWidget(this);

    QGridLayout *layout = new QGridLayout(widget);

    QScrollArea *areasettings = new QScrollArea(this);

#ifndef Q_OS_ANDROID
    areasettings->setWidgetResizable(true);
    areasettings->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
#endif

#ifdef Q_OS_ANDROID
    areasettings->setWidgetResizable(true);

    areasettings->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    areasettings->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    QScroller::grabGesture(areasettings, QScroller::LeftMouseButtonGesture);
#endif

#ifndef Q_OS_ANDROID
    m_ffmpeg = new FFMPEGClass(this);
#endif

    m_ffmpeg_terminating = false;

    m_ffmpeg_playing = false;

    QWidget *widgetsettings = new QWidget(this);

    QGridLayout *layout1 = new QGridLayout(widgetsettings);

    buttonloopbackaudioinput = new QRadioButton(this);
    buttonloopbackaudioinput->setText("Loopback (What you hear)");

    buttonffmpegaudioinput = new QRadioButton(this);
    buttonffmpegaudioinput->setText("FFMPEG");

    buttonaudioinput = new QRadioButton(this);
    buttonaudioinput->setText("Library input audio device");

    comboboxaudioinput = new QComboBox(this);

    boxlisteninput = new QCheckBox(this);
    boxlisteninput->setText("Listen");

    listconnections = new QListWidget(this);

    lineport = new QLineEdit(this);

    linemaxconnections = new QLineEdit(this);

    linesamplerate = new QLineEdit(this);

    linechannels = new QLineEdit(this);

    buttonstart = new QPushButton(this);
    buttonstart->setText("Start Server");

    lineid = new QLineEdit(this);

    linepassword = new QLineEdit(this);
    linepassword->setEchoMode(QLineEdit::Password);

    labelvolume = new QLabel(this);
    slidervolume = new QSlider(Qt::Horizontal, this);

    groupboxtype = new QGroupBox(this);

    QVBoxLayout *vbox = new QVBoxLayout();

    vbox->addWidget(buttonloopbackaudioinput);
    vbox->addWidget(buttonffmpegaudioinput);
    vbox->addWidget(buttonaudioinput);

    groupboxtype->setLayout(vbox);

#ifndef Q_OS_WIN
    buttonloopbackaudioinput->setEnabled(false);
#endif

#ifdef Q_OS_ANDROID
    buttonffmpegaudioinput->setEnabled(false);
#endif

    slidervolume->setRange(0, 150);

    buttonstart->setDefault(true);

    texteditsettings = new QPlainTextEdit(this);

    layout1->addWidget(new QLabel("Input type:", this), 0, 0);
    layout1->addWidget(groupboxtype, 0, 1, 1, 2);
    layout1->addWidget(new QLabel("Input device:", this), 1, 0);
    layout1->addWidget(comboboxaudioinput, 1, 1);
    layout1->addWidget(boxlisteninput, 1, 2);
    layout1->addWidget(new QLabel("Port:", this), 2, 0);
    layout1->addWidget(lineport, 2, 1);
    layout1->addWidget(buttonstart, 2, 2);
    layout1->addWidget(new QLabel("Maximum connections:", this), 3, 0);
    layout1->addWidget(linemaxconnections, 3, 1, 1, 2);
    layout1->addWidget(new QLabel("ID:", this), 4, 0);
    layout1->addWidget(lineid, 4, 1, 1, 2);
    layout1->addWidget(new QLabel("Password:", this), 5, 0);
    layout1->addWidget(linepassword, 5, 1, 1, 2);
    layout1->addWidget(new QLabel("Sample rate:", this), 6, 0);
    layout1->addWidget(linesamplerate, 6, 1, 1, 2);
    layout1->addWidget(new QLabel("Channels:", this), 7, 0);
    layout1->addWidget(linechannels, 7, 1, 1, 2);

    areasettings->setWidget(widgetsettings);

    bars = new BarsWidget(this);
    waveform = new WaveFormWidget(this);
    level = new LevelWidget(this);

    waveform->setMinimumHeight(10);
    waveform->setMaximumHeight(100);
    bars->setMinimumHeight(100);

    QWidget *analyzer = new QWidget(this);

    QGridLayout *layout_analyzer = new QGridLayout(analyzer);
    layout_analyzer->setMargin(0);
    layout_analyzer->addWidget(waveform, 0, 0);
    layout_analyzer->addWidget(bars, 1, 0);

    linerecordpath = new QLineEdit(this);
    buttonsearch = new QPushButton(this);
    buttonrecord = new QPushButton(this);
    buttonrecordstop = new QPushButton(this);
    lcdtime = new QLCDNumber(this);
    boxautostart = new QCheckBox("Auto start recording when server starts", this);

    QWidget *recorder = new QWidget(this);

    QGridLayout *layout_record = new QGridLayout(recorder);
    layout_record->addWidget(new QLabel("Audio file:", this), 0, 0);
    layout_record->addWidget(linerecordpath, 0, 1);
    layout_record->addWidget(buttonsearch, 0, 2);
    layout_record->addWidget(buttonrecord, 0, 3);
    layout_record->addWidget(buttonrecordstop, 0, 4);
    layout_record->addWidget(lcdtime, 1, 0, 1, 5);
    layout_record->addWidget(boxautostart, 2, 0, 1, 5);

    QWidget *log = new QWidget(this);

    texteditlog = new QPlainTextEdit(this);
    texteditlog->setMaximumBlockCount(10000);

    boxlogtowidget = new QCheckBox("Log to widget", this);
    buttonclearlog = new QPushButton("Clear", this);

    debug_edit = texteditlog;

    QHBoxLayout *layout_log_widgets = new QHBoxLayout();
    layout_log_widgets->addWidget(boxlogtowidget);
    layout_log_widgets->addWidget(buttonclearlog);
    layout_log_widgets->addStretch();

    QVBoxLayout *layout_log = new QVBoxLayout(log);
    layout_log->setMargin(0);
    layout_log->addWidget(texteditlog);
    layout_log->addLayout(layout_log_widgets);

#ifdef Q_OS_ANDROID
    texteditlog->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    texteditlog->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    QScroller::grabGesture(texteditlog, QScroller::LeftMouseButtonGesture);
#endif

    tabwidget->addTab(areasettings," Settings");
#ifndef Q_OS_ANDROID
    tabwidget->addTab(m_ffmpeg, "FFMPEG");
#else
    tabwidget->addTab(new QWidget(this), "FFMPEG");
    tabwidget->setTabEnabled(1, false);
#endif
    tabwidget->addTab(analyzer, "Analyzer");
    tabwidget->addTab(listconnections, "Connections");
    tabwidget->addTab(recorder, "Record");
    tabwidget->addTab(texteditsettings, "Info");
    tabwidget->addTab(log, "Log");

    layout->addWidget(tabwidget, 0, 0, 1, 1);
    layout->addWidget(labelvolume, 1, 0, 1, 1);
    layout->addWidget(slidervolume, 2, 0, 1, 1);
    layout->addWidget(level, 0, 1, 3, 1);

    slidervolume->setEnabled(false);

    texteditsettings->setReadOnly(true);

    texteditlog->setReadOnly(true);

    connect(boxlisteninput, &QCheckBox::clicked, this, &MainWindow::boxListenInputClicked);

    connect(slidervolume, &QSlider::valueChanged, this, &MainWindow::volumeChanged);
    connect(lineport, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(linemaxconnections, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(lineid, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(linepassword, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(buttonstart, &QPushButton::clicked, this, &MainWindow::start);

    connect(buttonsearch, &QPushButton::clicked, this, &MainWindow::setRecordPath);
    connect(buttonrecord, &QPushButton::clicked, this, &MainWindow::startPauseRecord);
    connect(buttonrecordstop, &QPushButton::clicked, this, &MainWindow::stopRecord);

    connect(boxlogtowidget, &QCheckBox::clicked, [=](bool checked){
        qInstallMessageHandler(checked ? messageHandler : nullptr);
    });

    connect(buttonclearlog, &QCheckBox::clicked, texteditlog, &QPlainTextEdit::clear);

    resetRecordPage();

#ifndef Q_OS_ANDROID
    connect(m_ffmpeg, &FFMPEGClass::rawAudio, this, &MainWindow::ffmpegdata);

    connect(m_ffmpeg, &FFMPEGClass::mediaPlay, this, &MainWindow::ffmpegplay);

    connect(m_ffmpeg, &FFMPEGClass::mediaPause, this, &MainWindow::ffmpegpause);

    connect(m_ffmpeg, &FFMPEGClass::mediaStop, this, &MainWindow::ffmpegstop);

    connect(m_ffmpeg, &FFMPEGClass::allFinished, this, &MainWindow::ffmpegallfinished);
#endif

    lineport->setText("1024");
    linemaxconnections->setText("10");

    slidervolume->setValue(100);

    widgetsettings->setFixedHeight(widgetsettings->sizeHint().height());

    setCentralWidget(widget);

    getDevInfo();

    connect(buttonloopbackaudioinput, &QRadioButton::clicked, this, &MainWindow::deviceTypeChanged);

    connect(buttonffmpegaudioinput, &QRadioButton::clicked, this, &MainWindow::deviceTypeChanged);

    connect(buttonaudioinput, &QRadioButton::clicked, this, &MainWindow::deviceTypeChanged);

    buttonaudioinput->setChecked(true);

    deviceTypeChanged();
}

void MainWindow::start()
{
    if (m_audio_lib)
    {
        stopRecord();

        buttonrecord->setEnabled(false);

        m_audio_lib->stop();
#ifdef Q_OS_WIN
        if (m_loopback)
            m_loopback->deleteLater();
#endif
        return;
    }

    if (buttonaudioinput->isChecked() && comboboxaudioinput->count() == 0)
        return;

    QByteArray password = linepassword->text().toLatin1();

    bool ok = false;
    int port = lineport->text().toInt(&ok);

    if (!ok || port < 1 || port > 65535)
    {
        msgBoxCritical("Error", "Port must have a value between 1 and 65535,\n"
                                "including these values", this);

        return;
    }

    int max_connections = linemaxconnections->text().toInt(&ok);

    if (!ok || max_connections < 1)
    {
        msgBoxCritical("Error", "Enter a value equal or higher to 1 on maximum connections", this);
        return;
    }

    m_audio_lib = new AudioStreamingLibCore(this);

    AudioStreamingLibInfo info;

    info.setWorkMode(AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer);
    info.setEncryptionEnabled(!password.isEmpty());
    info.setGetAudioEnabled(true);
    info.setListenAudioInputEnabled(boxlisteninput->isChecked());
    info.setNegotiationString("BroadcastTCPDemo");
    info.setID(lineid->text().trimmed());

    if (buttonloopbackaudioinput->isChecked())
    {
#ifdef Q_OS_WIN
        m_loopback = new QWinLoopback(this);
        
        connect(m_loopback, &QObject::destroyed, this, [=]
        {
            if (isVisible())
                m_buffer.clear();
        });
        
        bool started = m_loopback->start();
        
        if (!started)
            return;
        
        info.setInputDeviceType(AudioStreamingLibInfo::AudioDeviceType::CustomAudioDevice);
        info.setCallBackEnabled(true);
        
        QAudioFormat format = m_loopback->format();
        
        connect(m_loopback, &QWinLoopback::readyRead, this, &MainWindow::loopbackdata);
        connect(m_audio_lib, &AudioStreamingLibCore::inputData, this, &MainWindow::process);
        
        info.setInputAudioFormat(format);
#endif
    }
    else if (buttonffmpegaudioinput->isChecked())
    {
        QAudioFormat format;
        
        format.setSampleSize(32);
        format.setSampleRate(48000);
        format.setChannelCount(2);
        format.setSampleType(QAudioFormat::Float);
        format.setByteOrder(QAudioFormat::LittleEndian);

        info.setInputDeviceType(AudioStreamingLibInfo::AudioDeviceType::CustomAudioDevice);
        info.setCallBackEnabled(true);
        
        connect(m_audio_lib, &AudioStreamingLibCore::inputData, this, &MainWindow::process);
        
        info.setInputAudioFormat(format);
    }
    else
    {
        QAudioDeviceInfo dev_info = comboboxaudioinput->currentData().value<QAudioDeviceInfo>();

        info.setInputDeviceInfo(dev_info);

        QAudioFormat format;

        format.setSampleSize(32);
        format.setSampleRate(linesamplerate->text().toInt());
        format.setChannelCount(linechannels->text().toInt());
        format.setSampleType(QAudioFormat::Float);
        format.setByteOrder(QAudioFormat::LittleEndian);

        info.setInputAudioFormat(format);
    }

    groupboxtype->setEnabled(false);

    lineport->setEnabled(false);
    linemaxconnections->setEnabled(false);
    lineid->setEnabled(false);
    linepassword->setEnabled(false);
    linesamplerate->setEnabled(false);
    linechannels->setEnabled(false);

    boxlisteninput->setEnabled(false);
    buttonstart->setText("Stop Server");

    buttonrecord->setEnabled(true);

    connect(m_audio_lib, &AudioStreamingLibCore::adjustSettings, this, &MainWindow::adjustSettings);
    connect(m_audio_lib, &AudioStreamingLibCore::inputLevel, level, &LevelWidget::setlevel);
    connect(m_audio_lib, &AudioStreamingLibCore::warning, this, &MainWindow::warning);
    connect(m_audio_lib, &AudioStreamingLibCore::error, this, &MainWindow::error);
    connect(m_audio_lib, &AudioStreamingLibCore::finished, this, &MainWindow::finished);
    connect(m_audio_lib, &AudioStreamingLibCore::connected, this, &MainWindow::updateConnections);
    connect(m_audio_lib, &AudioStreamingLibCore::disconnected, this, &MainWindow::updateConnections);

    m_audio_lib->start(info);

    if (info.isListenAudioInputEnabled())
        m_audio_lib->setVolume(slidervolume->value());

    m_audio_lib->listen(quint16(port), true, password, max_connections);

    boxautostart->setEnabled(false);

    if (boxautostart->isChecked())
        startPauseRecord(); //Auto start recording when server starts

#ifndef Q_OS_ANDROID
    m_ffmpeg->setReadingEnabled(true);
#endif

    QString title = QString("%0 connection(s) - %1").arg(0).arg(TITLE);

    setWindowTitle(title);
}

void MainWindow::loopbackdata(const QByteArray &data)
{
#ifdef Q_OS_WIN
    if (!m_loopback)
        return;

    m_buffer.append(data);
#else
    Q_UNUSED(data)
#endif
}

void MainWindow::ffmpegdata(const QByteArray &data)
{
    if (!m_audio_lib || !m_audio_lib->isRunning())
       return;

    m_buffer.append(data);
}

void MainWindow::ffmpegplay()
{
    m_ffmpeg_terminating = false;
    m_ffmpeg_playing = true;
}

void MainWindow::ffmpegpause()
{
    m_ffmpeg_playing = false;
}

void MainWindow::ffmpegstop()
{
    m_ffmpeg_playing = false;
    m_buffer.clear();
}

void MainWindow::ffmpegallfinished()
{
    m_ffmpeg_terminating = true;
}

void MainWindow::process(const QByteArray &data)
{
    if (!m_audio_lib || !m_audio_lib->isRunning())
       return;

    bool loopback = buttonloopbackaudioinput->isChecked();

    int size = data.size();

    QByteArray mid = data;

    if (loopback || m_ffmpeg_playing)
    {
        if (m_buffer.size() >= size)
        {
            mid = m_buffer.mid(0, size);
            m_buffer.remove(0, size);
        }
        else if (m_ffmpeg_terminating)
        {
            m_ffmpeg_terminating = false;
            m_ffmpeg_playing = false;
        }
    }

#ifndef Q_OS_ANDROID
    if (m_ffmpeg_playing && SIZETOTIME(m_buffer.size()) < 1000)
        m_ffmpeg->read();
#endif

    m_audio_lib->inputDataBack(mid);
}

void MainWindow::adjustSettings()
{
    QAudioFormat inputFormat = m_audio_lib->inputAudioFormat();
    QAudioFormat format = m_audio_lib->audioFormat();

    QString str;

    str.append("Input format:\n\n");

    str.append(QString("Sample size: %0 bits\n").arg(inputFormat.sampleSize()));
    str.append(QString("Sample rate: %0 hz\n").arg(inputFormat.sampleRate()));
    str.append(QString("Channels: %0\n").arg(inputFormat.channelCount()));
    str.append(QString("Sample type: %0\n").arg((inputFormat.sampleType()  == QAudioFormat::Float) ? "Float" : "Integer"));
    str.append(QString("Byte order: %0\n").arg((inputFormat.byteOrder() == QAudioFormat::LittleEndian) ? "Little endian" : "Big endian"));

    str.append("\n");

    str.append("Resampled format:\n\n");

    str.append(QString("Sample size: %0 bits\n").arg(format.sampleSize()));
    str.append(QString("Sample rate: %0 hz\n").arg(format.sampleRate()));
    str.append(QString("Channels: %0\n").arg(format.channelCount()));
    str.append(QString("Sample type: %0\n").arg((format.sampleType()  == QAudioFormat::Float) ? "Float" : "Integer"));
    str.append(QString("Byte order: %0").arg((format.byteOrder() == QAudioFormat::LittleEndian) ? "Little endian" : "Big endian"));

    str.append("\n\n");

    str.append("Eigen instructions set:\n");

    str.append(AudioStreamingLibCore::EigenInstructionsSet());

    texteditsettings->setPlainText(str);

    if (!m_spectrum_analyzer)
    {
        m_spectrum_analyzer = new SpectrumAnalyzer();
        QThread *thread = new QThread();
        m_spectrum_analyzer->moveToThread(thread);

        connect(m_audio_lib, &AudioStreamingLibCore::veryInputData, m_spectrum_analyzer, &SpectrumAnalyzer::calculateSpectrum);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::spectrumChanged, bars, &BarsWidget::setValues);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::destroyed, bars, &BarsWidget::clear);

        connect(m_audio_lib, &AudioStreamingLibCore::finished, m_spectrum_analyzer, &SpectrumAnalyzer::deleteLater);
        connect(this, &MainWindow::destroyed, m_spectrum_analyzer, &SpectrumAnalyzer::deleteLater);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::destroyed, thread, &QThread::quit);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);

        QMetaObject::invokeMethod(m_spectrum_analyzer, "start", Qt::QueuedConnection, Q_ARG(QAudioFormat, format));

        thread->start();
    }

    {
        waveform->start(format);
        connect(m_audio_lib, &AudioStreamingLibCore::veryInputData, waveform, &WaveFormWidget::calculateWaveForm);
        connect(m_audio_lib, &AudioStreamingLibCore::finished, waveform, &WaveFormWidget::clear);
    }

    {
        connect(m_audio_lib, &AudioStreamingLibCore::veryInputData, level, &LevelWidget::calculateRMSLevel);
    }
}

void MainWindow::setRecordPath()
{
    QString selected_filter;
    QString path = QFileDialog::getSaveFileName(this, "Select the audio path", QString(),
                                                "WAVE (*.wav);;MP3 (*.mp3)", &selected_filter,
                                                QFileDialog::DontConfirmOverwrite);

    if (path.isEmpty())
        return;

    if (selected_filter == "WAVE (*.wav)" && !path.endsWith(".wav", Qt::CaseInsensitive))
        path.append(".wav");
    else if (selected_filter == "MP3 (*.mp3)" && !path.endsWith(".mp3", Qt::CaseInsensitive))
        path.append(".mp3");

    linerecordpath->setText(QDir::toNativeSeparators(path));
}

void MainWindow::startPauseRecord()
{
    if (!m_audio_recorder && !m_audio_recorder_mp3)
    {
        if (QFileInfo(linerecordpath->text()).exists())
        {
            int result = msgBoxQuestion("File already exists",
                                        QString("File %0 already exists. Replace it?")
                                        .arg(QFileInfo(linerecordpath->text()).fileName()), this);

            if (result != QMessageBox::Yes)
                return;
        }
    }

    if (m_paused)
    {
        linerecordpath->setEnabled(false);
        buttonsearch->setEnabled(false);
        buttonrecordstop->setEnabled(true);

        m_format = m_audio_lib->audioFormat();
        //Change to compatible settings
        m_format.setSampleSize(16);
        m_format.setSampleType(QAudioFormat::SignedInt);

        if (!linerecordpath->text().endsWith(".mp3"))
        {
            if (!m_audio_recorder)
            {
                m_audio_recorder = new AudioRecorder(linerecordpath->text(), m_format, m_audio_lib);

                if (!m_audio_recorder->open())
                {
                    stopRecord();

                    msgBoxCritical("Error", "Error openning wave file for record!", this);

                    return;
                }
            }
        }
        else
        {
            if (!m_audio_recorder_mp3)
            {
                m_audio_recorder_mp3 = new MP3Recorder(this);

                if (!m_audio_recorder_mp3->start(linerecordpath->text(), m_format, 128))
                {
                    stopRecord();

                    msgBoxCritical("Error", "Error openning mp3 file for record!", this);

                    return;
                }
            }
        }

        connect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);

        buttonrecord->setText("Pause");

        m_paused = false;
    }
    else
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);

        buttonrecord->setText("Record");

        m_paused = true;
    }
}

void MainWindow::stopRecord()
{
    if (m_audio_recorder)
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);
        m_audio_recorder->deleteLater();
    }
    else if (m_audio_recorder_mp3)
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);
        m_audio_recorder_mp3->deleteLater();
    }

    m_total_size = 0;

    linerecordpath->setEnabled(true);
    buttonsearch->setEnabled(true);
    buttonrecord->setText("Record");
    buttonrecordstop->setEnabled(false);

    m_paused = true;

    lcdtime->display("--:--");

    m_format = QAudioFormat();
}

void MainWindow::resetRecordPage()
{
    buttonsearch->setText("...");
    buttonrecord->setText("Record");
    buttonrecordstop->setText("Stop record");

    buttonrecord->setEnabled(false);
    buttonrecordstop->setEnabled(false);

    lcdtime->display("--:--");
}

void MainWindow::writeToFile(const QByteArray &data)
{
    if (m_paused)
        return;

    if (m_audio_recorder)
        m_audio_recorder->write(AudioStreamingLibCore::convertFloatToInt16(data));
    else if (m_audio_recorder_mp3)
        m_audio_recorder_mp3->encode(AudioStreamingLibCore::convertFloatToInt16(data));
    else
        return;

    m_total_size += data.size() / int(sizeof(float) / sizeof(qint16));

    int recorded = int(AudioStreamingLibCore::sizeToTime(m_total_size, m_format));

    QTime time = QTime(0, 0, 0).addMSecs(recorded);

    lcdtime->display(time.toString(Qt::ISODate));
}

void MainWindow::updateConnections()
{
    QList<QHostAddress> connections = m_audio_lib->connectionsList();

    listconnections->clear();

    for (const QHostAddress &addr : connections)
        listconnections->addItem(addr.toString());

    QString title = QString("%0 connection(s) - %1").arg(connections.size()).arg(TITLE);

    setWindowTitle(title);
}

void MainWindow::boxListenInputClicked(bool checked)
{
    slidervolume->setEnabled(checked);

    if (!checked)
        slidervolume->setValue(100);
}

void MainWindow::volumeChanged(int volume)
{
    if (m_audio_lib)
        m_audio_lib->setVolume(volume);

    QString str = QString("Volume: %0").arg(volume);
    labelvolume->setText(str);
}

void MainWindow::warning(const QString &warning)
{
    if (!warning.isEmpty())
        msgBoxWarning("Warning", warning, this);
}

void MainWindow::error(const QString &error)
{
    if (!error.isEmpty())
        msgBoxCritical("Error", error, this);
}

void MainWindow::finished()
{
    if (!isVisible())
        return;

#ifndef Q_OS_ANDROID
    m_ffmpeg->setReadingEnabled(false);
#endif

    stopRecord();
    resetRecordPage();

    boxautostart->setEnabled(true);

    lineport->setEnabled(true);
    linemaxconnections->setEnabled(true);
    lineid->setEnabled(true);
    linepassword->setEnabled(true);

    groupboxtype->setEnabled(true);

    boxlisteninput->setEnabled(true);
    buttonstart->setText("Start Server");

    texteditsettings->clear();

    listconnections->clear();

    if (m_audio_lib)
        m_audio_lib->deleteLater();

    m_buffer.clear();

    setWindowTitle(TITLE);

#ifdef Q_OS_WIN
        if (m_loopback)
            m_loopback->deleteLater();
#endif
}

void MainWindow::deviceTypeChanged()
{
    comboboxaudioinput->setEnabled(buttonaudioinput->isChecked());

    //Disable some settings if in Loopback or FFMPEG mode
    bool disable = (!buttonaudioinput->isChecked());

    if (disable)
    {
        linesamplerate->setEnabled(false);
        linechannels->setEnabled(false);

        linesamplerate->clear();
        linechannels->clear();
    }
    else
    {
        linesamplerate->setEnabled(true);
        linechannels->setEnabled(true);

        linesamplerate->setText("44100");
        linechannels->setText("2");
    }

#ifndef Q_OS_ANDROID
    bool enabled = buttonffmpegaudioinput->isEnabled();
    tabwidget->setTabEnabled(1, enabled);
    if (!enabled)
        m_ffmpeg->clearPlayList();
#else
    tabwidget->setTabEnabled(1, false);
#endif
}

void MainWindow::currentIndexChanged(int index)
{
    if (!m_audio_lib)
        return;

    QAudioDeviceInfo dev_info = comboboxaudioinput->itemData(index).value<QAudioDeviceInfo>();

    m_audio_lib->changeInputDevice(dev_info);
}

void MainWindow::getDevInfo()
{
    QList<QAudioDeviceInfo> inputdevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);

    for (int i = 0; i < inputdevices.size(); i++)
        comboboxaudioinput->addItem(inputdevices.at(i).deviceName(), qVariantFromValue(inputdevices.at(i)));

    if (comboboxaudioinput->count() == 0)
        comboboxaudioinput->setEnabled(false);

    connect(comboboxaudioinput, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &MainWindow::currentIndexChanged);
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/r8brain.cpp<|end_filename|>
#include "r8brain.h"
#include "common.h"

//Resampler class

//Used by resampler
#define RESAMPLER_BUFFER_LEN 1024*1024
#define RESAMPLER_2_CHANNELS 2

r8brain::r8brain(QObject *parent) : QObject(parent)
{
    m_bits = 0;
    m_channels_input = 0;
    m_channels_output = 0;
    m_in_bufs = nullptr;
    m_resamps = nullptr;
    m_opp = nullptr;
    m_initialized = false;

    START_THREAD
}

r8brain::~r8brain()
{
    if (m_initialized)
    {
        m_initialized = false;

        delete[] m_in_bufs;
        delete[] m_resamps;
        delete[] m_opp;
    }

    STOP_THREAD
}

void r8brain::stopPrivate()
{
    deleteLater();
}

void r8brain::stop()
{
    QTimer::singleShot(0, this, &r8brain::stopPrivate);
}

void r8brain::startPrivate(int in_sample_rate, int out_sample_rate, int in_channels, int out_channels, int sample_size)
{
    if (m_initialized)
        return;

    if (sample_size != 32)
    {
        emit error("Only 32 bits per sample are allowed!");
        return;
    }

    if (in_sample_rate <= 0 || out_sample_rate <= 0)
    {
        emit error("Invalid sample rate!");
        return;
    }

    if (in_channels <= 0 || out_channels <= 0)
    {
        emit error("Invalid channel count!");
        return;
    }

    if (in_channels != 1 && in_channels != 2 &&
            in_channels != 4 && in_channels != 6 && in_channels != 8)
    {
        emit error(QString("Input channel count %0 are not allowed!").arg(in_channels));
        return;
    }

    if (out_channels != 1 && out_channels != 2 &&
            out_channels != 4 && out_channels != 6 && out_channels != 8)
    {
        emit error(QString("Output channel count %0 are not allowed!").arg(out_channels));
        return;
    }

    m_initialized = true;

    m_bits = 32;

    m_channels_input = in_channels;
    m_channels_output = out_channels;

    m_in_bufs = new CFixedBuffer<double>[RESAMPLER_2_CHANNELS];
    m_resamps = new CPtrKeeper<CDSPResampler24*>[RESAMPLER_2_CHANNELS];

    for (int i = 0; i < RESAMPLER_2_CHANNELS; i++)
    {
        m_in_bufs[i].alloc(RESAMPLER_BUFFER_LEN);
        m_resamps[i] = new CDSPResampler24(double(in_sample_rate), double(out_sample_rate), RESAMPLER_BUFFER_LEN);
    }

    m_opp = new double*[RESAMPLER_2_CHANNELS];
}

void r8brain::start(int in_sample_rate, int out_sample_rate, int in_channels, int out_channels, int sample_size)
{
    QMetaObject::invokeMethod(this, "startPrivate", Qt::QueuedConnection,
                              Q_ARG(int, in_sample_rate),
                              Q_ARG(int, out_sample_rate),
                              Q_ARG(int, in_channels),
                              Q_ARG(int, out_channels),
                              Q_ARG(int, sample_size));
}

void r8brain::writePrivate(const QByteArray &input)
{
    if (!m_initialized)
        return;

    QByteArray data;

    switch (m_bits)
    {
    case 32:
    {
        QByteArray downchanneldata;

        //Convert input channels to 2 channels
        {
            if (input.size() % int(sizeof(float)) != 0)
            {
                emit error("Corrupted input!");
                return;
            }

            const float *samples = reinterpret_cast<const float*>(input.data());

            int samplescount = input.size() / int(sizeof(float)) / m_channels_input;

            switch (m_channels_input)
            {
            case 8:
            {
                Eigen::VectorXf FL(samplescount);
                Eigen::VectorXf FR(samplescount);
                Eigen::VectorXf C(samplescount);
                Eigen::VectorXf LFE(samplescount); // (sub-woofer)
                Eigen::VectorXf RL(samplescount);
                Eigen::VectorXf RR(samplescount);
                Eigen::VectorXf SL(samplescount);
                Eigen::VectorXf SR(samplescount);

                for (int i = 0, j = 0; i < samplescount; i++, j += m_channels_input)
                {
                    FL[i] = samples[j + 0];
                    FR[i] = samples[j + 1];
                    C[i] = samples[j + 2];
                    LFE[i] = samples[j + 3]; // (sub-woofer)
                    RL[i] = samples[j + 4];
                    RR[i] = samples[j + 5];
                    SL[i] = samples[j + 6];
                    SR[i] = samples[j + 7];
                }

                Eigen::VectorXf L = (FL / 5 + RL / 5 + SL / 5 + C / 5 + LFE / 5);
                Eigen::VectorXf R = (FR / 5 + RR / 5 + SR / 5 + C / 5 + LFE / 5);

                for (int i = 0; i < samplescount; i++)
                {
                    downchanneldata.append(reinterpret_cast<char*>(&L[i]), sizeof(float));
                    downchanneldata.append(reinterpret_cast<char*>(&R[i]), sizeof(float));
                }

                break;
            }
            case 6:
            {
                Eigen::VectorXf FL(samplescount);
                Eigen::VectorXf FR(samplescount);
                Eigen::VectorXf C(samplescount);
                Eigen::VectorXf LFE(samplescount); // (sub-woofer)
                Eigen::VectorXf RL(samplescount);
                Eigen::VectorXf RR(samplescount);

                for (int i = 0, j = 0; i < samplescount; i++, j += m_channels_input)
                {
                    FL[i] = samples[j + 0];
                    FR[i] = samples[j + 1];
                    C[i] = samples[j + 2];
                    LFE[i] = samples[j + 3]; // (sub-woofer)
                    RL[i] = samples[j + 4];
                    RR[i] = samples[j + 5];
                }

                Eigen::VectorXf L = (FL / 4 + RL / 4 + C / 4 + LFE / 4);
                Eigen::VectorXf R = (FR / 4 + RR / 4 + C / 4 + LFE / 4);

                for (int i = 0; i < samplescount; i++)
                {
                    downchanneldata.append(reinterpret_cast<char*>(&L[i]), sizeof(float));
                    downchanneldata.append(reinterpret_cast<char*>(&R[i]), sizeof(float));
                }

                break;
            }
            case 4:
            {
                Eigen::VectorXf FL(samplescount);
                Eigen::VectorXf FR(samplescount);
                Eigen::VectorXf RL(samplescount);
                Eigen::VectorXf RR(samplescount);

                for (int i = 0, j = 0; i < samplescount; i++, j += m_channels_input)
                {
                    FL[i] = samples[j + 0];
                    FR[i] = samples[j + 1];
                    RL[i] = samples[j + 2];
                    RR[i] = samples[j + 3];
                }

                Eigen::VectorXf L = (FL / 2 + RL / 2);
                Eigen::VectorXf R = (FR / 2 + RR / 2);

                for (int i = 0; i < samplescount; i++)
                {
                    downchanneldata.append(reinterpret_cast<char*>(&L[i]), sizeof(float));
                    downchanneldata.append(reinterpret_cast<char*>(&R[i]), sizeof(float));
                }

                break;
            }
            case 2:
            {
                Eigen::VectorXf L(samplescount);
                Eigen::VectorXf R(samplescount);

                for (int i = 0, j = 0; i < samplescount; i++, j += m_channels_input)
                {
                    L[i] = samples[j];
                    R[i] = samples[j + 1];
                }

                for (int i = 0; i < samplescount; i++)
                {
                    downchanneldata.append(reinterpret_cast<char*>(&L[i]), sizeof(float));
                    downchanneldata.append(reinterpret_cast<char*>(&R[i]), sizeof(float));
                }

                break;
            }
            case 1:
            {
                Eigen::VectorXf L(samplescount);
                Eigen::VectorXf R(samplescount);

                for (int i = 0, j = 0; i < samplescount; i++, j += m_channels_input)
                {
                    L[i] = samples[j];
                    R[i] = samples[j];
                }

                for (int i = 0; i < samplescount; i++)
                {
                    downchanneldata.append(reinterpret_cast<char*>(&L[i]), sizeof(float));
                    downchanneldata.append(reinterpret_cast<char*>(&R[i]), sizeof(float));
                }

                break;
            }
            default:
                break;
            }
        }

        //Resample step
        int readCount = 0;

        float *outputsamples = reinterpret_cast<float*>(downchanneldata.data());

        readCount = downchanneldata.size() / int(sizeof(float)) / RESAMPLER_2_CHANNELS;

        for (int j = 0; j < readCount; j++)
            for (int i = 0; i < RESAMPLER_2_CHANNELS; i++)
                m_in_bufs[i][j] = double(outputsamples[j * RESAMPLER_2_CHANNELS + i]);

        int writeCount = 0;

        for (int i = 0; i < RESAMPLER_2_CHANNELS; i++)
            writeCount = m_resamps[i]->process(m_in_bufs[i], readCount, m_opp[i]);

        QByteArray tmpdata;

        for (int j = 0; j < writeCount; j++)
        {
            for (int i = 0; i < RESAMPLER_2_CHANNELS; i++)
            {
                float val = float(m_opp[i][j]);
                tmpdata.append(reinterpret_cast<char*>(&val), sizeof(float));
            }
        }

        QByteArray upchanneldata;

        //Increase output channels
        {
            if (tmpdata.size() % int(sizeof(float)) != 0)
            {
                emit error("Corrupted input!");
                return;
            }

            const float *samples = reinterpret_cast<const float*>(tmpdata.data());

            int samplescount = tmpdata.size() / int(sizeof(float));

            switch (m_channels_output)
            {
            case 8:
            {
                for (int i = 0; i < samplescount; i += RESAMPLER_2_CHANNELS)
                {
                    float L, R, mixLR;

                    L = samples[i];
                    R = samples[i + 1];

                    mixLR = L / 2 + R / 2;

                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //FL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //FR
                    upchanneldata.append(reinterpret_cast<char*>(&mixLR), sizeof(float)); //C
                    upchanneldata.append(reinterpret_cast<char*>(&mixLR), sizeof(float)); //LFE (sub-woofer)
                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //RL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //RR
                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //SL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //SR
                }

                break;
            }
            case 6:
            {
                for (int i = 0; i < samplescount; i += RESAMPLER_2_CHANNELS)
                {
                    float L, R, mixLR;

                    L = samples[i];
                    R = samples[i + 1];

                    mixLR = L / 2 + R / 2;

                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //FL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //FR
                    upchanneldata.append(reinterpret_cast<char*>(&mixLR), sizeof(float)); //C
                    upchanneldata.append(reinterpret_cast<char*>(&mixLR), sizeof(float)); //LFE (sub-woofer)
                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //RL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //RR
                }

                break;
            }
            case 4:
            {
                for (int i = 0; i < samplescount; i += RESAMPLER_2_CHANNELS)
                {
                    float L, R;

                    L = samples[i];
                    R = samples[i + 1];

                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //FL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //FR
                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //RL
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //RR
                }

                break;
            }
            case 2:
            {
                for (int i = 0; i < samplescount; i += RESAMPLER_2_CHANNELS)
                {
                    float L, R;

                    L = samples[i];
                    R = samples[i + 1];

                    upchanneldata.append(reinterpret_cast<char*>(&L), sizeof(float)); //L
                    upchanneldata.append(reinterpret_cast<char*>(&R), sizeof(float)); //R
                }

                break;
            }
            case 1:
            {
                for (int i = 0; i < samplescount; i += RESAMPLER_2_CHANNELS)
                {
                    float mixLR;

                    mixLR = samples[i] / 2 + samples[i + 1] / 2;

                    upchanneldata.append(reinterpret_cast<char*>(&mixLR), sizeof(float)); //LR
                }

                break;
            }
            default:
                break;
            }

            data = upchanneldata;
        }

        break;
    }
    default:
        break;
    }

    //Bound to -1 - +1
    {
        int middle_size = data.size() / int(sizeof(float));

        float *middle_data = reinterpret_cast<float*>(data.data());

        for (int i = 0; i < middle_size; i++)
            middle_data[i] = qBound(float(-1), middle_data[i], float(1));
    }

    emit resampled(data);
}

void r8brain::write(const QByteArray &input)
{
    QMetaObject::invokeMethod(this, "writePrivate", Qt::QueuedConnection,
                              Q_ARG(QByteArray, input));
}


<|start_filename|>AudioStreamingLib/Demos/BroadcastClient/mainwindow.cpp<|end_filename|>
#include "mainwindow.h"

#define TITLE "Broadcast Client Demo"

static QPlainTextEdit *debug_edit = nullptr;
static QMutex debug_mutex;

void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Q_UNUSED(type)
    Q_UNUSED(context)

    QMutexLocker locker(&debug_mutex);

    if (debug_edit)
        QMetaObject::invokeMethod(debug_edit, "appendPlainText", Q_ARG(QString, msg));
}

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
    qRegisterMetaType<QVector<SpectrumStruct>>("QVector<SpectrumStruct>");

    setWindowTitle(TITLE);

    m_discover_instance = new AudioStreamingLibCore(this);

    m_total_size = 0;

    initWidgets();
}

MainWindow::~MainWindow()
{
    QMutexLocker locker(&debug_mutex);

    debug_edit = nullptr;
}

void MainWindow::initWidgets()
{
    comboboxaudiooutput = new QComboBox(this);

    QWidget *widget = new QWidget(this);

    linehost = new QLineEdit(this);
    buttonconnect = new QPushButton(this);
    lineport = new QLineEdit(this);
    linetime = new QLineEdit(this);
    linepassword = new QLineEdit(this);
    tabwidget = new QTabWidget(this);
    texteditsettings = new QPlainTextEdit(this);
    listwidgetpeers = new QListWidget(this);
    labelvolume = new QLabel(this);
    slidervolume = new QSlider(Qt::Horizontal, this);

    bars = new BarsWidget(this);
    waveform = new WaveFormWidget(this);
    level = new LevelWidget(this);

    waveform->setMinimumHeight(10);
    waveform->setMaximumHeight(100);
    bars->setMinimumHeight(100);

    QWidget *analyzer = new QWidget(this);

    QGridLayout *layout_analyzer = new QGridLayout(analyzer);
    layout_analyzer->setMargin(0);
    layout_analyzer->addWidget(waveform, 0, 0);
    layout_analyzer->addWidget(bars, 1, 0);

    QScrollArea *areasettings = new QScrollArea(this);

#ifndef Q_OS_ANDROID
    areasettings->setWidgetResizable(true);
    areasettings->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
#endif

#ifdef Q_OS_ANDROID
    areasettings->setWidgetResizable(true);

    areasettings->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    areasettings->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    QScroller::grabGesture(areasettings, QScroller::LeftMouseButtonGesture);
#endif

    QWidget *widgetsettings = new QWidget(this);

    QGridLayout *layout1 = new QGridLayout(widgetsettings);

    QGridLayout *layout = new QGridLayout(widget);

    layout1->addWidget(new QLabel("Output device:", this), 0, 0);
    layout1->addWidget(comboboxaudiooutput, 0, 1, 1, 2);
    layout1->addWidget(new QLabel("Host:", this), 1, 0);
    layout1->addWidget(linehost, 1, 1, 1, 2);
    layout1->addWidget(new QLabel("Port:", this), 2, 0);
    layout1->addWidget(lineport, 2, 1);
    layout1->addWidget(buttonconnect, 2, 2);
    layout1->addWidget(new QLabel("Buffer time(ms):", this), 3, 0);
    layout1->addWidget(linetime, 3, 1, 1, 2);
    layout1->addWidget(new QLabel("Password:", this), 4, 0);
    layout1->addWidget(linepassword, 4, 1, 1, 2);

    areasettings->setWidget(widgetsettings);

    layout->addWidget(tabwidget, 0, 0, 1, 1);
    layout->addWidget(labelvolume, 1, 0, 1, 1);
    layout->addWidget(slidervolume, 2, 0, 1, 1);
    layout->addWidget(level, 0, 1, 3, 1);

    linerecordpath = new QLineEdit(this);
    buttonsearch = new QPushButton(this);
    buttonrecord = new QPushButton(this);
    buttonrecordstop = new QPushButton(this);
    lcdtime = new QLCDNumber(this);
    boxautostart = new QCheckBox("Auto start recording when connected", this);

    QWidget *recorder = new QWidget(this);

    QGridLayout *layout_record = new QGridLayout(recorder);
    layout_record->addWidget(new QLabel("Audio file:", this), 0, 0);
    layout_record->addWidget(linerecordpath, 0, 1);
    layout_record->addWidget(buttonsearch, 0, 2);
    layout_record->addWidget(buttonrecord, 0, 3);
    layout_record->addWidget(buttonrecordstop, 0, 4);
    layout_record->addWidget(lcdtime, 1, 0, 1, 5);
    layout_record->addWidget(boxautostart, 2, 0, 1, 5);

    QWidget *log = new QWidget(this);

    texteditlog = new QPlainTextEdit(this);
    texteditlog->setMaximumBlockCount(10000);

    boxlogtowidget = new QCheckBox("Log to widget", this);
    buttonclearlog = new QPushButton("Clear", this);

    debug_edit = texteditlog;

    QHBoxLayout *layout_log_widgets = new QHBoxLayout();
    layout_log_widgets->addWidget(boxlogtowidget);
    layout_log_widgets->addWidget(buttonclearlog);
    layout_log_widgets->addStretch();

    QVBoxLayout *layout_log = new QVBoxLayout(log);
    layout_log->setMargin(0);
    layout_log->addWidget(texteditlog);
    layout_log->addLayout(layout_log_widgets);

#ifdef Q_OS_ANDROID
    texteditlog->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    texteditlog->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    QScroller::grabGesture(texteditlog, QScroller::LeftMouseButtonGesture);
#endif

    tabwidget->addTab(areasettings, "Settings");
    tabwidget->addTab(analyzer, "Analyzer");
    tabwidget->addTab(listwidgetpeers, "Search");
    tabwidget->addTab(recorder, "Record");
    tabwidget->addTab(texteditsettings, "Info");
    tabwidget->addTab(log, "Log");

    connect(listwidgetpeers, &QListWidget::itemClicked, this, &MainWindow::selectPeer);

    connect(tabwidget, &QTabWidget::currentChanged, this, &MainWindow::currentChanged);

    linepassword->setEchoMode(QLineEdit::Password);
    buttonconnect->setText("Connect");
    texteditsettings->setReadOnly(true);
    slidervolume->setRange(0, 150);

    buttonconnect->setDefault(true);

    texteditlog->setReadOnly(true);

    connect(slidervolume, &QSlider::valueChanged, this, &MainWindow::volumeChanged);
    connect(linehost, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(lineport, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(linetime, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(linepassword, &QLineEdit::returnPressed, this, &MainWindow::start);
    connect(buttonconnect, &QPushButton::clicked, this, &MainWindow::start);

    connect(buttonsearch, &QPushButton::clicked, this, &MainWindow::setRecordPath);
    connect(buttonrecord, &QPushButton::clicked, this, &MainWindow::startPauseRecord);
    connect(buttonrecordstop, &QPushButton::clicked, this, &MainWindow::stopRecord);

    connect(boxlogtowidget, &QCheckBox::clicked, [=](bool checked){
        qInstallMessageHandler(checked ? messageHandler : nullptr);
    });

    connect(buttonclearlog, &QCheckBox::clicked, texteditlog, &QPlainTextEdit::clear);

    widgetsettings->setFixedHeight(widgetsettings->sizeHint().height());

    slidervolume->setValue(100);

    resetRecordPage();

    setCentralWidget(widget);

    linehost->setText("localhost");
    lineport->setText("1024");
    linetime->setText("0");

    getDevInfo();
}

void MainWindow::currentChanged(int index)
{
    if (index < 0)
        return;

    if (index == 2)
        startDiscover();
    else
        stopDiscover();
}

void MainWindow::startDiscover()
{
    if (!isVisible())
        return;

    listwidgetpeers->clear();

    DiscoverClient *instance = m_discover_instance->discoverInstance();

    connect(instance, &DiscoverClient::peerFound, this, &MainWindow::peerFound);
    connect(this, &MainWindow::stoprequest, instance, &DiscoverClient::deleteLater);

    instance->discover(quint16(lineport->text().toInt()), QByteArray("BroadcastTCPDemo"));
}

void MainWindow::stopDiscover()
{
    if (!isVisible())
        return;

    emit stoprequest();
    listwidgetpeers->clear();
}

void MainWindow::peerFound(const QHostAddress &address, const QString &id)
{
    QString peer;

    QString ip = QHostAddress(address.toIPv4Address()).toString();

    peer.append(QString("IP: %0").arg(ip));

    if (!id.isEmpty())
        peer.append(QString(" - ID: %0").arg(id));

    QListWidgetItem *item = new QListWidgetItem();
    item->setText(peer);
    item->setData(Qt::UserRole, ip);

    listwidgetpeers->addItem(item);
}

void MainWindow::selectPeer(QListWidgetItem *item)
{
    linehost->setText(item->data(Qt::UserRole).toString());
    linehost->setFocus();
    tabwidget->setCurrentIndex(0);
}

void MainWindow::start()
{
    if (m_audio_lib)
    {
        stopRecord();

        m_audio_lib->stop();
        return;
    }

    if (comboboxaudiooutput->count() == 0)
        return;

    buttonconnect->setText("Disconnect");

    linehost->setEnabled(false);
    lineport->setEnabled(false);
    linetime->setEnabled(false);
    linepassword->setEnabled(false);
    tabwidget->setTabEnabled(2, false);

    buttonrecord->setEnabled(true);

    m_audio_lib = new AudioStreamingLibCore(this);

    QByteArray password = linepassword->text().toLatin1();

    AudioStreamingLibInfo info;

    QAudioDeviceInfo outputdevinfo = comboboxaudiooutput->currentData().value<QAudioDeviceInfo>();

    info.setWorkMode(AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient);
    info.setOutputDeviceInfo(outputdevinfo);
    info.setTimeToBuffer(linetime->text().toInt());
    info.setEncryptionEnabled(!password.isEmpty());
    info.setGetAudioEnabled(true);
    info.setNegotiationString("BroadcastTCPDemo");

    connect(m_audio_lib, &AudioStreamingLibCore::adjustSettings, this, &MainWindow::adjustSettings);
    connect(m_audio_lib, &AudioStreamingLibCore::outputLevel, level, &LevelWidget::setlevel);
    connect(m_audio_lib, &AudioStreamingLibCore::warning, this, &MainWindow::warning);
    connect(m_audio_lib, &AudioStreamingLibCore::error, this, &MainWindow::error);
    connect(m_audio_lib, &AudioStreamingLibCore::connected, this, &MainWindow::connected);
    connect(m_audio_lib, &AudioStreamingLibCore::disconnected, this, &MainWindow::disconnected);
    connect(m_audio_lib, &AudioStreamingLibCore::finished, this, &MainWindow::finished);

    m_audio_lib->start(info);

    m_audio_lib->setVolume(slidervolume->value());

    m_audio_lib->connectToHost(linehost->text().trimmed(), quint16(lineport->text().toInt()), password);
}

void MainWindow::adjustSettings()
{
    QAudioFormat inputFormat = m_audio_lib->inputAudioFormat();
    QAudioFormat format = m_audio_lib->audioFormat();
    qint32 bufferTime = m_audio_lib->audioStreamingLibInfo().timeToBuffer();

    QString str;

    str.append("Input format:\n\n");

    str.append(QString("Sample size: %0 bits\n").arg(inputFormat.sampleSize()));
    str.append(QString("Sample rate: %0 hz\n").arg(inputFormat.sampleRate()));
    str.append(QString("Channels: %0\n").arg(inputFormat.channelCount()));
    str.append(QString("Sample type: %0\n").arg((inputFormat.sampleType()  == QAudioFormat::Float) ? "Float" : "Integer"));
    str.append(QString("Byte order: %0\n").arg((inputFormat.byteOrder() == QAudioFormat::LittleEndian) ? "Little endian" : "Big endian"));

    str.append("\n");

    str.append("Resampled format:\n\n");

    str.append(QString("Sample size: %0 bits\n").arg(format.sampleSize()));
    str.append(QString("Sample rate: %0 hz\n").arg(format.sampleRate()));
    str.append(QString("Channels: %0\n").arg(format.channelCount()));
    str.append(QString("Sample type: %0\n").arg((format.sampleType()  == QAudioFormat::Float) ? "Float" : "Integer"));
    str.append(QString("Byte order: %0\n").arg((format.byteOrder() == QAudioFormat::LittleEndian) ? "Little endian" : "Big endian"));

    str.append(QString("\nBuffer time: %0 ms%1").arg(bufferTime).arg(bufferTime == 0 ? ("(Smart buffer)") : QString()));

    str.append("\n\n");

    str.append("Eigen instructions set:\n");

    str.append(AudioStreamingLibCore::EigenInstructionsSet());

    texteditsettings->setPlainText(str);

    if (!m_spectrum_analyzer)
    {
        m_spectrum_analyzer = new SpectrumAnalyzer();
        QThread *thread = new QThread();
        m_spectrum_analyzer->moveToThread(thread);

        connect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, m_spectrum_analyzer, &SpectrumAnalyzer::calculateSpectrum);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::spectrumChanged, bars, &BarsWidget::setValues);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::destroyed, bars, &BarsWidget::clear);

        connect(m_audio_lib, &AudioStreamingLibCore::finished, m_spectrum_analyzer, &SpectrumAnalyzer::deleteLater);
        connect(this, &MainWindow::destroyed, m_spectrum_analyzer, &SpectrumAnalyzer::deleteLater);
        connect(m_spectrum_analyzer, &SpectrumAnalyzer::destroyed, thread, &QThread::quit);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);

        QMetaObject::invokeMethod(m_spectrum_analyzer, "start", Qt::QueuedConnection, Q_ARG(QAudioFormat, format));

        thread->start();
    }

    {
        waveform->start(format);
        connect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, waveform, &WaveFormWidget::calculateWaveForm);
        connect(m_audio_lib, &AudioStreamingLibCore::finished, waveform, &WaveFormWidget::clear);
    }

    {
        connect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, level, &LevelWidget::calculateRMSLevel);
    }

    {
        connect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, level, &LevelWidget::calculateRMSLevel);
    }
}

void MainWindow::setRecordPath()
{
    QString selected_filter;
    QString path = QFileDialog::getSaveFileName(this, "Select the audio path", QString(),
                                                "WAVE (*.wav);;MP3 (*.mp3)", &selected_filter,
                                                QFileDialog::DontConfirmOverwrite);

    if (path.isEmpty())
        return;

    if (selected_filter == "WAVE (*.wav)" && !path.endsWith(".wav", Qt::CaseInsensitive))
        path.append(".wav");
    else if (selected_filter == "MP3 (*.mp3)" && !path.endsWith(".mp3", Qt::CaseInsensitive))
        path.append(".mp3");

    linerecordpath->setText(QDir::toNativeSeparators(path));
}

void MainWindow::startPauseRecord()
{
    if (!m_audio_recorder && !m_audio_recorder_mp3)
    {
        if (QFileInfo(linerecordpath->text()).exists())
        {
            int result = msgBoxQuestion("File already exists",
                                        QString("File %0 already exists. Replace it?")
                                        .arg(QFileInfo(linerecordpath->text()).fileName()), this);

            if (result != QMessageBox::Yes)
                return;
        }
    }

    if (m_paused)
    {
        linerecordpath->setEnabled(false);
        buttonsearch->setEnabled(false);
        buttonrecordstop->setEnabled(true);

        m_format = m_audio_lib->audioFormat();
        //Change to compatible settings
        m_format.setSampleSize(16);
        m_format.setSampleType(QAudioFormat::SignedInt);

        if (!linerecordpath->text().endsWith(".mp3"))
        {
            if (!m_audio_recorder)
            {
                m_audio_recorder = new AudioRecorder(linerecordpath->text(), m_format, m_audio_lib);

                if (!m_audio_recorder->open())
                {
                    stopRecord();

                    msgBoxCritical("Error", "Error openning wave file for record!", this);

                    return;
                }
            }
        }
        else
        {
            if (!m_audio_recorder_mp3)
            {
                m_audio_recorder_mp3 = new MP3Recorder(this);

                if (!m_audio_recorder_mp3->start(linerecordpath->text(), m_format, 128))
                {
                    stopRecord();

                    msgBoxCritical("Error", "Error openning mp3 file for record!", this);

                    return;
                }
            }
        }

        connect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, this, &MainWindow::writeToFile);

        buttonrecord->setText("Pause");

        m_paused = false;
    }
    else
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryOutputData, this, &MainWindow::writeToFile);

        buttonrecord->setText("Record");

        m_paused = true;
    }
}

void MainWindow::stopRecord()
{
    if (m_audio_recorder)
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);
        m_audio_recorder->deleteLater();
    }
    else if (m_audio_recorder_mp3)
    {
        disconnect(m_audio_lib, &AudioStreamingLibCore::veryInputData, this, &MainWindow::writeToFile);
        m_audio_recorder_mp3->deleteLater();
    }

    m_total_size = 0;

    linerecordpath->setEnabled(true);
    buttonsearch->setEnabled(true);
    buttonrecord->setText("Record");
    buttonrecordstop->setEnabled(false);

    m_paused = true;

    lcdtime->display("--:--");

    m_format = QAudioFormat();
}

void MainWindow::resetRecordPage()
{
    buttonsearch->setText("...");
    buttonrecord->setText("Record");
    buttonrecordstop->setText("Stop record");

    buttonrecord->setEnabled(false);
    buttonrecordstop->setEnabled(false);

    lcdtime->display("--:--");
}

void MainWindow::writeToFile(const QByteArray &data)
{
    if (m_paused)
        return;

    if (m_audio_recorder)
        m_audio_recorder->write(AudioStreamingLibCore::convertFloatToInt16(data));
    else if (m_audio_recorder_mp3)
        m_audio_recorder_mp3->encode(AudioStreamingLibCore::convertFloatToInt16(data));
    else
        return;

    m_total_size += data.size() / int(sizeof(float) / sizeof(qint16));

    int recorded = int(AudioStreamingLibCore::sizeToTime(m_total_size, m_format));

    QTime time = QTime(0, 0, 0).addMSecs(recorded);

    lcdtime->display(time.toString(Qt::ISODate));
}

void MainWindow::volumeChanged(int volume)
{
    if (m_audio_lib)
        m_audio_lib->setVolume(volume);

    QString str = QString("Volume: %0").arg(volume);
    labelvolume->setText(str);
}

void MainWindow::warning(const QString &warning)
{
    if (!warning.isEmpty())
        msgBoxWarning("Warning", warning, this);
}

void MainWindow::error(const QString &error)
{
    if (!error.isEmpty())
        msgBoxCritical("Error", error, this);
}

void MainWindow::connected(const QHostAddress &address, const QString &id)
{
    QString peer = !id.isEmpty() ? id : QHostAddress(address.toIPv4Address()).toString();

    QString title = QString("Connected to: %0 - %1").arg(peer).arg(TITLE);

    setWindowTitle(title);

    boxautostart->setEnabled(false);

    if (boxautostart->isChecked())
        startPauseRecord(); //Auto start recording when connected
}

void MainWindow::disconnected(const QHostAddress &address)
{
    Q_UNUSED(address)

    stopRecord();
    resetRecordPage();

    if (m_audio_lib)
        m_audio_lib->stop();

    boxautostart->setEnabled(true);

    setWindowTitle(TITLE);
}

void MainWindow::finished()
{
    if (!isVisible())
        return;

    stopRecord();
    resetRecordPage();

    boxautostart->setEnabled(true);

    buttonconnect->setText("Connect");

    linehost->setEnabled(true);
    lineport->setEnabled(true);
    linetime->setEnabled(true);
    linepassword->setEnabled(true);
    tabwidget->setTabEnabled(2, true);

    texteditsettings->clear();

    if (m_audio_lib)
        m_audio_lib->deleteLater();

    setWindowTitle(TITLE);
}

void MainWindow::currentIndexChanged(int index)
{
    if (!m_audio_lib)
        return;

    QAudioDeviceInfo dev_info = comboboxaudiooutput->itemData(index).value<QAudioDeviceInfo>();

    m_audio_lib->changeOutputDevice(dev_info);
}

void MainWindow::getDevInfo()
{
    QList<QAudioDeviceInfo> outputdevices = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);

    for (int i = 0; i < outputdevices.size(); i++)
        comboboxaudiooutput->addItem(outputdevices.at(i).deviceName(), qVariantFromValue(outputdevices.at(i)));

    if (comboboxaudiooutput->count() == 0)
        comboboxaudiooutput->setEnabled(false);

    connect(comboboxaudiooutput, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
            this, &MainWindow::currentIndexChanged);
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/common.h<|end_filename|>
#ifndef COMMON_H
#define COMMON_H

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wattributes"
#endif

#include <QtCore>
#include <QtMultimedia>
#include "audiostreaminglibcore.h"
#include "audiocommon.h"
#include "r8brain.h"

#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN
#error "Only little endian machines are supported"
#endif

#define TIMEOUT 30*1000

#define AES_PADDING 32

#define MAX_PENDING_BEATS 50

#define MAX_NETWORK_CHUNK_SIZE 10*1024*1024

#if defined(IS_TO_DEBUG_VERBOSE_1) || defined(IS_TO_DEBUG_VERBOSE_2)
#define IS_TO_DEBUG

extern QMutex record_mutex;
extern qint64 record_count;

#define DEBUG_FUNCTION(message)\
{\
    record_mutex.lock();\
    \
    qDebug()\
    << "Date and time(UTC):" << qPrintable(QDateTime::currentDateTimeUtc().toString("yyyy/MM/dd hh:mm:ss"))\
    << "\nFile:" << __FILE__\
    << "\nLine:" << __LINE__\
    << "\nFunction:" << __FUNCTION__\
    << "\nIndex:" << ++record_count\
    \
    << "\nMessage:"\
    << message\
    << "\n";\
    \
    record_mutex.unlock();\
}

#endif

#if defined(IS_TO_DEBUG_VERBOSE_1)

#define LIB_DEBUG_LEVEL_1(message)\
    DEBUG_FUNCTION(message)
#define LIB_DEBUG_LEVEL_2(message) //Does nothing

#elif defined(IS_TO_DEBUG_VERBOSE_2)

#define LIB_DEBUG_LEVEL_1(message)\
    DEBUG_FUNCTION(message)
#define LIB_DEBUG_LEVEL_2(message)\
    DEBUG_FUNCTION(message)

#else

#define LIB_DEBUG_LEVEL_1(message) //Does nothing
#define LIB_DEBUG_LEVEL_2(message) //Does nothing

#endif //IS_TO_DEBUG

#ifdef OPUS

#define MAX_PACKET_SIZE 3*1276

#endif //OPUS

//\cond HIDDEN_SYMBOLS
typedef struct PeerData {
    QByteArray data;
    QHostAddress host;
    qintptr descriptor;
} PeerData;

namespace Command
{
enum
{
    AudioHeader,
    AudioData,
    HeartBeat,
    ExtraData,
    ExtraDataReceived
};
}

namespace ServerCommand
{
enum
{
    PeerTryConnect,
    ConnectionRequested,
    ConnectionAnswer,
    LoggedIn,
    ConnectedToPeer,
    DisconnectedFromPeer,
    P2PData,
    Alive,
    Warning,
    XML
};
}
//\endcond

static inline QString cleanString(const QString &s)
{
    QString diacriticLetters;
    QStringList noDiacriticLetters;
    QStringList acceptedCharacters;

    diacriticLetters = QString::fromUtf8("ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ");
    noDiacriticLetters << "S"<<"OE"<<"Z"<<"s"<<"oe"<<"z"<<"Y"<<"Y"<<"u"<<"A"<<"A"<<"A"<<"A"<<"A"<<"A"<<"AE"<<"C"
                       <<"E"<<"E"<<"E"<<"E"<<"I"<<"I"<<"I"<<"I"<<"D"<<"N"<<"O"<<"O"<<"O"<<"O"<<"O"<<"O"<<"U"<<"U"
                      <<"U"<<"U"<<"Y"<<"s"<<"a"<<"a"<<"a"<<"a"<<"a"<<"a"<<"ae"<<"c"<<"e"<<"e"<<"e"<<"e"<<"i"<<"i"
                     <<"i"<<"i"<<"o"<<"n"<<"o"<<"o"<<"o"<<"o"<<"o"<<"o"<<"u"<<"u"<<"u"<<"u"<<"y"<<"y";

    acceptedCharacters << "0" << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9"
                       << "a" << "b" << "c" << "d" << "e" << "f" << "g" << "h" << "i" << "j"
                       << "k" << "l" << "m" << "n" << "o" << "p" << "q" << "r" << "s" << "t"
                       << "u" << "v" << "w" << "x" << "y" << "z";

    QString output_tmp;

    for (int i = 0; i < s.length(); i++)
    {
        QChar c = s[i];
        int dIndex = diacriticLetters.indexOf(c);
        if (dIndex < 0)
        {
            output_tmp.append(c);
        }
        else
        {
            QString replacement = noDiacriticLetters[dIndex];
            output_tmp.append(replacement);
        }
    }

    output_tmp = output_tmp.toLower();

    QString output;

    for (int i = 0; i < output_tmp.length(); i++)
    {
        QChar c = output_tmp[i];

        if (acceptedCharacters.contains(c))
            output.append(c);
    }

    return output;
}

template <typename T>
static inline QByteArray getBytes(T input)
{
    QByteArray tmp;
    QDataStream data(&tmp, QIODevice::WriteOnly);
    data << input;
    return tmp;
}

template <typename T>
static inline T getValue(QByteArray bytes)
{
    T tmp;
    QDataStream data(&bytes, QIODevice::ReadOnly);
    data >> tmp;
    return tmp;
}

#endif // COMMON_H


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/threadcommon.h<|end_filename|>
#ifndef THREADCOMMON_H
#define THREADCOMMON_H

#include <QtCore>

extern int m_worker_count;
extern QMutex m_worker_mutex;
extern QSemaphore m_worker_semaphore;

#define START_THREAD \
{\
    QMutexLocker locker(&m_worker_mutex);\
    Q_UNUSED(locker)\
    \
    setParent(nullptr);\
    \
    QThread *new_thread = new QThread();\
    moveToThread(new_thread);\
    \
    connect(parent, &QObject::destroyed, this, [=]{stop();});\
    \
    m_worker_count++;\
    \
    new_thread->start();\
}

#define STOP_THREAD \
{\
    QMutexLocker locker(&m_worker_mutex);\
    Q_UNUSED(locker)\
    \
    thread()->quit();\
    \
    bool empty = (--m_worker_count == 0);\
    \
    if (empty)\
        m_worker_semaphore.release();\
}

#define CLASS_MEMBER_HEADER \
public slots:\
    void stop();\
\
private slots:\
    void stopPrivate();\

#endif // THREADCOMMON_H


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/audiostreaminglibcore.cpp<|end_filename|>
#include "audiostreaminglibcore.h"
#include "audiostreamingworker.h"

#define RUNNING (m_running && m_worker)

int m_worker_count = 0;
QMutex m_worker_mutex;
QSemaphore m_worker_semaphore;

AudioStreamingLibCore::AudioStreamingLibCore(QObject *parent) : QObject(parent)
{
    m_worker = nullptr;

    m_input_muted = false;
    m_volume = 0;

    m_running = false;

    qRegisterMetaType<AudioStreamingLibInfo>("AudioStreamingLibInfo");
    qRegisterMetaType<QHostAddress>("QHostAddress");
}

AudioStreamingLibCore::~AudioStreamingLibCore()
{
    stop();
}

bool AudioStreamingLibCore::start(const AudioStreamingLibInfo &streaming_info)
{
    if (m_running)
        return false;

    m_worker = new AudioStreamingWorker(this);

    connect(m_worker, &AudioStreamingWorker::connected, this, &AudioStreamingLibCore::connected);
    connect(m_worker, &AudioStreamingWorker::connectedToServer, this, &AudioStreamingLibCore::connectedToServer);
    connect(m_worker, &AudioStreamingWorker::disconnected, this, &AudioStreamingLibCore::disconnected);
    connect(m_worker, &AudioStreamingWorker::pending, this, &AudioStreamingLibCore::pending);
    connect(m_worker, &AudioStreamingWorker::webClientLoggedIn, this, &AudioStreamingLibCore::webClientLoggedIn);
    connect(m_worker, &AudioStreamingWorker::webClientWarning, this, &AudioStreamingLibCore::webClientWarning);
    connect(m_worker, &AudioStreamingWorker::inputData, this, &AudioStreamingLibCore::inputData);
    connect(m_worker, &AudioStreamingWorker::veryInputData, this, &AudioStreamingLibCore::veryInputData);
    connect(m_worker, &AudioStreamingWorker::outputData, this, &AudioStreamingLibCore::outputData);
    connect(m_worker, &AudioStreamingWorker::veryOutputData, this, &AudioStreamingLibCore::veryOutputData);
    connect(m_worker, &AudioStreamingWorker::extraData, this, &AudioStreamingLibCore::extraData);
    connect(m_worker, &AudioStreamingWorker::commandXML, this, &AudioStreamingLibCore::commandXML);
    connect(m_worker, &AudioStreamingWorker::inputLevel, this, &AudioStreamingLibCore::inputLevel);
    connect(m_worker, &AudioStreamingWorker::outputLevel, this, &AudioStreamingLibCore::outputLevel);
    connect(m_worker, &AudioStreamingWorker::adjustSettings, this, &AudioStreamingLibCore::adjustSettings);
    connect(m_worker, &AudioStreamingWorker::extraDataWritten, this, &AudioStreamingLibCore::extraDataWritten);
    connect(m_worker, &AudioStreamingWorker::warning, this, &AudioStreamingLibCore::warning);
    connect(m_worker, &AudioStreamingWorker::error, this, &AudioStreamingLibCore::error);
    connect(m_worker, &AudioStreamingWorker::error, this, &AudioStreamingLibCore::stop);

    QMetaObject::invokeMethod(m_worker, "start", Qt::QueuedConnection, Q_ARG(AudioStreamingLibInfo, streaming_info));

    QMetaObject::invokeMethod(m_worker, "setVolume", Qt::QueuedConnection, Q_ARG(int, m_volume));
    QMetaObject::invokeMethod(m_worker, "setInputMuted", Qt::QueuedConnection, Q_ARG(bool, m_input_muted));

    m_running = true;

    return true;
}

void AudioStreamingLibCore::stop()
{
    if (!RUNNING)
        return;

    m_running = false;

    m_audio_format = QAudioFormat();
    m_input_format = QAudioFormat();

    m_worker->deleteLater();

    m_worker_semaphore.acquire();

    m_worker = nullptr;

    emit finished();
}

qint64 AudioStreamingLibCore::timeToSize(qint64 ms_time, int channel_count, int sample_size, int sample_rate)
{
    return ::timeToSize(ms_time, channel_count, sample_size, sample_rate);
}

qint64 AudioStreamingLibCore::timeToSize(qint64 ms_time, const QAudioFormat &format)
{
    return ::timeToSize(ms_time, format);
}

qint64 AudioStreamingLibCore::sizeToTime(qint64 bytes, int channel_count, int sample_size, int sample_rate)
{
    return ::sizeToTime(bytes, channel_count, sample_size, sample_rate);
}

qint64 AudioStreamingLibCore::sizeToTime(qint64 bytes, const QAudioFormat &format)
{
    return ::sizeToTime(bytes, format);
}

QByteArray AudioStreamingLibCore::convertFloatToInt16(const QByteArray &data)
{
    QAudioFormat format;
    format.setSampleSize(16);
    format.setSampleType(QAudioFormat::SignedInt);

    QByteArray new_data;
    new_data.resize(data.size());

    const float *datafloat = reinterpret_cast<const float*>(data.constData());

    float *newfloat = reinterpret_cast<float*>(new_data.data());

    int size = data.size() / int(sizeof(float));

    for (int i = 0; i < size; i++)
    {
        newfloat[i] = qBound(float(-1), datafloat[i], float(1));
    }

    return convertSamplesToInt(new_data, format);
}

QByteArray AudioStreamingLibCore::convertInt16ToFloat(const QByteArray &data)
{
    QAudioFormat format;
    format.setSampleSize(16);
    format.setSampleType(QAudioFormat::SignedInt);

    return convertSamplesToFloat(data, format);
}

QByteArray AudioStreamingLibCore::mixFloatAudio(const QByteArray &data1, const QByteArray &data2)
{
    if (data1.isEmpty() || data2.isEmpty())
        return QByteArray();

    if (data1.size() != data2.size())
        return QByteArray();

    QByteArray mixed;
    mixed.resize(data1.size());

    const float *data1float = reinterpret_cast<const float*>(data1.constData());
    const float *data2float = reinterpret_cast<const float*>(data2.constData());

    float *mixedfloat = reinterpret_cast<float*>(mixed.data());

    int size = data1.size() / int(sizeof(float));

    for (int i = 0; i < size; i++)
    {
        if ((data1float[i] < 0 && data2float[i] < 0) || (data1float[i] > 0 && data2float[i] > 0))
        {
            mixedfloat[i] = (data1float[i] + data2float[i]) - (data1float[i] * data2float[i]);
        }
        else
        {
            mixedfloat[i] = data1float[i] + data2float[i];
        }

        mixedfloat[i] = qBound(float(-1), mixedfloat[i], float(1));
    }

    return mixed;
}

QString AudioStreamingLibCore::EigenInstructionsSet()
{
    return QString(Eigen::SimdInstructionSetsInUse());
}

bool AudioStreamingLibCore::isRunning()
{
    return RUNNING;
}

void AudioStreamingLibCore::changeInputDevice(const QAudioDeviceInfo &dev_info)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "changeInputDevice", Qt::QueuedConnection, Q_ARG(QAudioDeviceInfo, dev_info));
}

void AudioStreamingLibCore::changeOutputDevice(const QAudioDeviceInfo &dev_info)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "changeOutputDevice", Qt::QueuedConnection, Q_ARG(QAudioDeviceInfo, dev_info));
}

DiscoverClient *AudioStreamingLibCore::discoverInstance(int time_to_destroy)
{
    if (m_client_discover)
        m_client_discover->deleteLater();

    m_client_discover = new DiscoverClient(this);

    time_to_destroy = qMax(time_to_destroy, 1000);

    QTimer::singleShot(time_to_destroy, m_client_discover, &DiscoverClient::deleteLater);

    return m_client_discover;
}

void AudioStreamingLibCore::listen(quint16 port, bool auto_accept, const QByteArray &password, int max_connections)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "listen", Qt::QueuedConnection,
                              Q_ARG(quint16, port), Q_ARG(bool, auto_accept), Q_ARG(QByteArray, password), Q_ARG(int, qMax(max_connections, 1)));
}

void AudioStreamingLibCore::connectToHost(const QString &host, quint16 port, const QByteArray &password)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "connectToHost", Qt::QueuedConnection,
                              Q_ARG(QString, host), Q_ARG(quint16, port), Q_ARG(QByteArray, password));
}

void AudioStreamingLibCore::writeCommandXML(const QByteArray &XML)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "writeCommandXML", Qt::QueuedConnection, Q_ARG(QByteArray, XML));
}

void AudioStreamingLibCore::connectToPeer(const QString &ID)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "connectToPeer", Qt::QueuedConnection, Q_ARG(QString, ID));
}

void AudioStreamingLibCore::disconnectFromPeer()
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "disconnectFromPeer", Qt::QueuedConnection);
}

void AudioStreamingLibCore::acceptConnection()
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "acceptConnection", Qt::QueuedConnection);
}

void AudioStreamingLibCore::rejectConnection()
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "rejectConnection", Qt::QueuedConnection);
}

void AudioStreamingLibCore::acceptSslCertificate()
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "acceptSslCertificate", Qt::QueuedConnection);
}

void AudioStreamingLibCore::writeExtraData(const QByteArray &data)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "writeExtraData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
}

void AudioStreamingLibCore::writeExtraDataResult()
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "writeExtraDataResult", Qt::QueuedConnection);
}

void AudioStreamingLibCore::inputDataBack(const QByteArray &data)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "inputDataBack", Qt::QueuedConnection, Q_ARG(QByteArray, data));
}

void AudioStreamingLibCore::outputDataBack(const QByteArray &data)
{
    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "outputDataBack", Qt::QueuedConnection, Q_ARG(QByteArray, data));
}

bool AudioStreamingLibCore::isInputMuted()
{
    if (!RUNNING)
        return false;

    bool m_input_muted;
    QMetaObject::invokeMethod(m_worker, "isInputMuted", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, m_input_muted));
    return m_input_muted;
}

void AudioStreamingLibCore::setInputMuted(bool mute)
{
    m_input_muted = mute;

    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "setInputMuted", Qt::QueuedConnection, Q_ARG(bool, m_input_muted));
}

int AudioStreamingLibCore::volume()
{
    if (!RUNNING)
        return 0;

    int m_volume;
    QMetaObject::invokeMethod(m_worker, "volume", Qt::BlockingQueuedConnection, Q_RETURN_ARG(int, m_volume));
    return m_volume;
}

void AudioStreamingLibCore::setVolume(int volume)
{
    m_volume = qBound(0, volume, 150);

    if (!RUNNING)
        return;

    QMetaObject::invokeMethod(m_worker, "setVolume", Qt::QueuedConnection, Q_ARG(int, m_volume));
}

AudioStreamingLibInfo AudioStreamingLibCore::audioStreamingLibInfo()
{
    if (!RUNNING)
        return AudioStreamingLibInfo();

    AudioStreamingLibInfo streaming_info;
    QMetaObject::invokeMethod(m_worker, "audioStreamingLibInfo", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AudioStreamingLibInfo, streaming_info));
    return streaming_info;
}

QList<QHostAddress> AudioStreamingLibCore::connectionsList()
{
    if (!RUNNING)
        return QList<QHostAddress>();

    QList<QHostAddress> connections;
    QMetaObject::invokeMethod(m_worker, "connectionsList", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QList<QHostAddress>, connections));
    return connections;
}

bool AudioStreamingLibCore::isReadyToWriteExtraData()
{
    if (!RUNNING)
        return false;

    bool isReady = false;
    QMetaObject::invokeMethod(m_worker, "isReadyToWriteExtraData", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, isReady));
    return isReady;
}

QAudioFormat AudioStreamingLibCore::audioFormat()
{
    if (!RUNNING)
        return QAudioFormat();

    if (m_audio_format.isValid())
        return m_audio_format;

    AudioStreamingLibInfo streaming_info;
    QMetaObject::invokeMethod(m_worker, "audioStreamingLibInfo", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AudioStreamingLibInfo, streaming_info));
    m_audio_format = streaming_info.audioFormat();

    return m_audio_format;
}

QAudioFormat AudioStreamingLibCore::inputAudioFormat()
{
    if (!RUNNING)
        return QAudioFormat();

    if (m_input_format.isValid())
        return m_input_format;

    AudioStreamingLibInfo streaming_info;
    QMetaObject::invokeMethod(m_worker, "audioStreamingLibInfo", Qt::BlockingQueuedConnection, Q_RETURN_ARG(AudioStreamingLibInfo, streaming_info));
    m_input_format = streaming_info.inputAudioFormat();

    return m_input_format;
}

QDataStream &operator<<(QDataStream &stream, AudioStreamingLibInfo &info)
{
    stream << info.negotiationString();

    //Serialize helpers
    stream << qint32(info.workMode());
    stream << qint32(info.timeToBuffer());
    stream << qint32(info.inputDeviceType());
    stream << qint32(info.outputDeviceType());
    stream << info.isCallBackEnabled();
    stream << info.isEncryptionEnabled();

    //Serialize audio format
    stream << qint32(info.audioFormat().sampleSize());
    stream << qint32(info.audioFormat().sampleRate());
    stream << qint32(info.audioFormat().channelCount());
    stream << qint32(info.audioFormat().sampleType());
    stream << qint32(info.audioFormat().byteOrder());

    //Serialize input audio format
    stream << qint32(info.inputAudioFormat().sampleSize());
    stream << qint32(info.inputAudioFormat().sampleRate());
    stream << qint32(info.inputAudioFormat().channelCount());
    stream << qint32(info.inputAudioFormat().sampleType());
    stream << qint32(info.inputAudioFormat().byteOrder());

    //Serialize input device info
    stream << info.inputDeviceInfo().deviceName();

    //Serialize output device info
    stream << info.outputDeviceInfo().deviceName();

    return stream;
}

QDataStream &operator>>(QDataStream &stream, AudioStreamingLibInfo &info)
{
    QByteArray negotiationString;
    stream >> negotiationString;

    //Serialize helpers
    qint32 workMode;
    stream >> workMode;

    qint32 timeToBuffer;
    stream >> timeToBuffer;

    qint32 inputDeviceType;
    stream >> inputDeviceType;

    qint32 outputDeviceType;
    stream >> outputDeviceType;

    bool isCallBackEnabled;
    stream >> isCallBackEnabled;

    bool isEncryptionEnabled;
    stream >> isEncryptionEnabled;

    //Serialize audio format
    qint32 sampleSize;
    stream >> sampleSize;
    qint32 sampleRate;
    stream >> sampleRate;
    qint32 channelCount;
    stream >> channelCount;
    qint32 sampleType;
    stream >> sampleType;
    qint32 byteOrder;
    stream >> byteOrder;

    QAudioFormat format;
    format.setSampleSize(sampleSize);
    format.setSampleRate(sampleRate);
    format.setChannelCount(channelCount);
    format.setSampleType(QAudioFormat::SampleType(sampleType));
    format.setByteOrder(QAudioFormat::Endian(byteOrder));

    //Serialize input audio format
    qint32 inputsampleSize;
    stream >> inputsampleSize;
    qint32 inputsampleRate;
    stream >> inputsampleRate;
    qint32 inputchannelCount;
    stream >> inputchannelCount;
    qint32 inputsampleType;
    stream >> inputsampleType;
    qint32 inputbyteOrder;
    stream >> inputbyteOrder;

    QAudioFormat inputformat;
    inputformat.setSampleSize(inputsampleSize);
    inputformat.setSampleRate(inputsampleRate);
    inputformat.setChannelCount(inputchannelCount);
    inputformat.setSampleType(QAudioFormat::SampleType(inputsampleType));
    inputformat.setByteOrder(QAudioFormat::Endian(inputbyteOrder));

    //Serialize input device info
    QString inputdeviceName;
    stream >> inputdeviceName;

    QAudioDeviceInfo inputinfo;

    for (const QAudioDeviceInfo &audio_info : QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
    {
        if (audio_info.deviceName() == inputdeviceName)
        {
            inputinfo = audio_info;
            break;
        }
    }

    //Serialize output device info
    QString outputdeviceName;
    stream >> outputdeviceName;

    QAudioDeviceInfo outputinfo;

    for (const QAudioDeviceInfo &audio_info : QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
    {
        if (audio_info.deviceName() == outputdeviceName)
        {
            outputinfo = audio_info;
            break;
        }
    }

    info.setNegotiationString(negotiationString);
    info.setWorkMode(AudioStreamingLibInfo::StreamingWorkMode(workMode));
    info.setTimeToBuffer(timeToBuffer);
    info.setInputDeviceType(AudioStreamingLibInfo::AudioDeviceType(inputDeviceType));
    info.setOutputDeviceType(AudioStreamingLibInfo::AudioDeviceType(outputDeviceType));
    info.setInputAudioFormat(inputformat);
    info.setAudioFormat(format);
    info.setInputDeviceInfo(inputinfo);
    info.setOutputDeviceInfo(outputinfo);
    info.setCallBackEnabled(isCallBackEnabled);
    info.setEncryptionEnabled(isEncryptionEnabled);

    return stream;
}

QDebug &operator<<(QDebug &debug, AudioStreamingLibInfo &info)
{
    debug << "Negotiation string:" << qPrintable(info.negotiationString().trimmed()) << endl;

    debug << "ID:" << qPrintable(info.ID().trimmed()) << endl;

    debug << "Streaming work mode:";

    switch (info.workMode())
    {
    case AudioStreamingLibInfo::StreamingWorkMode::Undefined:
        debug << "Undefined" << endl;
        break;
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastClient:
        debug << "BroadcastClient" << endl;
        break;
    case AudioStreamingLibInfo::StreamingWorkMode::BroadcastServer:
        debug << "BroadcastServer" << endl;
        break;
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieClient:
        debug << "WalkieTalkieClient" << endl;
        break;
    case AudioStreamingLibInfo::StreamingWorkMode::WalkieTalkieServer:
        debug << "WalkieTalkieServer" << endl;
        break;
    default:
        break;
    }

    debug << "Time to buffer:" << info.timeToBuffer() << "ms" << endl;

    debug << "Input device type:";

    switch (info.inputDeviceType())
    {
    case AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice:
        debug << "LibraryAudioDevice" << endl;
        break;
    case AudioStreamingLibInfo::AudioDeviceType::CustomAudioDevice:
        debug << "CustomAudioDevice" << endl;
        break;
    }

    debug << "Output device type:";

    switch (info.outputDeviceType())
    {
    case AudioStreamingLibInfo::AudioDeviceType::LibraryAudioDevice:
        debug << "LibraryAudioDevice" << endl;
        break;
    case AudioStreamingLibInfo::AudioDeviceType::CustomAudioDevice:
        debug << "CustomAudioDevice" << endl;
        break;
    }

    debug << "Input audio format:" << info.inputAudioFormat() << endl;

    debug << "Resampled audio format:" << info.audioFormat() << endl;

    debug << "Input device name:" << qPrintable(info.inputDeviceInfo().deviceName()) << endl;

    debug << "Output device name:" << qPrintable(info.outputDeviceInfo().deviceName()) << endl;

#ifdef OPUS
    debug << "Opus bitrate:" << info.OpusBitrate() << endl;
#else
    debug << "Opus bitrate:" << "N/A" << endl;
#endif

    debug << "Call back enabled:" << info.isCallBackEnabled() << endl;

    debug << "Get audio enabled:" << info.isGetAudioEnabled() << endl;

    debug << "Encryption enabled:" << info.isEncryptionEnabled() << endl;

    return debug;
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/flowcontrol.h<|end_filename|>
#ifndef FLOWCONTROL_H
#define FLOWCONTROL_H

#include <QtCore>
#include "common.h"

//\cond HIDDEN_SYMBOLS
class FlowControl : public QObject
{
    Q_OBJECT
public:
    explicit FlowControl(QObject *parent = nullptr);

signals:
    void error(QString);
    void readyRead(QByteArray);

public slots:
    void stop();
    void start(int sample_rate, int channels, int bits_per_sample);

private slots:
    void askForBytes();

private:
    QElapsedTimer m_time;
    qint64 m_elapsed_time;

    QPointer<QTimer> m_timer;
    QPointer<r8brain> m_resampler;

    int m_sample_rate;
    int m_channels;
    int m_bits_per_sample;
};
//\endcond

#endif // FLOWCONTROL_H


<|start_filename|>AudioStreamingLib/Demos/BroadcastClient/mainwindow.h<|end_filename|>
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <AudioStreamingLibCore>
#include "common.h"
#include "spectrumanalyzer.h"
#include "barswidget.h"
#include "waveformwidget.h"
#include "levelwidget.h"
#include "audiorecorder.h"
#include "mp3recorder.h"

class MainWindow : public QMainWindow
{
    Q_OBJECT
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:
    void stoprequest();

private slots:
    void initWidgets();
    void currentChanged(int index);
    void startDiscover();
    void stopDiscover();
    void peerFound(const QHostAddress &address, const QString &id);
    void selectPeer(QListWidgetItem *item);
    void start();
    void adjustSettings();
    void setRecordPath();
    void startPauseRecord();
    void stopRecord();
    void resetRecordPage();
    void writeToFile(const QByteArray &data);
    void volumeChanged(int volume);
    void warning(const QString &warning);
    void error(const QString &error);
    void connected(const QHostAddress &address, const QString &id);
    void disconnected(const QHostAddress &address);
    void finished();
    void currentIndexChanged(int index);
    void getDevInfo();

private:
    QPointer<AudioStreamingLibCore> m_audio_lib;
    QPointer<AudioStreamingLibCore> m_discover_instance;

    QPointer<SpectrumAnalyzer> m_spectrum_analyzer;

    QComboBox *comboboxaudiooutput;

    QLineEdit *linehost;
    QLineEdit *lineport;
    QLineEdit *linetime;
    QPushButton *buttonconnect;
    QLineEdit *linepassword;
    QTabWidget *tabwidget;
    QPlainTextEdit *texteditsettings;
    QListWidget *listwidgetpeers;
    QLabel *labelvolume;
    QSlider *slidervolume;
    BarsWidget *bars;
    WaveFormWidget *waveform;
    LevelWidget *level;

    QLineEdit *linerecordpath;
    QPushButton *buttonsearch;
    QPushButton *buttonrecord;
    QPushButton *buttonrecordstop;
    QLCDNumber *lcdtime;
    QCheckBox *boxautostart;

    QPlainTextEdit *texteditlog;
    QCheckBox *boxlogtowidget;
    QPushButton *buttonclearlog;

    QPointer<AudioRecorder> m_audio_recorder;
    QPointer<MP3Recorder> m_audio_recorder_mp3;
    QAudioFormat m_format;
    qint64 m_total_size;
    bool m_paused;
};

#endif // MAINWINDOW_H


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/openssllib.cpp<|end_filename|>
#include "openssllib.h"

OpenSslLib::OpenSslLib(QObject *parent) : QObject(parent)
{
    enc_ctx = nullptr;
    dec_ctx = nullptr;

    loaded = false;

    loadFunctions();
}

OpenSslLib::~OpenSslLib()
{

}

void OpenSslLib::loadFunctions()
{
    if (loaded)
        return;

#ifndef OPENSSL
#if defined Q_OS_WIN64
    const char *crypto = "libcrypto-1_1-x64";
#elif defined Q_OS_WIN32
    const char *crypto = "libcrypto-1_1";
#else
    const char *crypto = "crypto";
#endif
    QLibrary openssl(crypto);
#endif

#ifndef OPENSSL
    if (!openssl.load())
        return;

    if (!(pRAND_bytes = reinterpret_cast<tRAND_bytes>(openssl.resolve("RAND_bytes"))))
        return;

    if (!(pSHA256_Init = reinterpret_cast<tSHA256_Init>(openssl.resolve("SHA256_Init"))))
        return;

    if (!(pSHA256_Update = reinterpret_cast<tSHA256_Update>(openssl.resolve("SHA256_Update"))))
        return;

    if (!(pSHA256_Final = reinterpret_cast<tSHA256_Final>(openssl.resolve("SHA256_Final"))))
        return;

    if (!(pBN_new = reinterpret_cast<tBN_new>(openssl.resolve("BN_new"))))
        return;

    if (!(pBN_free = reinterpret_cast<tBN_free>(openssl.resolve("BN_free"))))
        return;

    if (!(pBN_set_word = reinterpret_cast<tBN_set_word>(openssl.resolve("BN_set_word"))))
        return;

    if (!(pBIO_new = reinterpret_cast<tBIO_new>(openssl.resolve("BIO_new"))))
        return;

    if (!(pBIO_s_mem = reinterpret_cast<tBIO_s_mem>(openssl.resolve("BIO_s_mem"))))
        return;

    if (!(pBIO_free_all = reinterpret_cast<tBIO_free_all>(openssl.resolve("BIO_free_all"))))
        return;

    if (!(pBIO_read = reinterpret_cast<tBIO_read>(openssl.resolve("BIO_read"))))
        return;

    if (!(pEVP_BytesToKey = reinterpret_cast<tEVP_BytesToKey>(openssl.resolve("EVP_BytesToKey"))))
        return;

    if (!(pEVP_aes_256_cbc = reinterpret_cast<tEVP_aes_256_cbc>(openssl.resolve("EVP_aes_256_cbc"))))
        return;

    if (!(pEVP_sha256 = reinterpret_cast<tEVP_sha256>(openssl.resolve("EVP_sha256"))))
        return;

    if (!(pEVP_CIPHER_CTX_new = reinterpret_cast<tEVP_CIPHER_CTX_new>(openssl.resolve("EVP_CIPHER_CTX_new"))))
        return;

    if (!(pEVP_EncryptInit_ex = reinterpret_cast<tEVP_EncryptInit_ex>(openssl.resolve("EVP_EncryptInit_ex"))))
        return;

    if (!(pEVP_EncryptUpdate = reinterpret_cast<tEVP_EncryptUpdate>(openssl.resolve("EVP_EncryptUpdate"))))
        return;

    if (!(pEVP_EncryptFinal_ex = reinterpret_cast<tEVP_EncryptFinal_ex>(openssl.resolve("EVP_EncryptFinal_ex"))))
        return;

    if (!(pEVP_CIPHER_CTX_free = reinterpret_cast<tEVP_CIPHER_CTX_free>(openssl.resolve("EVP_CIPHER_CTX_free"))))
        return;

    if (!(pEVP_DecryptInit_ex = reinterpret_cast<tEVP_DecryptInit_ex>(openssl.resolve("EVP_DecryptInit_ex"))))
        return;

    if (!(pEVP_DecryptUpdate = reinterpret_cast<tEVP_DecryptUpdate>(openssl.resolve("EVP_DecryptUpdate"))))
        return;

    if (!(pEVP_DecryptFinal_ex = reinterpret_cast<tEVP_DecryptFinal_ex>(openssl.resolve("EVP_DecryptFinal_ex"))))
        return;
#else
    pRAND_bytes = reinterpret_cast<tRAND_bytes>(&RAND_bytes);

    pSHA256_Init = reinterpret_cast<tSHA256_Init>(&SHA256_Init);

    pSHA256_Update = reinterpret_cast<tSHA256_Update>(&SHA256_Update);

    pSHA256_Final = reinterpret_cast<tSHA256_Final>(&SHA256_Final);

    pBN_new = reinterpret_cast<tBN_new>(&BN_new);

    pBN_free = reinterpret_cast<tBN_free>(&BN_free);

    pBN_set_word = reinterpret_cast<tBN_set_word>(&BN_set_word);

    pBIO_new = reinterpret_cast<tBIO_new>(&BIO_new);

    pBIO_s_mem = reinterpret_cast<tBIO_s_mem>(&BIO_s_mem);

    pBIO_free_all = reinterpret_cast<tBIO_free_all>(&BIO_free_all);

    pBIO_read = reinterpret_cast<tBIO_read>(&BIO_read);

    pEVP_BytesToKey = reinterpret_cast<tEVP_BytesToKey>(&EVP_BytesToKey);

    pEVP_aes_256_cbc = reinterpret_cast<tEVP_aes_256_cbc>(&EVP_aes_256_cbc);

    pEVP_sha256 = reinterpret_cast<tEVP_sha256>(&EVP_sha256);

    pEVP_CIPHER_CTX_new = reinterpret_cast<tEVP_CIPHER_CTX_new>(&EVP_CIPHER_CTX_new);

    pEVP_EncryptInit_ex = reinterpret_cast<tEVP_EncryptInit_ex>(&EVP_EncryptInit_ex);

    pEVP_EncryptUpdate = reinterpret_cast<tEVP_EncryptUpdate>(&EVP_EncryptUpdate);

    pEVP_EncryptFinal_ex = reinterpret_cast<tEVP_EncryptFinal_ex>(&EVP_EncryptFinal_ex);

    pEVP_CIPHER_CTX_free = reinterpret_cast<tEVP_CIPHER_CTX_free>(&EVP_CIPHER_CTX_free);

    pEVP_DecryptInit_ex = reinterpret_cast<tEVP_DecryptInit_ex>(&EVP_DecryptInit_ex);

    pEVP_DecryptUpdate = reinterpret_cast<tEVP_DecryptUpdate>(&EVP_DecryptUpdate);

    pEVP_DecryptFinal_ex = reinterpret_cast<tEVP_DecryptFinal_ex>(&EVP_DecryptFinal_ex);
#endif
    loaded = true;
}

bool OpenSslLib::isLoaded()
{
    return loaded;
}

QByteArray OpenSslLib::RANDbytes(int size)
{
    OpenSslLib ssl;

    if (!ssl.loaded)
        return QByteArray();

    QByteArray output;
    output.resize(size);

    ssl.pRAND_bytes(reinterpret_cast<uchar*>(output.data()), size);

    return output;
}

QByteArray OpenSslLib::SHA256(const QByteArray &data, const QByteArray &salt)
{
    OpenSslLib ssl;

    SHA256_CTX ctx;

    unsigned char md[SHA256_DIGEST_LENGTH];

    ssl.pSHA256_Init(&ctx);
    ssl.pSHA256_Update(&ctx, reinterpret_cast<const unsigned char*>(data.data()), uint(data.size()));
    ssl.pSHA256_Update(&ctx, reinterpret_cast<const unsigned char*>(salt.data()), uint(salt.size()));
    ssl.pSHA256_Final(md, &ctx);

    QByteArray hash = QByteArray(reinterpret_cast<char*>(md), sizeof(md));

    return hash;
}

void OpenSslLib::startEncrypt(const QByteArray &salt)
{
    if (enc_ctx)
        return;

    unsigned char *key_data;
    int key_data_len;

    key_data = reinterpret_cast<unsigned char*>(m_password.data());
    key_data_len = m_password.size();

    int i, nrounds = 1;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];

    i = pEVP_BytesToKey(pEVP_aes_256_cbc(), pEVP_sha256(), reinterpret_cast<const unsigned char*>(salt.data()), key_data, key_data_len, nrounds, key, iv);

    if (i != 32)
        return;

    enc_ctx = pEVP_CIPHER_CTX_new();
    pEVP_EncryptInit_ex(enc_ctx, pEVP_aes_256_cbc(), nullptr, key, iv);
}

QByteArray OpenSslLib::encryptPrivate(const QByteArray &input)
{
    if (!enc_ctx)
        return QByteArray();

    QByteArray preencrypted;
    preencrypted.append(input);

    int len = preencrypted.size();

    int c_len = len + AES_BLOCK_SIZE, f_len = 0;
    QByteArray cipherdata;
    cipherdata.resize(c_len);

    if (pEVP_EncryptInit_ex(enc_ctx, nullptr, nullptr, nullptr, nullptr) != 1)
        return QByteArray();
    if (pEVP_EncryptUpdate(enc_ctx, reinterpret_cast<unsigned char*>(cipherdata.data()), &c_len, reinterpret_cast<const unsigned char*>(preencrypted.data()), len) != 1)
        return QByteArray();
    if (pEVP_EncryptFinal_ex(enc_ctx, reinterpret_cast<unsigned char*>(cipherdata.data()) + c_len, &f_len) != 1)
        return QByteArray();

    QByteArray output = QByteArray(reinterpret_cast<char*>(cipherdata.data()), c_len + f_len);

    return output;
}

void OpenSslLib::stopEncrypt()
{
    if (!enc_ctx)
        return;

    pEVP_CIPHER_CTX_free(enc_ctx);
    enc_ctx = nullptr;
}

void OpenSslLib::startDecrypt(const QByteArray &salt)
{
    if (dec_ctx)
        return;

    unsigned char *key_data;
    int key_data_len;

    key_data = reinterpret_cast<unsigned char*>(m_password.data());
    key_data_len = m_password.size();

    int i, nrounds = 1;
    unsigned char key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];

    i = pEVP_BytesToKey(pEVP_aes_256_cbc(), pEVP_sha256(), reinterpret_cast<const unsigned char*>(salt.data()), key_data, key_data_len, nrounds, key, iv);

    if (i != 32)
        return;

    dec_ctx = pEVP_CIPHER_CTX_new();
    pEVP_DecryptInit_ex(dec_ctx, pEVP_aes_256_cbc(), nullptr, key, iv);
}

QByteArray OpenSslLib::decryptPrivate(const QByteArray &input)
{
    if (!dec_ctx)
        return QByteArray();

    int len = input.size();

    int p_len = len, f_len = 0;
    QByteArray plaindata;
    plaindata.resize(p_len + AES_BLOCK_SIZE);

    if (pEVP_DecryptInit_ex(dec_ctx, nullptr, nullptr, nullptr, nullptr) != 1)
        return QByteArray();
    if (pEVP_DecryptUpdate(dec_ctx, reinterpret_cast<unsigned char*>(plaindata.data()), &p_len, reinterpret_cast<const unsigned char*>(input.data()), len) != 1)
        return QByteArray();
    if (pEVP_DecryptFinal_ex(dec_ctx, reinterpret_cast<unsigned char*>(plaindata.data()) + p_len, &f_len) != 1)
        return QByteArray();

    QByteArray output = QByteArray(reinterpret_cast<char*>(plaindata.data()), p_len + f_len);

    return output;
}

void OpenSslLib::stopDecrypt()
{
    if (!dec_ctx)
        return;

    pEVP_CIPHER_CTX_free(dec_ctx);
    dec_ctx = nullptr;
}

QByteArray OpenSslLib::encrypt(QByteArray data)
{
    QByteArray salt;
    QByteArray encrypted;
    QByteArray encryptedheader;

    salt = OpenSslLib::RANDbytes(8);

    startEncrypt(salt);
    encrypted = encryptPrivate(data);
    stopEncrypt();

    encryptedheader.append(QByteArray("Salted__", 8));
    encryptedheader.append(salt);
    encryptedheader.append(encrypted);

    return encryptedheader;
}

QByteArray OpenSslLib::decrypt(QByteArray data)
{
    QByteArray decrypted;
    QByteArray salt;

    data.remove(0, 8); //Remove 'Salted__'
    salt = data.mid(0, 8);
    data.remove(0, 8);

    startDecrypt(salt);
    decrypted = decryptPrivate(data);
    stopDecrypt();

    return decrypted;
}

void OpenSslLib::setPassword(const QByteArray &password)
{
    m_password = password;
}


<|start_filename|>AudioStreamingLib/AudioStreamingLibCore/AudioStreamingLibCore/levelmeter.h<|end_filename|>
#ifndef LEVELMETER_H
#define LEVELMETER_H

#include <QtCore>
#include <QtMultimedia>
#include "common.h"
#include "threadcommon.h"

//\cond HIDDEN_SYMBOLS
class LevelMeter : public QObject
{
    Q_OBJECT
public:
    explicit LevelMeter(QObject *parent = nullptr);
    ~LevelMeter();

    CLASS_MEMBER_HEADER

signals:
    void currentlevel(float);

public slots:
    void start(const QAudioFormat &format);
    void write(const QByteArray &data);

private slots:
    void startPrivate(const QAudioFormat &format);
    void writePrivate(const QByteArray &data);
    void currentlevelPrivate();
    void process();

private:
    QAudioFormat m_format;
    float m_level;
    QByteArray m_buffer;
    QPointer<QTimer> m_timer;
};
//\endcond

#endif // LEVELMETER_H
