Skip to content

Commit 3e45eab

Browse files
authored
Merge pull request #308 from sensorium/dev/small_buffer
Smaller audio buffers config option
2 parents 3bf2959 + 78ebf73 commit 3e45eab

9 files changed

Lines changed: 151 additions & 14 deletions

CircularBuffer.h

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,113 @@ Modified from https://en.wikipedia.org/wiki/Circular_buffer
1414
Mirroring version
1515
On 18 April 2014, the simplified version on the Wikipedia page for power of 2 sized buffers
1616
doesn't work - cbIsEmpty() returns true whether the buffer is full or empty.
17+
18+
April 2025: modified for different buffer sizes under the suggestion
19+
of Meebleeps (https://github.com/sensorium/Mozzi/issues/281)
1720
*/
1821

19-
#define MOZZI_BUFFER_SIZE 256 // do not expect to change and it to work.
20-
// just here for forward compatibility if one day
21-
// the buffer size might be editable
2222

23-
/** Circular buffer object. Has a fixed number of cells, set to 256.
23+
24+
/** Circular buffer object. Has a fixed number of cells, set by BUFFER_SIZE.
2425
@tparam ITEM_TYPE the kind of data to store, eg. int, int8_t etc.
26+
@tparam BUFFER_SIZE the size of the circular buffer
2527
*/
26-
template <class ITEM_TYPE>
28+
template <class ITEM_TYPE, int16_t BUFFER_SIZE>
2729
class CircularBuffer
2830
{
2931

32+
public:
33+
/** Constructor
34+
*/
35+
CircularBuffer(): start(0),end(0),s_msb(0),e_msb(0)
36+
{
37+
}
38+
39+
inline
40+
bool isFull() {
41+
return end == start && e_msb != s_msb;
42+
}
43+
44+
inline
45+
bool isEmpty() {
46+
return end == start && e_msb == s_msb;
47+
}
48+
49+
inline
50+
void write(ITEM_TYPE in) {
51+
items[end] = in;
52+
//if (isFull()) cbIncrStart(); /* full, overwrite moves start pointer */
53+
cbIncrEnd();
54+
}
55+
56+
inline
57+
ITEM_TYPE read() {
58+
ITEM_TYPE out = items[start];
59+
cbIncrStart();
60+
return out;
61+
}
62+
63+
inline
64+
unsigned long count() {
65+
return (num_buffers_read << COUNT_LSHIFT) + start;
66+
}
67+
inline
68+
ITEM_TYPE * address() {
69+
return items;
70+
}
71+
72+
private:
73+
ITEM_TYPE items[BUFFER_SIZE];
74+
uint8_t start; /* index of oldest itement */
75+
uint8_t end; /* index at which to write new itement */
76+
uint8_t s_msb;
77+
uint8_t e_msb;
78+
unsigned long num_buffers_read;
79+
static constexpr unsigned long COUNT_LSHIFT =
80+
(BUFFER_SIZE == 256) ? 8 :
81+
(BUFFER_SIZE == 128) ? 7 :
82+
(BUFFER_SIZE == 64) ? 6 :
83+
(BUFFER_SIZE == 32) ? 5 :
84+
(BUFFER_SIZE == 16) ? 4 :
85+
(BUFFER_SIZE == 8) ? 3 :
86+
(BUFFER_SIZE == 4) ? 2 :
87+
(BUFFER_SIZE == 2) ? 1 : 0;
88+
89+
inline
90+
void cbIncrStart() {
91+
start++;
92+
if (start == BUFFER_SIZE)
93+
{
94+
start = 0;
95+
s_msb ^= 1;
96+
num_buffers_read++;
97+
}
98+
}
99+
100+
inline
101+
void cbIncrEnd()
102+
{
103+
end++;
104+
if (end == BUFFER_SIZE)
105+
{
106+
end = 0;
107+
e_msb ^= 1;
108+
}
109+
}
110+
111+
112+
};
113+
114+
115+
116+
/** Circular buffer object. Specialization for size of 256.
117+
Note: Lot of duplication but C++ does not allow for specialization of the
118+
function member only (partial specialization).
119+
@tparam ITEM_TYPE the kind of data to store, eg. int, int8_t etc.
120+
*/
121+
template <class ITEM_TYPE>
122+
class CircularBuffer<ITEM_TYPE, 256>
123+
{
30124
public:
31125
/** Constructor
32126
*/
@@ -68,7 +162,7 @@ class CircularBuffer
68162
}
69163

70164
private:
71-
ITEM_TYPE items[MOZZI_BUFFER_SIZE];
165+
ITEM_TYPE items[256];
72166
uint8_t start; /* index of oldest itement */
73167
uint8_t end; /* index at which to write new itement */
74168
uint8_t s_msb;
@@ -90,5 +184,5 @@ class CircularBuffer
90184
end++;
91185
if (end == 0) e_msb ^= 1;
92186
}
93-
94187
};
188+

config/mozzi_config_documentation.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,22 @@
249249
* */
250250
#define MOZZI_AUDIO_PIN_1 FOR_DOXYGEN_ONLY
251251

252+
/** @ingroup config
253+
* @def MOZZI_OUTPUT_BUFFER_SIZE
254+
*
255+
* @brief Audio buffer setting.
256+
*
257+
* For a lot of outputting modes, Mozzi is buffering the audio samples in order to be able to coop with varying loads on the processor.
258+
* The bigger the buffer, the more able Mozzi will be to coop with big change of processor loads as the buffered values can compensate for that.
259+
* At the same time, a bigger buffer produces a bigger latency as the time between when Mozzi produces the sample and the time it is actually outputted increases. For instance, for a long time Mozzi's buffer size was of a fixed size of 256. This produces a potential latency of 15.6 ms for a MOZZI_AUDIO_RATE of 16384, and half this value for a MOZZI_AUDIO_RATE of 32768.
260+
* Depending on the application, this is usually not a problem but can lead to synchronisation issues in some cases (for instance when working with clocks).
261+
* MOZZI_OUTPUT_BUFFER_SIZE can be reduced to smaller values with this config, leading to more accurate timings but potentially to glitches if the buffer runs low.
262+
* Valid values are power of two from 256 downward (128, 64, …).
263+
* Note that this might not have an effect in all modes/platforms combination as Mozzi is sometimes using an external buffer which is not always configurable.
264+
*
265+
*/
266+
#define MOZZI_OUTPUT_BUFFER_SIZE FOR_DOXYGEN_ONLY
267+
252268

253269
/***************************************** ADVANCED SETTTINGS -- External audio output ******************************************
254270
*

internal/MozziGuts.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ inline void bufferAudioOutput(const AudioOutput f) {
8686
++samples_written_to_buffer;
8787
}
8888
#else
89-
CircularBuffer<AudioOutput> output_buffer; // fixed size 256
89+
CircularBuffer<AudioOutput, MOZZI_OUTPUT_BUFFER_SIZE> output_buffer;
9090
# define canBufferAudioOutput() (!output_buffer.isFull())
9191
# define bufferAudioOutput(f) output_buffer.write(f)
9292
static void CACHED_FUNCTION_ATTR defaultAudioOutput() {
@@ -150,7 +150,7 @@ uint16_t getAudioInput() { return audio_input; }
150150

151151
#if MOZZI_IS(MOZZI__LEGACY_AUDIO_INPUT_IMPL, 1)
152152
// ring buffer for audio input
153-
CircularBuffer<uint16_t> input_buffer; // fixed size 256
153+
CircularBuffer<uint16_t, 256> input_buffer; // fixed size 256
154154
#define audioInputAvailable() (!input_buffer.isEmpty())
155155
#define readAudioInput() (input_buffer.read())
156156
/** NOTE: Triggered at MOZZI_AUDIO_RATE via defaultAudioOutput(). In addition to the MOZZI_AUDIO_INPUT_PIN, at most one reading is taken for mozziAnalogRead(). */

internal/MozziGuts_impl_RENESAS.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ FspTimer timer;
8686
#endif
8787

8888
#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_INTERNAL_DAC)
89-
CircularBuffer<uint16_t> output_buffer;
89+
CircularBuffer<uint16_t, MOZZI_OUTPUT_BUFFER_SIZE> output_buffer;
9090
} // namespace MozziPrivate
9191
#include "MozziGuts_impl_RENESAS_analog.hpp"
9292
namespace MozziPrivate {
@@ -159,7 +159,7 @@ static void startAudio() {
159159

160160
// The following branches the DAC straight on Mozzi's circular buffer.
161161
dtc_cfg.p_info->p_src = output_buffer.address();
162-
dtc_cfg.p_info->length = MOZZI_BUFFER_SIZE;
162+
dtc_cfg.p_info->length = MOZZI_OUTPUT_BUFFER_SIZE;
163163
R_DTC_Reconfigure(&dtc_ctrl, dtc_cfg.p_info);
164164
timer_dac.start();
165165
#endif

internal/config_checks_esp32.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE)
220220
// All modes besides timed external bypass the output buffer!
221221
#if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPUT_INTERNAL_DAC, MOZZI_OUTPUT_PWM)
222222
# define BYPASS_MOZZI_OUTPUT_BUFFER true
223+
#if (MOZZI_OUTPUT_BUFFER_SIZE != 256)
224+
# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode.
225+
#endif
223226
#endif
224227

225228
#define MOZZI__INTERNAL_ANALOG_READ_RESOLUTION 12

internal/config_checks_esp8266.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_BITS, 16)
114114
// esp. since i2s output already has output rate control -> no need for a
115115
// separate output timer
116116
#define BYPASS_MOZZI_OUTPUT_BUFFER true
117+
#if (MOZZI_OUTPUT_BUFFER_SIZE != 256)
118+
# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode.
119+
#endif
117120
#endif
118121

119122
#define MOZZI__INTERNAL_ANALOG_READ_RESOLUTION 10

internal/config_checks_generic.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
#define MOZZI_AUDIO_INPUT_PIN 0
7979
#endif
8080

81+
#if not defined(MOZZI_OUTPUT_BUFFER_SIZE)
82+
#define MOZZI_OUTPUT_BUFFER_SIZE 256
83+
#endif
84+
8185
//MOZZI_PWM_RATE -> hardware specific
8286
//MOZZI_AUDIO_PIN_1 -> hardware specific
8387
//MOZZI_AUDIO_PIN_1_LOW -> hardware specific
@@ -121,6 +125,10 @@
121125
/// Step 3: Apply various generic checks that make sense on more than one platform
122126
MOZZI_CHECK_POW2(MOZZI_AUDIO_RATE)
123127
MOZZI_CHECK_POW2(MOZZI_CONTROL_RATE)
128+
MOZZI_CHECK_POW2(MOZZI_OUTPUT_BUFFER_SIZE)
129+
#if (MOZZI_OUTPUT_BUFFER_SIZE > 256)
130+
#error "Mozzi does not support buffer sizes greated than 256 at the moment"
131+
#endif
124132

125133
#if MOZZI_IS(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_STANDARD) && MOZZI_IS(MOZZI_ANALOG_READ, MOZZI_ANALOG_READ_NONE)
126134
#error "MOZZI_AUDIO_INPUT depends on MOZZI_ANALOG_READ option"

internal/config_checks_mbed.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_INPUT, MOZZI_AUDIO_INPUT_NONE, MOZZI_AUDIO_INP
117117
# endif
118118
#endif
119119

120-
// All modes besides timed external bypass the output buffer!
120+
// All modes besides timed external bypass the output buffer! In these modes, the buffer size is not configurable at the moment: throw an error if the user tries to change it.
121121
#if !MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED)
122122
# define BYPASS_MOZZI_OUTPUT_BUFFER true
123+
# if (MOZZI_OUTPUT_BUFFER_SIZE != 256) // has been modified
124+
# warning MOZZI_OUTPUT_BUFFER_SIZE does not have an effect in this mode.
125+
# endif
123126
#endif
124127

125128
// TODO: This value is correct for Arduino Giga and Arduino Portenta, but not necessarily everywhere else

internal/config_checks_rp2040.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPU
9595
# endif
9696
# define BYPASS_MOZZI_OUTPUT_BUFFER true
9797
# define MOZZI_RP2040_BUFFERS 8 // number of DMA buffers used
98-
# define MOZZI_RP2040_BUFFER_SIZE 256 // total size of the buffer, in samples
98+
# if !defined MOZZI_RP2040_BUFFER_SIZE
99+
# define MOZZI_RP2040_BUFFER_SIZE MOZZI_OUTPUT_BUFFER_SIZE // total size of the buffer, in samples
100+
# if (MOZZI_OUTPUT_BUFFER_SIZE < MOZZI_RP2040_BUFFERS)
101+
# error MOZZI_OUTPUT_BUFFER_SIZE cannot be lower than 8 on this platform at the moment
102+
# endif
103+
# endif
99104
#endif
100105

101106
#if MOZZI_IS(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_I2S_DAC)
@@ -115,7 +120,12 @@ MOZZI_CHECK_SUPPORTED(MOZZI_AUDIO_MODE, MOZZI_OUTPUT_EXTERNAL_TIMED, MOZZI_OUTPU
115120
MOZZI_CHECK_SUPPORTED(MOZZI_I2S_FORMAT, MOZZI_I2S_FORMAT_PLAIN, MOZZI_I2S_FORMAT_LSBJ)
116121
# define BYPASS_MOZZI_OUTPUT_BUFFER true
117122
# define MOZZI_RP2040_BUFFERS 8 // number of DMA buffers used
118-
# define MOZZI_RP2040_BUFFER_SIZE 256 // total size of the buffer, in samples
123+
# if !defined MOZZI_RP2040_BUFFER_SIZE
124+
# define MOZZI_RP2040_BUFFER_SIZE MOZZI_OUTPUT_BUFFER_SIZE // total size of the buffer, in samples
125+
# if (MOZZI_OUTPUT_BUFFER_SIZE < MOZZI_RP2040_BUFFERS)
126+
# error MOZZI_OUTPUT_BUFFER_SIZE cannot be lower than 8 on this platform at the moment
127+
# endif
128+
# endif
119129
#endif
120130

121131
#if !defined(MOZZI_ANALOG_READ)

0 commit comments

Comments
 (0)