diff --git a/OSBindings/Qt/ClockSignal.pro b/OSBindings/Qt/ClockSignal.pro index 26c6ef020..6ba3b6561 100644 --- a/OSBindings/Qt/ClockSignal.pro +++ b/OSBindings/Qt/ClockSignal.pro @@ -1,4 +1,4 @@ -QT += core gui +QT += core gui multimedia greaterThan(QT_MAJOR_VERSION, 4): QT += widgets diff --git a/OSBindings/Qt/mainwindow.cpp b/OSBindings/Qt/mainwindow.cpp index 85c45dc19..e353e6b93 100644 --- a/OSBindings/Qt/mainwindow.cpp +++ b/OSBindings/Qt/mainwindow.cpp @@ -142,9 +142,17 @@ void MainWindow::launchMachine() { switch(error) { default: { + // TODO: correct assumptions herein that this is the first machine to be + // assigned to this window. ui->missingROMsBox->setVisible(false); uiPhase = UIPhase::RunningMachine; + // Install user-friendly options. TODO: plus user overrides. +// const auto configurable = machine->configurable_device(); +// if(configurable) { +// configurable->set_options(configurable->get_options()); +// } + // Supply the scan target. // TODO: in the future, hypothetically, deal with non-scan producers. const auto scan_producer = machine->scan_producer(); @@ -152,6 +160,33 @@ void MainWindow::launchMachine() { scan_producer->set_scan_target(ui->openGLWidget->getScanTarget()); } + // Install audio output if required. + const auto audio_producer = machine->audio_producer(); + if(audio_producer) { + const auto speaker = audio_producer->get_speaker(); + if(speaker) { + const QAudioDeviceInfo &defaultDeviceInfo = QAudioDeviceInfo::defaultOutputDevice(); + QAudioFormat idealFormat = defaultDeviceInfo.preferredFormat(); + + // Use the ideal format's sample rate, provide stereo as long as at least two channels + // are available, and — at least for now — assume 512 samples/buffer is a good size. + audioIsStereo = (idealFormat.channelCount() > 1) && speaker->get_is_stereo(); + audioIs8bit = idealFormat.sampleSize() < 16; + const int samplesPerBuffer = 512; + speaker->set_output_rate(idealFormat.sampleRate(), samplesPerBuffer, audioIsStereo); + + // Adjust format appropriately, and create an audio output. + idealFormat.setChannelCount(1 + int(audioIsStereo)); + idealFormat.setSampleSize(audioIs8bit ? 8 : 16); + audioOutput = std::make_unique(idealFormat, this); + audioOutput->setBufferSize(samplesPerBuffer * (audioIsStereo ? 2 : 1) * (audioIs8bit ? 1 : 2)); + + // Start the output. + speaker->set_delegate(this); + audioIODevice = audioOutput->start(); + } + } + // If this is a timed machine, start up the timer. const auto timedMachine = machine->timed_machine(); if(timedMachine) { @@ -189,6 +224,12 @@ void MainWindow::launchMachine() { } } +void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector &buffer) { + const auto bytesWritten = audioIODevice->write(reinterpret_cast(buffer.data()), qint64(buffer.size())); +// qDebug() << bytesWritten << "; " << audioOutput->state(); + (void)bytesWritten; +} + void MainWindow::dragEnterEvent(QDragEnterEvent* event) { // Always accept dragged files. if(event->mimeData()->hasUrls()) diff --git a/OSBindings/Qt/mainwindow.h b/OSBindings/Qt/mainwindow.h index c6d30736b..4dfa39231 100644 --- a/OSBindings/Qt/mainwindow.h +++ b/OSBindings/Qt/mainwindow.h @@ -1,7 +1,9 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include #include + #include #include "timer.h" #include "ui_mainwindow.h" @@ -13,7 +15,7 @@ QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE -class MainWindow : public QMainWindow { +class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegate { Q_OBJECT void createActions(); @@ -49,6 +51,11 @@ class MainWindow : public QMainWindow { std::unique_ptr machine; std::mutex machineMutex; + std::unique_ptr audioOutput; + QIODevice *audioIODevice = nullptr; + bool audioIs8bit = false, audioIsStereo = false; + void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector &buffer) override; + bool processEvent(QKeyEvent *); private slots: