implemented base audio playback and scrubbing
This commit is contained in:
@@ -16,6 +16,10 @@
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
audio/audiohybriddevice.h
|
||||
audio/audiohybriddevice.cpp
|
||||
audio/audiolayouts.h
|
||||
audio/audiolayouts.cpp
|
||||
audio/audiomanager.h
|
||||
audio/audiomanager.cpp
|
||||
PARENT_SCOPE
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include "audiohybriddevice.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QtMath>
|
||||
|
||||
AudioHybridDevice::AudioHybridDevice(QObject *parent) :
|
||||
QIODevice(parent),
|
||||
device_(nullptr),
|
||||
sample_index_(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AudioHybridDevice::Push(const QByteArray& samples)
|
||||
{
|
||||
pushed_samples_ = samples;
|
||||
sample_index_ = 0;
|
||||
|
||||
if (!pushed_samples_.isEmpty()) {
|
||||
emit HasSamples();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioHybridDevice::Stop()
|
||||
{
|
||||
// Whatever is happening, stop it
|
||||
pushed_samples_.clear();
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
void AudioHybridDevice::ConnectDevice(QIODevice *device)
|
||||
{
|
||||
// Clear any previous device or pushed sample
|
||||
Stop();
|
||||
|
||||
device_ = device;
|
||||
|
||||
if (device_ != nullptr) {
|
||||
emit HasSamples();
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioHybridDevice::IsIdle()
|
||||
{
|
||||
return device_ == nullptr && pushed_samples_.isEmpty();
|
||||
}
|
||||
|
||||
qint64 AudioHybridDevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
// If a device is connected, passthrough to it
|
||||
if (device_ != nullptr) {
|
||||
qint64 read_count = device_->read(data, maxSize);
|
||||
|
||||
// Stop reading this device
|
||||
if (device_->atEnd()) {
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
return read_count;
|
||||
}
|
||||
|
||||
// If there are samples to push, push those
|
||||
if (!pushed_samples_.isEmpty()) {
|
||||
qint64 read_count = qMin(maxSize, pushed_samples_.size() - sample_index_);
|
||||
|
||||
memcpy(data, pushed_samples_.data()+sample_index_, static_cast<size_t>(read_count));
|
||||
|
||||
sample_index_ += read_count;
|
||||
|
||||
if (sample_index_ == pushed_samples_.size()) {
|
||||
pushed_samples_.clear();
|
||||
}
|
||||
|
||||
if (read_count < maxSize) {
|
||||
memset(data + read_count, 0, static_cast<size_t>(maxSize - read_count));
|
||||
}
|
||||
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
memset(data, 0, static_cast<size_t>(maxSize));
|
||||
return maxSize;
|
||||
}
|
||||
|
||||
qint64 AudioHybridDevice::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
Q_UNUSED(maxSize)
|
||||
|
||||
// This device doesn't support writing
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef AUDIOHYBRIDDEVICE_H
|
||||
#define AUDIOHYBRIDDEVICE_H
|
||||
|
||||
#include <QIODevice>
|
||||
|
||||
typedef float sample;
|
||||
|
||||
class AudioHybridDevice : public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AudioHybridDevice(QObject* parent = nullptr);
|
||||
|
||||
void Push(const QByteArray &samples);
|
||||
|
||||
void Stop();
|
||||
|
||||
void ConnectDevice(QIODevice* device);
|
||||
|
||||
bool IsIdle();
|
||||
|
||||
signals:
|
||||
void HasSamples();
|
||||
|
||||
protected:
|
||||
virtual qint64 readData(char *data, qint64 maxSize) override;
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize) override;
|
||||
|
||||
private:
|
||||
QIODevice* device_;
|
||||
|
||||
QByteArray pushed_samples_;
|
||||
qint64 sample_index_;
|
||||
|
||||
};
|
||||
|
||||
#endif // AUDIOHYBRIDDEVICE_H
|
||||
+36
-14
@@ -61,32 +61,29 @@ bool AudioManager::IsRefreshing()
|
||||
return refreshing_devices_;
|
||||
}
|
||||
|
||||
void AudioManager::PushToOutput(const QByteArray &samples)
|
||||
{
|
||||
output_manager_.Push(samples);
|
||||
}
|
||||
|
||||
void AudioManager::StartOutput(QIODevice *device)
|
||||
{
|
||||
if (output_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
output_file_ = device;
|
||||
|
||||
output_->start(output_file_);
|
||||
output_manager_.ConnectDevice(device);
|
||||
}
|
||||
|
||||
void AudioManager::StopOutput()
|
||||
{
|
||||
if (output_ == nullptr || output_file_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
output_->stop();
|
||||
|
||||
output_file_->close();
|
||||
delete output_file_;
|
||||
output_file_ = nullptr;
|
||||
output_manager_.Stop();
|
||||
}
|
||||
|
||||
void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
{
|
||||
StopOutput();
|
||||
|
||||
QAudioFormat format;
|
||||
format.setSampleRate(48000);
|
||||
format.setChannelCount(2);
|
||||
@@ -96,6 +93,8 @@ void AudioManager::SetOutputDevice(const QAudioDeviceInfo &info)
|
||||
format.setSampleType(QAudioFormat::Float);
|
||||
|
||||
output_ = std::unique_ptr<QAudioOutput>(new QAudioOutput(info, format, this));
|
||||
connect(output_.get(), SIGNAL(stateChanged(QAudio::State)), this, SLOT(OutputStateChanged(QAudio::State)));
|
||||
connect(output_.get(), SIGNAL(notify()), this, SLOT(OutputNotified()));
|
||||
output_device_info_ = info;
|
||||
}
|
||||
|
||||
@@ -117,14 +116,17 @@ const QList<QAudioDeviceInfo> &AudioManager::ListOutputDevices()
|
||||
|
||||
AudioManager::AudioManager() :
|
||||
output_(nullptr),
|
||||
output_file_(nullptr),
|
||||
input_(nullptr),
|
||||
input_file_(nullptr),
|
||||
refreshing_devices_(false)
|
||||
{
|
||||
connect(&refresh_thread_, SIGNAL(ListsReady()), this, SLOT(RefreshThreadDone()));
|
||||
|
||||
RefreshDevices();
|
||||
|
||||
connect(&refresh_thread_, SIGNAL(ListsReady()), this, SLOT(RefreshThreadDone()));
|
||||
connect(&output_manager_, SIGNAL(HasSamples()), this, SLOT(OutputManagerHasSamples()));
|
||||
|
||||
output_manager_.open(AudioHybridDevice::ReadOnly);
|
||||
}
|
||||
|
||||
void AudioManager::RefreshThreadDone()
|
||||
@@ -169,6 +171,26 @@ void AudioManager::RefreshThreadDone()
|
||||
emit DeviceListReady();
|
||||
}
|
||||
|
||||
void AudioManager::OutputManagerHasSamples()
|
||||
{
|
||||
if (output_ != nullptr && output_->state() != QAudio::ActiveState) {
|
||||
output_->start(&output_manager_);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioManager::OutputStateChanged(QAudio::State state)
|
||||
{
|
||||
qDebug() << state;
|
||||
}
|
||||
|
||||
void AudioManager::OutputNotified()
|
||||
{
|
||||
qDebug() << "Notified";
|
||||
if (output_manager_.IsIdle()) {
|
||||
static_cast<QAudioOutput*>(sender())->stop();
|
||||
}
|
||||
}
|
||||
|
||||
AudioRefreshDevicesThread::AudioRefreshDevicesThread()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <QAudioOutput>
|
||||
#include <QThread>
|
||||
|
||||
#include "audiohybriddevice.h"
|
||||
|
||||
/**
|
||||
* @brief A thread for refreshing the total list of devices on the system
|
||||
*
|
||||
@@ -62,6 +64,8 @@ public:
|
||||
|
||||
bool IsRefreshing();
|
||||
|
||||
void PushToOutput(const QByteArray& samples);
|
||||
|
||||
/**
|
||||
* @brief Start playing audio from QIODevice
|
||||
*
|
||||
@@ -93,9 +97,10 @@ private:
|
||||
|
||||
static AudioManager* audio_;
|
||||
|
||||
AudioHybridDevice output_manager_;
|
||||
|
||||
std::unique_ptr<QAudioOutput> output_;
|
||||
QAudioDeviceInfo output_device_info_;
|
||||
QIODevice* output_file_;
|
||||
|
||||
std::unique_ptr<QAudioInput> input_;
|
||||
QAudioDeviceInfo input_device_info_;
|
||||
@@ -108,6 +113,12 @@ private:
|
||||
private slots:
|
||||
void RefreshThreadDone();
|
||||
|
||||
void OutputManagerHasSamples();
|
||||
|
||||
void OutputStateChanged(QAudio::State state);
|
||||
|
||||
void OutputNotified();
|
||||
|
||||
};
|
||||
|
||||
#endif // AUDIOMANAGER_H
|
||||
|
||||
@@ -32,8 +32,15 @@
|
||||
ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
viewer_node_(nullptr),
|
||||
playback_speed_(0)
|
||||
playback_speed_(0),
|
||||
// FIXME: Test code
|
||||
test_file_("/home/matt/Desktop/11 - Santa Monica.raw")
|
||||
// End test code
|
||||
{
|
||||
// FIXME: Test code
|
||||
test_file_.open(QFile::ReadOnly);
|
||||
// End test code
|
||||
|
||||
// Set up main layout
|
||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||
layout->setMargin(0);
|
||||
@@ -172,6 +179,8 @@ void ViewerWidget::UpdateTimeInternal(int64_t i)
|
||||
|
||||
if (viewer_node_ != nullptr) {
|
||||
UpdateTextureFromNode(time_set);
|
||||
|
||||
PushScrubbedAudio();
|
||||
}
|
||||
|
||||
emit TimeChanged(i);
|
||||
@@ -199,18 +208,28 @@ void ViewerWidget::PlayInternal(int speed)
|
||||
start_timestamp_ = ruler_->GetTime();
|
||||
playback_speed_ = speed;
|
||||
|
||||
QFile* file = new QFile("/home/matt/Desktop/11 - Santa Monica.raw");
|
||||
if (file->open(QFile::ReadOnly)) {
|
||||
file->seek(static_cast<qint64>(qFloor(GetTime().toDouble() * 48000 * 2)) * static_cast<qint64>(sizeof(float)));
|
||||
|
||||
AudioManager::instance()->StartOutput(file);
|
||||
}
|
||||
// FIXME: Test code
|
||||
test_file_.seek(static_cast<qint64>(qFloor(GetTime().toDouble() * 48000 * 2)) * static_cast<qint64>(sizeof(float)));
|
||||
AudioManager::instance()->StartOutput(&test_file_);
|
||||
// End test code
|
||||
|
||||
playback_timer_.start();
|
||||
|
||||
controls_->ShowPauseButton();
|
||||
}
|
||||
|
||||
void ViewerWidget::PushScrubbedAudio()
|
||||
{
|
||||
// FIXME: Test code
|
||||
if (!IsPlaying()) {
|
||||
int size_of_sample = qFloor(2 * 48000 * time_base_dbl_) * static_cast<int>(sizeof(float));
|
||||
test_file_.seek(static_cast<qint64>(qFloor(GetTime().toDouble() * 48000 * 2)) * static_cast<qint64>(sizeof(float)));
|
||||
QByteArray frame_audio = test_file_.read(size_of_sample);
|
||||
AudioManager::instance()->PushToOutput(frame_audio);
|
||||
}
|
||||
// End test code
|
||||
}
|
||||
|
||||
void ViewerWidget::RulerTimeChange(int64_t i)
|
||||
{
|
||||
Pause();
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#ifndef VIEWER_WIDGET_H
|
||||
#define VIEWER_WIDGET_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QScrollBar>
|
||||
@@ -106,6 +107,8 @@ private:
|
||||
|
||||
void PlayInternal(int speed);
|
||||
|
||||
void PushScrubbedAudio();
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
|
||||
ViewerGLWidget* gl_widget_;
|
||||
@@ -129,6 +132,10 @@ private:
|
||||
|
||||
int playback_speed_;
|
||||
|
||||
// FIXME: Test code
|
||||
QFile test_file_;
|
||||
// End test code
|
||||
|
||||
private slots:
|
||||
void RulerTimeChange(int64_t);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user