arduino-audio-tools
MiniAudioStream.h
1 #pragma once
8 #include <mutex>
9 #include <thread>
10 #include "AudioTools.h"
11 
12 #define MINIAUDIO_IMPLEMENTATION
13 #include "miniaudio.h"
14 
15 #define MA_BUFFER_COUNT 20
16 #define MA_START_COUNT MA_BUFFER_COUNT-2
17 
18 namespace audio_tools {
24 class MiniAudioConfig : public AudioInfo {
25  public:
26  MiniAudioConfig() {
27  sample_rate = 44100;
28  channels = 2;
29  bits_per_sample = 16;
30  };
31  MiniAudioConfig(const MiniAudioConfig &) = default;
32  MiniAudioConfig(const AudioInfo &in) {
34  channels = in.channels;
36  }
37 
38  bool is_input = false;
39  bool is_output = true;
40 };
41 
48 class MiniAudioStream : public AudioStream {
49  public:
50  MiniAudioStream() = default;
51  ~MiniAudioStream() { end(); };
52 
53  MiniAudioConfig defaultConfig(RxTxMode mode = RXTX_MODE) {
54  MiniAudioConfig info;
55  info.sample_rate = 44100;
56  info.channels = 2;
57  info.bits_per_sample = 16;
58  switch (mode) {
59  case RX_MODE:
60  info.is_input = true;
61  info.is_output = false;
62  break;
63  case TX_MODE:
64  info.is_input = false;
65  info.is_output = true;
66  break;
67  case RXTX_MODE:
68  info.is_input = true;
69  info.is_output = true;
70  break;
71  default:
72  info.is_input = false;
73  info.is_output = false;
74  break;
75  }
76  return info;
77  }
78 
79  void setAudioInfo(AudioInfo in) override {
81  if (in.sample_rate != info.sample_rate || in.channels != info.channels ||
82  in.bits_per_sample != info.bits_per_sample) {
83  config.copyFrom(in);
84  if (is_active){
85  end();
86  begin();
87  }
88  }
89  }
90 
91  bool begin(MiniAudioConfig info) {
93  this->config = info;
94  return begin();
95  }
96 
97  bool begin() override {
98  TRACEI();
99  MiniAudioConfig info = config;
100  if (info.is_output && !info.is_input)
101  config_ma = ma_device_config_init(ma_device_type_playback);
102  else if (!info.is_output && info.is_input)
103  config_ma = ma_device_config_init(ma_device_type_capture);
104  else if (info.is_output && info.is_input)
105  config_ma = ma_device_config_init(ma_device_type_duplex);
106  else if (!info.is_output && !info.is_input)
107  config_ma = ma_device_config_init(ma_device_type_loopback);
108 
109 
110  config_ma.playback.channels = info.channels;
111  config_ma.sampleRate = info.sample_rate;
112  config_ma.dataCallback = data_callback;
113  switch (info.bits_per_sample) {
114  case 16:
115  config_ma.playback.format = ma_format_s16;
116  break;
117  case 24:
118  config_ma.playback.format = ma_format_s24;
119  break;
120  case 32:
121  config_ma.playback.format = ma_format_s32;
122  break;
123  default:
124  LOGE("Invalid format");
125  return false;
126  }
127  config_ma.pUserData = this;
128 
129  if (ma_device_init(NULL, &config_ma, &device_ma) != MA_SUCCESS) {
130  // Failed to initialize the device.
131  return false;
132  }
133 
134  // The device is sleeping by default so you'll need to start it manually.
135  ma_device_start(&device_ma);
136 
137  is_active = true;
138  return is_active;
139  }
140 
141  void end() override {
142  is_active = false;
143  is_playing = false;
144  // This will stop the device so no need to do that manually.
145  ma_device_uninit(&device_ma);
146  // release buffer memory
147  delete (p_buffer_in);
148  p_buffer_in = nullptr;
149  delete (p_buffer_out);
150  p_buffer_out = nullptr;
151  }
152 
153  int availableForWrite() override { return p_buffer_out==nullptr? 0 : p_buffer_out->availableForWrite(); }
154 
155  size_t write(const uint8_t *data, size_t len) override {
156  if (p_buffer_out==nullptr) return 0;
157  LOGD("write: %zu", len);
158  //std::lock_guard<mutex> qLock(mtxQueue);
159  size_t result = p_buffer_out->writeArray(data, len);
160  if (!is_playing && p_buffer_out->bufferCountFilled()>=MA_START_COUNT) {
161  is_playing = true;
162  }
163  //std::this_thread::yield();
164  return result;
165  }
166 
167  int available() override { return p_buffer_in==nullptr ? 0 : p_buffer_in->available(); }
168 
169  size_t readBytes(uint8_t *data, size_t len) override {
170  if (p_buffer_in==nullptr) return 0;
171  LOGD("write: %zu", len);
172  //std::lock_guard<mutex> qLock(mtxQueue);
173  return p_buffer_in->readArray(data, len);
174  }
175 
176  protected:
177  MiniAudioConfig config;
178  ma_device_config config_ma;
179  ma_device device_ma;
180  bool is_playing = false;
181  bool is_active = false;
182  bool is_buffers_setup = false;
183  NBuffer<uint8_t> *p_buffer_out = nullptr;
184  NBuffer<uint8_t> *p_buffer_in = nullptr;
185  //std::mutex mtxQueue;
186 
187  // In playback mode copy data to pOutput. In capture mode read data from
188  // pInput. In full-duplex mode, both pOutput and pInput will be valid and
189  // you can move data from pInput into pOutput. Never process more than
190  // frameCount frames.
191 
192  void setupBuffers(int size) {
193  if (is_buffers_setup) return;
194  if (p_buffer_out == nullptr && config.is_output)
195  p_buffer_out = new NBuffer<uint8_t>(size, MA_BUFFER_COUNT);
196  if (p_buffer_in==nullptr && config.is_input)
197  p_buffer_in = new NBuffer<uint8_t>(size, MA_BUFFER_COUNT);
198  is_buffers_setup = true;
199  }
200 
201  static void data_callback(ma_device *pDevice, void *pOutput,
202  const void *pInput, ma_uint32 frameCount) {
203  MiniAudioStream *self = (MiniAudioStream *)pDevice->pUserData;
204  AudioInfo cfg = self->audioInfo();
205 
206  int bytes = frameCount * cfg.channels * cfg.bits_per_sample / 8;
207  self->setupBuffers(bytes);
208 
209  if (pInput) {
210  //std::unique_lock<mutex> qLock(self->mtxQueue);
211  self->p_buffer_in->writeArray((uint8_t *)pInput, bytes);
212  }
213 
214  if (pOutput) {
215  memset(pOutput, 0, bytes);
216  if (self->is_playing ) {
217  //std::lock_guard<mutex> qLock(self->mtxQueue);
218  self->p_buffer_out->readArray((uint8_t *)pOutput, bytes);
219  std::this_thread::yield();
220  }
221  }
222  }
223 };
224 
225 } // namespace audio_tools
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: AudioStreams.h:24
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition: AudioStreams.h:32
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:24
MiniAudio: https://miniaud.io/.
Definition: MiniAudioStream.h:48
void setAudioInfo(AudioInfo in) override
Defines the input AudioInfo.
Definition: MiniAudioStream.h:79
int available()
provides the number of entries that are available to read
Definition: Buffers.h:616
int availableForWrite()
provides the number of entries that are available to write
Definition: Buffers.h:634
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
void copyFrom(AudioInfo info)
Same as set.
Definition: AudioTypes.h:105
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:57