andrewm@66: /** andrewm@66: * @file andrewm@66: * @brief Wiring-inspired utility functions and macros andrewm@0: * andrewm@66: * Macros and functions for I/O and data processing taking after the Wiring andrewm@66: * (Arduino) language. This code began as part of the Hackable Instruments andrewm@66: * project (EPSRC) at Queen Mary University of London, 2013-14. andrewm@66: * andrewm@66: * (c) 2014-15 Andrew McPherson, Victor Zappi and Giulio Moro, andrewm@66: * Queen Mary University of London andrewm@0: */ andrewm@0: andrewm@0: #ifndef UTILITIES_H_ andrewm@0: #define UTILITIES_H_ andrewm@0: giuliomoro@301: #include "Bela.h" andrewm@45: andrewm@95: /** andrewm@95: * \defgroup iofunctions I/O functions and constants andrewm@95: * andrewm@95: * These functions and macros are used for audio, analog and digital I/O. All the giuliomoro@301: * I/O functions require the BelaContext data structure from render() to be passed andrewm@95: * in. This means that these functions are, by design, \b only usable from within andrewm@95: * the rendering thread. andrewm@95: * andrewm@95: * The naming conventions are loosely derived from the Arduino environment, and the andrewm@95: * syntax is similar. Unlike Arduino, the I/O functions require the frame number at which andrewm@95: * the read or write should take place, since all I/O happens synchronously with the andrewm@95: * audio clock. andrewm@95: * andrewm@95: * @{ andrewm@95: */ andrewm@95: giuliomoro@187: #ifndef INPUT giuliomoro@187: #define INPUT 0x0 giuliomoro@187: #endif /* INPUT */ andrewm@72: giuliomoro@187: #ifndef OUTPUT giuliomoro@187: #define OUTPUT 0x1 giuliomoro@187: #endif /* OUTPUT */ andrewm@72: andrewm@95: /** @} */ andrewm@95: andrewm@95: /** andrewm@95: * \ingroup wiring andrewm@95: * andrewm@95: * @{ andrewm@95: */ andrewm@95: andrewm@66: /// Set the given bit in \c word to 1. andrewm@45: #define setBit(word,bit) ((word) | (1 << (bit))) andrewm@66: andrewm@66: /// Clear the given bit in \c word to 0. andrewm@45: #define clearBit(word,bit) ((word) &~ (1 << (bit))) andrewm@66: andrewm@66: /// Check if the given bit in \c word is 1 (returns nonzero) or 0 (returns zero). andrewm@45: #define getBit(word,bit) (((word) >> (bit)) & 1) andrewm@66: andrewm@66: /// Set/clear the given bit in \c word to \c value. andrewm@45: #define changeBit(word,bit,value) ((clearBit((word),(bit))) | ((value) << (bit))) andrewm@45: andrewm@95: /** @} */ andrewm@95: andrewm@95: /** andrewm@95: * \ingroup iofunctions andrewm@95: * andrewm@95: * @{ andrewm@95: */ andrewm@95: andrewm@45: #if 1 andrewm@56: // Note: pinMode(), analogWrite() and digitalWrite() should be able to be called from setup() andrewm@56: // Likewise, thread launch should be able to be called from setup() andrewm@45: // Also, make volume change functions callable from render() thread -- as an aux task? andrewm@45: andrewm@68: /** giuliomoro@179: * \brief Read an audio input, specifying the frame number (when to read) and the channel. giuliomoro@179: * giuliomoro@179: * This function returns the value of an audio input, at the time indicated by \c frame. giuliomoro@179: * The returned value ranges from -1 to 1. giuliomoro@179: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). giuliomoro@179: * \param frame Which frame (i.e. what time) to read the audio input. Valid values range giuliomoro@179: * from 0 to (context->audioFrames - 1). giuliomoro@179: * \param channel Which audio input to read. Valid values are between 0 and giuliomoro@179: * (context->audioChannels - 1), typically 0 to 1 by default. giuliomoro@179: * \return Value of the analog input, range to 1. giuliomoro@179: */ giuliomoro@301: static inline float audioReadFrame(BelaContext *context, int frame, int channel); giuliomoro@179: giuliomoro@179: /** giuliomoro@179: * \brief Write an audio output, specifying the frame number (when to write) and the channel. giuliomoro@179: * giuliomoro@179: * This function sets the value of an audio output, at the time indicated by \c frame. Valid giuliomoro@179: * values are between -1 and 1. giuliomoro@179: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). giuliomoro@179: * \param frame Which frame (i.e. what time) to write the audio output. Valid values range giuliomoro@179: * from 0 to (context->audioFrames - 1). giuliomoro@179: * \param channel Which analog output to write. Valid values are between 0 and giuliomoro@179: * (context->audioChannels - 1), typically 0 to 1 by default. giuliomoro@179: * \param value Value to write to the output, range -1 to 1. giuliomoro@179: */ giuliomoro@301: static inline void audioWriteFrame(BelaContext *context, int frame, int channel, float value); giuliomoro@179: giuliomoro@179: /** andrewm@68: * \brief Read an analog input, specifying the frame number (when to read) and the channel. andrewm@68: * andrewm@68: * This function returns the value of an analog input, at the time indicated by \c frame. andrewm@68: * The returned value ranges from 0 to 1, corresponding to a voltage range of 0 to 4.096V. andrewm@68: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@68: * \param frame Which frame (i.e. what time) to read the analog input. Valid values range andrewm@68: * from 0 to (context->analogFrames - 1). andrewm@68: * \param channel Which analog input to read. Valid values are between 0 and andrewm@68: * (context->analogChannels - 1), typically 0 to 7 by default. andrewm@68: * \return Value of the analog input, range 0 to 1. andrewm@68: */ giuliomoro@301: static inline float analogReadFrame(BelaContext *context, int frame, int channel); andrewm@68: andrewm@68: /** andrewm@68: * \brief Write an analog output, specifying the frame number (when to write) and the channel. andrewm@68: * andrewm@68: * This function sets the value of an analog output, at the time indicated by \c frame. Valid andrewm@68: * values are between 0 and 1, corresponding to the range 0 to 5V. andrewm@68: * andrewm@68: * The value written will persist for all future frames if BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST andrewm@68: * is set in context->flags. This is the default behaviour. andrewm@68: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@68: * \param frame Which frame (i.e. what time) to write the analog output. Valid values range andrewm@68: * from 0 to (context->analogFrames - 1). andrewm@68: * \param channel Which analog output to write. Valid values are between 0 and andrewm@68: * (context->analogChannels - 1), typically 0 to 7 by default. andrewm@68: * \param value Value to write to the output, range 0 to 1. andrewm@68: */ giuliomoro@301: static inline void analogWriteFrame(BelaContext *context, int frame, int channel, float value); andrewm@68: andrewm@68: /** andrewm@68: * \brief Write an analog output, specifying the frame number (when to write) and the channel. andrewm@68: * andrewm@68: * This function sets the value of an analog output, at the time indicated by \c frame. Valid andrewm@68: * values are between 0 and 1, corresponding to the range 0 to 5V. andrewm@68: * andrewm@68: * Unlike analogWriteFrame(), the value written will affect \b only the frame specified, with andrewm@72: * future values unchanged. This is faster than analogWriteFrame() so is better suited andrewm@68: * to applications where every frame will be written to a different value. If andrewm@68: * BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST is not set within context->flags, then andrewm@68: * analogWriteFrameOnce() and analogWriteFrame() are equivalent. andrewm@68: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@68: * \param frame Which frame (i.e. what time) to write the analog output. Valid values range andrewm@68: * from 0 to (context->analogFrames - 1). andrewm@68: * \param channel Which analog output to write. Valid values are between 0 and andrewm@68: * (context->analogChannels - 1), typically 0 to 7 by default. andrewm@68: * \param value Value to write to the output, range 0 to 1. andrewm@68: */ giuliomoro@301: static inline void analogWriteFrameOnce(BelaContext *context, int frame, int channel, float value); andrewm@45: andrewm@72: /** andrewm@72: * \brief Read a digital input, specifying the frame number (when to read) and the pin. andrewm@72: * andrewm@72: * This function returns the value of a digital input, at the time indicated by \c frame. andrewm@72: * The value is 0 if the pin is low, and nonzero if the pin is high (3.3V). andrewm@72: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@72: * \param frame Which frame (i.e. what time) to read the digital input. Valid values range andrewm@72: * from 0 to (context->digitalFrames - 1). andrewm@72: * \param channel Which digital pin to read. 16 pins across the P8 and P9 headers of the andrewm@72: * BeagleBone Black are available. See the constants P8_xx and P9_xx defined in andrewm@72: * digital_gpio_mapping.h. andrewm@72: * \return Value of the digital input. andrewm@72: */ giuliomoro@301: static inline int digitalReadFrame(BelaContext *context, int frame, int channel); andrewm@72: andrewm@72: /** andrewm@72: * \brief Write a digital output, specifying the frame number (when to write) and the pin. andrewm@72: * andrewm@72: * This function sets the value of a digital output, at the time indicated by \c frame. andrewm@72: * A value of 0 sets the pin low; any other value sets the pin high (3.3V). andrewm@72: * andrewm@72: * The value written will persist for all future frames. andrewm@72: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@72: * \param frame Which frame (i.e. what time) to write the digital output. Valid values range andrewm@72: * from 0 to (context->digitalFrames - 1). andrewm@72: * \param channel Which digital output to write. 16 pins across the P8 and P9 headers of the andrewm@72: * BeagleBone Black are available. See the constants P8_xx and P9_xx defined in andrewm@72: * digital_gpio_mapping.h. andrewm@72: * \param value Value to write to the output. andrewm@72: */ giuliomoro@301: static inline void digitalWriteFrame(BelaContext *context, int frame, int channel, int value); andrewm@72: andrewm@72: /** andrewm@72: * \brief Write a digital output, specifying the frame number (when to write) and the pin. andrewm@72: * andrewm@72: * This function sets the value of a digital output, at the time indicated by \c frame. andrewm@72: * A value of 0 sets the pin low; any other value sets the pin high (3.3V). andrewm@72: * andrewm@72: * Unlike digitalWriteFrame(), the value written will affect \b only the frame specified, with andrewm@72: * future values unchanged. This is faster than digitalWriteFrame() so is better suited andrewm@72: * to applications where every frame will be written to a different value. andrewm@72: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@72: * \param frame Which frame (i.e. what time) to write the digital output. Valid values range andrewm@72: * from 0 to (context->digitalFrames - 1). andrewm@72: * \param channel Which digital output to write. 16 pins across the P8 and P9 headers of the andrewm@72: * BeagleBone Black are available. See the constants P8_xx and P9_xx defined in andrewm@72: * digital_gpio_mapping.h. andrewm@72: * \param value Value to write to the output. andrewm@72: */ giuliomoro@301: static inline void digitalWriteFrameOnce(BelaContext *context, int frame, int channel, int value); andrewm@45: andrewm@72: /** andrewm@72: * \brief Set the direction of a digital pin to input or output. andrewm@72: * andrewm@72: * This function sets the direction of a digital pin, at the time indicated by \c frame. andrewm@72: * Valid values are \c INPUT and \c OUTPUT. All pins begin as inputs by default. andrewm@72: * andrewm@72: * The value written will persist for all future frames. andrewm@72: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@72: * \param frame Which frame (i.e. what time) to set the pin direction. Valid values range andrewm@72: * from 0 to (context->digitalFrames - 1). andrewm@72: * \param channel Which digital output to write. 16 pins across the P8 and P9 headers of the andrewm@72: * BeagleBone Black are available. See the constants P8_xx and P9_xx defined in andrewm@72: * digital_gpio_mapping.h. andrewm@72: * \param value Direction of the pin (\c INPUT or \c OUTPUT). andrewm@72: */ giuliomoro@301: static inline void pinModeFrame(BelaContext *context, int frame, int channel, int mode); andrewm@72: andrewm@72: /** andrewm@72: * \brief Set the direction of a digital pin to input or output. andrewm@72: * andrewm@72: * This function sets the direction of a digital pin, at the time indicated by \c frame. andrewm@72: * Valid values are \c INPUT and \c OUTPUT. All pins begin as inputs by default. andrewm@72: * andrewm@72: * The value written will affect only the specified frame. andrewm@72: * giuliomoro@301: * \param context The I/O data structure which is passed by Bela to render(). andrewm@72: * \param frame Which frame (i.e. what time) to set the pin direction. Valid values range andrewm@72: * from 0 to (context->digitalFrames - 1). andrewm@72: * \param channel Which digital output to write. 16 pins across the P8 and P9 headers of the andrewm@72: * BeagleBone Black are available. See the constants P8_xx and P9_xx defined in andrewm@72: * digital_gpio_mapping.h. andrewm@72: * \param value Direction of the pin (\c INPUT or \c OUTPUT). andrewm@72: */ giuliomoro@301: static inline void pinModeFrameOnce(BelaContext *context, int frame, int channel, int mode); andrewm@45: andrewm@95: /** @} */ andrewm@95: andrewm@45: #else andrewm@13: giuliomoro@19: // Macros for accessing the analog values: usable _only_ within render() andrewm@5: giuliomoro@19: // Read an Analog input from input pin p at frame f giuliomoro@23: #define analogRead(p, f) (analogIn[(f)*gNumAnalogChannels + (p)]) giuliomoro@19: // Write an Analog output frame at output pin p, frame f, to value v giuliomoro@23: #define analogWriteFrame(p, f, v) (analogOut[(f)*gNumAnalogChannels + (p)] = (v)) giuliomoro@23: #define analogWrite(pin, frame, value) \ giuliomoro@18: (({do {\ giuliomoro@19: for (int _privateI=(frame); _privateIaudioIn[frame * context->audioChannels + channel]; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // audioWriteFrame() giuliomoro@187: // giuliomoro@187: // Sets a given audio output channel to a value for the current frame giuliomoro@301: static inline void audioWriteFrame(BelaContext *context, int frame, int channel, float value) { giuliomoro@187: context->audioOut[frame * context->audioChannels + channel] = value; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // analogReadFrame() giuliomoro@187: // giuliomoro@187: // Returns the value of the given analog input at the given frame number. giuliomoro@301: static inline float analogReadFrame(BelaContext *context, int frame, int channel) { giuliomoro@187: return context->analogIn[frame * context->analogChannels + channel]; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // analogWriteFrame() giuliomoro@187: // giuliomoro@187: // Sets a given analog output channel to a value for the current frame and, if persistent outputs are giuliomoro@187: // enabled, for all subsequent frames giuliomoro@301: static inline void analogWriteFrame(BelaContext *context, int frame, int channel, float value) { giuliomoro@187: if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { giuliomoro@187: for(unsigned int f = frame; f < context->analogFrames; f++) giuliomoro@187: context->analogOut[frame * context->analogChannels + channel] = value; giuliomoro@187: } giuliomoro@187: else giuliomoro@187: context->analogOut[frame * context->analogChannels + channel] = value; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // analogWriteFrameOnce() giuliomoro@187: // giuliomoro@187: // Sets a given channel to a value for only the current frame giuliomoro@301: static inline void analogWriteFrameOnce(BelaContext *context, int frame, int channel, float value) { giuliomoro@187: context->analogOut[frame * context->analogChannels + channel] = value; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // digitalReadFrame() giuliomoro@187: // giuliomoro@187: // Returns the value of a given digital input at the given frame number giuliomoro@301: static inline int digitalReadFrame(BelaContext *context, int frame, int channel) { giuliomoro@187: return getBit(context->digital[frame], channel + 16); giuliomoro@187: } giuliomoro@187: giuliomoro@187: // digitalWriteFrame() giuliomoro@187: // giuliomoro@187: // Sets a given digital output channel to a value for the current frame and all subsequent frames giuliomoro@301: static inline void digitalWriteFrame(BelaContext *context, int frame, int channel, int value) { giuliomoro@187: for(unsigned int f = frame; f < context->digitalFrames; f++) { giuliomoro@187: if(value) giuliomoro@187: context->digital[f] |= 1 << (channel + 16); giuliomoro@187: else giuliomoro@187: context->digital[f] &= ~(1 << (channel + 16)); giuliomoro@187: } giuliomoro@187: } giuliomoro@187: giuliomoro@187: // digitalWriteFrameOnce() giuliomoro@187: // giuliomoro@187: // Sets a given digital output channel to a value for the current frame only giuliomoro@301: static inline void digitalWriteFrameOnce(BelaContext *context, int frame, int channel, int value) { giuliomoro@187: if(value) giuliomoro@187: context->digital[frame] |= 1 << (channel + 16); giuliomoro@187: else giuliomoro@187: context->digital[frame] &= ~(1 << (channel + 16)); giuliomoro@187: } giuliomoro@187: giuliomoro@187: // pinModeFrame() giuliomoro@187: // giuliomoro@187: // Sets the direction of a digital pin for the current frame and all subsequent frames giuliomoro@301: static inline void pinModeFrame(BelaContext *context, int frame, int channel, int mode) { giuliomoro@187: for(unsigned int f = frame; f < context->digitalFrames; f++) { giuliomoro@187: if(mode == INPUT) giuliomoro@187: context->digital[f] |= (1 << channel); giuliomoro@187: else giuliomoro@187: context->digital[f] &= ~(1 << channel); giuliomoro@187: } giuliomoro@187: } giuliomoro@187: giuliomoro@187: // pinModeFrameOnce() giuliomoro@187: // giuliomoro@187: // Sets the direction of a digital pin for the current frame only giuliomoro@301: static inline void pinModeFrameOnce(BelaContext *context, int frame, int channel, int mode) { giuliomoro@187: if(mode == INPUT) giuliomoro@187: context->digital[frame] |= (1 << channel); giuliomoro@187: else giuliomoro@187: context->digital[frame] &= ~(1 << channel); giuliomoro@187: } giuliomoro@187: giuliomoro@187: giuliomoro@187: giuliomoro@187: // map() giuliomoro@187: // giuliomoro@187: // Scale an input value from one range to another. Works like its Wiring language equivalent. giuliomoro@187: // x is the value to scale; in_min and in_max are the input range; out_min and out_max giuliomoro@187: // are the output range. giuliomoro@187: giuliomoro@187: static inline float map(float x, float in_min, float in_max, float out_min, float out_max) giuliomoro@187: { giuliomoro@187: return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; giuliomoro@187: } giuliomoro@187: giuliomoro@187: // constrain() giuliomoro@187: // giuliomoro@187: // Clips an input value to be between two end points giuliomoro@187: // x is the value to constrain; min_val and max_val are the range giuliomoro@187: giuliomoro@187: static inline float constrain(float x, float min_val, float max_val) giuliomoro@187: { giuliomoro@187: if(x < min_val) return min_val; giuliomoro@187: if(x > max_val) return max_val; giuliomoro@187: return x; giuliomoro@187: } andrewm@95: andrewm@0: #endif /* UTILITIES_H_ */