wiRedPanda
Logic Circuit Simulator
Loading...
Searching...
No Matches
Buzzer.cpp
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
5
6#include <cmath>
7
8#include <QAudioSink>
9
15#include "App/IO/VersionInfo.h"
16
17template<>
20 .type = ElementType::Buzzer,
21 .group = ElementGroup::Output,
22 .minInputSize = 1,
23 .maxInputSize = 1,
24 .canChangeAppearance = true,
25 .hasFrequency = true,
26 .hasLabel = true,
27 .hasVolume = true,
28 .rotatesGraphic = false,
29 };
30 static_assert(validate(constraints));
31
33 {
35 meta.pixmapPath = []{ return QStringLiteral(":/Components/Output/Buzzer/BuzzerOff.svg"); };
36 meta.titleText = QT_TRANSLATE_NOOP("Buzzer", "BUZZER");
37 meta.translatedName = QT_TRANSLATE_NOOP("Buzzer", "Buzzer");
38 meta.trContext = "Buzzer";
39 meta.defaultAppearances = QStringList({
40 ":/Components/Output/Buzzer/BuzzerOff.svg",
41 ":/Components/Output/Buzzer/BuzzerOn.svg",
42 });
43 return meta;
44 }
45
46 static inline const bool registered = []() {
48 ElementFactory::registerCreator(constraints.type, [] { return new Buzzer(); });
49 return true;
50 }();
51};
52
53Buzzer::Buzzer(QGraphicsItem *parent)
55{
57 m_generator = new ToneGenerator(this);
58 m_generator->setFrequency(m_frequency);
59 m_sink = new QAudioSink(ToneGenerator::format(), this);
60 m_sink->setVolume(static_cast<qreal>(m_volume));
61 }
62}
63
64double Buzzer::frequency() const
65{
66 return m_frequency;
67}
68
69void Buzzer::setFrequency(double freq)
70{
71 // A .panda file (or any other caller) can supply a non-finite or non-positive value;
72 // ToneGenerator::readData() casts a NaN-derived sample to qint16, which is undefined
73 // behaviour, and a non-positive frequency has no physical meaning for a buzzer. Reject
74 // it here — the one setter every caller (file load, GUI, MCP, undo/redo) goes through —
75 // mirroring Clock::setFrequency()'s identical guard for the same untrusted-value class.
76 if (!std::isfinite(freq) || freq <= 0.0) {
77 return;
78 }
79
80 m_frequency = freq;
81
82 if (!m_hasOutputDevice) {
83 return;
84 }
85
86 m_generator->setFrequency(freq);
87
88 // If already playing, restart so the new frequency takes effect immediately
89 if (m_isPlaying) {
90 m_sink->stop();
91 m_generator->start();
92 m_sink->start(m_generator);
93 }
94}
95
96void Buzzer::setAudio(const QString &note)
97{
98 if (note.isEmpty()) {
99 return;
100 }
101
103}
104
106{
107 m_generator->start();
108 m_sink->start(m_generator);
109}
110
112{
113 m_sink->stop();
114 m_generator->close();
115}
116
118{
119 if (!m_muted) {
120 m_sink->setVolume(static_cast<qreal>(m_volume));
121 }
122}
123
125{
126 m_sink->setVolume(m_muted ? 0.0 : static_cast<qreal>(m_volume));
127}
128
129int Buzzer::noteToFrequency(const QString &note)
130{
131 static const QHash<QString, int> map = {
132 {"C6", 1047}, {"D6", 1175}, {"E6", 1319}, {"F6", 1397},
133 {"G6", 1568}, {"A6", 1760}, {"B6", 1976}, {"C7", 2093},
134 // Backward-compatible aliases: the scale historically listed A7/B7 (an
135 // octave above their neighbours); pre-4.x files persist the note *name*,
136 // so keep resolving these to their true pitches.
137 {"A7", 3520}, {"B7", 3951},
138 };
139
140 return map.value(note, 1047);
141}
142
143void Buzzer::save(QDataStream &stream, SerializationOptions options) const
144{
145 GraphicElement::save(stream, options);
146
147 const bool slim = (options.purpose == SerializationPurpose::PortableFile);
148
149 QMap<QString, QVariant> map;
150 // PortableFile streams omit defaults; fresh-loaded elements start there
151 // anyway. Snapshots write unconditionally (reloaded into live elements).
152 if (!slim || m_frequency != kDefaultFrequency) {
153 map.insert("frequency", m_frequency);
154 }
155 if (!slim || m_volume != kDefaultVolume) {
156 map.insert("volume", static_cast<double>(m_volume));
157 }
158
159 stream << map;
160}
161
162void Buzzer::load(QDataStream &stream, SerializationContext &context)
163{
164 GraphicElement::load(stream, context);
165
166 if (!VersionInfo::hasAudio(context.version)) {
167 return;
168 }
169
170 if (!VersionInfo::hasQMapFormat(context.version)) {
171 // v2.4–4.0 stored the note name as a bare QString
172 const QString note = Serialization::readBoundedString(stream);
173 setAudio(note);
174 }
175
176 if (VersionInfo::hasQMapFormat(context.version)) {
177 QMap<QString, QVariant> map = Serialization::readBoundedMetadata(stream);
178
179 // New format: frequency in Hz
180 if (map.contains("frequency")) {
181 setFrequency(map.value("frequency").toDouble());
182 } else if (map.contains("note")) {
183 // Old format: note name → convert to frequency
184 setAudio(map.value("note").toString());
185 }
186
187 if (map.contains("volume")) {
188 bool ok = false;
189 const float vol = map.value("volume").toFloat(&ok);
190 if (ok) {
191 setVolume(vol);
192 }
193 }
194 }
195}
Graphic element for the buzzer tone output.
Singleton factory for all circuit element types.
Self-registering element trait template and compile-time constraint validation.
ElementMetadata metadataFromConstraints(const ElementConstraints &c)
Converts ElementConstraints to an ElementMetadata with all constraint-derived fields set.
Definition ElementInfo.h:80
constexpr bool validate(const ElementConstraints &c)
Validates element constraints at compile time.
Definition ElementInfo.h:48
Enums::ElementType ElementType
Definition Enums.h:107
Deserialization/serialization context structs passed through load()/save() call chains.
Circuit and waveform file serialization/deserialization utilities.
QIODevice that generates a continuous sine-wave tone for audio playback.
Named version predicates for file-format compatibility checks.
void setVolume(float vol) override
Sets the audio playback volume to vol (0.0–1.0).
AudioOutputElement(ElementType type, QGraphicsItem *parent=nullptr, float initialVolume=kDefaultVolume)
double frequency() const override
Returns the tone frequency in Hz.
Definition Buzzer.cpp:64
void applyMute() override
Applies the current m_muted state to the hardware backend.
Definition Buzzer.cpp:124
void stopAudio() override
Stops hardware playback (called from stop() when m_hasOutputDevice is true).
Definition Buzzer.cpp:111
Buzzer(QGraphicsItem *parent=nullptr)
Constructs a Buzzer element (default: 1047 Hz / C6).
Definition Buzzer.cpp:53
void startAudio() override
Starts hardware playback (called from play() when m_hasOutputDevice is true).
Definition Buzzer.cpp:105
void setFrequency(double freq) override
Sets the tone frequency in Hz.
Definition Buzzer.cpp:69
void save(QDataStream &stream, SerializationOptions options) const override
Definition Buzzer.cpp:143
void setAudio(const QString &note) override
Sets the tone by note name (e.g. "C6") for backward compatibility.
Definition Buzzer.cpp:96
static constexpr float kDefaultVolume
Definition Buzzer.h:30
void load(QDataStream &stream, SerializationContext &context) override
Definition Buzzer.cpp:162
static int noteToFrequency(const QString &note)
Maps a note name (e.g. "C6") to its frequency in Hz. Returns 1047 for unknown notes.
Definition Buzzer.cpp:129
static constexpr double kDefaultFrequency
Definition Buzzer.h:25
void applyVolume() override
Applies the current m_volume to the hardware backend.
Definition Buzzer.cpp:117
static void registerCreator(ElementType type, std::function< GraphicElement *()> creator)
Registers a creator lambda for type, used by buildElement().
static void registerMetadata(const ElementMetadata &meta)
Registers meta in the global map (called once per element type at startup).
virtual void load(QDataStream &stream, SerializationContext &context)
Loads the graphic element through a binary data stream.
virtual void save(QDataStream &stream, SerializationOptions options) const
static QString readBoundedString(QDataStream &stream)
Reads a QString from stream, refusing to allocate more bytes than remain in the stream (prevents OOM ...
static QMap< QString, QVariant > readBoundedMetadata(QDataStream &stream)
Reads the file-level metadata QMap<QString,QVariant> from stream without calling QList::reserve() wit...
static QAudioFormat format()
Returns the QAudioFormat matching this generator's output.
bool hasQMapFormat(const QVersionNumber &v)
V4.1: Format changed from flat binary to keyed QMap; port serial IDs; rotation fix.
Definition VersionInfo.h:60
bool hasAudio(const QVersionNumber &v)
V2.4: Audio element support (Buzzer, AudioBox).
Definition VersionInfo.h:42
Compile-time-validatable subset of ElementMetadata.
Definition ElementInfo.h:21
static const bool registered
Definition Buzzer.cpp:46
static ElementMetadata metadata()
Definition Buzzer.cpp:32
static constexpr ElementConstraints constraints
Definition Buzzer.cpp:19
Self-registering element information trait.
Compile-time-registered properties for one element type.
Bundles all per-deserialization state so that load() overrides receive it through one explicit parame...
QVersionNumber version
File-format version read from the stream header.
Options passed to GraphicElement::save() (and friends); the save-side counterpart of SerializationCon...
SerializationPurpose purpose
What this serialization is for; see SerializationPurpose. Mandatory, no default.