diff --git a/examples/Boards/Teensy/Audio/1.Volume-Controlled-USB-DAC/1.Volume-Controlled-USB-DAC.ino b/examples/Boards/Teensy/Audio/1.Volume-Controlled-USB-DAC/1.Volume-Controlled-USB-DAC.ino new file mode 100644 index 0000000000..f671eff509 --- /dev/null +++ b/examples/Boards/Teensy/Audio/1.Volume-Controlled-USB-DAC/1.Volume-Controlled-USB-DAC.ino @@ -0,0 +1,61 @@ +/** + * This is an example of the VolumeControl class of the Control Surface library. + * + * @boards Teensy 3.x + * + * Connections + * ----------- + * + * - A0: wiper of a potentiometer to change the output volume + * - 9: BCK (I²S) + * - 11: SCK (I²S) + * - 22: DIN (I²S) + * - 23: LRCK (I²S) + * + * Select a USB audio option from the Tools menu in the IDE. + * + * Behavior + * -------- + * + * Upload the sketch, and select the Control Surface as the audio output of your + * computer. Connect the output of the DAC to a pair of headphones or powered + * speakers, and play some music. + * You can now adjust the volume using the potentiometer. + * + * Mapping + * ------- + * + * None. + * + * Written by + * + */ + +#include // Include the Control Surface library + +AudioInputUSB audioInputUSB; // The audio input from the USB connection +AudioMixer4 mixer_L; // Two mixers for volume control +AudioMixer4 mixer_R; +AudioOutputI2S audioOutputI2S; // The output to the I²S DAC + +AudioConnection patchCord1(audioInputUSB, 0, mixer_L, 0); // Connect the input +AudioConnection patchCord3(audioInputUSB, 1, mixer_R, 0); // to the mixer +AudioConnection patchCord5(mixer_L, 0, audioOutputI2S, 1); // Connect the mixer +AudioConnection patchCord6(mixer_R, 0, audioOutputI2S, 0); // to the output + +// Instantiate a VolumeControl object with a potentiometer on pin A0 and a +// maximum gain of 1.0 +VolumeControl<2> volume = {{&mixer_L, &mixer_R}, A0, 1.0}; + +void setup() { + volume.begin(); + // Add a dead zone if you can't get the volume all the way to zero + // volume.map([](analog_t i) -> analog_t { + // return map(constrain(i, 10, 1013), 10, 1013, 0, 1023); + // }); + AudioMemory(6); +} + +void loop() { + volume.update(); +} diff --git a/examples/examples.h b/examples/examples.h index 0b552a984b..ebf8e39115 100644 --- a/examples/examples.h +++ b/examples/examples.h @@ -454,6 +454,44 @@ * https://github.com/tttapa/Control-Surface */ +/** + * @example "1.Volume-Controlled-USB-DAC.ino" + * + * 1.Volume-Controlled-USB-DAC + * =========================== + * + * This is an example of the VolumeControl class of the Control Surface library. + * + * @boards Teensy 3.x + * + * Connections + * ----------- + * + * - A0: wiper of a potentiometer to change the output volume + * - 9: BCK (I²S) + * - 11: SCK (I²S) + * - 22: DIN (I²S) + * - 23: LRCK (I²S) + * + * Select a USB audio option from the Tools menu in the IDE. + * + * Behavior + * -------- + * + * Upload the sketch, and select the Control Surface as the audio output of your + * computer. Connect the output of the DAC to a pair of headphones or powered + * speakers, and play some music. + * You can now adjust the volume using the potentiometer. + * + * Mapping + * ------- + * + * None. + * + * Written by + * + */ + /** * @example "VU-Meter-Bridge.ino" * diff --git a/src/Audio/VolumeControl.hpp b/src/Audio/VolumeControl.hpp index 84db97c521..dbb2a68c77 100644 --- a/src/Audio/VolumeControl.hpp +++ b/src/Audio/VolumeControl.hpp @@ -29,7 +29,8 @@ class VolumeControl : public Updatable { * object. * @param analogPin * The analog pin with the potentiometer connected. - * @param The maximum gain for the mixers. + * @param maxGain + * The maximum gain for the mixers. */ VolumeControl(const Array &mixers, pin_t analogPin, float maxGain = 1.0) @@ -52,6 +53,19 @@ class VolumeControl : public Updatable { */ void begin() override {} + /** + * @brief Specify a mapping function that is applied to the raw + * analog value before setting the volume. + * + * @param fn + * A function pointer to the mapping function. This function + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map + */ + void map(MappingFunction fn) { filteredAnalog.map(fn); } + private: Array mixers; FilteredAnalog<7> filteredAnalog; diff --git a/src/Def/Def.hpp b/src/Def/Def.hpp index eca754740c..49f1c86a17 100644 --- a/src/Def/Def.hpp +++ b/src/Def/Def.hpp @@ -18,7 +18,7 @@ constexpr pin_t NO_PIN = 1 << (8 * sizeof(pin_t) - 1); /// A function pointer to a mapping function to map analog values. /// @see MIDIFilteredAnalog::map() -using MappingFunction = uint8_t (*)(uint8_t); +using MappingFunction = analog_t (*)(analog_t); /// An easy alias for two-dimensional Arrays. template diff --git a/src/Hardware/FilteredAnalog.hpp b/src/Hardware/FilteredAnalog.hpp index d634d2d9c2..03965f5cc2 100755 --- a/src/Hardware/FilteredAnalog.hpp +++ b/src/Hardware/FilteredAnalog.hpp @@ -35,14 +35,15 @@ class FilteredAnalog { * * @param fn * A function pointer to the mapping function. This function - * should take the filtered value (of `PRECISION` bits wide) as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered value (of 10 bits wide) as a + * parameter, and should return a value of 10 bits. * * @note Applying the mapping function before filtering could result in * the noise being amplified to such an extent that filtering it * afterwards would be ineffective. + * Applying it after hysteresis would result in a lower resolution. * That's why the mapping function is applied after filtering and - * hysteresis. + * before hysteresis. */ void map(MappingFunction fn); @@ -67,13 +68,13 @@ class FilteredAnalog { uint8_t getValue() const; /** - * @brief Get the filtered value of the analog input without the mapping - * function applied. + * @brief Get the filtered value of the analog input any filtering or + * mapping applied. * - * @return The filtered value of the analog input, as a number - * of `PRECISION` bits wide. + * @return The filtered value of the analog input, as a number of 10 bits + * wide. */ - uint8_t getRawValue() const; + analog_t getRawValue() const; private: const pin_t analogPin; @@ -96,23 +97,22 @@ FilteredAnalog::FilteredAnalog(pin_t analogPin) template bool FilteredAnalog::update() { - analog_t input = - ExtIO::analogRead(analogPin); // read the raw analog input value - input = filter.filter(input); // apply a low-pass EMA filter - return hysteresis.update(input); // apply hysteresis + analog_t input = getRawValue(); // read the raw analog input value + input = filter.filter(input); // apply a low-pass EMA filter + if (mapFn) // If a mapping function is specified, + input = mapFn(input); // apply it + return hysteresis.update(input); // apply hysteresis, and return true if + // the value changed since last time } template uint8_t FilteredAnalog::getValue() const { - uint8_t value = getRawValue(); - if (mapFn != nullptr) // if a map function is specified - value = mapFn(value); // apply the map function to the value - return value; + return hysteresis.getValue(); } template -uint8_t FilteredAnalog::getRawValue() const { - return hysteresis.getValue(); +analog_t FilteredAnalog::getRawValue() const { + return ExtIO::analogRead(analogPin); } template diff --git a/src/Helpers/TeensyUSBTypes.hpp b/src/Helpers/TeensyUSBTypes.hpp index 7eb8fe215a..b8b99d1765 100644 --- a/src/Helpers/TeensyUSBTypes.hpp +++ b/src/Helpers/TeensyUSBTypes.hpp @@ -17,7 +17,7 @@ #endif #if defined(USB_MIDI_AUDIO_SERIAL) || defined(USB_MIDI16_AUDIO_SERIAL) || \ - defined(USB_EVERYTHING) + defined(USB_AUDIO) || defined(USB_EVERYTHING) #define TEENSY_AUDIOUSB_ENABLED #endif diff --git a/src/MIDI_Outputs/Abstract/MIDIFilteredAnalog.hpp b/src/MIDI_Outputs/Abstract/MIDIFilteredAnalog.hpp index 0d9d371ee0..ed21cd4e26 100644 --- a/src/MIDI_Outputs/Abstract/MIDIFilteredAnalog.hpp +++ b/src/MIDI_Outputs/Abstract/MIDIFilteredAnalog.hpp @@ -43,8 +43,10 @@ class MIDIFilteredAnalogAddressable : public MIDIOutputElement { * * @param fn * A function pointer to the mapping function. This function - * should take the filtered analog value of `PRECISION` bits as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map */ void map(MappingFunction fn) { filteredAnalog.map(fn); } @@ -107,8 +109,10 @@ class MIDIFilteredAnalog : public MIDIOutputElement { * * @param fn * A function pointer to the mapping function. This function - * should take the filtered analog value of `PRECISION` bits as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map */ void map(MappingFunction fn) { filteredAnalog.map(fn); } diff --git a/src/MIDI_Outputs/Bankable/Abstract/MIDIFilteredAnalog.hpp b/src/MIDI_Outputs/Bankable/Abstract/MIDIFilteredAnalog.hpp index 02533f8bb6..a0a8a2094e 100644 --- a/src/MIDI_Outputs/Bankable/Abstract/MIDIFilteredAnalog.hpp +++ b/src/MIDI_Outputs/Bankable/Abstract/MIDIFilteredAnalog.hpp @@ -51,8 +51,10 @@ class MIDIFilteredAnalogAddressable : public MIDIOutputElement, * * @param fn * A function pointer to the mapping function. This function - * should take the filtered analog value of `PRECISION` bits as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map */ void map(MappingFunction fn) { filteredAnalog.map(fn); } @@ -117,8 +119,10 @@ class MIDIFilteredAnalog : public MIDIOutputElement, public BankableMIDIOutput { * * @param fn * A function pointer to the mapping function. This function - * should take the filtered analog value of `PRECISION` bits as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map */ void map(MappingFunction fn) { filteredAnalog.map(fn); } diff --git a/src/MIDI_Outputs/ManyAddresses/Abstract/MIDIFilteredAnalog.hpp b/src/MIDI_Outputs/ManyAddresses/Abstract/MIDIFilteredAnalog.hpp index dcddaef1e7..411fa70dde 100644 --- a/src/MIDI_Outputs/ManyAddresses/Abstract/MIDIFilteredAnalog.hpp +++ b/src/MIDI_Outputs/ManyAddresses/Abstract/MIDIFilteredAnalog.hpp @@ -47,8 +47,10 @@ class MIDIFilteredAnalogAddressable : public MIDIOutputElement, * * @param fn * A function pointer to the mapping function. This function - * should take the filtered analog value of `PRECISION` bits as a - * parameter, and should return a value of `PRECISION` bits. + * should take the filtered analog value of 10 bits as a + * parameter, and should return a value of 10 bits. + * + * @see FilteredAnalog::map */ void map(MappingFunction fn) { filteredAnalog.map(fn); }