rp2040-i2s
All Classes Files Functions Variables Typedefs Modules Pages
sample_conversion.h
1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef SOFTWARE_SAMPLE_CONVERSION_H
8 #define SOFTWARE_SAMPLE_CONVERSION_H
9 
10 #include <algorithm>
11 #include <cstring>
12 #include "pico_audio/audio.h"
14 
15 template<typename _sample_t>
16 struct FmtDetails {
17 public:
18  static const uint channel_count = 1;
19  static const uint frame_stride = channel_count * sizeof(_sample_t);
20  typedef _sample_t sample_t;
21 };
22 
23 typedef struct : public FmtDetails<uint8_t> {
24 } FmtU8;
25 
26 typedef struct : public FmtDetails<int8_t> {
27 } FmtS8;
28 
29 typedef struct : public FmtDetails<uint16_t> {
30 } FmtU16;
31 
32 typedef struct : public FmtDetails<int16_t> {
33 } FmtS16;
34 
35 // Multi channel is just N samples back to back
36 template<typename Fmt, uint ChannelCount>
38  static const uint channel_count = ChannelCount;
39  static const uint frame_stride = ChannelCount * Fmt::frame_stride;
40  typedef typename Fmt::sample_t sample_t;
41 };
42 
43 // define Mono<X> details as one channel
44 template<typename Fmt> using Mono = MultiChannelFmt<Fmt, 1>;
45 
46 // define Stereo<X> details as two channels
47 template<typename Fmt> using Stereo = MultiChannelFmt<Fmt, 2>;
48 
49 template<typename ToFmt, typename FromFmt>
51  static typename ToFmt::sample_t convert_sample(const typename FromFmt::sample_t &sample);
52 };
53 
54 // noop conversion
55 
56 template<typename Fmt>
57 struct sample_converter<Fmt, Fmt> {
58  static typename Fmt::sample_t convert_sample(const typename Fmt::sample_t &sample) {
59  return sample;
60  }
61 };
62 
63 // converters to S16
64 template<>
66  static int16_t convert_sample(const uint16_t &sample) {
67  return sample ^ 0x8000u;
68  }
69 };
70 
71 template<>
73  static int16_t convert_sample(const int8_t &sample) {
74  return sample << 8u;
75  }
76 };
77 
78 template<>
80  static int16_t convert_sample(const uint8_t &sample) {
81  return (sample << 8u) ^ 0x8000u;
82  }
83 };
84 
85 // converters to U16
86 
87 template<>
89  static uint16_t convert_sample(const int8_t &sample) {
90  return (sample << 8u) ^ 0x8000u;
91  }
92 };
93 
94 template<>
96  static uint16_t convert_sample(const uint8_t &sample) {
97  return sample << 8u;
98  }
99 };
100 
101 template<>
103  static uint16_t convert_sample(const int16_t &sample) {
104  return sample ^ 0x8000u;
105  }
106 };
107 
108 // converters to S8
109 
110 template<>
112  static int8_t convert_sample(const uint16_t &sample) {
113  return (sample ^ 0x8000u) >> 8u;
114  }
115 };
116 
117 template<>
119  static int8_t convert_sample(const uint8_t &sample) {
120  return sample ^ 0x80;
121  }
122 };
123 
124 template<>
126  static int8_t convert_sample(const int16_t &sample) {
127  return sample >> 8u;
128  }
129 };
130 
131 // converters to U8
132 
133 template<>
135  static uint8_t convert_sample(const uint16_t &sample) {
136  return sample >> 8u;
137  }
138 };
139 
140 template<>
142  static uint8_t convert_sample(const int8_t &sample) {
143  return sample ^ 0x80;
144  }
145 };
146 
147 template<>
149  static uint8_t convert_sample(const int16_t &sample) {
150  return (sample ^ 0x8000u) >> 8u;
151  }
152 };
153 
154 // template type for doing sample conversion
155 template<typename ToFmt, typename FromFmt>
157  static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count);
158 };
159 
160 // Efficient copies of same sample type
161 
162 template<class Fmt, uint ChannelCount>
163 struct converting_copy<MultiChannelFmt<Fmt, ChannelCount>, MultiChannelFmt<Fmt, ChannelCount>> {
164  static void copy(typename MultiChannelFmt<Fmt, ChannelCount>::sample_t *dest,
165  const typename MultiChannelFmt<Fmt, ChannelCount>::sample_t *src,
166  uint sample_count) {
167  memcpy((void *) dest, (const void *) src, sample_count * MultiChannelFmt<Fmt, ChannelCount>::frame_stride);
168  }
169 };
170 
171 // N channel to N channel
172 template<typename ToFmt, typename FromFmt, uint NumChannels>
173 struct converting_copy<MultiChannelFmt<ToFmt, NumChannels>, MultiChannelFmt<FromFmt, NumChannels>> {
174  static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) {
175  for (uint i = 0; i < sample_count * NumChannels; i++) {
177  }
178  }
179 };
180 
181 
182 // mono->stereo conversion
183 template<typename ToFmt, typename FromFmt>
184 struct converting_copy<Stereo<ToFmt>, Mono<FromFmt>> {
185  static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) {
186  for (; sample_count; sample_count--) {
187  typename ToFmt::sample_t mono_sample = sample_converter<ToFmt, FromFmt>::convert_sample(*src++);
188  *dest++ = mono_sample;
189  *dest++ = mono_sample;
190  }
191  }
192 };
193 
194 // stereo->mono conversion
195 template<typename ToFmt, typename FromFmt>
196 struct converting_copy<Mono<ToFmt>, Stereo<FromFmt>> {
197  static void copy(typename ToFmt::sample_t *dest, const typename FromFmt::sample_t *src, uint sample_count) {
198  for (; sample_count; sample_count--) {
199  // average first in case precision is better in source
200  typename FromFmt::sample_t averaged_sample = (src[0] + src[1]) / 2;
201  src += 2;
202  *dest++ = sample_converter<ToFmt, FromFmt>::convert_sample(averaged_sample);
203  }
204  }
205 };
206 
207 template<typename ToFmt, typename FromFmt>
208 audio_buffer_t *consumer_pool_take(audio_connection_t *connection, bool block) {
210  // for now we block until we have all the data in consumer buffers
211  audio_buffer_t *buffer = get_free_audio_buffer(cc->core.consumer_pool, block);
212  if (!buffer) return NULL;
213  assert(buffer->format->sample_stride == ToFmt::frame_stride);
214 
215  uint32_t pos = 0;
216  while (pos < buffer->max_sample_count) {
217  if (!cc->current_producer_buffer) {
218  cc->current_producer_buffer = get_full_audio_buffer(cc->core.producer_pool, block);
219  if (!cc->current_producer_buffer) {
220  assert(!block);
221  if (!pos) {
222  queue_free_audio_buffer(cc->core.consumer_pool, buffer);
223  return NULL;
224  }
225  break;
226  }
227  assert(cc->current_producer_buffer->format->format->channel_count == FromFmt::channel_count);
228  assert(cc->current_producer_buffer->format->sample_stride == FromFmt::frame_stride);
229  cc->current_producer_buffer_pos = 0;
230  }
231  uint sample_count = std::min(buffer->max_sample_count - pos,
232  cc->current_producer_buffer->sample_count - cc->current_producer_buffer_pos);
234  ((typename ToFmt::sample_t *) buffer->buffer->bytes) + pos * ToFmt::channel_count,
235  ((typename FromFmt::sample_t *) cc->current_producer_buffer->buffer->bytes) +
236  cc->current_producer_buffer_pos * FromFmt::channel_count,
237  sample_count);
238  pos += sample_count;
239  cc->current_producer_buffer_pos += sample_count;
240  if (cc->current_producer_buffer_pos == cc->current_producer_buffer->sample_count) {
241  queue_free_audio_buffer(cc->core.producer_pool, cc->current_producer_buffer);
242  cc->current_producer_buffer = NULL;
243  }
244  }
245  buffer->sample_count = pos;
246  return buffer;
247 }
248 
249 template<typename ToFmt, typename FromFmt>
250 void producer_pool_blocking_give(audio_connection_t *connection, audio_buffer_t *buffer) {
252  // for now we block until we have all the data in consumer buffers
253  uint32_t pos = 0;
254  while (pos < buffer->sample_count) {
255  if (!pbc->current_consumer_buffer) {
256  pbc->current_consumer_buffer = get_free_audio_buffer(pbc->core.consumer_pool, true);
257  pbc->current_consumer_buffer_pos = 0;
258  }
259  uint sample_count = std::min(buffer->sample_count - pos,
260  pbc->current_consumer_buffer->max_sample_count - pbc->current_consumer_buffer_pos);
261  assert(buffer->format->sample_stride == FromFmt::frame_stride);
262  assert(buffer->format->format->channel_count == FromFmt::channel_count);
264  ((typename ToFmt::sample_t *) pbc->current_consumer_buffer->buffer->bytes) +
265  pbc->current_consumer_buffer_pos * ToFmt::channel_count,
266  ((typename FromFmt::sample_t *) buffer->buffer->bytes) + pos * FromFmt::channel_count, sample_count);
267  pos += sample_count;
268  pbc->current_consumer_buffer_pos += sample_count;
269  if (pbc->current_consumer_buffer_pos == pbc->current_consumer_buffer->max_sample_count) {
270  pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer->max_sample_count;
271  queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer);
272  pbc->current_consumer_buffer = NULL;
273  }
274  }
275  // todo this should be a connection configuration (or a seaparate connection type)
276 #ifdef BLOCKING_GIVE_SYNCHRONIZE_BUFFERS
277  if (pbc->current_consumer_buffer) {
278  pbc->current_consumer_buffer->sample_count = pbc->current_consumer_buffer_pos;
279  queue_full_audio_buffer(pbc->core.consumer_pool, pbc->current_consumer_buffer);
280  pbc->current_consumer_buffer = NULL;
281  }
282 #endif
283  assert(pos == buffer->sample_count);
284  queue_free_audio_buffer(pbc->core.producer_pool, buffer);
285 }
286 
287 #endif //SOFTWARE_SAMPLE_CONVERSION_H
audio_buffer_t * get_full_audio_buffer(audio_buffer_pool_t *context, bool block)
Definition: audio.cpp:98
void queue_free_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab)
Definition: audio.cpp:90
void queue_full_audio_buffer(audio_buffer_pool_t *context, audio_buffer_t *ab)
Definition: audio.cpp:111
audio_buffer_t * get_free_audio_buffer(audio_buffer_pool_t *context, bool block)
Definition: audio.cpp:77
Definition: sample_conversion.h:16
Definition: sample_conversion.h:32
Definition: sample_conversion.h:26
Definition: sample_conversion.h:29
Definition: sample_conversion.h:23
Definition: sample_conversion.h:37
uint16_t sample_stride
Sample stride.
Definition: audio.h:58
const audio_format_t * format
Audio format.
Definition: audio.h:57
Audio buffer definition.
Definition: audio.h:63
Definition: audio.h:92
uint16_t channel_count
Number of channels.
Definition: audio.h:51
Definition: sample_conversion.h:156
Definition: sample_conversion.h:50