2011-10-15 19 views
13

Umieściłem to pytanie na forum Qt, ale nie otrzymałem odpowiedzi. Dlatego zamieszczam go tutaj.Qt - jak nagrywać i odtwarzać dźwięk jednocześnie

Chciałem wiedzieć, czy istnieje sposób nagrywania i odtwarzania dźwięku w tym samym czasie w Qt. Chcę nagrywać dźwięk z mikrofonu, a jednocześnie chcę go odtwarzać w głośniku/słuchawce.

Czy jest jakiś sposób to zrobić w Qt? Czy muszę użyć innej biblioteki?

Byłoby świetnie, gdyby to rozwiązanie było wieloplatformowe (muszę zakryć okna, linux i mac). Jeśli nie jest to możliwe, rozwiąże się rozwiązanie linuksowe.

Używam Qt 4.7 przy okazji.

Edit

Moja najnowsza realizacja podano here. Stworzyłem podklasę QIODevice i ponownie zaimplementowałem jej metodę writeData i readData, tak aby odczyt i zapis można było wykonać za pomocą bufora kołowego. Zrobiłem to jak na this suggestion. Kod ten również nie działa, ponieważ instancja QAudioOutput twarze Underrun Error, który według this documentation oznacza -

dane audio nie jest podawany do urządzenia audio w wystarczająco szybkim tempie

Mam stosowane hack do tymczasowego rozwiązania tego problemu. W metodzie outputStateChanged sprawdzam, czy stan wyjścia zmienił się na IDLE, a jeśli tak, to ponownie wołam o metodę start(), określając wspólny bufor. Nie chcę tego używać jako stałego rozwiązania, ponieważ czuje się naprawdę beznadziejnie i dlatego, że połknę błąd bez odpowiedniego zbadania jego przyczyn.

Co należy zrobić, aby rozwiązać ten problem?

Próbowałem również rozwiązać ten problem, używając Phonon, ale nie udało mi się, ponieważ nie mam wystarczającej wiedzy o tym module.

+0

@BrianRoach: Nic nie próbowałem, ponieważ nie mogłem znaleźć sposobu na początek. Wiem, że mogę wziąć dźwięk wejściowy za pomocą QAudioInput i zagrać dźwięk. Mogę użyć QAudioOutput, ale oba te elementy działają na pliku, tzn. QAudioInput przechowuje dane wejściowe w pliku, a QAudioOutput odtwarza dźwięki z tego pliku. Takie podejście na pewno nie zadziała w pełnym dupleksie, prawda? Znalazłem kilka poprzednich odpowiedzi, ale wszystkie są dość stare i sugerują użycie innych bibliotek, takich jak openAL, portAudio itp.Chciałem wiedzieć, czy dostępne jest jakieś rozwiązanie, które wykorzystuje biblioteki Qt. –

Odpowiedz

9

Nie mam duże doświadczenie z Qt, ale jestem z obsługą multimediów, więc wybacz mi jeśli moja odpowiedź nie jest bardzo konkretna, ale zamiast tego rozwiązuje twój problem z bardziej ogólnego punktu widzenia.

Spojrzałem na twój kod i myślę, że ogólnie twój pomysł powinien zadziałać.Widzę jednak pewne problemy:

  • metoda writeData nie wydaje się być przygotowany do obsługi bufora pełny stan. Gdy bufor cykliczny wypełni go, wystarczy nadpisać stare dane i niepoprawnie kontynuować zwiększanie zmiennej currentBufferLength. Myślę, że poprawną rzeczą jest aktualizacja readPosition, aby pominąć utracone dane i aby zapobiec powiększaniu się rozmiaru bufora o currentBufferLength.

  • Zaczynasz zarówno pisarza, jak i czytelnika w tym samym czasie. Zamiast tego powinieneś uruchomić program piszący i przygotować bufor cykliczny, a następnie uruchomić czytnik. Pamiętaj, że nigdy nie będziesz w stanie nagrywać i grać z zerowym opóźnieniem. Przynajmniej opóźnienie będzie wielkością indywidualnego zapisu bufora, ale w praktyce prawdopodobnie będziesz potrzebował pisarza wyprzedzić o kilka buforów, aby uniknąć czkawki.

  • Należy debugować czytnik i program piszący osobno. Skonfiguruj tylko program piszący i sprawdź, czy bufor cykliczny jest zapisywany w regularnych odstępach czasu (najpierw napraw warunek przepełnienia, jak zasugerowałem powyżej). Aby debugować, możesz zrzucić bufory do pliku, a następnie sprawdzić plik w odtwarzaczu audio (na przykład Audacity) lub skorzystać z debugowania printf, aby zapewnić ciągłe pobieranie danych. Następnie zrób coś podobnego tylko z czytnikiem.

  • Ostateczna myśl. Kod wywołujący metody readData i writeData prawdopodobnie działa na innych wątkach, prawdopodobnie w dwóch różnych wątkach, jeden dla czytnika, a drugi dla pisarza. Jeśli moje przypuszczenie jest poprawne, masz duży problem z kolistą strukturą. Musisz chronić dostęp do zmiennych, które określają pozycje i rozmiary odczytu i zapisu, jeśli nie, będziesz mieć warunki wyścigu.

Powodzenia.

+0

Dziękuję Miguel. Twoja odpowiedź jest dość pouczająca. Będę o tym pamiętać :-). –

1

Możesz pobrać QIOStream, który otrzymasz po uruchomieniu QAudioInput i użyć go do utworzenia Phonona :: MediaSource. Następnie utworzysz ścieżkę między tym Phononem :: MediaSource i obiektem Phonon :: AudioOutput. Aby uzyskać więcej informacji, zapoznaj się z dokumentacją kasy do wersji Phonon::AudioOutput i Phonon::MediaSource.

+0

Nie, nie próbowałem tego, w rzeczywistości nie wiedziałem, że taki sposób istnieje (jestem początkującym w Qt). Pozwól mi spróbować tego podejścia. –

+0

Nie wiem, jak utworzyć ścieżkę między tymi dwoma klasami, ponieważ żadna z nich nie jest częścią Phonon. –

+0

@SayemAhmed Dobra rada. Poprawiłem moją odpowiedź, by odpowiedzieć na twoje pytanie. –

2

Nie widzę powodu, dla którego wystąpiłoby problem z wykorzystaniem klas wymienionych w komentarzu. Żadne z nich nie ogranicza się tylko do używania plików.

Weź QIODevice zwrócony z metody QAudioInputstart() i dać go do sposobu start() z QAudioOutput:

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

Próbowałem Twojego podejścia. Na początku wyglądało na to, że działa, ale po jakimś czasie stan wyjścia przechodzi w stan bezczynności. Prawdopodobnie z powodu problemu z synchronizacją lub czegoś innego, nie wiem. Publikuję mój kod w edycji, abyś mógł rzucić okiem. –

+0

Rozumiem, co się dzieje. Obiekt 'audioOutput' stoi w obliczu błędu niedopełnienia. –

+0

Mam znowu kłopoty. Zobacz edycję. –

2

Uruchom urządzenie wejściowe i wyjściowe jak ten

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

i napisać próbkę wejścia do wyjścia w ReadMore()

m_output->write(outdata, len); 

Proszę spojrzeć na ten artykuł o więcej.
Ta przykładowa aplikacja jest tworzona w Qt będzie nagrywać z mikrofonu i odtwarzania dźwięku jednocześnie http://www.codeproject.com/Articles/421287/Cross-Platform-Microphone-Audio-Processing-Utility

2

Poniżej znajduje się kod napisany w QT5 czytać wejście audio, mikrofon i umieszcza je w buforze kołowym 64K. Gdy bufor ma dane, zapisuje je do wyjścia audio, głośnika na komputerze. Jest to kod barebone, który powinien być dobrym punktem wyjścia do zapoznania się z urządzeniem dźwiękowym. Zauważ, że tutaj wejście i wyjście dźwięku znajdują się w jednym obiekcie, co może powodować problemy z buforem. Aby to osiągnąć, należy utworzyć oddzielne obiekty dla wejścia i wyjścia. Program jest w dwóch plikach, z których pierwszy to profil qt (.pro), a drugi to plik 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;} 
Powiązane problemy