arduino-audio-tools
VolumeStream.h
1 
2 #pragma once
3 #include "AudioTools/CoreAudio/AudioStreams.h"
4 #include "AudioTools/CoreAudio/AudioOutput.h"
5 #include "AudioTools/CoreAudio/VolumeControl.h"
6 #include "AudioTools/CoreAudio/AudioTypes.h"
7 
8 namespace audio_tools {
9 
15 struct VolumeStreamConfig : public AudioInfo {
17  bits_per_sample = 16;
18  channels = 2;
19  }
20  bool allow_boost = false;
21  float volume=1.0; // start_volume
22 };
23 
24 
34 class VolumeStream : public ModifyingStream, public VolumeSupport {
35  public:
37  VolumeStream() = default;
38 
41  setOutput(out);
42  }
43 
46  setStream(in);
47  }
48 
51  Print *p_print = &out;
52  setOutput(*p_print);
54  }
55 
58  Stream *p_stream = &io;
59  setStream(*p_stream);
61  }
62 
64  void setStream(Stream &in){
65  p_in = ∈
66  p_out = p_in;
67  }
68 
70  void setOutput(Print &out){
71  p_out = &out;
72  }
73 
75  void setOutput(Stream &in){
76  p_in = ∈
77  p_out = p_in;
78  }
79 
81  void setStream(Print &out){
82  p_out = &out;
83  }
84 
85  VolumeStreamConfig defaultConfig() {
87  return c;
88  }
89 
90  bool begin() override {
91  return begin(info);
92  }
93 
94  bool begin(AudioInfo cfg) {
95  VolumeStreamConfig cfg1 = setupAudioInfo(cfg);
96  return begin(cfg1);
97  }
98 
101  TRACED();
103  // usually we use a exponential volume control - except if we allow values > 1.0
104  if (cfg.allow_boost){
105  setVolumeControl(linear_vc);
106  } else {
107  setVolumeControl(pot_vc);
108  }
109 
110  // set start volume
111  setVolume(cfg.volume);
112  is_started = true;
113  return true;
114  }
115 
116  void end() override {
117  is_started = false;
118  }
119 
120 
123  cached_volume.setVolumeControl(vc);
124  }
125 
128  cached_volume.setVolumeControl(pot_vc);
129  }
130 
132  virtual size_t readBytes(uint8_t *data, size_t len) override {
133  TRACED();
134  if (data==nullptr || p_in==nullptr){
135  LOGE("NPE");
136  return 0;
137  }
138  size_t result = p_in->readBytes(data, len);
139  if (isVolumeUpdate()) applyVolume(data, result);
140  return result;
141  }
142 
144  virtual size_t write(const uint8_t *data, size_t len) override {
145  LOGD("VolumeStream::write: %zu", len);
146  if (data==nullptr || p_out==nullptr){
147  LOGE("NPE");
148  return 0;
149  }
150  if (isVolumeUpdate()) applyVolume(data,len);
151  return p_out->write(data, len);
152  }
153 
155  virtual int availableForWrite() override {
156  return p_out==nullptr? 0 : p_out->availableForWrite();
157  }
158 
160  virtual int available() override {
161  return p_in==nullptr? 0 : p_in->available();
162  }
163 
165  void setAudioInfo(AudioInfo cfg) override {
166  TRACED();
167  // pass on notification
168  notifyAudioChange(cfg);
169 
170  if (is_started){
173  } else {
174  begin(cfg);
175  }
176  }
177 
179  bool setVolume(float vol) override {
180  bool result = true;
181  // just to make sure that we have a valid start volume before begin
182  info.volume = vol;
183  for (int j=0;j<info.channels;j++){
184  result = setVolume(vol, j);
185  }
186  return result;
187  }
188 
190  bool setVolume(float vol, int channel){
191  if ((vol > 1.0 && !info.allow_boost) || vol < 0) {
192  LOGE("Invalid volume: %f", vol);
193  return false;
194  }
195  if (channel < info.channels){
196  setupVectors();
197  float volume_value = volumeValue(vol);
198  if (volume_values[channel] != volume_value){
199  LOGI("setVolume: %f at %d", volume_value, channel);
200  float factor = volumeControl().getVolumeFactor(volume_value);
201  volume_values[channel]=volume_value;
202  #if PREFER_FIXEDPOINT
203  //convert float to fixed point 2.6
204  //Fixedpoint-Math from https://github.com/earlephilhower/ESP8266Audio/blob/0abcf71012f6128d52a6bcd155ed1404d6cc6dcd/src/AudioOutput.h#L67
205  if(factor > 4.0) factor = 4.0;//factor can only be >1 if allow_boost == true TODO: should we update volume_values[channel] if factor got clipped to 4.0?
206  uint8_t factorF2P6 = (uint8_t) (factor*(1<<6));
207  factor_for_channel[channel] = factorF2P6;
208  #else
209  factor_for_channel[channel]=factor;
210  #endif
211  }
212  return true;
213  } else {
214  LOGE("Invalid channel %d - max: %d", channel, info.channels-1);
215  return false;
216  }
217  }
218 
220  float volume() override {
221  // prevent npe
222  if (volume_values.size()==0) return 0;
223  // calculate avg
224  float total = 0;
225  int cnt = volume_values.size();
226  for (int j=0; j<cnt; j++){
227  total += volume_values[j];
228  }
229  return total / static_cast<float>(cnt);
230  }
231 
233  float volume(int channel) {
234  return channel>=info.channels? 0 : volume_values[channel];
235  }
236 
237  protected:
238  Print *p_out=nullptr;
239  Stream *p_in=nullptr;
240  VolumeStreamConfig info;
241  LinearVolumeControl linear_vc{true};
242  SimulatedAudioPot pot_vc;
243  CachedVolumeControl cached_volume{pot_vc};
244  Vector<float> volume_values;
245  #if PREFER_FIXEDPOINT
246  Vector<uint8_t> factor_for_channel; //Fixed point 2.6
247  #else
248  Vector<float> factor_for_channel;
249  #endif
250  bool is_started = false;
251  float max_value = 32767; // max value for clipping
252  int max_channels = 0;
253 
254  // checks if volume needs to be updated
255  bool isVolumeUpdate(){
256  if (!is_started) return false;
257  if (isAllChannelsFullVolume()) return false;
258  return true;
259  }
260 
261  bool isAllChannelsFullVolume(){
262  for (int ch=0;ch<info.channels;ch++){
263  if (volume_values[ch]!=1.0) return false;
264  }
265  return true;
266  }
267 
269  void setupVectors() {
270  factor_for_channel.resize(info.channels);
271  volume_values.resize(info.channels);
272  }
273 
276  VolumeStreamConfig cfg1;
277  cfg1.channels = cfg.channels;
278  cfg1.sample_rate = cfg.sample_rate;
279  cfg1.bits_per_sample = cfg.bits_per_sample;
280  // keep volume which might habe been defined befor calling begin
281  cfg1.volume = info.volume;
282  return cfg1;
283  }
284 
287  info = cfg;
288  max_value = NumberConverter::maxValue(info.bits_per_sample);
289  if (info.channels>max_channels){
290  max_channels = info.channels;
291  }
292  }
293 
294  float volumeValue(float vol){
295  if (!info.allow_boost && vol>1.0f) vol = 1.0;
296  if (vol<0.0f) vol = 0.0;
297 
298  // round to 2 digits
299  float value = (int)(vol * 100 + .5f);
300  float volume_value = (float)value / 100;
301  return volume_value;
302  }
303 
304  VolumeControl &volumeControl(){
305  return cached_volume;
306  }
307 
308  #if PREFER_FIXEDPOINT
309  uint8_t factorForChannel(int channel){
310  #else
311  float factorForChannel(int channel){
312  #endif
313  return factor_for_channel.size()==0? 1.0 : factor_for_channel[channel];
314  }
315 
316  void applyVolume(const uint8_t *buffer, size_t size){
317  switch(info.bits_per_sample){
318  case 16:
319  applyVolume16((int16_t*)buffer, size/2);
320  break;
321  case 24:
322  applyVolume24((int24_t*)buffer, size/sizeof(int24_t));
323  break;
324  case 32:
325  applyVolume32((int32_t*)buffer, size/4);
326  break;
327  default:
328  LOGE("Unsupported bits_per_sample: %d", info.bits_per_sample);
329  }
330  }
331 
332  void applyVolume16(int16_t* data, size_t size){
333  for (size_t j=0;j<size;j++){
334  #if PREFER_FIXEDPOINT
335  int32_t result = (data[j] * factorForChannel(j%info.channels)) >> 6; //Fixedpoint-Math from https://github.com/earlephilhower/ESP8266Audio/blob/0abcf71012f6128d52a6bcd155ed1404d6cc6dcd/src/AudioOutput.h#L67
336  #else
337  float result = factorForChannel(j%info.channels) * data[j];
338  #endif
339  if (!info.allow_boost){
340  if (result>max_value) result = max_value;
341  if (result<-max_value) result = -max_value;
342  }
343  data[j]= static_cast<int16_t>(result);
344  }
345  }
346 
347  void applyVolume24(int24_t* data, size_t size) {
348  for (size_t j=0;j<size;j++){
349  #if PREFER_FIXEDPOINT
350  int32_t result = (data[j] * factorForChannel(j%info.channels)) >> 6; //8bits * 24bits = fits into 32
351  #else
352  float result = factorForChannel(j%info.channels) * data[j];
353  #endif
354  if (!info.allow_boost){
355  if (result>max_value) result = max_value;
356  if (result<-max_value) result = -max_value;
357  }
358  int32_t result1 = result;
359  data[j]= static_cast<int24_t>(result1);
360  }
361  }
362 
363  void applyVolume32(int32_t* data, size_t size) {
364  for (size_t j=0;j<size;j++){
365  #if PREFER_FIXEDPOINT
366  int64_t result = (static_cast<int64_t>(data[j]) * static_cast<int64_t>(factorForChannel(j%info.channels))) >> 6;
367  #else
368  float result = factorForChannel(j%info.channels) * data[j];
369  #endif
370  if (!info.allow_boost){
371  if (result>max_value) result = max_value;
372  if (result<-max_value) result = -max_value;
373  }
374  data[j]= static_cast<int32_t>(result);
375  }
376  }
377 };
378 
379 }
virtual void addNotifyAudioChange(AudioInfoSupport &bi)
Adds target to be notified about audio changes.
Definition: AudioTypes.h:162
Abstract Audio Ouptut class.
Definition: AudioOutput.h:22
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition: BaseStream.h:109
The simplest possible implementation of a VolumeControl: The input = output which describes a linear ...
Definition: VolumeControl.h:149
Abstract class: Objects can be put into a pipleline.
Definition: AudioStreams.h:69
static int64_t maxValue(int value_bits_per_sample)
provides the biggest number for the indicated number of bits
Definition: AudioTypes.h:317
Definition: NoArduino.h:58
Definition: NoArduino.h:125
Abstract class for handling of the linear input volume to determine the multiplication factor which s...
Definition: VolumeControl.h:17
virtual float getVolumeFactor(float volume)=0
determines a multiplication factor (0.0 to 1.0) from an input value (0.0 to 1.0).
Adjust the volume of the related input or output: To work properly the class needs to know the bits p...
Definition: VolumeStream.h:34
virtual size_t write(const uint8_t *data, size_t len) override
Writes raw PCM audio data, which will be the input for the volume control.
Definition: VolumeStream.h:144
VolumeStreamConfig setupAudioInfo(AudioInfo cfg)
Provides a VolumeStreamConfig based on a AudioInfo.
Definition: VolumeStream.h:275
void setOutput(Stream &in)
same as setStream
Definition: VolumeStream.h:75
virtual size_t readBytes(uint8_t *data, size_t len) override
Read raw PCM audio data, which will be the input for the volume control.
Definition: VolumeStream.h:132
void setStream(Print &out)
same as set Output
Definition: VolumeStream.h:81
virtual int availableForWrite() override
Provides the nubmer of bytes we can write.
Definition: VolumeStream.h:155
float volume() override
Provides the current (avg) volume accross all channels.
Definition: VolumeStream.h:220
bool begin(VolumeStreamConfig cfg)
starts the processing
Definition: VolumeStream.h:100
void setupVolumeStreamConfig(VolumeStreamConfig cfg)
Stores the local variable and calculates some max values.
Definition: VolumeStream.h:286
VolumeStream()=default
Default Constructor.
VolumeStream(AudioStream &io)
Constructor which assigns Stream input or output.
Definition: VolumeStream.h:57
void setupVectors()
Resizes the vectors.
Definition: VolumeStream.h:269
VolumeStream(AudioOutput &out)
Constructor which assigns Print output.
Definition: VolumeStream.h:50
void resetVolumeControl()
Resets the volume control to use the standard logic.
Definition: VolumeStream.h:127
bool setVolume(float vol) override
Defines the volume for all channels: needs to be in the range of 0 to 1.0 (if allow boost has not bee...
Definition: VolumeStream.h:179
VolumeStream(Stream &in)
Constructor which assigns Stream input or output.
Definition: VolumeStream.h:45
void setOutput(Print &out)
Defines/Changes the output target.
Definition: VolumeStream.h:70
void setAudioInfo(AudioInfo cfg) override
Detines the Audio info - The bits_per_sample are critical to work properly!
Definition: VolumeStream.h:165
VolumeStream(Print &out)
Constructor which assigns Print output.
Definition: VolumeStream.h:40
bool setVolume(float vol, int channel)
Sets the volume for one channel.
Definition: VolumeStream.h:190
void setVolumeControl(VolumeControl &vc)
Defines the volume control logic.
Definition: VolumeStream.h:122
float volume(int channel)
Provides the current volume setting for the indicated channel.
Definition: VolumeStream.h:233
virtual int available() override
Provides the nubmer of bytes we can write.
Definition: VolumeStream.h:160
void setStream(Stream &in)
Defines/Changes the input & output.
Definition: VolumeStream.h:64
Supports the setting and getting of the volume.
Definition: AudioTypes.h:207
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AudioConfig.h:823
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:52
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:55
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:57
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition: AudioTypes.h:59
Config for VolumeStream.
Definition: VolumeStream.h:15