9

My goal is to transmit *.wav file over LAN network without delay or with minimal one.

Also we read a file on a server machine by parts, 320 bytes both. After this we send packets by UDP and write receiving in jitter-buffer. The size of the jitter-buffer is 10. What delays should I set on timers for clear sound?

Here is the sender:

void MainWindow::on_start_tx_triggered()
{
    timer1 = new QTimer (this);
    udpSocketout = new QUdpSocket(this);
    qDebug()<<"Start";
    for (int i = 0; i < playlist.size(); ++i)
    {
        inputFile.setFileName(playlist.at(i));
        qDebug()<<inputFile.fileName();
        if (!inputFile.open(QIODevice::ReadOnly))
        {
            qDebug()<< "file not found;";
        }
    }
    connect(timer1, SIGNAL(timeout()), this, SLOT(writeDatagrams()));
    timer1->start(5);
}

void MainWindow::writeDatagrams()
{
    if(!inputFile.atEnd()){
        payloadout = inputFile.read(320);
    }
    qDebug()<<payloadout;
    QDataStream out(&datagramout, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_7);
    out << qint64(0);
    out << payloadout;
    out.device()->seek(qint64(0));
    out << qint64(datagramout.size() - sizeof(qint64));
    qint64 writtenBytes = udpSocketout->writeDatagram(datagramout, remoteHOST, remotePORT);
    qDebug() << "Sent " << writtenBytes << " bytes.";
}

Here is the receiver and player:

void MainWindow::on_start_rx_triggered()
{
    udpSocketin = new QUdpSocket(this);
    udpSocketin->bind(localHOST, localPORT);
    connect(udpSocketin, SIGNAL(readyRead()),
            this, SLOT(readDatagrams()));
    QDataStream out(&datagramout, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_7);
    timer2 =  new QTimer (this);
    connect(timer2, SIGNAL(timeout()), this, SLOT(playbuff()));
    timer2->start(50);
    audioout = new QAudioOutput(format, this);
}

void MainWindow::readDatagrams()
{
    datagramin.resize(udpSocketin->pendingDatagramSize());
    qint64 receiveBytes = udpSocketin->readDatagram(datagramin.data(), datagramin.size());
    qDebug() << "Receive " << receiveBytes << " bytes.";
    QDataStream in(&datagramin, QIODevice::ReadOnly);
    in.setVersion(QDataStream::Qt_4_7);
    quint64 size = 0;
    if(in.device()->size() > sizeof(quint64))
    {
        in >> size;
    }
    else
        return;
    if(in.device()->size() < size)
        return;
    in >> payloadin;
    qDebug() << payloadin.size();
    emit jitterbuff();
}

void MainWindow::jitterbuff()
{
    if (buff_pos < SIZE_OF_BUF)
    {
        QDataStream out(&buffered, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_7);
        out << payloadin;
        buff_pos++;
    }
    else
        buff_pos = 0;
}

void MainWindow::playbuff()
{
    qDebug() << "YES!!!";
    buffer = new QBuffer(&buffered);
    buffer->open(QIODevice::ReadOnly);
    audioout->start(buffer);
    QEventLoop loop;
    QTimer::singleShot(50, &loop, SLOT(quit()));
    loop.exec();
    buffer->close();
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
HoBBiT
  • 171
  • 2
  • 7
  • I didn't look over your code because I'm not familiar with Qt, but some general remarks about sending audio over lans: 1. Although packet loss is low, you should still be prepared for occasional packet loss. 2. I don't have tons of experience with lans, but I would guess you could get around 100 ms of latency or less. – Bjorn Roche May 22 '12 at 14:24
  • 100 ms it's so high latency for streaming audio... Yes, i prepare for packet loss, because i use UDP. But i want to get clear sound on output. i use buffering data(for example on last.fm this doing) – HoBBiT May 22 '12 at 23:15
  • Also, how i understand if i use: sampleRate = 8000 kHz sampleSize = 8 bit and read from file by 320bytes, then i have 5ms of sound in data for example size of jitter buffer is 10 cells then i have 5*10 ms delay for jitter buffer + 5 ms on packetization. in result i will have 55ms total delay. isn't it? let me know if it's wrong. – HoBBiT May 22 '12 at 23:34
  • You say "100 ms it's so high latency for streaming audio...", well it depends on your application: for speech/telephony, no. for music, probably. For video, maybe (I would try to keep total latency for audio for video applications to under one video frame which is often about 1/30 of a second = 33 ms). If you need all clients to play the audio in phase-accurate sync, you will need something way more sophisticated than what you are doing. – Bjorn Roche May 23 '12 at 02:47
  • Ignoring the header, an uncompressed, mono, 8-bit 8kHz file contains 40 ms in each 320 byte block: 1 sample = 8 bits = 8 bytes. 8 kHz/320 samples = .04 sec = 40 ms. Note that I calculated that by letting the units cancel (Hz = 1/sec) http://en.wikipedia.org/wiki/Dimensional_analysis – Bjorn Roche May 23 '12 at 02:51
  • Pretty sure about - I've been doing this for a long time - but you should always double check other people's math :) – Bjorn Roche May 23 '12 at 22:22
  • @Bjorn Roche Thanks! Yes. I forgot, that i have 320 bytes and devided this on 8 bit=1byte (5 ms - my math and 40ms - your math . it's differ in 8 times ). I will correct my program. And i will tell you about results. – HoBBiT May 23 '12 at 23:39
  • Its confusing, though, Title being audio streaming, and the goal is to transmit *.wav file over LAN .... transmitting a *.wav "File" in real time is not strictly STREAMING! – Mohammad Kanan Dec 26 '17 at 21:02

1 Answers1

8

This problem was solved. QAdioOutput has two modes; there are "push" and "pull". I give a pointer to a QIODevice, and write data directly to this. Solution:

Reading UDP socket:

void MainWindow::on_start_rx_triggered()
{
    udpSocketin = new QUdpSocket(this);
    udpSocketin->bind(localPORT);
    connect(udpSocketin, SIGNAL(readyRead()), this, SLOT(readDatagrams()));
    QDataStream out(&datagramout, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_7);
    timer2 = new QTimer (this);
    connect(timer2, SIGNAL(timeout()), this, SLOT(playbuff()));
    timer2->setInterval(15*9);
    audioout = new QAudioOutput(format, this);
    input = audioout->start();
}

void MainWindow::readDatagrams()
{
    if (udpSocketin->hasPendingDatagrams()){
    datagramin.resize(udpSocketin->pendingDatagramSize());
    qint64 receiveBytes = udpSocketin->readDatagram(datagramin.data(), datagramin.size());
    if (receiveBytes <= 0)
    {
        msg.warning(this, "File ERROR", "The end!", QMessageBox::Ok);
        emit on_stop_rx_triggered();
    }
    QDataStream in(&datagramin, QIODevice::ReadOnly);
    in.setVersion(QDataStream::Qt_4_7);
    quint64 size = 0;
    if(in.device()->size() > sizeof(quint64))
    {
        in >> size;
    }
    else return;
    in >> rxfilename;
    in >> name;
    in >> payloadin;
    emit jitterbuff();
}

void MainWindow::jitterbuff()
{
    if (buff_pos < SIZE_OF_BUF)
    {
        buffered.append(payloadin);
        buff_pos++;
    }
    else
    {
        timer2->start();
        buffered.clear();
        buff_pos = 0;
    }
}

void MainWindow::playbuff()
{
    if (!buffered.isEmpty())
    {
        buffer = new QBuffer(&buffered);
        buffer->open(QIODevice::ReadOnly);
        input->write(buffered);
        buffer->close();
    }
}

Writing to UDP socket:

void MainWindow::on_start_tx_triggered()
{
    timer1 = new QTimer (this);
    udpSocketout = new QUdpSocket(this);
    inputFile.setFileName(playlist.at(playIDfile));
    if (!inputFile.open(QIODevice::ReadOnly))
    {
        msg.warning(this, "File ERROR", "File not found!", QMessageBox::Ok);
        return;
    }
    fileinfo = new QFileInfo (inputFile);
    txfilename = fileinfo->fileName();
    ui->playedFile->setText("Now played: " + txfilename);
    connect(timer1, SIGNAL(timeout()), this, SLOT(writeDatagrams()));
    timer1->start(15);
}
void  MainWindow::writeDatagrams()
{

    if(!inputFile.atEnd()){
        payloadout = inputFile.read(SIZE_OF_SOUND);
        QDataStream out(&datagramout, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_7);
        out << qint64(0);
        out << txfilename;
        out << name;
        out << payloadout;
        out.device()->seek(qint64(0));
        out << qint64(datagramout.size() - sizeof(qint64));
        qint64 writtenBytes = udpSocketout->writeDatagram(datagramout, remoteHOST, remotePORT);
    }
}

If somebody will have seems problem, I will try to help him/her.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
HoBBiT
  • 171
  • 2
  • 7