audio checkpoint

This commit is contained in:
2025-12-21 22:49:10 -06:00
parent 9ac5eaaba8
commit 783330990e
8 changed files with 241 additions and 6 deletions

27
src/synth/AudioStream.cpp Normal file
View File

@@ -0,0 +1,27 @@
#include "AudioStream.h"
#include <iostream>
AudioStream::AudioStream(Synth *synth, QObject *parent) : QIODevice(parent), synth_(synth) {
}
void AudioStream::start() {
// std::cout << "AudioStream::start()" << std::endl;
open(QIODevice::ReadOnly);
}
void AudioStream::stop() {
close();
}
qint64 AudioStream::readData(char *data, qint64 maxlen) {
// std::cout << "sample out " << std::endl;
QByteArray samples = synth_->generateSamples(maxlen);
memcpy(data, samples.constData(), samples.size());
return samples.size();
}

23
src/synth/AudioStream.h Normal file
View File

@@ -0,0 +1,23 @@
#pragma once
#include <QIODevice>
#include "Synth.h"
class AudioStream : public QIODevice
{
Q_OBJECT
public:
explicit AudioStream(Synth *synth, QObject *parent = nullptr);
void start();
void stop();
protected:
qint64 readData(char *data, qint64 maxlen) override;
qint64 writeData(const char *, qint64) override { return 0; }
private:
Synth *synth_;
};

112
src/synth/Synth.cpp Normal file
View File

@@ -0,0 +1,112 @@
#include "Synth.h"
#include <QMediaDevices>
#include <QtMath>
#include <iostream>
Synth::Synth(QObject *parent) : QObject(parent) {
format_.setSampleRate(44100);
format_.setChannelCount(1);
format_.setSampleFormat(QAudioFormat::Int16);
QAudioDevice device = QMediaDevices::defaultAudioOutput();
if (!device.isFormatSupported(format_)) {
format_ = device.preferredFormat();
}
audioSink_ = new QAudioSink(device, format_, this);
audioSink_->setBufferSize(4096);
}
Synth::~Synth() {
stop();
}
void Synth::start() {
/*
// std::cout << "Synth::start()" << std::endl;
if (audioSink_->state() == QAudio::ActiveState)
return;
audioDevice_ = audioSink_->start();
*/
}
void Synth::stop() {
if (audioSink_) {
audioSink_->stop();
audioDevice_ = nullptr;
}
}
void Synth::setFrequency(float frequency) {
frequency_ = qMax(1.0f, frequency);
}
QByteArray Synth::generateSamples(qint64 bytes) {
QByteArray buffer(bytes, Qt::Uninitialized);
const int channels = format_.channelCount();
const int sampleRate = format_.sampleRate();
//const float phaseInc = 2.0f * M_PI * frequency_ / sampleRate;
freq += 1.0f;
const float phaseInc = 2.0f * M_PI * freq / sampleRate;
if (format_.sampleFormat() == QAudioFormat::Int16) {
int16_t* out = reinterpret_cast<int16_t*>(buffer.data());
int frames = bytes / (sizeof(int16_t) * channels);
for (int i = 0; i < frames; ++i) {
int16_t s = static_cast<int16_t>(32767 * std::sin(phase_));
for (int c = 0; c < channels; ++c)
*out++ = s;
phase_ += phaseInc;
}
}
else if (format_.sampleFormat() == QAudioFormat::Float) {
float* out = reinterpret_cast<float*>(buffer.data());
int frames = bytes / (sizeof(float) * channels);
for (int i = 0; i < frames; ++i) {
float s = std::sin(phase_);
for (int c = 0; c < channels; ++c)
*out++ = s;
phase_ += phaseInc;
}
}
return buffer;
}
void Synth::applyConfig(const AudioConfig& config) {
// map struct values to the QAudioFormat
QAudioFormat format;
format.setSampleRate(config.sampleRate);
format.setChannelCount(config.channelCount);
format.setSampleFormat(config.sampleFormat);
// must create a new device
QAudioDevice device = QMediaDevices::defaultAudioOutput();
if (!device.isFormatSupported(format)) {
std::cout << "Requested format not supported, using preferred format" << std::endl;
format = device.preferredFormat();
}
format_ = format;
// and must create a new audioSink
delete audioSink_;
audioSink_ = new QAudioSink(device, format_, this);
audioSink_->setBufferSize(config.bufferSize);
}

48
src/synth/Synth.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
#include <QObject>
#include <QAudioFormat>
#include <QAudioSink>
#include <QIODevice>
#include <atomic>
struct AudioConfig {
int sampleRate = 44100;
int channelCount = 1;
QAudioFormat::SampleFormat sampleFormat = QAudioFormat::Int16;
int bufferSize = 4096; // bytes
};
class Synth : public QObject {
Q_OBJECT
public:
explicit Synth(QObject *parent = nullptr);
~Synth();
// audioSink is the media consumer for the audio data
QAudioSink* audioSink() { return audioSink_; }
// audio config setter/getter
void applyConfig(const AudioConfig& config);
const QAudioFormat& format() const { return format_; }
// synth commands
void start();
void stop();
void setFrequency(float frequency);
// bread and butter right here
QByteArray generateSamples(qint64 bytes);
private:
QAudioFormat format_;
QAudioSink *audioSink_ = nullptr;
QIODevice *audioDevice_ = nullptr;
std::atomic<float> frequency_{440.0f};
float phase_ = 0.0f;
float freq = 400.0f;
};