arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Modules Pages
MiniAudioStream.h
1#pragma once
8#include "AudioTools.h"
9#include <mutex>
10
11#define MINIAUDIO_IMPLEMENTATION
12#include "miniaudio.h"
13
14#define MA_BUFFER_COUNT 100
15#define MA_START_COUNT MA_BUFFER_COUNT - 2
16#define MA_DELAY 10
17
18namespace audio_tools {
19
25class MiniAudioConfig : public AudioInfo {
26 public:
28 sample_rate = 44100;
29 channels = 2;
30 bits_per_sample = 16;
31 };
32 MiniAudioConfig(const MiniAudioConfig &) = default;
33 MiniAudioConfig(const AudioInfo &in) {
35 channels = in.channels;
37 }
38
39 bool is_input = false;
40 bool is_output = true;
41 int delay_ms_if_buffer_full = MA_DELAY;
42 int buffer_count = MA_BUFFER_COUNT;
43 int buffer_start_count = MA_START_COUNT;
44};
45
53 public:
54 MiniAudioStream() = default;
55 ~MiniAudioStream() { end(); };
56
57 MiniAudioConfig defaultConfig(RxTxMode mode = RXTX_MODE) {
58 MiniAudioConfig info;
59 info.sample_rate = 44100;
60 info.channels = 2;
61 info.bits_per_sample = 16;
62 switch (mode) {
63 case RX_MODE:
64 info.is_input = true;
65 info.is_output = false;
66 break;
67 case TX_MODE:
68 info.is_input = false;
69 info.is_output = true;
70 break;
71 case RXTX_MODE:
72 info.is_input = true;
73 info.is_output = true;
74 break;
75 default:
76 info.is_input = false;
77 info.is_output = false;
78 break;
79 }
80 return info;
81 }
82
83 void setAudioInfo(AudioInfo in) override {
85 if (in.sample_rate != config.sample_rate ||
86 in.channels != config.channels ||
87 in.bits_per_sample != config.bits_per_sample) {
88 config.copyFrom(in);
89 if (is_active) {
90 is_active = false;
91 is_playing = false;
92 // This will stop the device so no need to do that manually.
93 ma_device_uninit(&device_ma);
94
95 begin();
96 }
97 }
98 }
99
100 bool begin(MiniAudioConfig info) {
102 this->config = info;
103 return begin();
104 }
105
106 bool begin() override {
107 TRACEI();
108 if (config.is_output && !config.is_input)
109 config_ma = ma_device_config_init(ma_device_type_playback);
110 else if (!config.is_output && config.is_input)
111 config_ma = ma_device_config_init(ma_device_type_capture);
112 else if (config.is_output && config.is_input)
113 config_ma = ma_device_config_init(ma_device_type_duplex);
114 else if (!config.is_output && !config.is_input)
115 config_ma = ma_device_config_init(ma_device_type_loopback);
116
117 config_ma.pUserData = this;
118 config_ma.playback.channels = config.channels;
119 config_ma.sampleRate = config.sample_rate;
120 config_ma.dataCallback = data_callback;
121 switch (config.bits_per_sample) {
122 case 8:
123 config_ma.playback.format = ma_format_u8;
124 break;
125 case 16:
126 config_ma.playback.format = ma_format_s16;
127 break;
128 case 24:
129 config_ma.playback.format = ma_format_s24;
130 break;
131 case 32:
132 config_ma.playback.format = ma_format_s32;
133 break;
134 default:
135 LOGE("Invalid format");
136 return false;
137 }
138
139 if (ma_device_init(NULL, &config_ma, &device_ma) != MA_SUCCESS) {
140 // Failed to initialize the device.
141 return false;
142 }
143
144 // The device is sleeping by default so you'll need to start it manually.
145 if (ma_device_start(&device_ma) != MA_SUCCESS) {
146 // Failed to initialize the device.
147 return false;
148 }
149
150 is_active = true;
151 return is_active;
152 }
153
154 void end() override {
155 is_active = false;
156 is_playing = false;
157 // This will stop the device so no need to do that manually.
158 ma_device_uninit(&device_ma);
159 // release buffer memory
160 buffer_in.resize(0);
161 buffer_out.resize(0);
162 }
163
164 int availableForWrite() override {
165 return buffer_out.size() == 0 ? 0 : DEFAULT_BUFFER_SIZE;
166 }
167
168 size_t write(const uint8_t *data, size_t len) override {
169 if (buffer_out.size() == 0) return 0;
170 LOGD("write: %zu", len);
171
172 // write data to buffer
173 int open = len;
174 int written = 0;
175 while (open > 0) {
176 size_t result = 0;
177 {
178 std::lock_guard<std::mutex> guard(write_mtx);
179 result = buffer_out.writeArray(data + written, open);
180 open -= result;
181 written += result;
182 }
183
184 if (result != len) doWait();
185 }
186
187 // activate playing
188 // if (!is_playing && buffer_out.bufferCountFilled()>=MA_START_COUNT) {
189 if (!is_playing && buffer_out.available() >= config.buffer_start_count * buffer_size) {
190 LOGI("starting audio");
191 is_playing = true;
192 }
193 // std::this_thread::yield();
194 return written;
195 }
196
197 int available() override {
198 return buffer_in.size() == 0 ? 0 : buffer_in.available();
199 }
200
201 size_t readBytes(uint8_t *data, size_t len) override {
202 if (buffer_in.size() == 0) return 0;
203 LOGD("write: %zu", len);
204 std::lock_guard<std::mutex> guard(read_mtx);
205 return buffer_in.readArray(data, len);
206 }
207
208 protected:
209 MiniAudioConfig config;
210 ma_device_config config_ma;
211 ma_device device_ma;
212 bool is_playing = false;
213 bool is_active = false;
214 bool is_buffers_setup = false;
215 RingBuffer<uint8_t> buffer_out{0};
216 RingBuffer<uint8_t> buffer_in{0};
217 std::mutex write_mtx;
218 std::mutex read_mtx;
219 int buffer_size = 0;
220
221 // In playback mode copy data to pOutput. In capture mode read data from
222 // pInput. In full-duplex mode, both pOutput and pInput will be valid and
223 // you can move data from pInput into pOutput. Never process more than
224 // frameCount frames.
225
226 void setupBuffers(int size) {
227 if (is_buffers_setup) return;
228 buffer_size = size;
229 int buffer_count = config.buffer_count;
230 LOGI("setupBuffers: %d * %d", size, buffer_count);
231 if (buffer_out.size() == 0 && config.is_output)
232 buffer_out.resize(size * buffer_count);
233 if (buffer_in.size() == 0 && config.is_input)
234 buffer_in.resize(size * buffer_count);
235 is_buffers_setup = true;
236 }
237
238 void doWait() {
239 //std::this_thread::yield();
240 delay(config.delay_ms_if_buffer_full);
241 //std::this_thread::sleep_for (std::chrono::milliseconds(MA_DELAY));
242 }
243
244 static void data_callback(ma_device *pDevice, void *pOutput,
245 const void *pInput, ma_uint32 frameCount) {
246 MiniAudioStream *self = (MiniAudioStream *)pDevice->pUserData;
247 AudioInfo cfg = self->audioInfo();
248
249 int bytes = frameCount * cfg.channels * cfg.bits_per_sample / 8;
250 self->setupBuffers(bytes);
251
252 if (pInput) {
253 int open = bytes;
254 int processed = 0;
255 while (open > 0) {
256 int len = 0;
257 {
258 std::unique_lock<std::mutex> guard(self->read_mtx);
259 int len =
260 self->buffer_in.writeArray((uint8_t *)pInput + processed, open);
261 open -= len;
262 processed += len;
263 }
264 if (len == 0) self->doWait();
265 }
266 }
267
268 if (pOutput) {
269 memset(pOutput, 0, bytes);
270 if (self->is_playing) {
271 int open = bytes;
272 int processed = 0;
273 while (open > 0) {
274 size_t len = 0;
275 {
276 std::lock_guard<std::mutex> guard(self->write_mtx);
277 len = self->buffer_out.readArray((uint8_t *)pOutput + processed,
278 bytes);
279 open -= len;
280 processed += len;
281 }
282 if (len != bytes) self->doWait();
283 }
284 }
285 }
286 }
287};
288
289} // namespace audio_tools
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:115
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:123
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:41
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:65
Configuration for MiniAudio.
Definition MiniAudioStream.h:25
MiniAudio: https://miniaud.io/.
Definition MiniAudioStream.h:52
void setAudioInfo(AudioInfo in) override
Defines the input AudioInfo.
Definition MiniAudioStream.h:83
virtual int available()
provides the number of entries that are available to read
Definition Buffers.h:366
virtual size_t size()
Returns the maximum capacity of the buffer.
Definition Buffers.h:383
Vector implementation which provides the most important methods as defined by std::vector....
Definition Vector.h:21
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:28
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioConfig.h:885
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:52
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:107
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:59