Skip to content

Commit 56d5e9a

Browse files
committed
VolumeStream support for indivigual channels
1 parent e08550c commit 56d5e9a

File tree

2 files changed

+135
-59
lines changed

2 files changed

+135
-59
lines changed

src/AudioTools/AudioPlayer.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ namespace audio_tools {
562562
LOGD(LOG_METHOD);
563563
this->p_source = &source;
564564
this->p_decoder = &decoder;
565-
this->volume_out.begin(output);
565+
this->volume_out.setTarget(output);
566566
this->p_out_decoding = new EncodedAudioStream(volume_out, decoder);
567567
this->p_final_print = &output;
568568

@@ -583,7 +583,7 @@ namespace audio_tools {
583583
LOGD(LOG_METHOD);
584584
this->p_source = &source;
585585
this->p_decoder = &decoder;
586-
this->volume_out.begin(output);
586+
this->volume_out.setTarget(output);
587587
this->p_out_decoding = new EncodedAudioStream(volume_out, decoder);
588588
setNotify(notify);
589589
}
@@ -599,7 +599,7 @@ namespace audio_tools {
599599
AudioPlayer(AudioSource& source, AudioStream& output, AudioDecoder& decoder) {
600600
LOGD(LOG_METHOD);
601601
this->p_source = &source;
602-
this->volume_out.begin(output);
602+
this->volume_out.setTarget(output);
603603
this->p_out_decoding = new EncodedAudioStream(volume_out, decoder);
604604
this->p_final_stream = &output;
605605

@@ -664,7 +664,7 @@ namespace audio_tools {
664664

665665
/// (Re)defines the output
666666
void setOutput(Print& output){
667-
this->volume_out.begin(output);
667+
this->volume_out.setTarget(output);
668668
}
669669

670670
/// (Re)defines the decoder

src/AudioTools/AudioStreams.h

Lines changed: 131 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -608,41 +608,80 @@ class ConvertedStream : public AudioStreamX {
608608

609609
};
610610

611+
/**
612+
* @brief Config for VolumeStream
613+
* @author Phil Schatzmann
614+
* @copyright GPLv3
615+
*/
616+
struct VolumeStreamConfig : public AudioBaseInfo {
617+
VolumeStreamConfig(){
618+
bits_per_sample = 16;
619+
channels = 2;
620+
}
621+
bool allow_boost = false;
622+
float volume=1.0; // start_volume
623+
};
624+
611625
/**
612626
* @brief Output PWM object on which we can apply some volume settings. To work properly the class needs to know the
613-
* bits per sample. If nothing is defined we assume 16 bits!
627+
* bits per sample and number of channels!
614628
* @author Phil Schatzmann
615629
* @copyright GPLv3
616630
*/
617-
//class VolumeOutput : public AudioPrint {
618631
class VolumeStream : public AudioStreamX {
619632
public:
620633
/// Default Constructor
621634
VolumeStream() = default;
622635

623-
VolumeStream(Print &out, bool allowBoost=false) {
624-
begin(out, allowBoost);
636+
~VolumeStream() {
637+
cleanup();
638+
};
639+
640+
VolumeStream(Print &out) {
641+
setTarget(out);
625642
}
626643

627644
/// Constructor which automatically calls begin(Print out)!
628-
VolumeStream(Stream &out, bool allowBoost=false) {
629-
begin(out, allowBoost);
645+
VolumeStream(Stream &in) {
646+
setTarget(in);
630647
}
631648

632-
/// Assigns the final output
633-
bool begin(Print &out, bool allowBoost=true){
634-
LOGD(LOG_METHOD);
649+
void setTarget(Print &out){
635650
p_out = &out;
636-
allow_boost = allowBoost;
637-
return true;
638651
}
639-
640-
/// Assigns the final output
641-
bool begin(Stream &in, bool allowBoost=false){
642-
LOGD(LOG_METHOD);
652+
653+
void setTarget(Stream &in){
643654
p_in = ∈
644655
p_out = p_in;
645-
allow_boost = allowBoost;
656+
}
657+
658+
VolumeStreamConfig defaultConfig() {
659+
VolumeStreamConfig c;
660+
return c;
661+
}
662+
663+
bool begin(AudioBaseInfo cfg){
664+
VolumeStreamConfig cfg1;
665+
cfg1.channels = cfg.channels;
666+
cfg1.sample_rate = cfg.sample_rate;
667+
cfg1.bits_per_sample = cfg.bits_per_sample;
668+
return begin(cfg1);
669+
}
670+
671+
void end() {
672+
is_active = false;
673+
}
674+
675+
/// starts the processing
676+
bool begin(VolumeStreamConfig cfg){
677+
LOGD(LOG_METHOD);
678+
info = cfg;
679+
max_value = NumberConverter::maxValue(info.bits_per_sample);
680+
if (info.channels>max_channels){
681+
max_channels = info.channels;
682+
}
683+
// set start volume
684+
setVolume(cfg.volume);
646685
return true;
647686
}
648687

@@ -664,19 +703,18 @@ class VolumeStream : public AudioStreamX {
664703
return 0;
665704
}
666705
size_t result = p_in->readBytes(buffer, length);
667-
if (volume_value != 1.0) applyVolume(buffer, result);
706+
if (is_active) applyVolume(buffer, result);
668707
return result;
669708
}
670709

671-
672710
/// Writes raw PCM audio data, which will be the input for the volume control
673711
virtual size_t write(const uint8_t *buffer, size_t size) override {
674712
LOGD(LOG_METHOD);
675713
if (buffer==nullptr || p_out==nullptr){
676714
LOGE("NPE");
677715
return 0;
678716
}
679-
if (volume_value != 1.0) applyVolume(buffer,size);
717+
if (is_active) applyVolume(buffer,size);
680718
return p_out->write(buffer, size);
681719
}
682720

@@ -691,54 +729,95 @@ class VolumeStream : public AudioStreamX {
691729
}
692730

693731
/// Detines the Audio info - The bits_per_sample are critical to work properly!
694-
void setAudioInfo(AudioBaseInfo info) override {
732+
void setAudioInfo(AudioBaseInfo cfg) override {
695733
LOGD(LOG_METHOD);
696-
this->info = info;
697-
max_value = NumberConverter::maxValue(info.bits_per_sample);
734+
begin(cfg);
698735
}
699736

700-
/// Shortcut method to define the sample size (alternative to setAudioInfo())
701-
void setBitsPerSample(int bits_per_sample){
702-
info.bits_per_sample = bits_per_sample;
703-
max_value = NumberConverter::maxValue(info.bits_per_sample);
704-
}
705-
706-
/// Shortcut method to define the sample size (alternative to setAudioInfo())
707-
void setBytesPerSample(int bytes_per_sample){
708-
info.bits_per_sample = bytes_per_sample * 8;
709-
max_value = NumberConverter::maxValue(info.bits_per_sample);
710-
}
711-
712-
/// Decreases the volume: needs to be in the range of 0 to 1.0
737+
/// Defines the volume for all channels: needs to be in the range of 0 to 1.0
713738
void setVolume(float vol){
714-
if (!allow_boost && vol>1.0) vol = 1.0;
715-
if (vol<0.0) vol = 0.0;
739+
for (int j=0;j<info.channels;j++){
740+
setVolume(vol, j);
741+
}
742+
}
716743

717-
// round to 2 digits
718-
float value = (int)(vol * 100 + .5);
719-
volume_value = (float)value / 100;;
720-
LOGI("setVolume: %f", volume_value);
744+
/// Sets the volume for one channel
745+
void setVolume(float vol, int channel){
746+
if (channel<info.channels){
747+
setup(vol);
748+
float volume_value = volumeValue(vol);
749+
LOGI("setVolume: %f", volume_value);
750+
float factor = volumeControl().getVolumeFactor(volume_value);
751+
factor_for_channel[channel]=factor;
752+
} else {
753+
LOGE("Invalid channel %d - max: %d", channel, info.channels-1);
754+
}
721755
}
722756

723757
/// Provides the current volume setting
724758
float volume() {
725-
return volume_value;
759+
return volume_values[0];
760+
}
761+
762+
/// Provides the current volume setting for the indicated channel
763+
float volume(int channel) {
764+
return channel>=info.channels? 0 : volume_values[channel];
726765
}
727766

728767
protected:
729768
Print *p_out=nullptr;
730769
Stream *p_in=nullptr;
731-
AudioBaseInfo info;
732-
float volume_value=1.0;
770+
VolumeStreamConfig info;
733771
SimulatedAudioPot default_volume;
734772
CachedVolumeControl cached_volume = CachedVolumeControl(default_volume);
735-
bool allow_boost = false; // allows a factor > 1.0
736-
float max_value = NumberConverter::maxValue(info.bits_per_sample); // max value for clippint
773+
float *volume_values = nullptr;
774+
float *factor_for_channel = nullptr;
775+
bool is_active = false;
776+
float max_value = 32767; // max value for clipping
777+
int max_channels=0;
778+
779+
void setup(float vol) {
780+
is_active = vol!=1.0;
781+
if (info.channels>max_channels){
782+
cleanup();
783+
}
784+
if (factor_for_channel==nullptr){
785+
factor_for_channel = new float[info.channels];
786+
}
787+
if (volume_values==nullptr){
788+
volume_values = new float[info.channels];
789+
}
790+
}
791+
792+
void cleanup() {
793+
if (factor_for_channel!=nullptr) {
794+
delete []factor_for_channel;
795+
factor_for_channel = nullptr;
796+
}
797+
if (volume_values!=nullptr) {
798+
delete []volume_values;
799+
volume_values = nullptr;
800+
}
801+
}
802+
803+
float volumeValue(float vol){
804+
if (!info.allow_boost && vol>1.0) vol = 1.0;
805+
if (vol<0.0) vol = 0.0;
806+
807+
// round to 2 digits
808+
float value = (int)(vol * 100 + .5);
809+
float volume_value = (float)value / 100;
810+
return volume_value;
811+
}
737812

738813
VolumeControl &volumeControl(){
739814
return cached_volume;
740815
}
741816

817+
float factorForChannel(int channel){
818+
return factor_for_channel==nullptr ? 1.0 : factor_for_channel[channel];
819+
}
820+
742821
void applyVolume(const uint8_t *buffer, size_t size){
743822
switch(info.bits_per_sample){
744823
case 16:
@@ -756,10 +835,9 @@ class VolumeStream : public AudioStreamX {
756835
}
757836

758837
void applyVolume16(int16_t* data, size_t size){
759-
float factor = volumeControl().getVolumeFactor(volume_value);
760838
for (size_t j=0;j<size;j++){
761-
float result = factor * data[j];
762-
if (allow_boost){
839+
float result = factorForChannel(j%info.channels) * data[j];
840+
if (info.allow_boost){
763841
if (result>max_value) result = max_value;
764842
if (result<-max_value) result = -max_value;
765843
}
@@ -768,10 +846,9 @@ class VolumeStream : public AudioStreamX {
768846
}
769847

770848
void applyVolume24(int24_t* data, size_t size) {
771-
float factor = volumeControl().getVolumeFactor(volume_value);
772849
for (size_t j=0;j<size;j++){
773-
float result = factor * data[j];
774-
if (allow_boost){
850+
float result = factorForChannel(j%info.channels) * data[j];
851+
if (info.allow_boost){
775852
if (result>max_value) result = max_value;
776853
if (result<-max_value) result = -max_value;
777854
}
@@ -781,10 +858,9 @@ class VolumeStream : public AudioStreamX {
781858
}
782859

783860
void applyVolume32(int32_t* data, size_t size) {
784-
float factor = volumeControl().getVolumeFactor(volume_value);
785861
for (size_t j=0;j<size;j++){
786-
float result = factor * data[j];
787-
if (allow_boost){
862+
float result = factorForChannel(j%info.channels) * data[j];
863+
if (info.allow_boost){
788864
if (result>max_value) result = max_value;
789865
if (result<-max_value) result = -max_value;
790866
}

0 commit comments

Comments
 (0)