annotate core/RTAudio.cpp @ 45:579c86316008 newapi

Major API overhaul. Moved to a single data structure for handling render functions. Functionally, generally similar except for scheduling within PRU loop function, which now uses interrupts from the PRU rather than polling. This requires an updated kernel.
author andrewm
date Thu, 28 May 2015 14:35:55 -0400
parents ad5cd8dd99b3
children 643cbee74eda
rev   line source
andrewm@0 1 /*
andrewm@0 2 * RTAudio.cpp
andrewm@0 3 *
andrewm@0 4 * Central control code for hard real-time audio on BeagleBone Black
andrewm@0 5 * using PRU and Xenomai Linux extensions. This code began as part
andrewm@0 6 * of the Hackable Instruments project (EPSRC) at Queen Mary University
andrewm@0 7 * of London, 2013-14.
andrewm@0 8 *
andrewm@0 9 * (c) 2014 Victor Zappi and Andrew McPherson
andrewm@0 10 * Queen Mary University of London
andrewm@0 11 */
andrewm@0 12
andrewm@0 13
andrewm@0 14 #include <stdio.h>
andrewm@0 15 #include <stdlib.h>
andrewm@0 16 #include <string.h>
andrewm@0 17 #include <strings.h>
andrewm@0 18 #include <math.h>
andrewm@0 19 #include <iostream>
andrewm@0 20 #include <assert.h>
andrewm@0 21 #include <vector>
andrewm@0 22
andrewm@0 23 // Xenomai-specific includes
andrewm@0 24 #include <sys/mman.h>
andrewm@0 25 #include <native/task.h>
andrewm@0 26 #include <native/timer.h>
andrewm@45 27 #include <native/intr.h>
andrewm@0 28 #include <rtdk.h>
andrewm@0 29
andrewm@45 30 #include "../include/BeagleRT.h"
andrewm@0 31 #include "../include/PRU.h"
andrewm@0 32 #include "../include/I2c_Codec.h"
andrewm@0 33 #include "../include/GPIOcontrol.h"
giuliomoro@24 34 #include "../include/client.h"
andrewm@0 35
andrewm@45 36 // ARM interrupt number for PRU event EVTOUT7
andrewm@45 37 #define PRU_RTAUDIO_IRQ 21
andrewm@45 38
andrewm@0 39 using namespace std;
andrewm@0 40
andrewm@0 41 // Data structure to keep track of auxiliary tasks we
andrewm@0 42 // can schedule
andrewm@0 43 typedef struct {
andrewm@0 44 RT_TASK task;
andrewm@0 45 void (*function)(void);
andrewm@0 46 char *name;
andrewm@0 47 int priority;
andrewm@0 48 } InternalAuxiliaryTask;
andrewm@0 49
andrewm@0 50 const char gRTAudioThreadName[] = "beaglert-audio";
andrewm@45 51 const char gRTAudioInterruptName[] = "beaglert-pru-irq";
andrewm@0 52
andrewm@0 53 // Real-time tasks and objects
andrewm@0 54 RT_TASK gRTAudioThread;
andrewm@45 55 RT_INTR gRTAudioInterrupt;
andrewm@0 56 PRU *gPRU = 0;
andrewm@0 57 I2c_Codec *gAudioCodec = 0;
andrewm@0 58
andrewm@0 59 vector<InternalAuxiliaryTask*> gAuxTasks;
andrewm@0 60
andrewm@0 61 // Flag which tells the audio task to stop
andrewm@0 62 bool gShouldStop = false;
andrewm@0 63
andrewm@0 64 // general settings
andrewm@45 65 char gPRUFilename[MAX_PRU_FILENAME_LENGTH]; // Path to PRU binary file (internal code if empty)_
andrewm@0 66 int gRTAudioVerbose = 0; // Verbosity level for debugging
andrewm@0 67 int gAmplifierMutePin = -1;
andrewm@5 68 int gAmplifierShouldBeginMuted = 0;
andrewm@0 69
andrewm@45 70 // Context which holds all the audio/sensor data passed to the render routines
andrewm@45 71 BeagleRTContext gContext;
andrewm@45 72
andrewm@45 73 // User data passed in from main()
andrewm@45 74 void *gUserData;
andrewm@0 75
andrewm@0 76 // initAudio() prepares the infrastructure for running PRU-based real-time
andrewm@0 77 // audio, but does not actually start the calculations.
andrewm@0 78 // periodSize indicates the number of _sensor_ frames per period: the audio period size
andrewm@0 79 // is twice this value. In total, the audio latency in frames will be 4*periodSize,
andrewm@0 80 // plus any latency inherent in the ADCs and DACs themselves.
giuliomoro@19 81 // useAnalog indicates whether to enable the ADC and DAC or just use the audio codec.
giuliomoro@19 82 // numAnalogChannels indicates how many ADC and DAC channels to use.
andrewm@0 83 // userData is an opaque pointer which will be passed through to the initialise_render()
andrewm@0 84 // function for application-specific use
andrewm@0 85 //
andrewm@0 86 // Returns 0 on success.
andrewm@0 87
andrewm@45 88 int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData)
andrewm@0 89 {
andrewm@0 90 rt_print_auto_init(1);
andrewm@45 91
andrewm@45 92 BeagleRT_setVerboseLevel(settings->verbose);
andrewm@45 93 strncpy(gPRUFilename, settings->pruFilename, MAX_PRU_FILENAME_LENGTH);
andrewm@45 94 gUserData = userData;
andrewm@45 95
andrewm@45 96 // Initialise context data structure
andrewm@45 97 memset(&gContext, 0, sizeof(BeagleRTContext));
andrewm@0 98
andrewm@5 99 if(gRTAudioVerbose) {
andrewm@5 100 cout << "Starting with period size " << settings->periodSize << "; ";
giuliomoro@19 101 if(settings->useAnalog)
giuliomoro@19 102 cout << "analog enabled\n";
andrewm@5 103 else
giuliomoro@19 104 cout << "analog disabled\n";
andrewm@5 105 cout << "DAC level " << settings->dacLevel << "dB; ADC level " << settings->adcLevel;
andrewm@5 106 cout << "dB; headphone level " << settings->headphoneLevel << "dB\n";
andrewm@5 107 if(settings->beginMuted)
andrewm@5 108 cout << "Beginning with speaker muted\n";
andrewm@5 109 }
andrewm@0 110
andrewm@0 111 // Prepare GPIO pins for amplifier mute and status LED
andrewm@5 112 if(settings->ampMutePin >= 0) {
andrewm@5 113 gAmplifierMutePin = settings->ampMutePin;
andrewm@5 114 gAmplifierShouldBeginMuted = settings->beginMuted;
andrewm@0 115
andrewm@5 116 if(gpio_export(settings->ampMutePin)) {
andrewm@0 117 if(gRTAudioVerbose)
giuliomoro@16 118 cout << "Warning: couldn't export amplifier mute pin " << settings-> ampMutePin << "\n";
andrewm@0 119 }
andrewm@5 120 if(gpio_set_dir(settings->ampMutePin, OUTPUT_PIN)) {
andrewm@0 121 if(gRTAudioVerbose)
andrewm@0 122 cout << "Couldn't set direction on amplifier mute pin\n";
andrewm@0 123 return -1;
andrewm@0 124 }
andrewm@5 125 if(gpio_set_value(settings->ampMutePin, LOW)) {
andrewm@0 126 if(gRTAudioVerbose)
andrewm@0 127 cout << "Couldn't set value on amplifier mute pin\n";
andrewm@0 128 return -1;
andrewm@0 129 }
andrewm@0 130 }
andrewm@0 131
giuliomoro@19 132 // Limit the analog channels to sane values
giuliomoro@19 133 if(settings->numAnalogChannels >= 8)
giuliomoro@19 134 settings->numAnalogChannels = 8;
giuliomoro@19 135 else if(settings->numAnalogChannels >= 4)
giuliomoro@19 136 settings->numAnalogChannels = 4;
andrewm@12 137 else
giuliomoro@19 138 settings->numAnalogChannels = 2;
andrewm@12 139
andrewm@12 140 // Sanity check the combination of channels and period size
giuliomoro@19 141 if(settings->numAnalogChannels <= 4 && settings->periodSize < 2) {
giuliomoro@19 142 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
andrewm@12 143 return 1;
andrewm@12 144 }
giuliomoro@19 145 if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) {
giuliomoro@19 146 cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
andrewm@12 147 return 1;
andrewm@12 148 }
andrewm@12 149
andrewm@45 150 // Initialise the rendering environment: sample rates, frame counts, numbers of channels
andrewm@45 151 gContext.audioSampleRate = 44100.0;
andrewm@45 152 gContext.audioChannels = 2;
andrewm@45 153
andrewm@45 154 if(settings->useAnalog) {
andrewm@45 155 gContext.audioFrames = settings->periodSize * settings->numAnalogChannels / 4;
andrewm@45 156
andrewm@45 157 gContext.analogFrames = settings->periodSize;
andrewm@45 158 gContext.analogChannels = settings->numAnalogChannels;
andrewm@45 159 gContext.analogSampleRate = gContext.audioSampleRate * 4.0 / (float)settings->numAnalogChannels;
andrewm@45 160 }
andrewm@45 161 else {
andrewm@45 162 gContext.audioFrames = settings->periodSize * 2;
andrewm@45 163
andrewm@45 164 gContext.analogFrames = 0;
andrewm@45 165 gContext.analogChannels = 0;
andrewm@45 166 gContext.analogSampleRate = 0;
andrewm@45 167 }
andrewm@45 168
andrewm@45 169 // For now, digital frame rate is equal to audio frame rate
andrewm@45 170 if(settings->useDigital) {
andrewm@45 171 gContext.digitalFrames = gContext.audioFrames;
andrewm@45 172 gContext.digitalSampleRate = gContext.audioSampleRate;
andrewm@45 173 gContext.digitalChannels = settings->numDigitalChannels;
andrewm@45 174 }
andrewm@45 175 else {
andrewm@45 176 gContext.digitalFrames = 0;
andrewm@45 177 gContext.digitalSampleRate = 0;
andrewm@45 178 gContext.digitalChannels = 0;
andrewm@45 179 }
andrewm@45 180
andrewm@45 181 // Set flags based on init settings
andrewm@45 182 if(settings->interleave)
andrewm@45 183 gContext.flags |= BEAGLERT_FLAG_INTERLEAVED;
andrewm@45 184 if(settings->analogOutputsPersist)
andrewm@45 185 gContext.flags |= BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST;
andrewm@45 186
andrewm@0 187 // Use PRU for audio
andrewm@45 188 gPRU = new PRU(&gContext);
andrewm@0 189 gAudioCodec = new I2c_Codec();
andrewm@0 190
andrewm@45 191 // Initialise the GPIO pins, including possibly the digital pins in the render routines
andrewm@45 192 if(gPRU->prepareGPIO(1, 1)) {
andrewm@0 193 cout << "Error: unable to prepare GPIO for PRU audio\n";
andrewm@0 194 return 1;
andrewm@0 195 }
andrewm@45 196
andrewm@45 197 // Get the PRU memory buffers ready to go
giuliomoro@19 198 if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) {
andrewm@0 199 cout << "Error: unable to initialise PRU\n";
andrewm@0 200 return 1;
andrewm@0 201 }
andrewm@45 202
andrewm@45 203 // Prepare the audio codec, which clocks the whole system
andrewm@5 204 if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) {
andrewm@0 205 cout << "Unable to open codec I2C\n";
andrewm@0 206 return 1;
andrewm@0 207 }
andrewm@0 208 if(gAudioCodec->initCodec()) {
andrewm@0 209 cout << "Error: unable to initialise audio codec\n";
andrewm@0 210 return 1;
andrewm@0 211 }
andrewm@0 212
andrewm@5 213 // Set default volume levels
andrewm@5 214 BeagleRT_setDACLevel(settings->dacLevel);
andrewm@5 215 BeagleRT_setADCLevel(settings->adcLevel);
andrewm@5 216 BeagleRT_setHeadphoneLevel(settings->headphoneLevel);
andrewm@5 217
andrewm@45 218 // Call the user-defined initialisation function
andrewm@45 219 if(!initialise_render(&gContext, userData)) {
andrewm@0 220 cout << "Couldn't initialise audio rendering\n";
andrewm@0 221 return 1;
andrewm@0 222 }
andrewm@0 223
andrewm@0 224 return 0;
andrewm@0 225 }
andrewm@0 226
andrewm@0 227 // audioLoop() is the main function which starts the PRU audio code
andrewm@0 228 // and then transfers control to the PRU object. The PRU object in
andrewm@0 229 // turn will call the audio render() callback function every time
andrewm@0 230 // there is new data to process.
andrewm@0 231
andrewm@0 232 void audioLoop(void *)
andrewm@0 233 {
andrewm@0 234 if(gRTAudioVerbose==1)
andrewm@0 235 rt_printf("_________________Audio Thread!\n");
andrewm@0 236
andrewm@0 237 // PRU audio
andrewm@0 238 assert(gAudioCodec != 0 && gPRU != 0);
andrewm@0 239
andrewm@0 240 if(gAudioCodec->startAudio(0)) {
andrewm@0 241 rt_printf("Error: unable to start I2C audio codec\n");
andrewm@0 242 gShouldStop = 1;
andrewm@0 243 }
andrewm@0 244 else {
giuliomoro@16 245 if(gPRU->start(gPRUFilename)) {
giuliomoro@16 246 rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename);
andrewm@0 247 gShouldStop = 1;
andrewm@0 248 }
andrewm@0 249 else {
andrewm@0 250 // All systems go. Run the loop; it will end when gShouldStop is set to 1
andrewm@5 251
andrewm@5 252 if(!gAmplifierShouldBeginMuted) {
andrewm@5 253 // First unmute the amplifier
andrewm@5 254 if(BeagleRT_muteSpeakers(0)) {
andrewm@5 255 if(gRTAudioVerbose)
andrewm@5 256 rt_printf("Warning: couldn't set value (high) on amplifier mute pin\n");
andrewm@5 257 }
andrewm@0 258 }
andrewm@0 259
andrewm@45 260 gPRU->loop(&gRTAudioInterrupt, gUserData);
andrewm@0 261
andrewm@0 262 // Now clean up
andrewm@0 263 // gPRU->waitForFinish();
andrewm@0 264 gPRU->disable();
andrewm@0 265 gAudioCodec->stopAudio();
andrewm@0 266 gPRU->cleanupGPIO();
andrewm@0 267 }
andrewm@0 268 }
andrewm@0 269
andrewm@0 270 if(gRTAudioVerbose == 1)
andrewm@0 271 rt_printf("audio thread ended\n");
andrewm@0 272 }
andrewm@0 273
andrewm@0 274 // Create a calculation loop which can run independently of the audio, at a different
andrewm@45 275 // (equal or lower) priority. Audio priority is defined in BEAGLERT_AUDIO_PRIORITY;
andrewm@45 276 // priority should be generally be less than this.
andrewm@0 277 // Returns an (opaque) pointer to the created task on success; 0 on failure
andrewm@0 278 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name)
andrewm@0 279 {
andrewm@0 280 InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask));
andrewm@0 281
andrewm@0 282 // Attempt to create the task
andrewm@0 283 if(rt_task_create(&(newTask->task), name, 0, priority, T_JOINABLE | T_FPU)) {
andrewm@0 284 cout << "Error: unable to create auxiliary task " << name << endl;
andrewm@0 285 free(newTask);
andrewm@0 286 return 0;
andrewm@0 287 }
andrewm@0 288
andrewm@0 289 // Populate the rest of the data structure and store it in the vector
andrewm@0 290 newTask->function = functionToCall;
andrewm@0 291 newTask->name = strdup(name);
andrewm@0 292 newTask->priority = priority;
andrewm@0 293
andrewm@0 294 gAuxTasks.push_back(newTask);
andrewm@0 295
andrewm@0 296 return (AuxiliaryTask)newTask;
andrewm@0 297 }
andrewm@0 298
andrewm@0 299 // Schedule a previously created auxiliary task. It will run when the priority rules next
andrewm@0 300 // allow it to be scheduled.
andrewm@0 301 void scheduleAuxiliaryTask(AuxiliaryTask task)
andrewm@0 302 {
andrewm@0 303 InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task;
andrewm@0 304
andrewm@0 305 rt_task_resume(&taskToSchedule->task);
andrewm@0 306 }
andrewm@0 307
andrewm@0 308 // Calculation loop that can be used for other tasks running at a lower
andrewm@0 309 // priority than the audio thread. Simple wrapper for Xenomai calls.
andrewm@0 310 // Treat the argument as containing the task structure
andrewm@0 311 void auxiliaryTaskLoop(void *taskStruct)
andrewm@0 312 {
andrewm@0 313 // Get function to call from the argument
andrewm@0 314 void (*auxiliary_function)(void) = ((InternalAuxiliaryTask *)taskStruct)->function;
andrewm@0 315 const char *name = ((InternalAuxiliaryTask *)taskStruct)->name;
andrewm@0 316
andrewm@0 317 // Wait for a notification
andrewm@0 318 rt_task_suspend(NULL);
andrewm@0 319
andrewm@0 320 while(!gShouldStop) {
andrewm@0 321 // Then run the calculations
andrewm@0 322 auxiliary_function();
andrewm@0 323
andrewm@0 324 // Wait for a notification
andrewm@0 325 rt_task_suspend(NULL);
andrewm@0 326 }
andrewm@0 327
andrewm@0 328 if(gRTAudioVerbose == 1)
andrewm@0 329 rt_printf("auxiliary task %s ended\n", name);
andrewm@0 330 }
andrewm@0 331
andrewm@0 332 // startAudio() should be called only after initAudio() successfully completes.
andrewm@0 333 // It launches the real-time Xenomai task which runs the audio loop. Returns 0
andrewm@0 334 // on success.
andrewm@0 335
andrewm@5 336 int BeagleRT_startAudio()
andrewm@0 337 {
andrewm@45 338 // Create audio thread with high Xenomai priority
andrewm@45 339 if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, BEAGLERT_AUDIO_PRIORITY, T_JOINABLE | T_FPU)) {
andrewm@0 340 cout << "Error: unable to create Xenomai audio thread" << endl;
andrewm@0 341 return -1;
andrewm@0 342 }
andrewm@0 343
andrewm@45 344 // Create an interrupt which the audio thread receives from the PRU
andrewm@45 345 int result = 0;
andrewm@45 346 if((result = rt_intr_create(&gRTAudioInterrupt, gRTAudioInterruptName, PRU_RTAUDIO_IRQ, I_NOAUTOENA)) != 0) {
andrewm@45 347 cout << "Error: unable to create Xenomai interrupt for PRU (error " << result << ")" << endl;
andrewm@45 348 return -1;
andrewm@45 349 }
andrewm@45 350
andrewm@0 351 // Start all RT threads
andrewm@0 352 if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) {
andrewm@0 353 cout << "Error: unable to start Xenomai audio thread" << endl;
andrewm@0 354 return -1;
andrewm@0 355 }
andrewm@0 356
andrewm@0 357 // The user may have created other tasks. Start those also.
andrewm@0 358 vector<InternalAuxiliaryTask*>::iterator it;
andrewm@0 359 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
andrewm@0 360 InternalAuxiliaryTask *taskStruct = *it;
andrewm@0 361
andrewm@0 362 if(rt_task_start(&(taskStruct->task), &auxiliaryTaskLoop, taskStruct)) {
andrewm@0 363 cerr << "Error: unable to start Xenomai task " << taskStruct->name << endl;
andrewm@0 364 return -1;
andrewm@0 365 }
andrewm@0 366 }
andrewm@0 367
andrewm@0 368 return 0;
andrewm@0 369 }
andrewm@0 370
andrewm@0 371 // Stop the PRU-based audio from running and wait
andrewm@0 372 // for the tasks to complete before returning.
andrewm@0 373
andrewm@5 374 void BeagleRT_stopAudio()
andrewm@0 375 {
andrewm@0 376 // Tell audio thread to stop (if this hasn't been done already)
andrewm@0 377 gShouldStop = true;
andrewm@0 378
andrewm@5 379 if(gRTAudioVerbose)
andrewm@5 380 cout << "Stopping audio...\n";
andrewm@5 381
andrewm@0 382 // Now wait for threads to respond and actually stop...
andrewm@0 383 rt_task_join(&gRTAudioThread);
andrewm@0 384
andrewm@0 385 // Stop all the auxiliary threads too
andrewm@0 386 vector<InternalAuxiliaryTask*>::iterator it;
andrewm@0 387 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
andrewm@0 388 InternalAuxiliaryTask *taskStruct = *it;
andrewm@0 389
andrewm@0 390 // Wake up each thread and join it
andrewm@0 391 rt_task_resume(&(taskStruct->task));
andrewm@0 392 rt_task_join(&(taskStruct->task));
andrewm@0 393 }
andrewm@0 394 }
andrewm@0 395
andrewm@0 396 // Free any resources associated with PRU real-time audio
andrewm@5 397 void BeagleRT_cleanupAudio()
andrewm@0 398 {
andrewm@45 399 cleanup_render(&gContext, gUserData);
andrewm@0 400
andrewm@0 401 // Clean up the auxiliary tasks
andrewm@0 402 vector<InternalAuxiliaryTask*>::iterator it;
andrewm@0 403 for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
andrewm@0 404 InternalAuxiliaryTask *taskStruct = *it;
andrewm@0 405
andrewm@45 406 // Delete the task
andrewm@45 407 rt_task_delete(&taskStruct->task);
andrewm@45 408
andrewm@0 409 // Free the name string and the struct itself
andrewm@0 410 free(taskStruct->name);
andrewm@0 411 free(taskStruct);
andrewm@0 412 }
andrewm@0 413 gAuxTasks.clear();
andrewm@0 414
andrewm@45 415 // Delete the audio task and its interrupt
andrewm@45 416 rt_intr_delete(&gRTAudioInterrupt);
andrewm@45 417 rt_task_delete(&gRTAudioThread);
andrewm@45 418
andrewm@0 419 if(gPRU != 0)
andrewm@0 420 delete gPRU;
andrewm@0 421 if(gAudioCodec != 0)
andrewm@0 422 delete gAudioCodec;
andrewm@0 423
andrewm@0 424 if(gAmplifierMutePin >= 0)
andrewm@0 425 gpio_unexport(gAmplifierMutePin);
andrewm@0 426 gAmplifierMutePin = -1;
andrewm@0 427 }
andrewm@0 428
andrewm@5 429 // Set the level of the DAC; affects all outputs (headphone, line, speaker)
andrewm@5 430 // 0dB is the maximum, -63.5dB is the minimum; 0.5dB steps
andrewm@5 431 int BeagleRT_setDACLevel(float decibels)
andrewm@5 432 {
andrewm@5 433 if(gAudioCodec == 0)
andrewm@5 434 return -1;
andrewm@5 435 return gAudioCodec->setDACVolume((int)floorf(decibels * 2.0 + 0.5));
andrewm@5 436 }
andrewm@5 437
andrewm@5 438 // Set the level of the ADC
andrewm@5 439 // 0dB is the maximum, -12dB is the minimum; 1.5dB steps
andrewm@5 440 int BeagleRT_setADCLevel(float decibels)
andrewm@5 441 {
andrewm@5 442 if(gAudioCodec == 0)
andrewm@5 443 return -1;
andrewm@5 444 return gAudioCodec->setADCVolume((int)floorf(decibels * 2.0 + 0.5));
andrewm@5 445 }
andrewm@5 446
andrewm@5 447 // Set the level of the onboard headphone amplifier; affects headphone
andrewm@5 448 // output only (not line out or speaker)
andrewm@5 449 // 0dB is the maximum, -63.5dB is the minimum; 0.5dB steps
andrewm@5 450 int BeagleRT_setHeadphoneLevel(float decibels)
andrewm@5 451 {
andrewm@5 452 if(gAudioCodec == 0)
andrewm@5 453 return -1;
andrewm@5 454 return gAudioCodec->setHPVolume((int)floorf(decibels * 2.0 + 0.5));
andrewm@5 455 }
andrewm@5 456
andrewm@5 457 // Mute or unmute the onboard speaker amplifiers
andrewm@5 458 // mute == 0 means unmute; otherwise mute
andrewm@5 459 // Returns 0 on success
andrewm@5 460 int BeagleRT_muteSpeakers(int mute)
andrewm@5 461 {
andrewm@5 462 int pinValue = mute ? LOW : HIGH;
andrewm@5 463
andrewm@5 464 // Check that we have an enabled pin for controlling the mute
andrewm@5 465 if(gAmplifierMutePin < 0)
andrewm@5 466 return -1;
andrewm@5 467
andrewm@5 468 return gpio_set_value(gAmplifierMutePin, pinValue);
andrewm@5 469 }
andrewm@5 470
andrewm@0 471 // Set the verbosity level
andrewm@45 472 void BeagleRT_setVerboseLevel(int level)
andrewm@0 473 {
andrewm@0 474 gRTAudioVerbose = level;
andrewm@0 475 }