diff core/RTAudio.cpp @ 108:3068421c0737 ultra-staging

Merged default into ultra-staging
author Giulio Moro <giuliomoro@yahoo.it>
date Tue, 18 Aug 2015 00:35:15 +0100
parents 4255ecbb9bec f944d0b60fa8
children
line wrap: on
line diff
--- a/core/RTAudio.cpp	Mon Jun 08 01:07:48 2015 +0100
+++ b/core/RTAudio.cpp	Tue Aug 18 00:35:15 2015 +0100
@@ -24,14 +24,16 @@
 #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;
 
@@ -45,9 +47,13 @@
 } InternalAuxiliaryTask;
 
 const char gRTAudioThreadName[] = "beaglert-audio";
+const char gRTAudioInterruptName[] = "beaglert-pru-irq";
 
 // Real-time tasks and objects
 RT_TASK gRTAudioThread;
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+RT_INTR gRTAudioInterrupt;
+#endif
 PRU *gPRU = 0;
 I2c_Codec *gAudioCodec = 0;
 
@@ -57,25 +63,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;
 
-
-void printIntervals(){
-	rt_printf("\n");
-	gPRU->renderTimer.print();
-	gPRU->sleepTimer.print();
-}
+// 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.
@@ -84,19 +81,21 @@
 // plus any latency inherent in the ADCs and DACs themselves.
 // useAnalog indicates whether to enable the ADC and DAC or just use the audio codec.
 // numAnalogChannels indicates how many ADC and DAC channels to use.
-// userData is an opaque pointer which will be passed through to the initialise_render()
+// userData is an opaque pointer which will be passed through to the setup()
 // function for application-specific use
 //
 // 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 << "; ";
@@ -149,19 +148,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;
@@ -176,28 +216,12 @@
 	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(!setup(&gContext, userData)) {
 		cout << "Couldn't initialise audio rendering\n";
 		return 1;
 	}
-	gPRU->printIntervalsTask=createAuxiliaryTaskLoop(*printIntervals, 10, "transmit-receive-data");
+
 	return 0;
 }
 
@@ -220,7 +244,7 @@
 	}
 	else {
 		if(gPRU->start(gPRUFilename)) {
-			rt_printf("Error: unable to start PRU %s\n", gPRUFilename);
+			rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename);
 			gShouldStop = 1;
 		}
 		else {
@@ -234,8 +258,11 @@
 				}
 			}
 
-			gPRU->loop();
-
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+			gPRU->loop(&gRTAudioInterrupt, gUserData);
+#else
+			gPRU->loop(0, gUserData);
+#endif
 			// Now clean up
 			// gPRU->waitForFinish();
 			gPRU->disable();
@@ -249,9 +276,10 @@
 }
 
 // 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)
+AuxiliaryTask BeagleRT_createAuxiliaryTask(void (*functionToCall)(void), int priority, const char *name)
 {
 	InternalAuxiliaryTask *newTask = (InternalAuxiliaryTask*)malloc(sizeof(InternalAuxiliaryTask));
 
@@ -274,7 +302,7 @@
 
 // Schedule a previously created auxiliary task. It will run when the priority rules next
 // allow it to be scheduled.
-void scheduleAuxiliaryTask(AuxiliaryTask task)
+void BeagleRT_scheduleAuxiliaryTask(AuxiliaryTask task)
 {
 	InternalAuxiliaryTask *taskToSchedule = (InternalAuxiliaryTask *)task;
 
@@ -311,12 +339,21 @@
 
 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;
 	}
 
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+	// 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;
+	}
+#endif
+
 	// Start all RT threads
 	if(rt_task_start(&gRTAudioThread, &audioLoop, 0)) {
 		  cout << "Error: unable to start Xenomai audio thread" << endl;
@@ -365,19 +402,28 @@
 // Free any resources associated with PRU real-time audio
 void BeagleRT_cleanupAudio()
 {
-	cleanup_render();
+	cleanup(&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
+#ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
+	rt_intr_delete(&gRTAudioInterrupt);
+#endif
+	rt_task_delete(&gRTAudioThread);
+
 	if(gPRU != 0)
 		delete gPRU;
 	if(gAudioCodec != 0)
@@ -431,7 +477,7 @@
 }
 
 // Set the verbosity level
-void setVerboseLevel(int level)
+void BeagleRT_setVerboseLevel(int level)
 {
 	gRTAudioVerbose = level;
 }