arduino-audio-tools
Loading...
Searching...
No Matches
RTSPFormat.h
1/*
2 * Author: Phil Schatzmann
3 *
4 * Based on Micro-RTSP library:
5 * https://github.com/geeksville/Micro-RTSP
6 * https://github.com/Tomp0801/Micro-RTSP-Audio
7 *
8 */
9#pragma once
10#include "AudioTools/AudioCodecs/AudioCodecsBase.h"
11#include "AudioTools/CoreAudio/AudioStreams.h"
12#include "AudioTools/CoreAudio/AudioTypes.h"
13#include "RTSPPlatform.h"
14#include "stdint.h"
15
16#define DEFAULT_PCM_FRAGMENT_SIZE 640
17
18namespace audio_tools {
19
49 public:
50 // Provides the format information: see see
51 // https://en.wikipedia.org/wiki/RTP_payload_formats
52 virtual const char *format(char *buffer, int len) = 0;
53
54 // Data convertsion e.g. to network format. len is in samples
55 virtual int convert(void *data, int sampleCount) { return sampleCount; }
56
57 // Initialize with AudioInfo like data
58 virtual void begin(AudioInfo info) { cfg = info; }
59 // Provide a default config (must be overridden by concrete subclass)
60 virtual AudioInfo defaultConfig() = 0;
61 // Access current AudioInfo
62 virtual AudioInfo audioInfo() { return cfg; }
63 // Name accessors
64 virtual const char *name() { return name_str; }
66 void setName(const char *name) { name_str = name; }
67
69 void setFragmentSize(int fragmentSize) { fragment_size = fragmentSize; }
70
72 virtual int fragmentSize() { return fragment_size; }
73
75 virtual int timestampIncrement() {
76 int sample_size_bytes = cfg.bits_per_sample / 8;
77 int samples_per_fragment =
78 fragmentSize() / (sample_size_bytes * cfg.channels);
79 return samples_per_fragment;
80 }
81
83 void setTimerPeriodUs(int period) { timer_period_us = period; }
84
86 virtual int timerPeriodUs() { return timer_period_us; }
87
89 virtual int rtpPayloadType() { return 96; }
90
92 virtual int readHeader(uint8_t *data) { return 0; }
93
95 virtual void setUseRfc2250Header(bool /*enable*/) {}
96 virtual bool useRfc2250Header() const { return false; }
97
98 protected:
99 const char *STD_URL_PRE_SUFFIX = "trackID";
100 // for sample rate 16000
101 int fragment_size = DEFAULT_PCM_FRAGMENT_SIZE;
102 int timer_period_us = 10000;
103 AudioInfo cfg{16000, 1, 16};
104 const char *name_str = "RTSPAudioTools";
105};
106
121class RTSPFormatPCM : public RTSPFormat {
122 public:
123 RTSPFormatPCM(AudioInfo info, int fragmentSize = DEFAULT_PCM_FRAGMENT_SIZE) {
124 cfg = info;
127 }
128
129 RTSPFormatPCM() {
130 AudioInfo tmp(16000, 1, 16); // Default: 16kHz mono 16-bit
131 cfg = tmp;
132 setFragmentSize(DEFAULT_PCM_FRAGMENT_SIZE);
133 setTimerPeriodUs(getTimerPeriod(DEFAULT_PCM_FRAGMENT_SIZE));
134 }
135
136 void begin(AudioInfo info) {
137 this->cfg = info;
138 setTimerPeriodUs(getTimerPeriod(this->fragment_size));
139 }
140
148 const char *format(char *buffer, int len) override {
149 int pt = rtpPayloadType();
150 snprintf(buffer, len,
151 "s=Microphone\r\n"
152 "c=IN IP4 0.0.0.0\r\n"
153 "t=0 0\r\n"
154 "m=audio 0 RTP/AVP %d\r\n"
155 "a=rtpmap:%d L16/%d/%d\r\n",
156 pt, pt, sampleRate(), channels());
157 LOGI("ftsp format: %s", buffer);
158 return (const char *)buffer;
159 }
160
167 int convert(void *data, int samples) {
168 // convert to network format (big endian)
169 int16_t *pt_16 = (int16_t *)data;
170 for (int j = 0; j < samples / 2; j++) {
171 pt_16[j] = htons(pt_16[j]);
172 }
173 return samples;
174 }
175
176 AudioInfo info() { return cfg; }
177
178 AudioInfo defaultConfig() override { return AudioInfo(16000, 1, 16); }
179
180 int rtpPayloadType() override {
181 // Static assignments per RFC 3551 only valid for 44100Hz mono/stereo
182 if (cfg.sample_rate == 44100) {
183 if (channels() == 1) return 11; // L16 mono 44.1kHz
184 if (channels() == 2) return 10; // L16 stereo 44.1kHz
185 }
186 return 96; // dynamic otherwise
187 }
188
189protected:
190
191 int sampleRate() { return cfg.sample_rate; }
192 int channels() { return cfg.channels; }
193 int bytesPerSample() { return cfg.bits_per_sample / 8; }
194
200 // Calculate how many samples are in the fragment
201 int samples_per_fragment = timestampIncrement();
202 // Calculate how long it takes to play these samples at the given sample
203 // rate
204 int timer_period =
205 (samples_per_fragment * 1000000) / cfg.sample_rate; // microseconds
206 return timer_period;
207 }
208
209
210};
211
219 public:
221 setTimerPeriodUs(20000); // 20ms standard for Opus
222 }
223
226 p_encoder = &encoder;
227 setTimerPeriodUs(encoder.frameDurationUs()); // Convert ms to us
228 }
229
230 // Provides the Opus format information:
231 // m=audio 54312 RTP/AVP 101
232 // a=rtpmap:101 opus/48000/2
233 // a=fmtp:101 stereo=1; sprop-stereo=1
234 const char *format(char *buffer, int len) override {
235 TRACEI();
236 snprintf(buffer, len,
237 "s=%s\r\n" // Stream Name
238 "c=IN IP4 0.0.0.0\r\n" // Connection Information
239 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
240 "m=audio 0 RTP/AVP 101\r\n" // UDP sessions with format 101=opus
241 "a=rtpmap:101 opus/%d/2\r\n"
242 "a=fmtp:101 stereo=1; sprop-stereo=%d\r\n",
243 name(), cfg.sample_rate, cfg.channels == 2);
244 return (const char *)buffer;
245 }
246 AudioInfo defaultConfig() {
247 AudioInfo cfg(48000, 2, 16);
248 return cfg;
249 }
250
251 void begin(AudioInfo info) override {
252 RTSPFormat::begin(info);
253 // Update timer period based on encoder configuration if available
254 if (p_encoder != nullptr) {
255 // p_encoder->setAudioInfo(info);
256 setTimerPeriodUs(p_encoder->frameDurationUs()); // Convert ms to us
257 }
258 }
259
260 protected:
261 AudioEncoder *p_encoder = nullptr;
262};
263
271 public:
273 setTimerPeriodUs(20000); // Default 20ms, will be recalculated in begin()
274 }
275
276 // Provides the SBC format information:
277 // m=audio 5004 RTP/AVP 98
278 // a=rtpmap:98 aptx/44100/2
279 // a=fmtp:98 variant=standard; bitresolution=16;
280 const char *format(char *buffer, int len) override {
281 TRACEI();
282 snprintf(buffer, len,
283 "s=%s\r\n" // Stream Name
284 "c=IN IP4 0.0.0.0\r\n" // Connection Information
285 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
286 "m=audio 0 RTP/AVP 98\r\n" // UDP sessions with format 98=aptx
287 "a=rtpmap:98 aptx/%d/%d\r\n"
288 "a=fmtp:98 variant=standard; bitresolution=%d\r\n",
289 name(), cfg.sample_rate, cfg.channels, cfg.bits_per_sample);
290 return (const char *)buffer;
291 }
292 AudioInfo defaultConfig() {
293 AudioInfo cfg(44100, 2, 16);
294 return cfg;
295 }
296
297 void begin(AudioInfo info) override {
298 RTSPFormat::begin(info);
299 // Calculate fragment-based timing for AptX
300 if (fragmentSize() > 0 && cfg.sample_rate > 0 && cfg.channels > 0) {
301 int bytesPerSample = (cfg.bits_per_sample + 7) / 8;
302 int samplesPerFragment = fragmentSize() / (cfg.channels * bytesPerSample);
303 int period = (samplesPerFragment * 1000000) / cfg.sample_rate;
304 setTimerPeriodUs(period);
305 }
306 }
307};
308
315class RTSPFormatGSM : public RTSPFormat {
316 public:
317 RTSPFormatGSM() {
318 setTimerPeriodUs(20000); // 20ms standard for GSM (160 samples at 8kHz)
319 }
320
322 const char *format(char *buffer, int len) override {
323 TRACEI();
324 assert(cfg.sample_rate == 8000);
325 assert(cfg.channels == 1);
326
327 snprintf(buffer, len,
328 "s=%s\r\n" // Stream Name
329 "c=IN IP4 0.0.0.0\r\n" // Connection Information
330 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
331 "m=audio 0 RTP/AVP 3\r\n" // UDP sessions with format 3=GSM
332 ,
333 name());
334 return (const char *)buffer;
335 }
336
337 AudioInfo defaultConfig() {
338 AudioInfo cfg(8000, 1, 16);
339 return cfg;
340 }
341};
342
351 public:
352 RTSPFormatG711(bool isUlaw) {
353 setTimerPeriodUs(20000); // 20ms standard for G.711 (160 samples at 8kHz)
354 setIsULaw(isUlaw);
355 }
356
358 const char *format(char *buffer, int len) override {
359 TRACEI();
360 assert(cfg.sample_rate == 8000);
361 assert(cfg.channels == 1);
362
363 snprintf(
364 buffer, len,
365 "s=%s\r\n" // Stream Name
366 "c=IN IP4 0.0.0.0\r\n" // Connection Information
367 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
368 "m=audio 0 RTP/AVP %d\r\n" // UDP sessions with format 0=G711 μ-Law
369 ,
370 name(), getFormat());
371 return (const char *)buffer;
372 }
373
375 void setIsULaw(bool flag) { is_ulaw = flag; }
376
377 AudioInfo defaultConfig() {
378 AudioInfo cfg(8000, 1, 16);
379 return cfg;
380 }
381
382 protected:
383 bool is_ulaw = true;
384 uint8_t getFormat() { return is_ulaw ? 0 : 8; }
385};
386
394 public:
396 setTimerPeriodUs(20000); // Default 20ms, will be recalculated in begin()
397 }
398
399 const char *format(char *buffer, int len) override {
400 TRACEI();
401 snprintf(buffer, len,
402 "s=%s\r\n" // Stream Name
403 "c=IN IP4 0.0.0.0\r\n" // Connection Information
404 "t=0 0\r\n" // start / stop - 0 -> unbounded and permanent session
405 "m=audio 0 RTP/AVP 96\r\n" // UDP sessions with format 96=dynamic
406 "a=rtpmap:96 l8/%d/%d\r\n",
407 name(), cfg.sample_rate, cfg.channels);
408 return (const char *)buffer;
409 }
410 AudioInfo defaultConfig() {
411 AudioInfo cfg(16000, 2, 8);
412 return cfg;
413 }
414
415 void begin(AudioInfo info) override {
416 RTSPFormat::begin(info);
417 // Calculate fragment-based timing for L8 (8-bit PCM)
418 if (fragmentSize() > 0 && cfg.sample_rate > 0 && cfg.channels > 0) {
419 int bytesPerSample = 1; // 8-bit = 1 byte per sample
420 int samplesPerFragment = fragmentSize() / cfg.channels;
421 int period = (samplesPerFragment * 1000000) / cfg.sample_rate;
422 setTimerPeriodUs(period);
423 }
424 }
425};
426
447template <class AudioEncoder>
449 public:
451 setTimerPeriodUs(20000); // Default 20ms, will be recalculated in begin()
452 }
453
454 RTSPFormatADPCM(AudioEncoder &encoder) {
455 p_encoder = &encoder;
456 encoder.begin();
457 setTimerPeriodUs(encoder.frameDurationUs()); // Convert ms to us
458 setFragmentSize(encoder.blockSize());
459 }
460
462 if (p_encoder != nullptr) return p_encoder->frameDurationUs();
464 }
465
466 // Provides the IMA ADPCM format information
467 // See RFC 3551 for details
468 const char *format(char *buffer, int len) override {
469 TRACEI();
470 // Only certain sample rates are valid for DVI4 (IMA ADPCM)
471 int sr = cfg.sample_rate;
472 int payload_type = 5; // default to 8000 Hz
473 switch (sr) {
474 case 8000:
475 payload_type = 5;
476 break;
477 case 16000:
478 payload_type = 6;
479 break;
480 case 11025:
481 payload_type = 16;
482 break;
483 case 22050:
484 payload_type = 17;
485 break;
486 default:
487 LOGE("Unsupported sample rate for IMA ADPCM: %d", sr);
488 sr = 8000;
489 payload_type = 5;
490 break;
491 }
492 snprintf(buffer, len,
493 "s=%s\r\n"
494 "c=IN IP4 0.0.0.0\r\n"
495 "t=0 0\r\n"
496 "m=audio 0 RTP/AVP %d\r\n"
497 "a=rtpmap:%d DVI4/%d\r\n",
498 name(), payload_type, payload_type, sr);
499 return (const char *)buffer;
500 }
501 AudioInfo defaultConfig() {
502 AudioInfo cfg(22050, 1, 16); // 4 bits per sample for IMA ADPCM
503 return cfg;
504 }
505
506 void begin(AudioInfo info) override {
507 RTSPFormat::begin(info);
508 if (p_encoder != nullptr) {
509 ((AudioInfoSupport *)p_encoder)->setAudioInfo(info);
510 setTimerPeriodUs(p_encoder->frameDurationUs()); // Convert ms to us
511 } else {
512 // Calculate timing for ADPCM (4 bits per sample = 0.5 bytes per sample)
513 if (fragmentSize() > 0 && cfg.sample_rate > 0) {
514 int samplesPerFragment =
515 fragmentSize() * 2; // 2 samples per byte for 4-bit ADPCM
516 int period = (samplesPerFragment * 1000000) / cfg.sample_rate;
517 setTimerPeriodUs(period);
518 }
519 }
520 }
521
522 AudioInfo audioInfo() override {
523 if (p_encoder != nullptr)
524 return ((AudioInfoSupport *)p_encoder)->audioInfo();
525 return RTSPFormat::audioInfo();
526 }
527
528 protected:
529 AudioEncoder *p_encoder = nullptr;
530};
531
538class RTSPFormatMP3 : public RTSPFormat {
539 public:
540 RTSPFormatMP3() {
541 setTimerPeriodUs(26122); // ~26ms for MP3 frames (1152 samples at 44.1kHz)
542 setFragmentSize(2884); // Trigger read that is big enough
543 }
544
547 setEncoder(encoder);
548 setFragmentSize(2884); // Trigger read that is big enough
549 setTimerPeriodUs(encoder.frameDurationUs()); // Convert ms to us
550 }
551
552 void setEncoder(AudioEncoder &encoder) { p_encoder = &encoder; }
553
555 if (p_encoder != nullptr) return p_encoder->frameDurationUs();
557 }
558
559 virtual int timestampIncrement() {
560 if (p_encoder != nullptr) return p_encoder->samplesPerFrame();
561 // MP3 frame size is typically 1152 samples
562 return 1152;
563 }
564
567 if (p_encoder != nullptr)
568 return ((AudioInfoSupport *)p_encoder)->audioInfo();
569 return RTSPFormat::audioInfo();
570 }
571
572 // Provides the MP3 format information:
573 // m=audio 0 RTP/AVP 14
574 // a=rtpmap:14 MPA/90000[/ch]
575 // See RFC 3551 for details: MP3 RTP clock is always 90kHz
576 const char *format(char *buffer, int len) override {
577 TRACEI();
578 int payload_type = rtpPayloadType(); // RTP/AVP 14 = MPEG audio (MP3)
579 int ch = cfg.channels;
580 if (ch <= 0) ch = 1;
581 // Include channels when not mono for clarity
582 int ptime_ms =
583 (cfg.sample_rate > 0) ? (int)((1152 * 1000) / cfg.sample_rate) : 26;
584 if (ptime_ms < 10) ptime_ms = 10; // clamp to a reasonable minimum
585 if (ch == 1) {
586 snprintf(buffer, len,
587 "m=audio 0 RTP/AVP %d\r\n"
588 "a=rtpmap:%d MPA/90000\r\n"
589 "a=fmtp:%d layer=3\r\n"
590 "a=ptime:%d\r\n",
591 payload_type, payload_type, payload_type, ptime_ms);
592 } else {
593 snprintf(buffer, len,
594 "m=audio 0 RTP/AVP %d\r\n"
595 "a=rtpmap:%d MPA/90000/%d\r\n"
596 "a=fmtp:%d layer=3\r\n"
597 "a=ptime:%d\r\n",
598 payload_type, payload_type, ch, payload_type, ptime_ms);
599 }
600 return (const char *)buffer;
601 }
602
603 int rtpPayloadType() override { return 14; }
604
605 AudioInfo defaultConfig() {
606 AudioInfo cfg(44100, 2, 16); // Typical MP3 config
607 return cfg;
608 }
609
610 void begin(AudioInfo info) override {
611 RTSPFormat::begin(info);
612 // MP3 frame size is typically 1152 samples
613 int samplesPerFrame = 1152;
614 if (cfg.sample_rate > 0) {
615 int period = (samplesPerFrame * 1000000) / cfg.sample_rate;
616 setTimerPeriodUs(period);
617 }
618 }
619
621 int readHeader(unsigned char *buffer) override {
622 // Some clients (e.g., FFmpeg/ffplay) expect the optional RFC2250 4-byte
623 // MPEG audio header for payload type 14, while others (e.g., VLC) expect
624 // raw frames. Make this behavior configurable.
625 if (use_rfc2250_header_) {
626 memset(buffer, 0, 4);
627 return 4;
628 }
629 (void)buffer;
630 return 0;
631 }
632
633 protected:
634 AudioEncoder *p_encoder = nullptr;
635 bool use_rfc2250_header_ = false;
636
637 public:
638 // Enable/disable RFC2250 4-byte MPEG audio header in RTP payload
639 void setUseRfc2250Header(bool enable) { use_rfc2250_header_ = enable; }
640 bool useRfc2250Header() const override { return use_rfc2250_header_; }
641};
642
650class RTSPFormatAAC : public RTSPFormat {
651 public:
652 RTSPFormatAAC() {
653 setTimerPeriodUs(23219); // ~23ms for AAC frames (1024 samples at 44.1kHz)
654 }
655
656 // Provides the AAC format information:
657 // m=audio 0 RTP/AVP 96
658 // a=rtpmap:96 MPEG4-GENERIC/44100/2
659 // a=fmtp:96 streamtype=5; profile-level-id=1; mode=AAC-hbr; ...
660 // For simplicity, only basic SDP lines are provided here
661 const char *format(char *buffer, int len) override {
662 TRACEI();
663 int payload_type = 96; // Dynamic payload type for AAC
664 int sr = cfg.sample_rate;
665 int ch = cfg.channels;
666 snprintf(buffer, len,
667 "s=%s\r\n"
668 "c=IN IP4 0.0.0.0\r\n"
669 "t=0 0\r\n"
670 "m=audio 0 RTP/AVP %d\r\n"
671 "a=rtpmap:%d MPEG4-GENERIC/%d/%d\r\n"
672 "a=fmtp:%d streamtype=5; profile-level-id=1; mode=AAC-hbr;\r\n",
673 name(), payload_type, payload_type, sr, ch, payload_type);
674 return (const char *)buffer;
675 }
676 AudioInfo defaultConfig() {
677 AudioInfo cfg(44100, 2, 16); // Typical AAC config
678 return cfg;
679 }
680
681 void begin(AudioInfo info) override {
682 RTSPFormat::begin(info);
683 // AAC frame size is typically 1024 samples
684 int samplesPerFrame = 1024;
685 if (cfg.sample_rate > 0) {
686 int period = (samplesPerFrame * 1000000) / cfg.sample_rate;
687 setTimerPeriodUs(period);
688 }
689 }
690};
691
692} // namespace audio_tools
Encoding of PCM data.
Definition AudioCodecsBase.h:97
virtual uint16_t samplesPerFrame()
Optional rtsp function: provide samples per the frame.
Definition AudioCodecsBase.h:113
virtual uint32_t frameDurationUs()
Optional rtsp function: provide the frame duration in microseconds.
Definition AudioCodecsBase.h:111
Supports changes to the sampling rate, bits and channels.
Definition AudioTypes.h:135
AAC format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats See RFC 3640 for details.
Definition RTSPFormat.h:650
RTSP/RTP formatter for mono IMA ADPCM (DVI4)
Definition RTSPFormat.h:448
int timerPeriodUs()
Timer period in microseconds.
Definition RTSPFormat.h:461
abtX format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats
Definition RTSPFormat.h:270
G711 μ-Law format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats Packet intervall: 20,...
Definition RTSPFormat.h:350
void setIsULaw(bool flag)
Defines if we use ulow ar alow; by default we use ulaw!
Definition RTSPFormat.h:375
const char * format(char *buffer, int len) override
Provides the G711 format information.
Definition RTSPFormat.h:358
GSM format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats.
Definition RTSPFormat.h:315
const char * format(char *buffer, int len) override
Provides the GSM format information.
Definition RTSPFormat.h:322
Audio Format Definition - Base class for RTSP audio formats.
Definition RTSPFormat.h:48
virtual int timerPeriodUs()
Timer period in microseconds.
Definition RTSPFormat.h:86
virtual int readHeader(uint8_t *data)
Optional header: e.g. rfc2250.
Definition RTSPFormat.h:92
void setTimerPeriodUs(int period)
Defines the timer period in microseconds.
Definition RTSPFormat.h:83
virtual int timestampIncrement()
Fragment size in samples.
Definition RTSPFormat.h:75
void setFragmentSize(int fragmentSize)
Defines the fragment size in bytes.
Definition RTSPFormat.h:69
virtual int fragmentSize()
Fragment (=write) size in bytes.
Definition RTSPFormat.h:72
virtual int rtpPayloadType()
default dynamic
Definition RTSPFormat.h:89
void setName(const char *name)
Defines the name of the stream.
Definition RTSPFormat.h:66
virtual void setUseRfc2250Header(bool)
Optional: Configure RFC2250 header usage (default: no-op)
Definition RTSPFormat.h:95
MP3 format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats.
Definition RTSPFormat.h:538
int timerPeriodUs()
Timer period in microseconds.
Definition RTSPFormat.h:554
virtual int timestampIncrement()
Fragment size in samples.
Definition RTSPFormat.h:559
AudioInfo audioInfo()
Provides the AudioInfo.
Definition RTSPFormat.h:566
RTSPFormatMP3(AudioEncoder &encoder)
Provide dynamic frame duration if encoder is available.
Definition RTSPFormat.h:546
int readHeader(unsigned char *buffer) override
rfc2250 header before the playload
Definition RTSPFormat.h:621
void setUseRfc2250Header(bool enable)
Optional: Configure RFC2250 header usage (default: no-op)
Definition RTSPFormat.h:639
int rtpPayloadType() override
default dynamic
Definition RTSPFormat.h:603
Opus format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats.
Definition RTSPFormat.h:218
RTSPFormatOpus(AudioEncoder &encoder)
Determine timer duration from opus configuration.
Definition RTSPFormat.h:225
L8 format for RTSP https://en.wikipedia.org/wiki/RTP_payload_formats.
Definition RTSPFormat.h:393
Linear PCM Format for RTSP Streaming.
Definition RTSPFormat.h:121
int convert(void *data, int samples)
Convert to network format.
Definition RTSPFormat.h:167
const char * format(char *buffer, int len) override
Provide format 10 or 11.
Definition RTSPFormat.h:148
int getTimerPeriod(int fragmentSize)
Get the timer period for streaming.
Definition RTSPFormat.h:199
int rtpPayloadType() override
default dynamic
Definition RTSPFormat.h:180
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61