sensorium / Mozzi

sound synthesis library for Arduino
https://sensorium.github.io/Mozzi/
GNU Lesser General Public License v2.1
1.07k stars 186 forks source link

StereoOutput Constructor to send 2xStereo/4xMono #267

Open justinjools22 opened 1 month ago

justinjools22 commented 1 month ago

Hi this isn't an issue, rather sharing code I've adapted.

I have two DACs and wanted to send 4 separate mono waveforms. The constructor only accepts two arguments L/R so I added an additional 2. This would useful to add to the library for anyone else wanting to do this, could be called StereoOutput2.

Example usage:

#define SS_PIN PB0
#define SS_PIN2 PB1

DAC_MCP49xx dac(DAC_MCP49xx::MCP4922, SS_PIN); 
DAC_MCP49xx dac2(DAC_MCP49xx::MCP4922, SS_PIN2); 

void audioOutput(const AudioOutput f)  // f is a structure containing both channels
{
  // signal is passed as 16 bit. This DAC expects 12 bits so shift back four bits, and add a bias of 2^(12-1)=2048
  uint16_t outL = (f.l() >> 4) + 2048;
  uint16_t outR = (f.r() >> 4) + 2048;
  uint16_t outL2 = (f.l2() >> 4) + 2048;
  uint16_t outR2 = (f.r2() >> 4) + 2048;

  dac.outputA(outL);
  dac.outputB(outR);
  dac2.outputA(outL2);
  dac2.outputB(outR2);
}

AudioOutput updateAudio() {  

  return StereoOutput::fromNBit(12, aSaw1.next(), aCos1.next(), aTri1.next(), aSin1.next()); 

}

// StereoOutput constructor in AudioOutput.h // 2xstereo/4xmono output: 4 arguments: l, r, l2, r2

/** This struct encapsulates one frame of mono audio output. Internally, it really just boils down to two int values, but the struct provides
 *  useful API an top of that. For more detail see @ref MonoOutput . */
struct StereoOutput {
  /** Construct an audio frame from raw values (zero-centered) */
//  StereoOutput(AudioOutputStorage_t l, AudioOutputStorage_t r) : _l(l), _r(r) {};
  StereoOutput(AudioOutputStorage_t l, AudioOutputStorage_t r, AudioOutputStorage_t l2, AudioOutputStorage_t r2) : _l(l), _r(r), _l2(l2), _r2(r2) {};  

  /** Default constructor. Does not initialize the sample! */
  StereoOutput() {};
#if !MOZZI_IS(MOZZI_AUDIO_CHANNELS, MOZZI_STEREO)
  /** Conversion to int operator: If used in a mono config, returns only the left channel (and gives a compile time warning). 
      This _could_ be turned into an operator for implicit conversion in this case. For now we chose to apply conversion on demand, only, as most of the time
      using StereoOutput in a mono config, is not intended. */
  inline AudioOutput portable() const __attribute__((deprecated("Sketch generates stereo output, but Mozzi is configured for mono. Check MOZZI_AUDIO_CHANNELS setting."))) { return _l; };
#  if GITHUB_RUNNER_ACCEPT_STEREO_IN_MONO
  inline operator AudioOutput() const __attribute__((deprecated("Stereo converted to mono on github runner"))) { return _l; };
#  endif
#endif
  AudioOutputStorage_t l() const { return _l; };
  AudioOutputStorage_t r() const { return _r; };
  AudioOutputStorage_t l2() const { return _l2; };
  AudioOutputStorage_t r2() const { return _r2; };
  /** See @ref MonoOutput::clip(). Clips both channels. */
  StereoOutput& clip() { _l = CLIP_AUDIO(_l); _r = CLIP_AUDIO(_r); return *this; };

  /** See @ref MonoOutput::fromNBit(), stereo variant */
template<typename T> static inline StereoOutput fromNBit(uint8_t bits, T l, T r, T l2, T r2) { return StereoOutput(SCALE_AUDIO(l, bits), SCALE_AUDIO(r, bits), SCALE_AUDIO(l2, bits), SCALE_AUDIO(r2, bits)); }

private:
  AudioOutputStorage_t _l;
  AudioOutputStorage_t _r;
  AudioOutputStorage_t _l2;
  AudioOutputStorage_t _r2;

};

#if MOZZI_AUDIO_CHANNELS > 1
StereoOutput MonoOutput::portable() const { return StereoOutput(_l, _l, _l, _l); };
#endif
tomcombriat commented 1 month ago

Nice! I remember that during the last rethinking of Mozzi's architecture, it was planned to make things easy for having more than two channels. It might even not be very hard to have an arbitrary number of them. A bit of time might be needed to make that cleanly but as it seems to be of use! Another thing to add to the to-do!

justinjools22 commented 1 month ago

Yes, I thought about some way to pass an argument for number of channels required but not needed for my use. For some context I am using this for a 4 channel mono digital oscillator project. I wanted to praise the ease of use and readability of Mozzi. Even with my limited coding skills I was able to adapt this. Thanks.