wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
ToneGenerator.h
Go to the documentation of this file.
1// Copyright 2015 - 2026, GIBIS-UNIFESP and the wiRedPanda contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3
6
7#pragma once
8
9#include <cmath>
10#include <cstring>
11
12#include <QAudioFormat>
13#include <QIODevice>
14
19class ToneGenerator : public QIODevice
20{
21 Q_OBJECT
22
23public:
24 explicit ToneGenerator(QObject *parent = nullptr)
25 : QIODevice(parent)
26 {
27 }
28
30 static QAudioFormat format()
31 {
32 QAudioFormat fmt;
33 fmt.setSampleRate(SampleRate);
34 fmt.setChannelCount(1);
35 fmt.setSampleFormat(QAudioFormat::Int16);
36 return fmt;
37 }
38
40 void setFrequency(double hz) { m_frequency = hz; }
41
43 double frequency() const { return m_frequency; }
44
46 bool isSequential() const override { return true; }
47
49 qint64 bytesAvailable() const override { return SampleRate * BytesPerSample; }
50
52 bool start()
53 {
54 m_phase = 0.0;
55 return open(QIODevice::ReadOnly);
56 }
57
58protected:
60 qint64 readData(char *data, qint64 maxSize) override
61 {
62 constexpr double twoPi = 2.0 * M_PI;
63 const double increment = m_frequency / SampleRate;
64
65 const qint64 samples = maxSize / BytesPerSample;
66
67 for (qint64 i = 0; i < samples; ++i) {
68 // data is Qt's raw QIODevice buffer — no alignment guarantee, so a
69 // reinterpret_cast<qint16*> store is a -Wcast-align violation on
70 // armhf/riscv64 (issue #453). memcpy of a compile-time-constant 2
71 // bytes inlines to a single (possibly unaligned) store at -O2, so
72 // this has no measurable cost in the real-time audio path.
73 const qint16 sample = static_cast<qint16>(Amplitude * std::sin(twoPi * m_phase));
74 std::memcpy(data + i * BytesPerSample, &sample, sizeof(sample));
75 m_phase += increment;
76 if (m_phase >= 1.0) {
77 m_phase -= 1.0;
78 }
79 }
80
81 return samples * BytesPerSample;
82 }
83
85 qint64 writeData(const char *, qint64) override { return -1; }
86
87private:
88 static constexpr int SampleRate = 44100;
89 static constexpr int BytesPerSample = sizeof(qint16);
90 static constexpr double Amplitude = 28000.0;
91
92 double m_frequency = 1047.0; // C6 default
93 double m_phase = 0.0;
94};
static QAudioFormat format()
Returns the QAudioFormat matching this generator's output.
void setFrequency(double hz)
Sets the tone frequency in Hz.
ToneGenerator(QObject *parent=nullptr)
qint64 bytesAvailable() const override
Reports available data so QAudioSink knows to pull from this device.
qint64 writeData(const char *, qint64) override
Writing is not supported.
bool start()
Starts the device for reading.
double frequency() const
Returns the current frequency in Hz.
qint64 readData(char *data, qint64 maxSize) override
Fills data with maxSize bytes of PCM sine-wave samples.
bool isSequential() const override
This is a sequential (non-seekable) device.