2011-10-15 30 views
13

Publiqué esta pregunta en el foro de Qt, pero no recibí ninguna respuesta. Es por eso que lo estoy publicando aquí.Qt - cómo grabar y reproducir sonido simultáneamente

Quería saber si hay alguna forma de grabar y reproducir sonido al mismo tiempo en Qt. Quiero grabar sonido desde un micrófono y, al mismo tiempo, quiero reproducirlo en el altavoz/auriculares.

¿Hay alguna manera de hacer esto en Qt? ¿O necesito usar otra biblioteca?

Sería genial si la solución es multiplataforma (necesito cubrir ventanas, linux y mac). Si no es posible, entonces una solución de Linux funcionará.

Estoy usando Qt 4.7 por cierto.

Editar

se da Mi última aplicación here. Creé una subclase del QIODevice y volví a implementar su método writeData y readData para que la lectura y la escritura se puedan realizar con un búfer circular. He hecho esto según this suggestion. Este código también no funciona porque la instancia QAudioOutput enfrenta Underrun Error, que según this documentation significa -

Los datos de audio no está siendo alimentado al dispositivo de audio a una velocidad suficientemente rápida

I han aplicado un truco para resolver este problema temporalmente. En el método outputStateChanged, estoy verificando si el estado de la salida ha cambiado a IDLE y si lo ha hecho, estoy nuevamente llamando al método start(), especificando el buffer común. No quiero usar esto como una solución permanente porque se siente muy hacky y porque estoy tragando un error sin investigar adecuadamente sus motivos.

¿Qué debo hacer para solucionar este problema?

También traté de resolver esto usando Phonon pero fallado porque no tengo suficiente conocimiento de este módulo.

+0

@BrianRoach: No he intentado nada, porque no he podido encontrar la manera de comenzar. Sé que puedo tomar entrada de sonido usando QAudioInput y para reproducir un sonido puedo usar QAudioOutput, pero ambos funcionan en un archivo, es decir, QAudioInput almacena la entrada en un archivo y luego QAudioOutput reproduce los sonidos de ese archivo. Este enfoque seguramente no funcionará en el escenario dúplex completo, ¿verdad? Encontré algunas de las respuestas anteriores, pero todas son bastante antiguas y sugieren usar otras bibliotecas como openAL, portAudio, etc.Quería saber si hay alguna solución disponible que use bibliotecas Qt. –

Respuesta

9

no estoy muy experimentado con Qt, pero estoy con la manipulación de los medios de comunicación, así que perdónenme si mi respuesta no es muy específica, sino que aborda su problema desde un punto de vista más general.

Miré su código, y creo que, en general, su idea debería funcionar.Veo algunos problemas sin embargo: no parece estar preparado para manejar una condición de búfer lleno

  • el método writeData. Cuando se llena el búfer circular, simplemente sobrescribirá los datos antiguos y continuará incrementando incorrectamente la variable currentBufferLength. Creo que lo correcto es actualizar el readPosition para saltear los datos que se perdieron y evitar que currentBufferLength crezca más allá del tamaño del búfer.

  • Estás comenzando tanto al escritor como al lector casi al mismo tiempo. En su lugar, debe iniciar el escritor y preparar el búfer circular y luego iniciar el lector. Tenga en cuenta que nunca podrá grabar y jugar con latencia cero. Por lo menos, su latencia será del tamaño de una escritura de búfer individual, pero en la práctica es probable que necesite que el escritor esté adelantado por algunos búferes para evitar contratiempos.

  • Debe depurar el lector y el escritor por separado. Configure solo el escritor y verifique que el búfer circular se está escribiendo a intervalos regulares (primero corrija la condición de desbordamiento como sugerí anteriormente). Para depurar, puede volcar los almacenamientos intermedios en un archivo y luego verificar el archivo en un reproductor de audio (Audacity, por ejemplo), o puede usar la depuración de printf para asegurarse de obtener constantemente datos. Luego haz algo similar con solo un lector.

  • Pensamiento final. El código que llama a sus métodos readData y writeData probablemente se esté ejecutando en otros hilos, probablemente dos hilos diferentes, uno para el lector y otro para el escritor. Si mi suposición es correcta, entonces tienes un gran problema con tu estructura circular. Debe proteger el acceso a las variables que determinan las posiciones y tamaños de lectura y escritura, de lo contrario tendrá condiciones de carrera.

Buena suerte.

+0

Gracias Miguel. Tu respuesta es bastante informativa. Voy a tener esto en cuenta :-). –

1

Usted toma el QIOStream que obtiene al iniciar el QAudioInput y lo utiliza para crear un Phonon :: MediaSource. A continuación, crea una ruta entre ese Phonon :: MediaSource y un objeto Phonon :: AudioOutput. Para obtener más información, consulte la documentación de Phonon::AudioOutput y Phonon::MediaSource.

+0

No, no lo he intentado, de hecho, no sabía que existiera tal forma (soy un principiante en Qt). Déjame probar este enfoque. –

+0

No sé cómo puedo crear una ruta entre estas dos clases, ya que ninguna de ellas es parte de Phonon. –

+0

@SayemAhmed Buen punto. Edité mi respuesta para responder a tu pregunta. –

2

No veo por qué habría un problema al usar las clases que mencionas en tu comentario. Ninguno de los dos está restringido a solo usar archivos.

Tome la QIODevice devueltos a partir del método de QAudioInputstart() y darle al método de start()QAudioOutput:

QIODevice *myDevice = myQAudioInput->start(); 
myQAudioOutput->start(myDevice); 
+0

He intentado su enfoque. Al principio, pareció funcionar, pero después de un tiempo, el estado de salida se queda inactivo. Probablemente sea por un problema de sincronización u otra cosa, no sé. Estoy publicando mi código en la edición para que pueda echar un vistazo. –

+0

He descubierto lo que está sucediendo. El objeto 'audioOutput' se enfrenta a un error de infrautilización. –

+0

Estoy teniendo problemas nuevamente. Por favor, mira la edición. –

2

Iniciar el dispositivo de entrada y salida como esta

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

y escribe la muestra de entrada al de salida en READMORE()

m_output->write(outdata, len); 

Por favor, busque en este artículo para más.
Esta aplicación de ejemplo se crea en Qt graba desde el micrófono y reproducir audio simultáneamente http://www.codeproject.com/Articles/421287/Cross-Platform-Microphone-Audio-Processing-Utility

2

A continuación se muestra el código escrito en QT5 para leer la entrada de audio, el micrófono, y lo coloca en un búfer circular 64K. Una vez que el buffer tiene datos, lo escribe en la salida de audio, el parlante en la PC. Este es el código básico que debería ser un buen punto de partida para familiarizarse con el dispositivo de sonido. Tenga en cuenta que aquí, la entrada y salida de sonido están en un objeto, esto puede causar problemas de búfer. Para vencer esto crea objetos separados para la entrada y la salida. El programa está en dos archivos, el primero es el perfil qt (.pro) y el segundo es el archivo 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;} 
Cuestiones relacionadas