5
5
#include " mozzi_analog.h"
6
6
#include " Mozzi.h"
7
7
8
-
9
8
namespace audio_tools {
10
9
10
+ /* *
11
+ * @brief Mozzi Configuration for input or output stream
12
+ *
13
+ */
14
+ struct MozziConfig : AudioBaseInfo {
15
+ uint16_t control_rate=0 ;
16
+ void (*updateControl)() = nullptr ; // &::updateControl;
17
+ AudioOutput_t (*updateAudio)() = nullptr ; // = &::updateAudio;
11
18
19
+ MozziConfig (){
20
+ channels = AUDIO_CHANNELS;
21
+ sample_rate = AUDIO_RATE;
22
+ bits_per_sample = sizeof (AudioOutputStorage_t)*8 ;
23
+ }
24
+ };
12
25
/* *
13
26
* @brief Support for https://sensorium.github.io/Mozzi/
14
27
* Define your updateControl() method.
@@ -18,42 +31,226 @@ namespace audio_tools {
18
31
19
32
* @author Phil Schatzmann
20
33
* @copyright GPLv3
21
- *
22
34
*/
23
- class MozziGenerator : public SoundGenerator <int16_t > {
35
+
36
+ class MozziGenerator : public SoundGenerator <AudioOutputStorage_t> {
24
37
public:
25
- MozziGenerator (int control_rate){
26
- info.sample_rate = AUDIO_RATE;
27
- info.bits_per_sample = 16 ;
28
- info.channels = 2 ;
29
- control_counter_max = info.sample_rate / control_rate;
38
+ MozziGenerator (){
39
+ LOGD (__FUNCTION__);
40
+ }
41
+
42
+ MozziGenerator (MozziConfig config){
43
+ begin (config);
44
+ }
45
+
46
+ virtual ~MozziGenerator () {
47
+ end ();
48
+ }
49
+
50
+ void begin (MozziConfig config){
51
+ info = config;
52
+ if (info.control_rate ==0 ){
53
+ info.control_rate = CONTROL_RATE;
54
+ }
55
+ control_counter_max = info.sample_rate / info.control_rate ;
30
56
if (control_counter_max <= 0 ){
31
57
control_counter_max = 1 ;
32
58
}
33
59
control_counter = control_counter_max;
34
60
}
35
61
36
62
// / Provides some key audio information
37
- AudioBaseInfo config () {
63
+ MozziConfig config () {
38
64
return info;
39
65
}
40
66
41
67
// / Provides a single sample
42
- virtual int16_t readSample () {
68
+ virtual AudioOutputStorage_t readSample () {
69
+ if (is_read_buffer_filled){
70
+ // for stereo output we might have the value already
71
+ is_read_buffer_filled = false ;
72
+ return read_buffer;
73
+ }
74
+
43
75
if (--control_counter<0 ){
44
76
control_counter = control_counter_max;
45
- :: updateControl ();
77
+ info. updateControl ();
46
78
}
47
- int result = (int ) ::updateAudio ();
48
- samples_written_to_buffer++;
79
+
80
+ AudioOutput out = info.updateAudio ();
81
+ // requested mono
82
+ AudioOutputStorage_t result = 0 ;
83
+ #if (AUDIO_CHANNELS == MONO)
84
+ // generated stereo
85
+ if (sizeof (out) == sizeof (AudioOutputStorage_t)*2 ){
86
+ result = out[0 ]/2 + out[1 ]/2 ;
87
+ // generated mono
88
+ } else if (sizeof (out) == sizeof (AudioOutputStorage_t)){
89
+ result = out[0 ];
90
+ }
91
+
92
+ // requested stereo
93
+ #elif (AUDIO_CHANNELS == STEREO)
94
+ result = out[0 ];
95
+ // generated stereo
96
+ if (sizeof (out) == sizeof (AudioOutputStorage_t)*2 ){
97
+ read_buffer = out[1 ];
98
+ is_read_buffer_filled = true ;
99
+ // generated mono
100
+ } else if (sizeof (out) == sizeof (AudioOutputStorage_t)){
101
+ read_buffer = out[0 ];
102
+ is_read_buffer_filled = true ;
103
+ }
104
+ #endif
49
105
return result;
50
106
}
51
107
52
108
protected:
53
- AudioBaseInfo info;
109
+ MozziConfig info;
54
110
int control_counter_max;
55
111
int control_counter;
56
- uint64_t samples_written_to_buffer;
112
+ int read_buffer;
113
+ bool is_read_buffer_filled = false ;
114
+
115
+ };
116
+
117
+
118
+
119
+ /* *
120
+ * @brief We use the output functionality of Mozzi to output audio data. We expect the data as array of int16_t with
121
+ * one or two channels. Though we support the setting of a sample rate, we recommend to use the default sample rate
122
+ * from Mozzi which is available with the AUDIO_RATE define.
123
+ */
124
+ class MozziStream : public Stream {
125
+
126
+ public:
127
+ MozziStream (){
128
+ LOGD (__FUNCTION__);
129
+ }
130
+
131
+ ~MozziStream (){
132
+ LOGD (__FUNCTION__);
133
+ end ();
134
+ if (input_ptr!=nullptr ){
135
+ delete input_ptr;
136
+ input_ptr = nullptr ;
137
+ }
138
+ }
139
+
140
+ MozziConfig defaultConfig () {
141
+ MozziConfig default_config;
142
+ return default_config;
143
+ }
144
+
145
+ // / Starts Mozzi with its default parameters
146
+ void begin (){
147
+ begin (defaultConfig ());
148
+ }
149
+
150
+ // Start Mozzi - if controlRate > 0 we actiavate the sound generation (=>allow reads); the parameters describe the values if the
151
+ // provided input stream or resulting output stream;
152
+ void begin (MozziConfig cfg){
153
+ LOGD (__FUNCTION__);
154
+ config = cfg;
155
+ Mozzi.setAudioRate (config.sample_rate );
156
+ // in output mode we do not allocate any unnecessary functionality
157
+ if (input_ptr == nullptr && config.control_rate >0 ){
158
+ input_ptr = new MozziGenerator (config);
159
+ }
160
+ Mozzi.start (0 );
161
+ }
162
+
163
+ void end (){
164
+ LOGD (__FUNCTION__);
165
+ Mozzi.stop ();
166
+ }
167
+
168
+ // / unsupported operations
169
+ virtual int availableForWrite () {
170
+ return Mozzi.canWrite () ? sizeof (int ) : 0 ;
171
+ }
172
+
173
+ // / write an individual byte - if the frame is complete we pass it to Mozzi
174
+ virtual size_t write (uint8_t c) {
175
+ if (Mozzi.canWrite ()){
176
+ writeChar (c);
177
+ return 1 ;
178
+ } else {
179
+ return 0 ;
180
+ }
181
+ }
182
+
183
+ virtual size_t write (const uint8_t *buffer, size_t size) {
184
+ for (size_t j=0 ;j<size;j++){
185
+ if (write (buffer[j])<=0 ){
186
+ return j;
187
+ }
188
+ }
189
+ return size;
190
+ }
191
+
192
+ virtual int available (){
193
+ return 100000 ; // just a random big number
194
+ }
195
+
196
+ virtual size_t readBytes (char *buffer, size_t length){
197
+ return input_ptr==nullptr ? 0 : input_ptr->readBytes ((uint8_t *)buffer, length);
198
+ }
199
+
200
+ virtual int read (){
201
+ LOGE (" read() not supported - use readBytes!" );
202
+ return -1 ;
203
+ }
204
+
205
+ virtual int peek (){
206
+ LOGE (" peek() not supported!" );
207
+ return -1 ;
208
+ }
209
+
210
+ virtual void flush (){
211
+ };
212
+
213
+ protected:
214
+ MozziConfig config;
215
+ MozziGenerator *input_ptr = nullptr ;
216
+ NumberReader numberReader;
217
+ int32_t frame[2 ];
218
+ uint8_t buffer[64 ];
219
+ int buffer_pos;
220
+
221
+ // writes individual bytes and puts them together as MonoOutput or StereoOutput
222
+ void writeChar (uint8_t c){
223
+ buffer[buffer_pos++] = c;
224
+
225
+ #if (AUDIO_CHANNELS == MONO)
226
+ #warning "Mozzi is configured for Mono Output (to one channel only)"
227
+ if (config.channels == 1 && buffer_pos==config.bits_per_sample /8 ){
228
+ numberReader.toNumbers (buffer, config.bits_per_sample ,sizeof (AudioOutputStorage_t) * 8 , true , 1 , frame );
229
+ MonoOutput out = MonoOutput (frame[0 ]);
230
+ Mozzi.write (out);
231
+ buffer_pos = 0 ;
232
+ } else if (config.channels == 2 && buffer_pos==config.bits_per_sample /8 *2 ){
233
+ numberReader.toNumbers (buffer, config.bits_per_sample ,sizeof (AudioOutputStorage_t) * 8 , true , 2 , frame );
234
+ MonoOutput out = MonoOutput (frame[0 ]/2 + frame[1 ]/2 );
235
+ Mozzi.write (out);
236
+ buffer_pos = 0 ;
237
+ }
238
+
239
+ #elif (AUDIO_CHANNELS == STEREO)
240
+ if (config.channels == 1 && buffer_pos==config.bits_per_sample /8 ){
241
+ numberReader.toNumbers (buffer, config.bits_per_sample ,sizeof (AudioOutputStorage_t) * 8 , true , 1 , frame );
242
+ StereoOutput out = StereoOutput (frame[0 ],frame[0 ]);
243
+ Mozzi.write (out);
244
+ buffer_pos = 0 ;
245
+ } else if (config.channels == 2 && buffer_pos==config.bits_per_sample /8 *2 ){
246
+ numberReader.toNumbers (buffer, config.bits_per_sample ,sizeof (AudioOutputStorage_t) * 8 , true , 2 , frame );
247
+ StereoOutput out = StereoOutput (frame[0 ],frame[1 ]);
248
+ Mozzi.write (out);
249
+ buffer_pos = 0 ;
250
+ }
251
+
252
+ #endif
253
+ }
57
254
58
255
};
59
256
0 commit comments