diff core/RTAudio.cpp @ 67:472e892c6e41

Merge newapi into default
author Andrew McPherson <a.mcpherson@qmul.ac.uk>
date Fri, 17 Jul 2015 15:28:18 +0100
parents 3c3a1357657d
children f944d0b60fa8
line wrap: on
line diff
--- a/core/RTAudio.cpp	Sun Feb 08 00:20:01 2015 +0000
+++ b/core/RTAudio.cpp	Fri Jul 17 15:28:18 2015 +0100
@@ -24,13 +24,17 @@
 #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;
 
@@ -44,9 +48,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;
 
@@ -56,43 +64,46 @@
 bool gShouldStop = false;
 
 // general settings
+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 matrix channels, globally accessible
-// At least gNumMatrixChannels needs to be global to be used
-// by the analogRead() and analogWrite() macros without creating
-// extra confusion in their use cases by passing this argument
-int gNumAudioChannels = 0;
-int gNumMatrixChannels = 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.
 // periodSize indicates the number of _sensor_ frames per period: the audio period size
 // is twice this value. In total, the audio latency in frames will be 4*periodSize,
 // plus any latency inherent in the ADCs and DACs themselves.
-// useMatrix indicates whether to enable the ADC and DAC or just use the audio codec.
-// numMatrixChannels indicates how many ADC and DAC channels to use.
-// userData is an opaque pointer which will be passed through to the initialise_render()
+// 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 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);
 
-	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 << "; ";
-		if(settings->useMatrix)
-			cout << "matrix enabled\n";
+		if(settings->useAnalog)
+			cout << "analog enabled\n";
 		else
-			cout << "matrix disabled\n";
+			cout << "analog disabled\n";
 		cout << "DAC level " << settings->dacLevel << "dB; ADC level " << settings->adcLevel;
 		cout << "dB; headphone level " << settings->headphoneLevel << "dB\n";
 		if(settings->beginMuted)
@@ -106,7 +117,7 @@
 
 		if(gpio_export(settings->ampMutePin)) {
 			if(gRTAudioVerbose)
-				cout << "Warning: couldn't export amplifier mute pin\n";
+				cout << "Warning: couldn't export amplifier mute pin " << settings-> ampMutePin << "\n";
 		}
 		if(gpio_set_dir(settings->ampMutePin, OUTPUT_PIN)) {
 			if(gRTAudioVerbose)
@@ -120,36 +131,78 @@
 		}
 	}
 
-	// Limit the matrix channels to sane values
-	if(settings->numMatrixChannels >= 8)
-		settings->numMatrixChannels = 8;
-	else if(settings->numMatrixChannels >= 4)
-		settings->numMatrixChannels = 4;
+	// Limit the analog channels to sane values
+	if(settings->numAnalogChannels >= 8)
+		settings->numAnalogChannels = 8;
+	else if(settings->numAnalogChannels >= 4)
+		settings->numAnalogChannels = 4;
 	else
-		settings->numMatrixChannels = 2;
+		settings->numAnalogChannels = 2;
 
 	// Sanity check the combination of channels and period size
-	if(settings->numMatrixChannels <= 4 && settings->periodSize < 2) {
-		cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
+	if(settings->numAnalogChannels <= 4 && settings->periodSize < 2) {
+		cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
 		return 1;
 	}
-	if(settings->numMatrixChannels <= 2 && settings->periodSize < 4) {
-		cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
+	if(settings->numAnalogChannels <= 2 && settings->periodSize < 4) {
+		cout << "Error: " << settings->numAnalogChannels << " channels and period size of " << settings->periodSize << " not supported.\n";
 		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();
 
-	if(gPRU->prepareGPIO(settings->useMatrix, 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;
 	}
-	if(gPRU->initialise(0, settings->periodSize, settings->numMatrixChannels, true)) {
+
+	// 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;
@@ -164,25 +217,8 @@
 	BeagleRT_setADCLevel(settings->adcLevel);
 	BeagleRT_setHeadphoneLevel(settings->headphoneLevel);
 
-	// Initialise the rendering environment: pass the number of audio and matrix
-	// channels, the period size for matrix and audio, and the sample rates
-
-	int audioPeriodSize = settings->periodSize * 2;
-	float audioSampleRate = 44100.0;
-	float matrixSampleRate = 22050.0;
-	if(settings->useMatrix) {
-		audioPeriodSize = settings->periodSize * settings->numMatrixChannels / 4;
-		matrixSampleRate = audioSampleRate * 4.0 / (float)settings->numMatrixChannels;
-	}
-
-	gNumAudioChannels = 2;
-	gNumMatrixChannels = settings->useMatrix ? settings->numMatrixChannels : 0;
-
-	if(!initialise_render(gNumMatrixChannels, gNumAudioChannels,
-				          settings->useMatrix ? settings->periodSize : 0, /* matrix period size */
-				          audioPeriodSize,
-				          matrixSampleRate, audioSampleRate,
-				          userData)) {
+	// Call the user-defined initialisation function
+	if(!setup(&gContext, userData)) {
 		cout << "Couldn't initialise audio rendering\n";
 		return 1;
 	}
@@ -208,8 +244,8 @@
 		gShouldStop = 1;
 	}
 	else {
-		if(gPRU->start()) {
-			rt_printf("Error: unable to start PRU\n");
+		if(gPRU->start(gPRUFilename)) {
+			rt_printf("Error: unable to start PRU from file %s\n", gPRUFilename);
 			gShouldStop = 1;
 		}
 		else {
@@ -223,8 +259,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();
@@ -238,9 +277,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));
 
@@ -263,7 +303,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;
 
@@ -300,12 +340,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;
@@ -354,19 +403,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)
@@ -420,7 +478,7 @@
 }
 
 // Set the verbosity level
-void setVerboseLevel(int level)
+void BeagleRT_setVerboseLevel(int level)
 {
 	gRTAudioVerbose = level;
 }