/* AudioOutputMixer Simple mixer which can combine multiple inputs to a single output stream Copyright (C) 2018 Earle F. Philhower, III This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "AudioOutputMixer.h" AudioOutputMixerStub::AudioOutputMixerStub(AudioOutputMixer *sink, int id) : AudioOutput() { this->id = id; this->parent = sink; SetGain(1.0); } AudioOutputMixerStub::~AudioOutputMixerStub() { parent->RemoveInput(id); } bool AudioOutputMixerStub::SetRate(int hz) { return parent->SetRate(hz, id); } bool AudioOutputMixerStub::SetBitsPerSample(int bits) { return parent->SetBitsPerSample(bits, id); } bool AudioOutputMixerStub::SetChannels(int channels) { return parent->SetChannels(channels, id); } bool AudioOutputMixerStub::begin() { return parent->begin(id); } bool AudioOutputMixerStub::ConsumeSample(int16_t sample[2]) { int16_t amp[2]; amp[LEFTCHANNEL] = Amplify(sample[LEFTCHANNEL]); amp[RIGHTCHANNEL] = Amplify(sample[RIGHTCHANNEL]); return parent->ConsumeSample(amp, id); } bool AudioOutputMixerStub::stop() { return parent->stop(id); } AudioOutputMixer::AudioOutputMixer(int buffSizeSamples, AudioOutput *dest) : AudioOutput() { buffSize = buffSizeSamples; leftAccum = (int32_t*)calloc(sizeof(int32_t), buffSize); rightAccum = (int32_t*)calloc(sizeof(int32_t), buffSize); for (int i=0; iSetRate(hz); } bool AudioOutputMixer::SetBitsPerSample(int bits, int id) { (void) id; return sink->SetBitsPerSample(bits); } bool AudioOutputMixer::SetChannels(int channels, int id) { (void) id; return sink->SetChannels(channels); } bool AudioOutputMixer::begin(int id) { stubRunning[id] = true; if (!sinkStarted) { sinkStarted = true; return sink->begin(); } else { return true; } } AudioOutputMixerStub *AudioOutputMixer::NewInput() { for (int i=0; i 32767) { s[LEFTCHANNEL] = 32767; } else if (leftAccum[readPtr] < -32767) { s[LEFTCHANNEL] = -32767; } else { s[LEFTCHANNEL] = leftAccum[readPtr]; } if (rightAccum[readPtr] > 32767) { s[RIGHTCHANNEL] = 32767; } else if (rightAccum[readPtr] < -32767) { s[RIGHTCHANNEL] = -32767; } else { s[RIGHTCHANNEL] = rightAccum[readPtr]; } // s[LEFTCHANNEL] = Amplify(s[LEFTCHANNEL]); // s[RIGHTCHANNEL] = Amplify(s[RIGHTCHANNEL]); if (!sink->ConsumeSample(s)) { break; // Can't stuff any more in I2S... } // Clear the accums and advance the pointer to next potential sample leftAccum[readPtr] = 0; rightAccum[readPtr] = 0; readPtr = (readPtr + 1) % buffSize; } } while (avail); return true; } bool AudioOutputMixer::ConsumeSample(int16_t sample[2], int id) { loop(); // Send any pre-existing, completed I2S data we can fit // Now, do we have space for a new sample? int nextWritePtr = (writePtr[id] + 1) % buffSize; if (nextWritePtr == readPtr) { return false; } leftAccum[writePtr[id]] += sample[LEFTCHANNEL]; rightAccum[writePtr[id]] += sample[RIGHTCHANNEL]; writePtr[id] = nextWritePtr; return true; } bool AudioOutputMixer::stop(int id) { stubRunning[id] = false; return true; }