diff 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
line wrap: on
line diff
--- a/core/RTAudio.cpp	Wed May 13 12:23:37 2015 +0100
+++ b/core/RTAudio.cpp	Thu May 28 14:35:55 2015 -0400
@@ -24,15 +24,18 @@
 #include <sys/mman.h>
 #include <native/task.h>
 #include <native/timer.h>
+#include <native/intr.h>
 #include <rtdk.h>
 
-#include "../include/RTAudio.h"
+#include "../include/BeagleRT.h"
 #include "../include/PRU.h"
 #include "../include/I2c_Codec.h"
-#include "../include/render.h"
 #include "../include/GPIOcontrol.h"
 #include "../include/client.h"
 
+// ARM interrupt number for PRU event EVTOUT7
+#define PRU_RTAUDIO_IRQ		21
+
 using namespace std;
 
 // Data structure to keep track of auxiliary tasks we
@@ -45,9 +48,11 @@
 } InternalAuxiliaryTask;
 
 const char gRTAudioThreadName[] = "beaglert-audio";
+const char gRTAudioInterruptName[] = "beaglert-pru-irq";
 
 // Real-time tasks and objects
 RT_TASK gRTAudioThread;
+RT_INTR gRTAudioInterrupt;
 PRU *gPRU = 0;
 I2c_Codec *gAudioCodec = 0;
 
@@ -57,18 +62,16 @@
 bool gShouldStop = false;
 
 // general settings
-char *gPRUFilename;//[256]	 = "pru_rtaudio.bin"; 	// path to PRU binary file
+char gPRUFilename[MAX_PRU_FILENAME_LENGTH];		// Path to PRU binary file (internal code if empty)_
 int gRTAudioVerbose = 0;   						// Verbosity level for debugging
 int gAmplifierMutePin = -1;
 int gAmplifierShouldBeginMuted = 0;
 
-// Number of audio and analog channels, globally accessible
-// At least gNumAnalogChannels and gNumDigitalChannels need to be global to be used
-// by the AnalogRead() and AnalogWrite() and the digital macros without creating
-// extra confusion in their use cases by passing this argument
-int gNumAudioChannels = 0;
-int gNumAnalogChannels = 0;
-int gNumDigitalChannels = 0;
+// Context which holds all the audio/sensor data passed to the render routines
+BeagleRTContext gContext;
+
+// User data passed in from main()
+void *gUserData;
 
 // initAudio() prepares the infrastructure for running PRU-based real-time
 // audio, but does not actually start the calculations.
@@ -82,14 +85,16 @@
 //
 // Returns 0 on success.
 
-
-int BeagleRT_initAudio(RTAudioSettings *settings, void *userData)
+int BeagleRT_initAudio(BeagleRTInitSettings *settings, void *userData)
 {
 	rt_print_auto_init(1);
-	setVerboseLevel(settings->verbose);
-	gPRUFilename=settings->pruFilename;
-	if(gRTAudioVerbose == 1)
-		rt_printf("Running with Xenomai\n");
+
+	BeagleRT_setVerboseLevel(settings->verbose);
+	strncpy(gPRUFilename, settings->pruFilename, MAX_PRU_FILENAME_LENGTH);
+	gUserData = userData;
+
+	// Initialise context data structure
+	memset(&gContext, 0, sizeof(BeagleRTContext));
 
 	if(gRTAudioVerbose) {
 		cout << "Starting with period size " << settings->periodSize << "; ";
@@ -142,19 +147,60 @@
 		return 1;
 	}
 
+	// Initialise the rendering environment: sample rates, frame counts, numbers of channels
+	gContext.audioSampleRate = 44100.0;
+	gContext.audioChannels = 2;
+
+	if(settings->useAnalog) {
+		gContext.audioFrames = settings->periodSize * settings->numAnalogChannels / 4;
+
+		gContext.analogFrames = settings->periodSize;
+		gContext.analogChannels = settings->numAnalogChannels;
+		gContext.analogSampleRate = gContext.audioSampleRate * 4.0 / (float)settings->numAnalogChannels;
+	}
+	else {
+		gContext.audioFrames = settings->periodSize * 2;
+
+		gContext.analogFrames = 0;
+		gContext.analogChannels = 0;
+		gContext.analogSampleRate = 0;
+	}
+
+	// For now, digital frame rate is equal to audio frame rate
+	if(settings->useDigital) {
+		gContext.digitalFrames = gContext.audioFrames;
+		gContext.digitalSampleRate = gContext.audioSampleRate;
+		gContext.digitalChannels = settings->numDigitalChannels;
+	}
+	else {
+		gContext.digitalFrames = 0;
+		gContext.digitalSampleRate = 0;
+		gContext.digitalChannels = 0;
+	}
+
+	// Set flags based on init settings
+	if(settings->interleave)
+		gContext.flags |= BEAGLERT_FLAG_INTERLEAVED;
+	if(settings->analogOutputsPersist)
+		gContext.flags |= BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST;
+
 	// Use PRU for audio
-	gPRU = new PRU();
+	gPRU = new PRU(&gContext);
 	gAudioCodec = new I2c_Codec();
 
-	gNumDigitalChannels = settings->useDigital ? settings->numDigitalChannels : 0; //this is called here to make sure prepareGPIO initializes the appropriate GPIO pins
-	if(gPRU->prepareGPIO(settings->useAnalog, settings->useDigital, 1, 1)) {
+	// Initialise the GPIO pins, including possibly the digital pins in the render routines
+	if(gPRU->prepareGPIO(1, 1)) {
 		cout << "Error: unable to prepare GPIO for PRU audio\n";
 		return 1;
 	}
+
+	// Get the PRU memory buffers ready to go
 	if(gPRU->initialise(0, settings->periodSize, settings->numAnalogChannels, true)) {
 		cout << "Error: unable to initialise PRU\n";
 		return 1;
 	}
+
+	// Prepare the audio codec, which clocks the whole system
 	if(gAudioCodec->initI2C_RW(2, settings->codecI2CAddress, -1)) {
 		cout << "Unable to open codec I2C\n";
 		return 1;
@@ -169,24 +215,8 @@
 	BeagleRT_setADCLevel(settings->adcLevel);
 	BeagleRT_setHeadphoneLevel(settings->headphoneLevel);
 
-	// Initialise the rendering environment: pass the number of audio and analog
-	// channels, the period size for analog and audio, and the sample rates
-
-	int audioPeriodSize = settings->periodSize * 2;
-	float audioSampleRate = 44100.0;
-	float analogSampleRate = 22050.0;
-	if(settings->useAnalog) {
-		audioPeriodSize = settings->periodSize * settings->numAnalogChannels / 4;
-		analogSampleRate = audioSampleRate * 4.0 / (float)settings->numAnalogChannels;
-	}
-
-	gNumAudioChannels = 2;
-	gNumAnalogChannels = settings->useAnalog ? settings->numAnalogChannels : 0;
-	if(!initialise_render(gNumAnalogChannels, gNumDigitalChannels, gNumAudioChannels,
-				          settings->useAnalog ? settings->periodSize : 0, /* analog period size */
-				          audioPeriodSize,
-				          analogSampleRate, audioSampleRate,
-				          userData, settings)) {
+	// Call the user-defined initialisation function
+	if(!initialise_render(&gContext, userData)) {
 		cout << "Couldn't initialise audio rendering\n";
 		return 1;
 	}
@@ -227,7 +257,7 @@
 				}
 			}
 
-			gPRU->loop();
+			gPRU->loop(&gRTAudioInterrupt, gUserData);
 
 			// Now clean up
 			// gPRU->waitForFinish();
@@ -242,7 +272,8 @@
 }
 
 // Create a calculation loop which can run independently of the audio, at a different
-// (equal or lower) priority. Audio priority is 99; priority should be generally be less than this.
+// (equal or lower) priority. Audio priority is defined in BEAGLERT_AUDIO_PRIORITY;
+// priority should be generally be less than this.
 // Returns an (opaque) pointer to the created task on success; 0 on failure
 AuxiliaryTask createAuxiliaryTaskLoop(void (*functionToCall)(void), int priority, const char *name)
 {
@@ -304,12 +335,19 @@
 
 int BeagleRT_startAudio()
 {
-	// Create audio thread with the highest priority
-	if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, 99, T_JOINABLE | T_FPU)) {
+	// Create audio thread with high Xenomai priority
+	if(rt_task_create(&gRTAudioThread, gRTAudioThreadName, 0, BEAGLERT_AUDIO_PRIORITY, T_JOINABLE | T_FPU)) {
 		  cout << "Error: unable to create Xenomai audio thread" << endl;
 		  return -1;
 	}
 
+	// Create an interrupt which the audio thread receives from the PRU
+	int result = 0;
+	if((result = rt_intr_create(&gRTAudioInterrupt, gRTAudioInterruptName, PRU_RTAUDIO_IRQ, I_NOAUTOENA)) != 0) {
+		cout << "Error: unable to create Xenomai interrupt for PRU (error " << result << ")" << endl;
+		return -1;
+	}
+
 	// Start all RT threads
 	if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) {
 		  cout << "Error: unable to start Xenomai audio thread" << endl;
@@ -358,19 +396,26 @@
 // Free any resources associated with PRU real-time audio
 void BeagleRT_cleanupAudio()
 {
-	cleanup_render();
+	cleanup_render(&gContext, gUserData);
 
 	// Clean up the auxiliary tasks
 	vector<InternalAuxiliaryTask*>::iterator it;
 	for(it = gAuxTasks.begin(); it != gAuxTasks.end(); it++) {
 		InternalAuxiliaryTask *taskStruct = *it;
 
+		// Delete the task
+		rt_task_delete(&taskStruct->task);
+
 		// Free the name string and the struct itself
 		free(taskStruct->name);
 		free(taskStruct);
 	}
 	gAuxTasks.clear();
 
+	// Delete the audio task and its interrupt
+	rt_intr_delete(&gRTAudioInterrupt);
+	rt_task_delete(&gRTAudioThread);
+
 	if(gPRU != 0)
 		delete gPRU;
 	if(gAudioCodec != 0)
@@ -424,7 +469,7 @@
 }
 
 // Set the verbosity level
-void setVerboseLevel(int level)
+void BeagleRT_setVerboseLevel(int level)
 {
 	gRTAudioVerbose = level;
 }