andrewm@46: /* andrewm@46: * RTAudio.h andrewm@46: * andrewm@46: * Central control code for hard real-time audio on BeagleBone Black andrewm@46: * using PRU and Xenomai Linux extensions. This code began as part andrewm@46: * of the Hackable Instruments project (EPSRC) at Queen Mary University andrewm@46: * of London, 2013-14. andrewm@46: * andrewm@46: * (c) 2014 Victor Zappi and Andrew McPherson andrewm@46: * Queen Mary University of London andrewm@46: */ andrewm@46: andrewm@46: andrewm@46: #ifndef BEAGLERT_H_ andrewm@46: #define BEAGLERT_H_ andrewm@46: andrewm@46: #include andrewm@46: #include "digital_gpio_mapping.h" andrewm@46: andrewm@46: // Useful constants andrewm@46: #define DBOX_CAPE // New custom cape andrewm@46: andrewm@46: #ifdef DBOX_CAPE andrewm@46: #define CODEC_I2C_ADDRESS 0x18 // Address of TLV320AIC3104 codec andrewm@46: #else andrewm@46: #define CODEC_I2C_ADDRESS 0x1B // Address of TLV320AIC3106 codec andrewm@46: #endif andrewm@46: andrewm@46: // Default volume levels andrewm@46: #define DEFAULT_DAC_LEVEL 0.0 andrewm@46: #define DEFAULT_ADC_LEVEL -6.0 andrewm@46: #define DEFAULT_HP_LEVEL -6.0 andrewm@46: andrewm@46: #define MAX_PRU_FILENAME_LENGTH 256 andrewm@46: #define MAX_SERVERNAME_LENGTH 256 andrewm@46: andrewm@46: // Priority at which BeagleRT audio code runs andrewm@46: // Higher numbers preempt the audio; lower numbers are preempted by it andrewm@46: #define BEAGLERT_AUDIO_PRIORITY 95 andrewm@46: andrewm@46: // Flags for BeagleRTContext data structure andrewm@46: #define BEAGLERT_FLAG_INTERLEAVED (1 << 0) // Set if buffers are interleaved andrewm@46: #define BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST (1 << 1) // Set if analog/digital outputs persist for future buffers andrewm@46: andrewm@46: // Mappings from pin numbers on PCB to actual DAC channels andrewm@46: // This gives the DAC and ADC connectors the same effective pinout andrewm@46: andrewm@46: #define DAC_PIN0 6 andrewm@46: #define DAC_PIN1 4 andrewm@46: #define DAC_PIN2 2 andrewm@46: #define DAC_PIN3 0 andrewm@46: #define DAC_PIN4 1 andrewm@46: #define DAC_PIN5 3 andrewm@46: #define DAC_PIN6 5 andrewm@46: #define DAC_PIN7 7 andrewm@46: andrewm@46: #define ADC_PIN0 0 andrewm@46: #define ADC_PIN1 1 andrewm@46: #define ADC_PIN2 2 andrewm@46: #define ADC_PIN3 3 andrewm@46: #define ADC_PIN4 4 andrewm@46: #define ADC_PIN5 5 andrewm@46: #define ADC_PIN6 6 andrewm@46: #define ADC_PIN7 7 andrewm@46: andrewm@46: // Structure which contains initialisation parameters for the andrewm@46: // real-time audio system andrewm@46: typedef struct { andrewm@46: // These items might be adjusted by the user: andrewm@46: int periodSize; // Number of (analog) frames per period; audio is twice this andrewm@46: int useAnalog; // Whether to use the analog andrewm@46: int useDigital; // Whether to use the 16 programmable GPIOs andrewm@46: int numAnalogChannels; // How many channels for the ADC and DAC andrewm@46: int numDigitalChannels; // How many channels for the GPIOs andrewm@46: andrewm@46: int beginMuted; // Whether to begin with the speakers muted andrewm@46: float dacLevel; // Level for the audio DAC output andrewm@46: float adcLevel; // Level for the audio ADC input andrewm@46: float headphoneLevel; // Level for the headphone output andrewm@46: andrewm@46: char pruFilename[MAX_PRU_FILENAME_LENGTH]; // The external .bin file to load. If empty will use PRU code from pru_rtaudio_bin.h andrewm@46: int verbose; // Whether to use verbose logging andrewm@46: andrewm@46: // These items are application-dependent but should probably be andrewm@46: // determined by the programmer rather than the user andrewm@46: int interleave; // Whether audio/analog data should be interleaved andrewm@46: int analogOutputsPersist; // Whether analog outputs should persist to future frames andrewm@46: // n.b. digital pins always persist, audio never does andrewm@46: andrewm@46: // These items are hardware-dependent and should only be changed andrewm@46: // to run on different hardware andrewm@46: int codecI2CAddress; // Where the codec can be found on the I2C bus andrewm@46: int ampMutePin; // Pin where amplifier mute can be found andrewm@46: int receivePort; // Port where the UDP server will listen andrewm@46: int transmitPort; // Port where the UDP client will transmit andrewm@46: char serverName[MAX_SERVERNAME_LENGTH]; andrewm@46: } BeagleRTInitSettings; andrewm@46: andrewm@46: // BeagleRTContext data structure andrewm@46: // Holds information passed to the render() function and related calls andrewm@46: // Contains the current audio and sensor settings and pointers to the data buffers andrewm@46: andrewm@46: typedef struct { andrewm@46: float *audioIn; andrewm@46: float *audioOut; andrewm@46: float *analogIn; andrewm@46: float *analogOut; andrewm@46: uint32_t *digital; andrewm@46: andrewm@46: uint32_t audioFrames; andrewm@46: uint32_t audioChannels; andrewm@46: float audioSampleRate; andrewm@46: andrewm@46: uint32_t analogFrames; andrewm@46: uint32_t analogChannels; andrewm@46: float analogSampleRate; andrewm@46: andrewm@46: uint32_t digitalFrames; andrewm@46: uint32_t digitalChannels; andrewm@46: float digitalSampleRate; andrewm@46: andrewm@46: uint64_t audioSampleCount; andrewm@46: uint32_t flags; andrewm@46: } BeagleRTContext; andrewm@46: andrewm@46: enum { andrewm@46: kAmplifierMutePin = 61 // P8-26 controls amplifier mute andrewm@46: }; andrewm@46: andrewm@46: typedef void* AuxiliaryTask; // Opaque data type to keep track of aux tasks andrewm@46: andrewm@46: // Flag that indicates when the audio will stop; can be read or andrewm@46: // set by other components which should end at the same time as the audio andrewm@46: extern bool gShouldStop; andrewm@46: andrewm@46: // *** User-defined render functions *** andrewm@46: andrewm@46: /** andrewm@46: * \brief User-defined initialisation function which runs before audio rendering begins. andrewm@46: * andrewm@46: * This function runs once at the beginning of the program, after most of the system andrewm@46: * initialisation has begun but before audio rendering starts. Use it to prepare any andrewm@46: * memory or resources that will be needed in render(). andrewm@46: * andrewm@46: * \param context Data structure holding information on sample rates, numbers of channels, andrewm@46: * frame sizes and other state. Note: the buffers for audio, analog and digital data will andrewm@46: * \b not yet be available to use. Do not attempt to read or write audio or sensor data andrewm@46: * in initialise_render(). andrewm@46: * \param userData An opaque pointer to an optional user-defined data structure. Whatever andrewm@46: * is passed as the second argument to BeagleRT_initAudio() will appear here. andrewm@46: * andrewm@46: * \return true on success, or false if an error occurred. If no initialisation is andrewm@46: * required, initialise_render() should return true. andrewm@46: */ andrewm@46: bool initialise_render(BeagleRTContext *context, void *userData); andrewm@46: andrewm@46: /** andrewm@46: * \brief User-defined callback function to process audio and sensor data. andrewm@46: * andrewm@46: * This function is called regularly by the system every time there is a new block of andrewm@46: * audio and/or sensor data to process. Your code should process the requested samples andrewm@46: * of data, store the results within \c context, and return. andrewm@46: * andrewm@46: * \param context Data structure holding buffers for audio, analog and digital data. The andrewm@46: * structure also holds information on numbers of channels, frame sizes and sample rates, andrewm@46: * which are guaranteed to remain the same throughout the program and to match what was andrewm@46: * passed to initialise_render(). andrewm@46: * \param userData An opaque pointer to an optional user-defined data structure. Will andrewm@46: * be the same as the \c userData parameter passed to initialise_render(). andrewm@46: */ andrewm@46: void render(BeagleRTContext *context, void *userData); andrewm@46: andrewm@46: /** andrewm@46: * \brief User-defined cleanup function which runs when the program finishes. andrewm@46: * andrewm@46: * This function is called by the system once after audio rendering has finished, before the andrewm@46: * program quits. Use it to release any memory allocated in initialise_render() and to perform andrewm@46: * any other required cleanup. If no initialisation is performed in initialise_render(), then andrewm@46: * this function will usually be empty. andrewm@46: * andrewm@46: * \param context Data structure holding information on sample rates, numbers of channels, andrewm@46: * frame sizes and other state. Note: the buffers for audio, analog and digital data will andrewm@46: * no longer be available to use. Do not attempt to read or write audio or sensor data andrewm@46: * in cleanup_render(). andrewm@46: * \param userData An opaque pointer to an optional user-defined data structure. Will andrewm@46: * be the same as the \c userData parameter passed to initialise_render() and render(). andrewm@46: */ andrewm@46: void cleanup_render(BeagleRTContext *context, void *userData); andrewm@46: andrewm@46: // *** Command-line settings *** andrewm@46: andrewm@46: /** andrewm@46: * \brief Initialise the data structure containing settings for BeagleRT. andrewm@46: * andrewm@46: * This function should be called in main() before parsing any command-line arguments. It andrewm@46: * sets default values in the data structure which specifies the BeagleRT settings, including andrewm@46: * frame sizes, numbers of channels, volume levels and other parameters. andrewm@46: * andrewm@46: * \param settings Structure holding initialisation data for BeagleRT. andrewm@46: */ andrewm@46: void BeagleRT_defaultSettings(BeagleRTInitSettings *settings); andrewm@46: andrewm@46: /** andrewm@46: * \brief Get long options from command line argument list, including BeagleRT standard options andrewm@46: * andrewm@46: * This function should be used in main() to process command line options, in place of the andrewm@46: * standard library getopt_long(). Internally, it parses standard BeagleRT command-line options, andrewm@46: * storing the results in the settings data structure. Any options which are not part of the andrewm@46: * BeagleRT standard options will be returned, as they would normally be in getopt_long(). andrewm@46: * andrewm@46: * \param argc Number of command line options, as passed to main(). andrewm@46: * \param argv Array of command line options, as passed to main(). andrewm@46: * \param customShortOptions List of short options to be parsed, analogous to getopt_long(). This andrewm@46: * list should not include any characters already parsed as part of the BeagleRT standard options. andrewm@46: * \param customLongOptions List of long options to parsed, analogous to getopt_long(). This andrewm@46: * list should not include any long options already parsed as part of the BeagleRT standard options. andrewm@46: * \param settings Data structure holding initialisation settings for BeagleRT. Any standard options andrewm@46: * parsed will automatically update this data structure. andrewm@46: * andrewm@46: * \return Value of the next option parsed which is not a BeagleRT standard option, or -1 when the andrewm@46: * argument list has been exhausted. Similar to the return value of getopt_long() except that BeagleRT andrewm@46: * standard options are handled internally and not returned. andrewm@46: */ andrewm@46: int BeagleRT_getopt_long(int argc, char *argv[], const char *customShortOptions, andrewm@46: const struct option *customLongOptions, BeagleRTInitSettings *settings); andrewm@46: andrewm@46: /** andrewm@46: * \brief Print usage information for BeagleRT standard options. andrewm@46: * andrewm@46: * This function should be called from your code wherever you wish to print usage information for the andrewm@46: * user. It will print usage information on BeagleRT standard options, after which you can print usage andrewm@46: * information for your own custom options. andrewm@46: */ andrewm@46: void BeagleRT_usage(); andrewm@46: andrewm@46: /** andrewm@46: * \brief Set level of verbose (debugging) printing. andrewm@46: * andrewm@46: * \param level Verbosity level of the internal BeagleRT system. 0 by default; higher values will andrewm@46: * print more information. Presently all positive numbers produce the same level of printing. andrewm@46: */ andrewm@46: void BeagleRT_setVerboseLevel(int level); andrewm@46: andrewm@46: // *** Audio control functions *** andrewm@46: andrewm@46: /** andrewm@46: * \brief Initialise audio and sensor rendering environment. andrewm@46: * andrewm@46: * This function prepares audio rendering in BeagleRT. It should be called from main() sometime andrewm@46: * after command line option parsing has finished. It will initialise the rendering system, which andrewm@46: * in the process will result in a call to the user-defined initialise_render() function. andrewm@46: * andrewm@46: * \param settings Data structure holding system settings, including numbers of channels, frame sizes, andrewm@46: * volume levels and other information. andrewm@46: * \param userData An opaque pointer to a user-defined data structure which will be passed to andrewm@46: * initialise_render(), render() and cleanup_render(). You can use this to pass custom information andrewm@46: * to the rendering functions, as an alternative to using global variables. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData); andrewm@46: andrewm@46: /** andrewm@46: * \brief Begin processing audio and sensor data. andrewm@46: * andrewm@46: * This function will start the BeagleRT audio/sensor system. After this function is called, the andrewm@46: * system will make periodic calls to render() until BeagleRT_stopAudio() is called. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_startAudio(); andrewm@46: andrewm@46: /** andrewm@46: * \brief Stop processing audio and sensor data. andrewm@46: * andrewm@46: * This function will stop the BeagleRT audio/sensor system. After this function returns, no further andrewm@46: * calls to render() will be issued. andrewm@46: */ andrewm@46: void BeagleRT_stopAudio(); andrewm@46: andrewm@46: /** andrewm@46: * \brief Clean up resources from audio and sensor processing. andrewm@46: * andrewm@46: * This function should only be called after BeagleRT_stopAudio(). It will release any andrewm@46: * internal resources for audio and sensor processing. In the process, it will call the andrewm@46: * user-defined cleanup_render() function. andrewm@46: */ andrewm@46: void BeagleRT_cleanupAudio(); andrewm@46: andrewm@46: // Volume/level controls andrewm@46: // These return 0 on success andrewm@46: andrewm@46: // *** Volume and level controls *** andrewm@46: andrewm@46: /** andrewm@46: * \brief Set the level of the audio DAC. andrewm@46: * andrewm@46: * This function sets the level of all audio outputs (headphone, line, speaker). It does andrewm@46: * not affect the level of the (non-audio) analog outputs. andrewm@46: * andrewm@46: * \b Important: do not call this function from within render(), as it does not make andrewm@46: * any guarantees on real-time performance. andrewm@46: * andrewm@46: * \param decibels Level of the DAC output. Valid levels range from -63.5 (lowest) to andrewm@46: * 0 (highest) in steps of 0.5dB. Levels between increments of 0.5 will be rounded down. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_setDACLevel(float decibels); andrewm@46: andrewm@46: /** andrewm@46: * \brief Set the level of the audio ADC. andrewm@46: * andrewm@46: * This function sets the level of the audio input. It does not affect the level of the andrewm@46: * (non-audio) analog inputs. andrewm@46: * andrewm@46: * \b Important: do not call this function from within render(), as it does not make andrewm@46: * any guarantees on real-time performance. andrewm@46: * andrewm@46: * \param decibels Level of the ADC input. Valid levels range from -12 (lowest) to andrewm@46: * 0 (highest) in steps of 1.5dB. Levels between increments of 1.5 will be rounded down. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_setADCLevel(float decibels); andrewm@46: andrewm@46: /** andrewm@46: * \brief Set the level of the onboard headphone amplifier. andrewm@46: * andrewm@46: * This function sets the level of the headphone output only (3-pin connector on the BeagleRT andrewm@46: * cape or the output jack on the BeagleBone Audio Cape). It does not affect the level of the andrewm@46: * speakers or the line out pads on the cape. andrewm@46: * andrewm@46: * \b Important: do not call this function from within render(), as it does not make andrewm@46: * any guarantees on real-time performance. andrewm@46: * andrewm@46: * \param decibels Level of the DAC output. Valid levels range from -63.5 (lowest) to andrewm@46: * 0 (highest) in steps of 0.5dB. Levels between increments of 0.5 will be rounded down. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_setHeadphoneLevel(float decibels); andrewm@46: andrewm@46: /** andrewm@46: * \brief Mute or unmute the onboard speaker amplifiers. andrewm@46: * andrewm@46: * This function mutes or unmutes the amplifiers on the BeagleRT cape. Whether the speakers begin andrewm@46: * muted or unmuted depends on the BeagleRTInitSettings structure passed to BeagleRT_initAudio(). andrewm@46: * andrewm@46: * \b Important: do not call this function from within render(), as it does not make andrewm@46: * any guarantees on real-time performance. andrewm@46: * andrewm@46: * \param mute 0 to enable the speakers, nonzero to mute the speakers. andrewm@46: * andrewm@46: * \return 0 on success, or nonzero if an error occurred. andrewm@46: */ andrewm@46: int BeagleRT_muteSpeakers(int mute); andrewm@46: andrewm@46: // *** Functions for creating auxiliary tasks *** andrewm@46: andrewm@46: /** andrewm@46: * \brief Create a new auxiliary task. andrewm@46: * andrewm@46: * This function creates a new auxiliary task which, when scheduled, runs the function specified andrewm@46: * in the first argument. Note that the task does not run until scheduleAuxiliaryTask() is called. andrewm@46: * Auxiliary tasks should be created in initialise_render() and never in render() itself. andrewm@46: * andrewm@46: * The second argument specifies the real-time priority. Valid values are between 0 andrewm@46: * and 99, and usually should be lower than BEAGLERT_AUDIO_PRIORITY. Tasks with higher priority always andrewm@46: * preempt tasks with lower priority. andrewm@46: * andrewm@46: * \param functionToCall Function which will run each time the auxiliary task is scheduled. andrewm@46: * \param priority Xenomai priority level at which the task should run. andrewm@46: * \param Name for this task, which should be unique system-wide (no other running program should use this name). andrewm@46: */ andrewm@46: AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name); andrewm@46: andrewm@46: /** andrewm@46: * \brief Run an auxiliary task which has previously been created. andrewm@46: * andrewm@46: * This function will schedule an auxiliary task to run. When the task runs, the function in the first andrewm@46: * argument of createAuxiliaryTaskLoop() will be called. andrewm@46: * andrewm@46: * scheduleAuxiliaryTask() is typically called from render() to start a lower-priority task. The function andrewm@46: * will not run immediately, but only once any active higher priority tasks have finished. andrewm@46: * andrewm@46: * \param task Task to schedule for running. andrewm@46: */ andrewm@46: void scheduleAuxiliaryTask(AuxiliaryTask task); andrewm@46: andrewm@46: #endif /* BEAGLERT_H_ */