mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-05 21:32:55 +00:00
Shunts audio into its own QThread.
For the record, this was the first means I found of attempting that which actually seemed to work. A plain QThread, with something `connect`ed to its `started` signal didn't seem to work (perhaps `connect` is smart at thread confinement?), `moveToThread` didn't work on the audio output after the fact, etc.
This commit is contained in:
parent
5f13ee7c19
commit
405e9e7c68
@ -249,6 +249,7 @@ HEADERS += \
|
||||
../../Storage/Tape/Parsers/*.hpp \
|
||||
\
|
||||
audiobuffer.h \
|
||||
functionthread.h \
|
||||
mainwindow.h \
|
||||
scantargetwidget.h \
|
||||
timer.h
|
||||
|
@ -24,6 +24,7 @@ struct AudioBuffer: public QIODevice {
|
||||
}
|
||||
|
||||
void setDepth(size_t depth) {
|
||||
std::lock_guard lock(mutex);
|
||||
buffer.resize(depth);
|
||||
}
|
||||
|
||||
@ -35,7 +36,7 @@ struct AudioBuffer: public QIODevice {
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
if(readPointer == writePointer) return 0;
|
||||
if(readPointer == writePointer || buffer.empty()) return 0;
|
||||
|
||||
const size_t dataAvailable = std::min(writePointer - readPointer, size_t(maxlen));
|
||||
size_t bytesToCopy = dataAvailable;
|
||||
@ -66,6 +67,7 @@ struct AudioBuffer: public QIODevice {
|
||||
// after the buffer is full will overwrite the newest data.
|
||||
void write(const std::vector<int16_t> &source) {
|
||||
std::lock_guard lock(mutex);
|
||||
if(buffer.empty()) return;
|
||||
const size_t sourceSize = source.size() * sizeof(int16_t);
|
||||
|
||||
size_t bytesToCopy = sourceSize;
|
||||
|
31
OSBindings/Qt/functionthread.h
Normal file
31
OSBindings/Qt/functionthread.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef FUNCTIONTHREAD_H
|
||||
#define FUNCTIONTHREAD_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
/*!
|
||||
* \brief The LambdaThread class
|
||||
*
|
||||
* Provides a QThread which performs a supplied lambda before kicking off its event loop.
|
||||
*
|
||||
* Disclaimer: this might be a crutch that reveals a misunderstanding of the Qt
|
||||
* threading infrastructure. We'll see.
|
||||
*/
|
||||
class FunctionThread: public QThread {
|
||||
public:
|
||||
FunctionThread() : QThread() {}
|
||||
|
||||
void setFunction(const std::function<void(void)> &function) {
|
||||
this->function = function;
|
||||
}
|
||||
|
||||
void run() override {
|
||||
function();
|
||||
exec();
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(void)> function;
|
||||
};
|
||||
|
||||
#endif // FUNCTIONTHREAD_H
|
@ -133,12 +133,6 @@ void MainWindow::launchMachine() {
|
||||
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();
|
||||
@ -159,19 +153,23 @@ void MainWindow::launchMachine() {
|
||||
// are available, and — at least for now — assume a good buffer size.
|
||||
audioIsStereo = (idealFormat.channelCount() > 1) && speaker->get_is_stereo();
|
||||
audioIs8bit = idealFormat.sampleSize() < 16;
|
||||
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<QAudioOutput>(idealFormat, this);
|
||||
|
||||
// Start the output. The additional `audioBuffer` is meant to minimise latency,
|
||||
// believe it or not, given Qt's semantics.
|
||||
speaker->set_output_rate(idealFormat.sampleRate(), samplesPerBuffer, audioIsStereo);
|
||||
speaker->set_delegate(this);
|
||||
audioOutput->setBufferSize(samplesPerBuffer * sizeof(int16_t));
|
||||
audioOutput->start(&audioBuffer);
|
||||
audioBuffer.setDepth(audioOutput->bufferSize());
|
||||
|
||||
audioThread.setFunction([this, idealFormat] {
|
||||
// Create an audio output.
|
||||
audioOutput = std::make_unique<QAudioOutput>(idealFormat);
|
||||
|
||||
// Start the output. The additional `audioBuffer` is meant to minimise latency,
|
||||
// believe it or not, given Qt's semantics.
|
||||
audioOutput->setBufferSize(samplesPerBuffer * sizeof(int16_t));
|
||||
audioOutput->start(&audioBuffer);
|
||||
audioBuffer.setDepth(audioOutput->bufferSize());
|
||||
});
|
||||
audioThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
@ -213,13 +211,6 @@ void MainWindow::launchMachine() {
|
||||
|
||||
void MainWindow::speaker_did_complete_samples(Outputs::Speaker::Speaker *, const std::vector<int16_t> &buffer) {
|
||||
audioBuffer.write(buffer);
|
||||
// const char *data = reinterpret_cast<const char *>(buffer.data());
|
||||
// size_t sizeLeft = buffer.size() * sizeof(int16_t);
|
||||
// while(sizeLeft) {
|
||||
// const auto bytesWritten = audioIODevice->write(data, qint64(sizeLeft));
|
||||
// sizeLeft -= bytesWritten;
|
||||
// data += bytesWritten;
|
||||
// }
|
||||
}
|
||||
|
||||
void MainWindow::dragEnterEvent(QDragEnterEvent* event) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "audiobuffer.h"
|
||||
#include "timer.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "functionthread.h"
|
||||
|
||||
#include "../../Analyser/Static/StaticAnalyser.hpp"
|
||||
#include "../../Machines/Utility/MachineForTarget.hpp"
|
||||
@ -54,6 +55,7 @@ class MainWindow : public QMainWindow, public Outputs::Speaker::Speaker::Delegat
|
||||
bool audioIs8bit = false, audioIsStereo = false;
|
||||
void speaker_did_complete_samples(Outputs::Speaker::Speaker *speaker, const std::vector<int16_t> &buffer) override;
|
||||
AudioBuffer audioBuffer;
|
||||
FunctionThread audioThread;
|
||||
|
||||
bool processEvent(QKeyEvent *);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user