arduino-audio-tools
Loading...
Searching...
No Matches
PortAudioStream.h
Go to the documentation of this file.
1#pragma once
8#include "AudioTools.h"
9#include "portaudio.h"
10
11namespace audio_tools {
12
17class PortAudioConfig : public AudioInfo {
18 public:
20 sample_rate = 44100;
21 channels = 2;
22 bits_per_sample = 16;
23 };
30
31 bool is_input = false;
32 bool is_output = true;
42 int buffer_size = 0;
43 int buffer_count = 1;
44};
45
55 public:
57
59 TRACED();
60 Pa_Terminate();
61 }
62
64 TRACED();
65 PortAudioConfig result;
66 switch (mode) {
67 case RX_MODE:
68 result.is_input = true;
69 result.is_output = false;
70 break;
71 case TX_MODE:
72 result.is_input = false;
73 result.is_output = true;
74 break;
75 case RXTX_MODE:
76 result.is_input = true;
77 result.is_output = true;
78 break;
79 default:
80 LOGE("Unsupported Mode")
81 break;
82 }
83
84 return result;
85 }
86
88 TRACED();
89 PortAudioConfig default_info;
90 return default_info;
91 }
92
94 void setAudioInfo(AudioInfo in) override {
95 TRACEI();
99 info.logInfo();
100 begin(info);
101 };
102
103 // start with default configuration
104 bool begin() override { return begin(defaultConfig()); }
105
106 // start with the indicated configuration
108 TRACED();
109 this->info = info;
110
111 if (info.channels > 0 && info.sample_rate && info.bits_per_sample > 0) {
112 LOGD("Pa_Initialize");
113 err = Pa_Initialize();
114 LOGD("Pa_Initialize - done");
115 if (err != paNoError) {
116 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
117 return false;
118 }
119
120 // calculate frames: buffer_size * buffer_count (bytes, buffer_size 0
121 // = let PortAudio/the host API pick its own default) converted to
122 // frames per buffer - a larger total adds latency but absorbs more
123 // jitter from a slow/bursty producer (e.g. this project's
124 // synchronous demux+decode+write loops), reducing "output
125 // underflowed" warnings.
126 int buffer_frames =
127 info.buffer_size > 0
130 : paFramesPerBufferUnspecified;
131
132 // setAudioInfo() (below) calls begin() again on its own whenever the
133 // decoder reports audio info - e.g. once per station in a radio
134 // player - without ever having closed whatever stream was already
135 // open. The leaked stream keeps the audio device busy, so the next
136 // Pa_OpenDefaultStream often fails outright, after which every
137 // write() logs "Stream is stopped" forever. Close first if needed.
138 if (stream != nullptr) {
139 Pa_StopStream(stream);
140 Pa_CloseStream(stream);
141 stream = nullptr;
142 stream_started = false;
143 }
144
145 // Open an audio I/O stream.
146 LOGD("Pa_OpenDefaultStream");
147 err = Pa_OpenDefaultStream(
148 &stream,
149 info.is_input ? info.channels : 0, // no input channels
150 info.is_output ? info.channels : 0, // no output
151 getFormat(), // format
152 info.sample_rate, // sample rate
153 buffer_frames, // frames per buffer
154 nullptr, nullptr);
155 LOGD("Pa_OpenDefaultStream - done");
156 if (err != paNoError && err != paOutputUnderflow) {
157 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
158 return false;
159 }
160 } else {
161 LOGI("basic audio information is missing...");
162 return false;
163 }
164 return true;
165 }
166
167 void end() override {
168 TRACED();
169 err = Pa_StopStream(stream);
170 if (err != paNoError) {
171 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
172 }
173
174 err = Pa_CloseStream(stream);
175 if (err != paNoError) {
176 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
177 }
178 stream_started = false;
179 }
180
181 operator bool() override { return err == paNoError; }
182
183 size_t write(const uint8_t* data, size_t len) override {
184 LOGD("write: %zu", len);
185
186 startStream();
187
188 size_t result = 0;
189 if (stream != nullptr) {
190 int frames = len / bytesPerSample() / info.channels;
191 err = Pa_WriteStream(stream, data, frames);
192 if (err == paNoError) {
193 LOGD("Pa_WriteStream: %zu", len);
194 result = len;
195 } else if (err == paOutputUnderflowed) {
196 // Advisory, not a failure: per PortAudio's own docs, this means
197 // silence was inserted for a gap *before* this call - the data
198 // passed to *this* call was still written. Reporting result=0 here
199 // would make the caller believe the data was rejected and drop it,
200 // permanently losing audio and compounding the underrun that
201 // triggered the warning in the first place.
202 LOGW("PortAudio: output underflowed");
203 result = len;
204 } else {
205 LOGE("PortAudio error: %s", Pa_GetErrorText(err));
206 }
207 } else {
208 LOGW("stream is null")
209 }
210 return result;
211 }
212
213 size_t readBytes(uint8_t* data, size_t len) override {
214 LOGD("readBytes: %zu", len);
215 size_t result = 0;
216 if (stream != nullptr) {
217 int frames = len / bytesPerSample() / info.channels;
218 err = Pa_ReadStream(stream, data, frames);
219 if (err == paNoError) {
220 result = len;
221 } else {
222 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
223 }
224 } else {
225 LOGW("stream is null")
226 }
227 return len;
228 }
229
230 protected:
231 PaStream* stream = nullptr;
232 PaError err = paNoError;
234 bool stream_started = false;
235
237 // return info.bits_per_sample / 8;
238 return info.bits_per_sample == 24 ? sizeof(int24_t)
239 : info.bits_per_sample / 8;
240 }
241
242 PaSampleFormat getFormat() {
243 switch (bytesPerSample()) {
244 case 1:
245 return paInt8;
246 case 2:
247 return paInt16;
248 case 3:
249 return paInt24;
250 case 4:
251 return paInt32;
252 }
253 // make sure that we return a valid value
254 return paInt16;
255 }
256
258 void startStream() {
259 if (!stream_started && stream != nullptr) {
260 TRACED();
261 err = Pa_StartStream(stream);
262 if (err == paNoError) {
263 stream_started = true;
264 } else {
265 stream_started = false;
266 LOGE("PortAudio error: %s\n", Pa_GetErrorText(err));
267 }
268 }
269 }
270};
271
272} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define TRACEI()
Definition AudioLoggerIDF.h:32
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:120
PortAudio information.
Definition PortAudioStream.h:17
int buffer_count
Definition PortAudioStream.h:43
PortAudioConfig()
Definition PortAudioStream.h:19
PortAudioConfig(const PortAudioConfig &)=default
bool is_output
Definition PortAudioStream.h:32
PortAudioConfig(const AudioInfo &in)
Definition PortAudioStream.h:25
bool is_input
Definition PortAudioStream.h:31
int buffer_size
Definition PortAudioStream.h:42
Arduino Audio Stream using PortAudio.
Definition PortAudioStream.h:54
PaSampleFormat getFormat()
Definition PortAudioStream.h:242
PortAudioConfig defaultConfig()
Definition PortAudioStream.h:87
bool stream_started
Definition PortAudioStream.h:234
~PortAudioStream()
Definition PortAudioStream.h:58
PortAudioConfig info
Definition PortAudioStream.h:233
size_t readBytes(uint8_t *data, size_t len) override
Definition PortAudioStream.h:213
void end() override
Definition PortAudioStream.h:167
size_t write(const uint8_t *data, size_t len) override
Definition PortAudioStream.h:183
int bytesPerSample()
Definition PortAudioStream.h:236
void startStream()
automatically start the stream when we start to get data
Definition PortAudioStream.h:258
PaStream * stream
Definition PortAudioStream.h:231
PortAudioConfig defaultConfig(RxTxMode mode)
Definition PortAudioStream.h:63
bool begin(PortAudioConfig info)
Definition PortAudioStream.h:107
PaError err
Definition PortAudioStream.h:232
void setAudioInfo(AudioInfo in) override
notification of audio info change
Definition PortAudioStream.h:94
bool begin() override
Definition PortAudioStream.h:104
PortAudioStream()
Definition PortAudioStream.h:56
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
int24_4bytes_t int24_t
Definition int24_t.h:12
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
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
virtual void logInfo(const char *source="")
Definition AudioTypes.h:122