Browse code

Add source code of the XiPC emulator

Pawel Jablonski authored on 17/03/2023 16:46:08
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,123 @@
1
+/*
2
+ * Author: Pawel Jablonski
3
+ * E-mail: pj@xirx.net
4
+ * WWW: xirx.net
5
+ * GIT: git.xirx.net
6
+ *
7
+ * License: You can use this code however you like
8
+ * but leave information about the original author.
9
+ * Code is free for non-commercial and commercial use.
10
+ */
11
+
12
+#ifndef SPEAKER_H
13
+#define SPEAKER_H
14
+
15
+#include <QObject>
16
+#include <QList>
17
+#include <QAudioFormat>
18
+#include <QAudioOutput>
19
+#include <QByteArray>
20
+#include <QScopedPointer>
21
+
22
+//! This class contains speaker functions
23
+class Speaker : public QObject
24
+{
25
+	Q_OBJECT
26
+
27
+	public:
28
+		static const int SAMPLE_RATE = 44100; //!< Sampple rate in Hz
29
+
30
+		static const int BUFFER_SIZE = 32; //!< Size of the buffer
31
+
32
+		static const int NOTE_QUANTITY = 89; //!< Note quantity available to play
33
+
34
+		static const int TIME_MIN = 1; //!< Minimum time to play for a note in ms multiplied by TIME_MUL
35
+		static const int TIME_MAX = 250; //!< Maximum time to play for a note in ms multiplied by TIME_MUL
36
+		static constexpr double TIME_MUL = 10.0; //!< Time multiplicator for a note
37
+
38
+		static const int FILL_MIN = 1; //!< Minimum fill of the square wave in percents divided by FILL_DIV
39
+		static const int FILL_MAX = 15; //!< Maximum fill of the square wave in percents divided by FILL_DIV
40
+		static const int FILL_MASK = 0x0f; //!< Mask for the fill value
41
+		static constexpr double FILL_DIV = 16.0; //!< Square wave fill divider
42
+
43
+		static const int VOLUME_MASK = 0x0f; //!< Mask for the volume value
44
+		static constexpr double VOLUME_MAX = 15.0; //!< Maximum volume level
45
+
46
+		static constexpr double VALUE_MUL = 127.0; //!< Output level multiplicator used to conversion
47
+		static constexpr double VALUE_VOLUME = 0.3; //!< Output volume level multiplicator
48
+
49
+		//! Fields used by speaker to describe note
50
+		struct Note
51
+		{
52
+			unsigned char note; //!< Number of the note
53
+			unsigned char time; //!< Time to play
54
+			unsigned char fill; //!< Square wave fill
55
+			unsigned char volume; //!< Volume level
56
+		};
57
+
58
+		Speaker(QObject *parent = nullptr);
59
+		~Speaker() override;
60
+
61
+		Speaker(const Speaker &) = delete;
62
+		Speaker &operator=(const Speaker &) = delete;
63
+		Speaker(Speaker &&) = delete;
64
+		Speaker &operator=(Speaker &&) = delete;
65
+
66
+		void reset();
67
+
68
+		void setVolume(unsigned int volume);
69
+
70
+		void clear();
71
+		void addNote(const Speaker::Note &note);
72
+
73
+		unsigned char getBufferFree();
74
+
75
+	private:
76
+		//! List of possible notes to play
77
+		enum NoteCode
78
+		{
79
+			NOTE_SILENT,
80
+			NOTE_A0, NOTE_AS0, NOTE_B0,
81
+			NOTE_C1, NOTE_CS1, NOTE_D1, NOTE_DS1, NOTE_E1, NOTE_F1, NOTE_FS1, NOTE_G1, NOTE_GS1, NOTE_A1, NOTE_AS1, NOTE_B1,
82
+			NOTE_C2, NOTE_CS2, NOTE_D2, NOTE_DS2, NOTE_E2, NOTE_F2, NOTE_FS2, NOTE_G2, NOTE_GS2, NOTE_A2, NOTE_AS2, NOTE_B2,
83
+			NOTE_C3, NOTE_CS3, NOTE_D3, NOTE_DS3, NOTE_E3, NOTE_F3, NOTE_FS3, NOTE_G3, NOTE_GS3, NOTE_A3, NOTE_AS3, NOTE_B3,
84
+			NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4,
85
+			NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5,
86
+			NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6,
87
+			NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7,
88
+			NOTE_C8
89
+		};
90
+
91
+		//! List of frequencies for notes
92
+		QVector<int> noteFrequency = QVector<int>({
93
+			20, // Silent
94
+			27, 29, 31, //  A0 - B0
95
+			33, 35, 37, 39, 41, 44, 46, 49, 52, 55, 58, 62, // C1 - B1
96
+			65, 69, 73, 78, 82, 87, 93, 98, 104, 110, 117, 123, // C2 - B2
97
+			131, 139, 147, 156, 165, 175, 185, 196, 208, 220, 233, 247, // C3 - B3
98
+			262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, // C4 - B4
99
+			523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, // C5 - B5
100
+			1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, // C6 - B6
101
+			2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, // C7 - B7
102
+			4186 // C8
103
+		});
104
+
105
+		bool playing; //!< Playing status
106
+		QList<struct Note> buffer; //!< Buffer of notes
107
+
108
+		QAudioFormat audioFormat; //!< Audio stream parameter information
109
+		QScopedPointer<QAudioOutput> audioOutput; //!< Interface to an audio output device
110
+		QIODevice *ioDevice; //!< IO device retuned by QAudioOutput
111
+
112
+		QByteArray bufferOut; //!< Raw buffer with prepared data ready to send to IO device
113
+
114
+		void playNextNote();
115
+
116
+	signals:
117
+		void updateStatusSignal(int bufferUsed);
118
+
119
+	private slots:
120
+		void audioNotifySlot();
121
+};
122
+
123
+#endif