Qt - как записывать и воспроизводить звук одновременно - PullRequest
13 голосов
/ 15 октября 2011

Я разместил этот вопрос на форуме Qt, но не получил ответов.Вот почему я публикую это здесь.

Я хотел знать, есть ли способ записать и воспроизвести звук одновременно в Qt.Я хочу записать звук с микрофона и одновременно воспроизвести его в динамике / наушниках.

Есть ли способ сделать это в Qt?Или мне нужно использовать любую другую библиотеку?

Было бы замечательно, если бы решение было кросс-платформенным (мне нужно охватить windows, linux и mac).Если это невозможно, тогда подойдет решение для Linux.

Кстати, я использую Qt 4.7.

Редактировать

Мои последниереализация дана здесь .Я создал подкласс QIODevice и повторно реализовал его writeData и readData , так что чтение и запись могут выполняться с помощью кольцевого буфера.Я сделал это согласно этому предложению .Этот код также не работает, потому что экземпляр QAudioOutput обращен к Underrun Error, что в соответствии с этой документацией означает -

Аудиоданные не передаютсяк аудиоустройству с достаточно высокой скоростью

Я временно применил хак для решения этой проблемы.В методе outputStateChanged я проверяю, изменилось ли состояние выходных данных на IDLE, и если это так, я снова вызываю метод start(), указывая общий буфер.Я не хочу использовать это как постоянное решение, потому что оно кажется действительно хакерским, и потому что я глотаю ошибку, не изучая ее причины должным образом.

Что я должен сделать, чтобы решить эту проблему?

Я также пытался решить эту проблему, используя Phonon , но не смог, потому что у меня недостаточно знаний об этом модуле.

Ответы [ 5 ]

9 голосов
/ 21 октября 2011

Я не очень разбираюсь в Qt, но я разбираюсь с медиа, поэтому простите, если мой ответ не очень конкретен, а вместо этого решает вашу проблему с более общей точки зрения.

Я посмотрел ваш код и думаю, что в целом ваша идея должна работать. Хотя я вижу некоторые проблемы:

  • метод writeData, похоже, не подготовлен для обработки условия заполнения буфера. Когда круговой буфер заполняется, он просто перезаписывает старые данные и неправильно продолжает увеличивать переменную currentBufferLength. Я думаю, что правильной вещью здесь является обновление readPosition, чтобы пропустить потерянные данные и предотвратить когда-либо currentBufferLength увеличение размера буфера.

  • Вы начинаете как писателя, так и читателя практически одновременно. Вместо этого вы должны запустить программу записи и заполнить кольцевой буфер, а затем запустить программу чтения. Имейте в виду, что вы никогда не сможете записывать и играть с нулевой задержкой. По крайней мере, ваша задержка будет равна размеру отдельной записи в буфер, но на практике вам, вероятно, понадобится, чтобы записывающее устройство было впереди на несколько буферов, чтобы избежать икоты.

  • Вы должны отладить читателя и писателя отдельно. Настройте только модуль записи и убедитесь, что циклический буфер записывается с регулярными интервалами (сначала исправьте условие переполнения, как я предлагал выше). Для отладки вы можете выгрузить буферы в файл, а затем проверить файл в аудиоплеере (например, Audacity), или вы можете использовать отладку printf, чтобы убедиться, что вы постоянно получаете данные. Затем сделайте нечто подобное только с ридером.

  • Заключительная мысль. Код, который вызывает ваши методы readData и writeData, вероятно, выполняется в других потоках, вероятно, в двух разных потоках, один для читателя, а другой для писателя. Если мое предположение верно, то у вас большая проблема с вашей круговой структурой. Вы должны защитить доступ к переменным, которые определяют позиции и размеры для чтения и записи, в противном случае у вас будут условия гонки.

Удачи.

2 голосов
/ 17 февраля 2015

Ниже приведен код, написанный в QT5 для чтения аудиовхода, микрофона и помещения его в кольцевой буфер 64 КБ. Как только в буфере есть данные, он записывает их на аудиовыход, динамик на ПК. Это простой код, который должен стать хорошей отправной точкой для знакомства со звуковым устройством. Обратите внимание, что здесь вход и выход звука находятся в одном объекте, это может вызвать проблемы с буфером. Чтобы преодолеть это создать отдельные объекты для ввода и вывода. Программа находится в двух файлах, первый из которых - профиль qt (.pro), а второй - файл main.cpp.

#AudioEcho.pro file for QT5.2.1

QT       += core
QT       -= gui
QT += multimedia widgets
TARGET = AudioEcho
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp


//main.cpp file
#include <QDebug>
#include <QIODevice>
#include <QAudioInput>
#include <QAudioOutput>
#include <QCoreApplication>

class myAudio :public QIODevice
{
  // Q_OBJECT

public:
     QAudioOutput *audioOut;
     QAudioInput  *audioIn;

     myAudio();
    ~myAudio(){}
    void fillBuffer();
     QAudioFormat formatIn,formatOut;
     QByteArray buff;
     char *pbuff;
     quint64 RXbuff;
     quint64 buffPtr;
protected:
     qint64 readData(char *data, qint64 maxlen);
     qint64 writeData(const char *data, qint64 len);
     qint64 bytesAvailable() const;
};

#define SAMPLE_RATE 22050
#define CHANNELS 1
#define SAMPLE_SIZE 16
#define SAMPLE_TYPE SignedInt

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    myAudio *m= new myAudio();
    return a.exec();
}
myAudio::myAudio()
    {
    formatIn.setSampleRate(SAMPLE_RATE);
    formatIn.setChannelCount(CHANNELS);
    formatIn.setSampleSize(SAMPLE_SIZE);
    formatIn.setCodec("audio/pcm");
    formatIn.setByteOrder(QAudioFormat::LittleEndian);
    formatIn.setSampleType(QAudioFormat::SAMPLE_TYPE);

    formatOut.setSampleRate(SAMPLE_RATE);
    formatOut.setChannelCount(CHANNELS);
    formatOut.setSampleSize(SAMPLE_SIZE);
    formatOut.setCodec("audio/pcm");
    formatOut.setByteOrder(QAudioFormat::LittleEndian);
    formatOut.setSampleType(QAudioFormat::SAMPLE_TYPE);

//print out the output device setup parameters
     QAudioDeviceInfo          deviceOut(QAudioDeviceInfo::availableDevices(QAudio::AudioOutput).at(0));     //select output device 0
     qDebug()<<"Selected Output device ="<<deviceOut.deviceName();

//print out the input device setup parameters
     QAudioDeviceInfo     deviceIn(QAudioDeviceInfo::availableDevices(QAudio::AudioInput).at(0));     //select output device 0
     qDebug()<<"Selected input device ="<<deviceIn.deviceName();

//configure device
     audioOut = new QAudioOutput(deviceOut,formatOut,0);
     audioIn  = new QAudioInput (deviceIn, formatIn,0);

//print out the device specifications
     foreach(const QAudioDeviceInfo &deviceInfo,     QAudioDeviceInfo::availableDevices(QAudio::AudioInput))
          {
          qDebug() << "\nSuported Input devices";
          qDebug() << "\nDevice name: "             << deviceInfo.deviceName();
          qDebug() << "Supported channel count: "   << deviceInfo.supportedChannelCounts();
          qDebug() << "Supported Codec: "           << deviceInfo.supportedCodecs();
          qDebug() << "Supported byte order: "      << deviceInfo.supportedByteOrders();
          qDebug() << "Supported Sample Rate: "     << deviceInfo.supportedSampleRates();
          qDebug() << "Supported Sample Size: "     << deviceInfo.supportedSampleSizes();
          qDebug() << "Supported Sample Type: "     << deviceInfo.supportedSampleTypes();
          qDebug() << "Preferred Device settings:"  << deviceInfo.preferredFormat();
          }
     foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))
         {
         qDebug() << "\nSuported output devices";
         qDebug() << "Device name: "             << deviceInfo.deviceName();
         qDebug() << "Supported channel count: "   << deviceInfo.supportedChannelCounts();
         qDebug() << "Supported Codec: "           << deviceInfo.supportedCodecs();
         qDebug() << "Supported byte order: "      << deviceInfo.supportedByteOrders();
         qDebug() << "Supported Sample Rate: "     << deviceInfo.supportedSampleRates();
         qDebug() << "Supported Sample Size: "     << deviceInfo.supportedSampleSizes();
         qDebug() << "Supported Sample Type: "     << deviceInfo.supportedSampleTypes();
         qDebug() << "Preferred Device settings:"  << deviceInfo.preferredFormat();
         }

      buff.resize(0x10000);   //create a rx buffer

      pbuff=buff.data();       //get the buff address;
      RXbuff=0;                //set RX buffer pointer

      qDebug()<<"File open"<<open(QIODevice::ReadWrite);
      qDebug()<<"is device Sequential="<<isSequential();
      audioIn->start(this); //start reading device

      audioOut->setVolume(0.5);  //volume 0 to 1.0
      audioOut->start(this);    //start writing to device
}

//QIODevice Class (Protected Functions)This function is called by QIODevice.
//send to output(Speaker)
qint64 myAudio::readData(char *data, qint64 len)
{
static quint64 TXbuff=0;
qint64 total = 0;
while (len > total  && RXbuff>TXbuff)//write and synchonise buffers
       {
         //write data to speaker
        memcpy(&data[total],&pbuff[TXbuff%0x10000],2);    //copy 2 Bytes
        TXbuff+=2; //point to next buffer 16 bit location
        total+=2;
       }
return total;  //the reset interval
}


//audio input (from Microphone)
qint64 myAudio::writeData(const char *data, qint64 len)
{
int total=0;
while (len > total)
       {
        memcpy(&pbuff[RXbuff%0x10000],&data[total], 2); //write 2Bytes into circular buffer(64K)
        RXbuff+=2; //next 16bit buffer location
        total+=2;  //next data location
      }
return (total); //return total number of bytes received
}

qint64 myAudio::bytesAvailable() const{return 0;}
2 голосов
/ 24 июля 2012

Запустите устройство ввода и вывода, подобное этому

m_output= m_audioOutput->start();
    m_input = m_audioInput->start();
    connect(m_input, SIGNAL(readyRead()), SLOT(readMore()));

и запишите пример ввода для вывода в readMore ()

m_output->write(outdata, len);

Пожалуйста, обратитесь к этой статье для получения дополнительной информации.
Этот пример приложения, созданный в Qt, будет записывать с микрофона и воспроизводить аудио одновременно http://www.codeproject.com/Articles/421287/Cross-Platform-Microphone-Audio-Processing-Utility

2 голосов
/ 15 октября 2011

Я не понимаю, почему возникнут проблемы с использованием классов, которые вы упомянули в своем комментарии. Ни один не ограничен только использованием файлов.

Возьмите QIODevice, возвращенный методом start() QAudioInput, и передайте его методу start() QAudioOutput:

QIODevice *myDevice = myQAudioInput->start();
myQAudioOutput->start( myDevice ); 
1 голос
/ 15 октября 2011

Вы берете QIOStream, который вы получаете при запуске QAudioInput, и используете его для создания Phonon :: MediaSource. Затем вы создаете путь между этим Phonon :: MediaSource и объектом Phonon :: AudioOutput. Для получения более подробной информации обратитесь к документации для Phonon :: AudioOutput и Phonon :: MediaSource .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...