# HG changeset patch # User andrewm # Date 1432838155 14400 # Node ID 579c8631600896b28fd0faf3106ff2b92a77a157 # Parent 419ce4ebfc4c04a29dc8fafddb3d4a429226e9be 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. diff -r 419ce4ebfc4c -r 579c86316008 .cproject --- a/.cproject Wed May 13 12:23:37 2015 +0100 +++ b/.cproject Thu May 28 14:35:55 2015 -0400 @@ -5,12 +5,12 @@ + - @@ -26,32 +26,32 @@ - - - - @@ -92,7 +92,7 @@ - + @@ -103,12 +103,12 @@ + - @@ -129,6 +129,7 @@ @@ -142,6 +143,9 @@ @@ -177,7 +181,7 @@ - + diff -r 419ce4ebfc4c -r 579c86316008 core/PRU.cpp --- a/core/PRU.cpp Wed May 13 12:23:37 2015 +0100 +++ b/core/PRU.cpp Thu May 28 14:35:55 2015 -0400 @@ -18,7 +18,7 @@ #include "../include/pruss_intc_mapping.h" #include "../include/digital_gpio_mapping.h" #include "../include/GPIOcontrol.h" -#include "../include/render.h" +#include "../include/BeagleRT.h" #include "../include/pru_rtaudio_bin.h" #include @@ -99,13 +99,17 @@ const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14 -extern int gShouldStop; +extern bool gShouldStop; extern int gRTAudioVerbose; // Constructor: specify a PRU number (0 or 1) -PRU::PRU() -: pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false), - gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0) +PRU::PRU(BeagleRTContext *input_context) +: context(input_context), pru_number(0), running(false), analog_enabled(false), + digital_enabled(false), gpio_enabled(false), led_enabled(false), + gpio_test_pin_enabled(false), + pru_buffer_comm(0), pru_buffer_spi_dac(0), pru_buffer_spi_adc(0), + pru_buffer_digital(0), pru_buffer_audio_dac(0), pru_buffer_audio_adc(0), + xenomai_gpio_fd(-1), xenomai_gpio(0) { } @@ -127,9 +131,9 @@ // viewed on a scope. If include_led is set, // user LED 3 on the BBB is taken over by the PRU // to indicate activity -int PRU::prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led) +int PRU::prepareGPIO(int include_test_pin, int include_led) { - if(use_spi) { + if(context->analogFrames != 0) { // Prepare DAC CS/ pin: output, high to begin if(gpio_export(kPruGPIODACSyncPin)) { if(gRTAudioVerbose) @@ -162,12 +166,11 @@ return -1; } - spi_enabled = true; + analog_enabled = true; } - if(use_digital){ - printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels); - for(int i=0; idigitalFrames != 0){ + for(unsigned int i = 0; i < context->digitalChannels; i++){ if(gpio_export(digitalPins[i])) { if(gRTAudioVerbose) cerr << "Warning: couldn't export digital GPIO pin " << digitalPins[i] << "\n"; // this is left as a warning because if the pin has been exported by somebody else, can still be used @@ -178,7 +181,7 @@ return -1; } } - digital_enabled=true; + digital_enabled = true; } if(include_test_pin) { @@ -246,12 +249,12 @@ { if(!gpio_enabled) return; - if(spi_enabled) { + if(analog_enabled) { gpio_unexport(kPruGPIODACSyncPin); gpio_unexport(kPruGPIOADCSyncPin); } if(digital_enabled){ - for(int i=0; idigitalChannels; i++){ gpio_unexport(digitalPins[i]); } } @@ -281,20 +284,13 @@ pru_number = pru_num; - /* Set number of SPI ADC / DAC channels to use. This implicitly - * also determines the sample rate relative to the audio clock - * (half audio clock for 8 channels, full audio clock for 4, - * double audio clock for 2) - */ - spi_num_channels = spi_channels; - /* Initialize structure used by prussdrv_pruintc_intc */ /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */ tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; /* Allocate and initialize memory */ prussdrv_init(); - if(prussdrv_open(pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1)) { + if(prussdrv_open(PRU_EVTOUT_0)) { rt_printf("Failed to open PRU driver\n"); return 1; } @@ -302,24 +298,20 @@ /* Map PRU's INTC */ prussdrv_pruintc_init(&pruss_intc_initdata); - spi_buffer_frames = frames_per_buffer; - audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4; - digital_buffer_frames = audio_buffer_frames; - /* Map PRU memory to pointers */ prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)]; pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)]; /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */ - pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_buffer_frames]; + pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * context->audioFrames]; - if(spi_enabled) { + if(analog_enabled) { prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem); pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)]; /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */ - pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames]; + pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * context->analogChannels * context->analogFrames]; } else { pru_buffer_spi_dac = pru_buffer_spi_adc = 0; @@ -332,10 +324,11 @@ else { pru_buffer_digital = 0; } + /* Set up flags */ pru_buffer_comm[PRU_SHOULD_STOP] = 0; pru_buffer_comm[PRU_CURRENT_BUFFER] = 0; - pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames; + pru_buffer_comm[PRU_BUFFER_FRAMES] = context->analogFrames; pru_buffer_comm[PRU_SHOULD_SYNC] = 0; pru_buffer_comm[PRU_SYNC_ADDRESS] = 0; pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0; @@ -347,9 +340,9 @@ pru_buffer_comm[PRU_LED_ADDRESS] = 0; pru_buffer_comm[PRU_LED_PIN_MASK] = 0; } - if(spi_enabled) { + if(analog_enabled) { pru_buffer_comm[PRU_USE_SPI] = 1; - pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels; + pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = context->analogChannels; } else { pru_buffer_comm[PRU_USE_SPI] = 0; @@ -367,7 +360,7 @@ /* Clear ADC and DAC memory.*/ //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames // (otherwise might cause issues if we move memory locations later on) - if(spi_enabled) { + if(analog_enabled) { for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++) pru_buffer_spi_dac[i] = 0; if(digital_enabled){ @@ -377,7 +370,7 @@ } for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++) pru_buffer_audio_dac[i] = 0; -//TODO: maybe the lines below are to be deleted, as we removed the test code from pru_rtaudio.p ? + /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */ if(xenomai_test_pin && xenomai_gpio_fd < 0) { xenomai_gpio_fd = open("/dev/mem", O_RDWR); @@ -401,10 +394,8 @@ int PRU::start(char * const filename) { /* Clear any old interrupt */ - if(pru_number == 0) - prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); - else - prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT); + prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); + /* Load and execute binary on PRU */ if(filename[0] == '\0') { //if the string is empty, load the embedded code if(gRTAudioVerbose) @@ -425,114 +416,187 @@ running = true; return 0; } -uint32_t empty[1024]={0x0}; // Main loop to read and write data from/to PRU -void PRU::loop() +void PRU::loop(RT_INTR *pru_interrupt, void *userData) { // Polling interval is 1/4 of the period - RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4; - float *audioInBuffer, *audioOutBuffer; - float *analogInBuffer, *analogOutBuffer, *lastAnalogOutFrame; + //RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4; + + RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period + float *lastAnalogOutFrame; uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer; + uint32_t pru_audio_offset, pru_spi_offset; + int result; - audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); - audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); - analogInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float)); - analogOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float)); - lastAnalogOutFrame = (float *)malloc(spi_num_channels * sizeof(float)); - digitalBuffer0 = pru_buffer_digital; - digitalBuffer1 = pru_buffer_digital+MEM_DIGITAL_BUFFER1_OFFSET/sizeof(uint32_t); - digital_buffer_frames = digital_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the digital is disabled e.g.: - // - embed in the digitalWrite/Read macros a check whether digital is enabled - // - allocate some memory in ARM just to allow render() to run regardless. - // in this case it can be digitalBuffer0 == digitalBuffer1 - printf("digital_buffer_frames: %d;\n",digital_buffer_frames); - lastDigitalBuffer = (uint32_t *)malloc(digital_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states - if(audioInBuffer == 0 || audioOutBuffer == 0) { + // Allocate audio buffers + context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float)); + context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float)); + if(context->audioIn == 0 || context->audioOut == 0) { rt_printf("Error: couldn't allocate audio buffers\n"); return; } - if(analogInBuffer == 0 || analogOutBuffer == 0 || lastAnalogOutFrame == 0) { - rt_printf("Error: couldn't allocate analog buffers\n"); - return; - } - if(lastDigitalBuffer == 0) { - rt_printf("Error: couldn't allocate digital buffers\n"); - return; + + // Allocate analog buffers + if(analog_enabled) { + context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float)); + context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float)); + lastAnalogOutFrame = (float *)malloc(context->analogChannels * sizeof(float)); + + if(context->analogIn == 0 || context->analogOut == 0 || lastAnalogOutFrame == 0) { + rt_printf("Error: couldn't allocate analog buffers\n"); + return; + } + + memset(lastAnalogOutFrame, 0, context->analogChannels * sizeof(float)); } - for(unsigned int n=0; ndigitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states + if(lastDigitalBuffer == 0) { + rt_printf("Error: couldn't allocate digital buffers\n"); + return; + } + + for(unsigned int n = 0; n < context->digitalFrames; n++){ + // Initialize lastDigitalFrames to all inputs + lastDigitalBuffer[n] = 0x0000ffff; + } } + + // TESTING + uint32_t testCount = 0; + // RTIME startTime = rt_timer_read(); + while(!gShouldStop) { - // Wait for PRU to move to buffer 1 - while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) { - rt_task_sleep(sleepTime); + // Wait for PRU to move to change buffers; + // PRU will send an interrupts which we wait for + rt_intr_enable(pru_interrupt); + while(!gShouldStop) { + result = rt_intr_wait(pru_interrupt, irqTimeout); + if(result >= 0) + break; + else if(result == -ETIMEDOUT) + rt_printf("Warning: PRU timeout!\n"); + else { + rt_printf("Error: wait for interrupt failed (%d)\n", result); + gShouldStop = 1; + } } + + // Clear pending PRU interrupt + prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT); + if(gShouldStop) break; + // Check which buffer we're on-- will have been set right + // before the interrupt was asserted + if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) { + // PRU is on buffer 1. We read and write to buffer 0 + pru_audio_offset = 0; + pru_spi_offset = 0; + if(digital_enabled) + context->digital = digitalBuffer0; + } + else { + // PRU is on buffer 0. We read and write to buffer 1 + pru_audio_offset = context->audioFrames * 2; + pru_spi_offset = context->analogFrames * context->analogChannels; + if(digital_enabled) + context->digital = digitalBuffer1; + } + + // FIXME: some sort of margin is needed here to prevent the audio + // code from completely eating the Linux system + testCount++; + //rt_task_sleep(sleepTime*4); + //rt_task_sleep(sleepTime/4); + if(xenomai_gpio != 0) { // Set the test pin high xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; } - // Render from/to buffer 0 + // Convert short (16-bit) samples to float + // TODO: NEON + for(unsigned int n = 0; n < 2 * context->audioFrames; n++) + context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0; - // Convert short (16-bit) samples to float - for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) - audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0; - if(spi_enabled) { - for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) - analogInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0; - //initialize the output buffer with the values that were in the last frame of the previous output - for(int n = 0; n < spi_num_channels; n++){ - for(unsigned int j = 0; j < spi_buffer_frames; j++){ - analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n]; + if(analog_enabled) { + // TODO: NEON + for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) + context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0; + + if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { + // Initialize the output buffer with the values that were in the last frame of the previous output + for(unsigned int ch = 0; ch < context->analogChannels; ch++){ + for(unsigned int n = 0; n < context->analogFrames; n++){ + context->analogOut[n * context->analogChannels + ch] = lastAnalogOutFrame[ch]; + } } } - //use past digital values to initialize the array properly. - //For each frame: - //- pins previously set as outputs will keep the output value they had in the last frame of the previous buffer, - //- pins previously set as inputs will carry the newly read input value - if(digital_enabled){ - for(unsigned int n = 0; n < digital_buffer_frames; n++){ - uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs -// printf("inputs: 0x%x\n",inputs); - uint16_t outputs=~inputs; //half-word has 1 for outputs and 0 for inputs; - digitalBuffer0[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer - (digitalBuffer0[n]&(inputs<<16)) | //inputs from current digitalBuffer0[n]; - (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n] -// digitalBuffer0[n]=digitalBufferTemp[n]; //ignores inputs - } - } - render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, - analogInBuffer, analogOutBuffer, digitalBuffer0); - //remember the content of the lastAnalogOutFrame - for(int n = 0; n < spi_num_channels; n++){ - lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n]; + else { + // Outputs are 0 unless set otherwise + memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float)); } - for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) { - int out = analogOutBuffer[n] * 65536.0; + } + + if(digital_enabled){ + // Use past digital values to initialize the array properly. + // For each frame: + // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer, + // - pins previously set as inputs will carry the newly read input value + + for(unsigned int n = 0; n < context->digitalFrames; n++){ + uint16_t inputs = lastDigitalBuffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs + + uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs; + context->digital[n] = (lastDigitalBuffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer + (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n]; + (lastDigitalBuffer[n] & (inputs)); // keep pin configuration from previous context->digital[n] +// context->digital[n]=digitalBufferTemp[n]; //ignores inputs + } + } + + // Call user render function + // *********************** + render(context, userData); + // *********************** + + if(analog_enabled) { + if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { + // Remember the content of the lastAnalogOutFrame + for(unsigned int ch = 0; ch < context->analogChannels; ch++){ + lastAnalogOutFrame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch]; + } + } + + // Convert float back to short for SPI output + for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) { + int out = context->analogOut[n] * 65536.0; if(out < 0) out = 0; else if(out > 65535) out = 65535; - pru_buffer_spi_dac[n] = (uint16_t)out; + pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out; } - if(digital_enabled){ // keep track of past digital values - for(unsigned int n = 0; n < digital_buffer_frames; n++){ - lastDigitalBuffer[n]=digitalBuffer0[n]; - } - } } - else - render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused - // Convert float back to short - for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { - int out = audioOutBuffer[n] * 32768.0; + + if(digital_enabled) { // keep track of past digital values + for(unsigned int n = 0; n < context->digitalFrames; n++){ + lastDigitalBuffer[n] = context->digital[n]; + } + } + + // Convert float back to short for audio + // TODO: NEON + for(unsigned int n = 0; n < 2 * context->audioFrames; n++) { + int out = context->audioOut[n] * 32768.0; if(out < -32768) out = -32768; else if(out > 32767) out = 32767; - pru_buffer_audio_dac[n] = (int16_t)out; + pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out; } if(xenomai_gpio != 0) { @@ -540,91 +604,42 @@ xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; } - // Wait for PRU to move to buffer 0 - while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) { - rt_task_sleep(sleepTime); - } + // FIXME: TESTING!! + if(testCount > 100000) + break; + } - if(gShouldStop) - break; + // Turn off the interrupt for the PRU if it isn't already off + rt_intr_disable(pru_interrupt); - if(xenomai_gpio != 0) { - // Set the test pin high - xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; - } - - // Render from/to buffer 1 - - // Convert short (16-bit) samples to float - for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) - audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0; - - if(spi_enabled) { - //convert input values TODO: move to PRU - for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){ - analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0; - } - //initialize the output buffer with the values that were in the last frame of the previous output - for(int n = 0; n < spi_num_channels; n++){ - for(unsigned int j = 0; j < spi_buffer_frames; j++){ - analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n]; - } - } - if(digital_enabled){ - for(unsigned int n = 0; n < digital_buffer_frames; n++){ - uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs - uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs; - digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer - (digitalBuffer1[n]&(inputs<<16)) | //inputs from current digitalBuffer1[n]; - (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n] -// digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs - } - } - render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, - analogInBuffer, analogOutBuffer, digitalBuffer1); - //remember the content of the lastAnalogOutFrame - for(int n = 0; n < spi_num_channels; n++){ - lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n]; - } - - for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) { - int out = analogOutBuffer[n] * 65536.0; - if(out < 0) out = 0; - else if(out > 65535) out = 65535; - pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out; - } - if(digital_enabled){ // keep track of past digital values - for(unsigned int n = 0; n < digital_buffer_frames; n++){ - lastDigitalBuffer[n]=digitalBuffer1[n]; - } - } - } - else - render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused - - // Convert float back to short - for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { - int out = audioOutBuffer[n] * 32768.0; - if(out < -32768) out = -32768; - else if(out > 32767) out = 32767; - pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out; - } - - if(xenomai_gpio != 0) { - // Set the test pin high - xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; - } - } + // FIXME: TESTING + // RTIME endTime = rt_timer_read(); + // RTIME diffTime = endTime - startTime; + // rt_printf("%d blocks elapsed in %f seconds, %f Hz block rate\n", testCount, ((float)diffTime / 1.0e9), (float)testCount / ((float)diffTime / 1.0e9)); // Tell PRU to stop pru_buffer_comm[PRU_SHOULD_STOP] = 1; - free(analogOutBuffer); - free(audioInBuffer); - free(audioOutBuffer); - free(analogInBuffer); - free(lastAnalogOutFrame); - free(lastDigitalBuffer); + // Wait two buffer lengths for the PRU to finish + rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2); + + // Clean up after ourselves + free(context->audioIn); + free(context->audioOut); + + if(analog_enabled) { + free(context->analogIn); + free(context->analogOut); + free(lastAnalogOutFrame); + } + + if(digital_enabled) { + free(lastDigitalBuffer); + } + + context->audioIn = context->audioOut = 0; + context->analogIn = context->analogOut = 0; + context->digital = 0; } // Wait for an interrupt from the PRU indicate it is finished @@ -632,11 +647,8 @@ { if(!running) return; - prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1); - if(pru_number == 0) - prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); - else - prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT); + prussdrv_pru_wait_event (PRU_EVTOUT_0); + prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); } // Turn off the PRU when done diff -r 419ce4ebfc4c -r 579c86316008 core/RTAudio.cpp --- 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 #include #include +#include #include -#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::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; } diff -r 419ce4ebfc4c -r 579c86316008 core/RTAudioCommandLine.cpp --- a/core/RTAudioCommandLine.cpp Wed May 13 12:23:37 2015 +0100 +++ b/core/RTAudioCommandLine.cpp Thu May 28 14:35:55 2015 -0400 @@ -9,10 +9,10 @@ #include #include #include -#include "../include/RTAudio.h" +#include "../include/BeagleRT.h" #ifndef OPT_PRU_FILE -#define OPT_PRU_FILE 176 // this is an extended ascii code +#define OPT_PRU_FILE 176 // this is an extended ASCII code #endif // Default command-line options for RTAudio @@ -30,31 +30,40 @@ {"hp-level", 1, NULL, 'H'}, {"receive-port", 1, NULL, 'r'}, {"transmit-port", 1, NULL, 't'}, - {"server-name",1,NULL,'s'}, - {"pru-file",1,NULL,OPT_PRU_FILE}, + {"server-name", 1, NULL, 's'}, + {"pru-file", 1, NULL, OPT_PRU_FILE}, {NULL, 0, NULL, 0} }; const char gDefaultShortOptions[] = "p:vm:M:C:D:A:H:g:G:r:t:s:"; -// This function sets the default settings for the RTAudioSettings structure -void BeagleRT_defaultSettings(RTAudioSettings *settings) +// This function sets the default settings for the BeagleRTInitSettings structure +void BeagleRT_defaultSettings(BeagleRTInitSettings *settings) { // Set default values for settings settings->periodSize = 8; + settings->useAnalog = 1; + settings->useDigital = 1; + settings->numAnalogChannels = 8; + settings->numDigitalChannels = 16; + settings->beginMuted = 0; settings->dacLevel = DEFAULT_DAC_LEVEL; settings->adcLevel = DEFAULT_ADC_LEVEL; settings->headphoneLevel = DEFAULT_HP_LEVEL; - settings->useAnalog = 1; - settings->useDigital = 1; - settings->numAnalogChannels = 8; - settings->numDigitalChannels = 16; + settings->verbose = 0; - settings->pruFilename[0]='\0'; + settings->pruFilename[0] = '\0'; + + // These two deliberately have no command-line flags by default. + // A given program might prefer one mode or another, but it's unlikely + // the user would want to switch at runtime + settings->interleave = 1; + settings->analogOutputsPersist = 1; + settings->codecI2CAddress = CODEC_I2C_ADDRESS; - settings->receivePort=9998; - settings->transmitPort=9999; + settings->receivePort = 9998; + settings->transmitPort = 9999; strcpy(settings->serverName, "127.0.0.1"); settings->ampMutePin = kAmplifierMutePin; } @@ -65,7 +74,7 @@ // be stored in settings, otherwise arguments will be returned // as getopt() normally does. -int BeagleRT_getopt_long(int argc, char *argv[], const char *customShortOptions, const struct option *customLongOptions, RTAudioSettings *settings) +int BeagleRT_getopt_long(int argc, char *argv[], const char *customShortOptions, const struct option *customLongOptions, BeagleRTInitSettings *settings) { static int firstRun = 1; static char totalShortOptions[256]; @@ -189,7 +198,7 @@ " Using default severName Instead ( " << settings->serverName << " ).\n"; break; case OPT_PRU_FILE: - if(strlen(optarg)pruFilename, optarg); else std::cerr << "Warning: filename for the PRU code is too long (>" << MAX_PRU_FILENAME_LENGTH << " characters). Using embedded PRU code instead\n"; diff -r 419ce4ebfc4c -r 579c86316008 core/Utilities.cpp --- a/core/Utilities.cpp Wed May 13 12:23:37 2015 +0100 +++ b/core/Utilities.cpp Thu May 28 14:35:55 2015 -0400 @@ -7,13 +7,91 @@ #include "../include/Utilities.h" +// analogReadFrame() +// +// Returns the value of the given analog input at the given frame number. +inline float analogReadFrame(BeagleRTContext *context, int frame, int channel) { + return context->analogIn[frame * context->analogChannels + channel]; +} + +// analogWriteFrame() +// +// Sets a given channel to a value for the current frame and, if persistent outputs are +// enabled, for all subsequent frames +inline void analogWriteFrame(BeagleRTContext *context, int frame, int channel, float value) { + if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { + for(unsigned int f = frame; f < context->analogFrames; f++) + context->analogOut[frame * context->analogChannels + channel] = value; + } + else + context->analogOut[frame * context->analogChannels + channel] = value; +} + +// analogWriteFrameOnce() +// +// Sets a given channel to a value for only the current frame +inline void analogWriteFrameOnce(BeagleRTContext *context, int frame, int channel, float value) { + context->analogOut[frame * context->analogChannels + channel] = value; +} + +// digitalReadFrame() +// +// Returns the value of a given digital input at the given frame number +inline int digitalReadFrame(BeagleRTContext *context, int frame, int channel) { + return getBit(context->digital[frame], channel + 16); +} + +// digitalWriteFrame() +// +// Sets a given digital output channel to a value for the current frame and all subsequent frames +inline void digitalWriteFrame(BeagleRTContext *context, int frame, int channel, int value) { + for(unsigned int f = frame; f < context->digitalFrames; f++) { + if(value) + context->digital[f] |= 1 << (channel + 16); + else + context->digital[f] &= ~(1 << (channel + 16)); + } +} + +// digitalWriteFrameOnce() +// +// Sets a given digital output channel to a value for the current frame only +inline void digitalWriteFrameOnce(BeagleRTContext *context, int frame, int channel, int value) { + if(value) + context->digital[frame] |= 1 << (channel + 16); + else + context->digital[frame] &= ~(1 << (channel + 16)); +} + +// pinModeFrame() +// +// Sets the direction of a digital pin for the current frame and all subsequent frames +inline void pinModeFrame(BeagleRTContext *context, int frame, int channel, int mode) { + for(unsigned int f = frame; f < context->digitalFrames; f++) { + if(mode) + context->digital[f] |= (1 << channel); + else + context->digital[f] &= ~(1 << channel); + } +} + +// pinModeFrameOnce() +// +// Sets the direction of a digital pin for the current frame only +inline void pinModeFrameOnce(BeagleRTContext *context, int frame, int channel, int mode) { + if(mode) + context->digital[frame] |= (1 << channel); + else + context->digital[frame] &= ~(1 << channel); +} + // map() // // Scale an input value from one range to another. Works like its Wiring language equivalent. // x is the value to scale; in_min and in_max are the input range; out_min and out_max // are the output range. -float map(float x, float in_min, float in_max, float out_min, float out_max) +inline float map(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } @@ -23,7 +101,7 @@ // Clips an input value to be between two end points // x is the value to constrain; min_val and max_val are the range -float constrain(float x, float min_val, float max_val) +inline float constrain(float x, float min_val, float max_val) { if(x < min_val) return min_val; if(x > max_val) return max_val; diff -r 419ce4ebfc4c -r 579c86316008 core/client.cpp --- a/core/client.cpp Wed May 13 12:23:37 2015 +0100 +++ b/core/client.cpp Thu May 28 14:35:55 2015 -0400 @@ -97,7 +97,7 @@ return -1; } printf("Received a datagram: "); - printf(inBuffer); + printf("%s", inBuffer); //the worst parser ever int previousN=0; int currentVariable=0; diff -r 419ce4ebfc4c -r 579c86316008 include/PRU.h --- a/include/PRU.h Wed May 13 12:23:37 2015 +0100 +++ b/include/PRU.h Thu May 28 14:35:55 2015 -0400 @@ -9,6 +9,8 @@ #define PRU_H_ #include +#include +#include "../include/BeagleRT.h" class PRU { @@ -21,26 +23,26 @@ public: // Constructor - PRU(); + PRU(BeagleRTContext *input_context); // Destructor ~PRU(); // Prepare the GPIO pins needed for the PRU - int prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led); + int prepareGPIO(int include_test_pin, int include_led); // Clean up the GPIO at the end void cleanupGPIO(); // Initialise and open the PRU - int initialise(int pru_num, int frames_per_buffer, int spi_channels, - bool xenomai_test_pin = false); + int initialise(int pru_num, int frames_per_buffer, + int spi_channels, bool xenomai_test_pin = false); // Run the code image in pru_rtaudio_bin.h int start(char * const filename); // Loop: read and write data from the PRU - void loop(); + void loop(RT_INTR *pru_interrupt, void *userData); // Wait for an interrupt from the PRU indicate it is finished void waitForFinish(); @@ -53,14 +55,15 @@ void clearGPIOTestPin(); private: + BeagleRTContext *context; // Overall settings + int pru_number; // Which PRU we use bool running; // Whether the PRU is running - bool spi_enabled; // Whether SPI ADC and DAC are used + bool analog_enabled; // Whether SPI ADC and DAC are used bool digital_enabled; // Whether digital is used bool gpio_enabled; // Whether GPIO has been prepared bool led_enabled; // Whether a user LED is enabled bool gpio_test_pin_enabled; // Whether the test pin was also enabled - int spi_num_channels; // How many channels to use for SPI ADC/DAC volatile uint32_t *pru_buffer_comm; uint16_t *pru_buffer_spi_dac; @@ -68,9 +71,6 @@ uint32_t *pru_buffer_digital; int16_t *pru_buffer_audio_dac; int16_t *pru_buffer_audio_adc; - unsigned int spi_buffer_frames; - unsigned int digital_buffer_frames; - unsigned int audio_buffer_frames; int xenomai_gpio_fd; // File descriptor for /dev/mem for fast GPIO uint32_t *xenomai_gpio; // Pointer to GPIO registers diff -r 419ce4ebfc4c -r 579c86316008 include/Utilities.h --- a/include/Utilities.h Wed May 13 12:23:37 2015 +0100 +++ b/include/Utilities.h Thu May 28 14:35:55 2015 -0400 @@ -8,8 +8,30 @@ #ifndef UTILITIES_H_ #define UTILITIES_H_ -extern int gNumAudioChannels; // How many audio channels are present -extern int gNumAnalogChannels; // How many analog channels are present +#include "BeagleRT.h" + +#define setBit(word,bit) ((word) | (1 << (bit))) +#define clearBit(word,bit) ((word) &~ (1 << (bit))) +#define getBit(word,bit) (((word) >> (bit)) & 1) +#define changeBit(word,bit,value) ((clearBit((word),(bit))) | ((value) << (bit))) + +#if 1 +// Note: pinMode(), analogWrite() and digitalWrite() should be able to be called from initialise_render() +// Likewise, thread launch should be able to be called from initialise_render() +// Also, make volume change functions callable from render() thread -- as an aux task? + +float analogReadFrame(BeagleRTContext *context, int frame, int channel); +void analogWriteFrame(BeagleRTContext *context, int frame, int channel, float value); +void analogWriteFrameOnce(BeagleRTContext *context, int frame, int channel, float value); + +int digitalReadFrame(BeagleRTContext *context, int frame, int channel); +void digitalWriteFrame(BeagleRTContext *context, int frame, int channel, int value); +void digitalWriteFrameOnce(BeagleRTContext *context, int frame, int channel, int value); + +void pinModeFrame(BeagleRTContext *context, int frame, int channel, int mode); +void pinModeFrameOnce(BeagleRTContext *context, int frame, int channel, int mode); + +#else // Macros for accessing the analog values: usable _only_ within render() @@ -24,10 +46,7 @@ }\ } while (0);}),(void)0)\ -#define setBit(word,bit) ((word)|(1<<(bit))) -#define clearBit(word,bit) ((word)&~(1<<(bit))) -#define getBit(word,bit) (((word)>>(bit))&1) -#define changeBit(word,bit,value) ((clearBit((word),(bit))) | ((value)<<(bit))) + //digital API: #define setDigitalDirectionFrame(pin,frame,direction) digital[(frame)]=changeBit(digital[(frame)],(pin),(direction)),void(0) #define setDigitalDirection(pin,frame,direction)\ @@ -46,6 +65,8 @@ #define digitalRead(pin, frame) ( getBit(digital[(frame)], pin+16) ) +#endif + float map(float x, float in_min, float in_max, float out_min, float out_max); float constrain(float x, float min_val, float max_val); diff -r 419ce4ebfc4c -r 579c86316008 projects/basic/main.cpp --- a/projects/basic/main.cpp Wed May 13 12:23:37 2015 +0100 +++ b/projects/basic/main.cpp Thu May 28 14:35:55 2015 -0400 @@ -10,7 +10,7 @@ #include #include #include -#include "../../include/RTAudio.h" +#include "../../include/BeagleRT.h" using namespace std; @@ -33,7 +33,7 @@ int main(int argc, char *argv[]) { - RTAudioSettings settings; // Standard audio settings + BeagleRTInitSettings settings; // Standard audio settings float frequency = 440.0; // Frequency of oscillator struct option customOptions[] = diff -r 419ce4ebfc4c -r 579c86316008 projects/basic/render.cpp --- a/projects/basic/render.cpp Wed May 13 12:23:37 2015 +0100 +++ b/projects/basic/render.cpp Thu May 28 14:35:55 2015 -0400 @@ -6,7 +6,7 @@ */ -#include "../../include/render.h" +#include "../../include/BeagleRT.h" #include float gFrequency; @@ -22,16 +22,12 @@ // // Return true on success; returning false halts the program. -bool initialise_render(int numMatrixChannels, int numDigitalChannels, int numAudioChannels, - int numMatrixFramesPerPeriod, - int numAudioFramesPerPeriod, - float matrixSampleRate, float audioSampleRate, - void *userData, RTAudioSettings* settings) +bool initialise_render(BeagleRTContext *context, void *userData) { // Retrieve a parameter passed in from the initAudio() call gFrequency = *(float *)userData; - gInverseSampleRate = 1.0 / audioSampleRate; + gInverseSampleRate = 1.0 / context->audioSampleRate; gPhase = 0.0; return true; @@ -42,24 +38,23 @@ // ADCs and DACs (if available). If only audio is available, numMatrixFrames // will be 0. -void render(int numAnalogFrames, int numAudioFrames, int numDigitalFrames, float *audioIn, float *audioOut, - float *analogIn, float *analogOut, uint32_t *digital) +void render(BeagleRTContext *context, void *userData) { - for(int n = 0; n < numAudioFrames; n++) { + for(unsigned int n = 0; n < context->audioFrames; n++) { float out = 0.8f * sinf(gPhase); gPhase += 2.0 * M_PI * gFrequency * gInverseSampleRate; if(gPhase > 2.0 * M_PI) gPhase -= 2.0 * M_PI; - for(int channel = 0; channel < gNumAudioChannels; channel++) - audioOut[n * gNumAudioChannels + channel] = out; + for(unsigned int channel = 0; channel < context->audioChannels; channel++) + context->audioOut[n * context->audioChannels + channel] = out; } } // cleanup_render() is called once at the end, after the audio has stopped. // Release any resources that were allocated in initialise_render(). -void cleanup_render() +void cleanup_render(BeagleRTContext *context, void *userData) { } diff -r 419ce4ebfc4c -r 579c86316008 projects/oscillator_bank/render.cpp --- a/projects/oscillator_bank/render.cpp Wed May 13 12:23:37 2015 +0100 +++ b/projects/oscillator_bank/render.cpp Thu May 28 14:35:55 2015 -0400 @@ -58,11 +58,11 @@ // in from the call to initAudio(). // // Return true on success; returning false halts the program. -bool initialise_render(int numMatrixChannels, int numAudioChannels, +bool initialise_render(int numMatrixChannels, int numDigitalChannels, int numAudioChannels, int numMatrixFramesPerPeriod, int numAudioFramesPerPeriod, float matrixSampleRate, float audioSampleRate, - void *userData) + void *userData, RTAudioSettings* settings) { srandom(time(NULL)); @@ -125,10 +125,28 @@ gDFrequencies[n] = gDAmplitudes[n] = 0.0; } + increment = 0; + freq = 440.0; + + for(int n = 0; n < gNumOscillators; n++) { + // Update the frequencies to a regular spread, plus a small amount of randomness + // to avoid weird phase effects + float randScale = 0.99 + .02 * (float)random() / (float)RAND_MAX; + float newFreq = freq * randScale; + + // For efficiency, frequency is expressed in change in wavetable position per sample, not Hz or radians + gFrequencies[n] = newFreq * (float)gWavetableLength / audioSampleRate; + + freq += increment; + } + // Initialise auxiliary tasks if((gFrequencyUpdateTask = createAuxiliaryTaskLoop(&recalculate_frequencies, 90, "beaglert-update-frequencies")) == 0) return false; + for(int n = 0; n < gNumOscillators; n++) + rt_printf("%f\n", gFrequencies[n]); + gAudioSampleRate = audioSampleRate; gSampleCount = 0; @@ -140,8 +158,8 @@ // ADCs and DACs (if available). If only audio is available, numMatrixFrames // will be 0. -void render(int numMatrixFrames, int numAudioFrames, float *audioIn, float *audioOut, - uint16_t *matrixIn, uint16_t *matrixOut) +void render(int numAnalogFrames, int numAudioFrames, int numDigitalFrames, float *audioIn, float *audioOut, + float *analogIn, float *analogOut, uint32_t *digital) { // Initialise buffer to 0 memset(audioOut, 0, 2 * numAudioFrames * sizeof(float)); @@ -153,10 +171,10 @@ gDFrequencies, gDAmplitudes, gWavetable); - if(numMatrixFrames != 0 && (gSampleCount += numAudioFrames) >= 128) { + if(numAnalogFrames != 0 && (gSampleCount += numAudioFrames) >= 128) { gSampleCount = 0; - gNewMinFrequency = map(matrixIn[0], 0, MATRIX_MAX, 20.0f, 8000.0f); - gNewMaxFrequency = map(matrixIn[1], 0, MATRIX_MAX, 20.0f, 8000.0f); + gNewMinFrequency = map(analogIn[0], 0, 1.0, 1000.0f, 8000.0f); + gNewMaxFrequency = map(analogIn[1], 0, 1.0, 1000.0f, 8000.0f); // Make sure max >= min if(gNewMaxFrequency < gNewMinFrequency) { @@ -166,7 +184,7 @@ } // Request that the lower-priority task run at next opportunity - scheduleAuxiliaryTask(gFrequencyUpdateTask); + //scheduleAuxiliaryTask(gFrequencyUpdateTask); } } diff -r 419ce4ebfc4c -r 579c86316008 pru_rtaudio.bin Binary file pru_rtaudio.bin has changed diff -r 419ce4ebfc4c -r 579c86316008 pru_rtaudio.p --- a/pru_rtaudio.p Wed May 13 12:23:37 2015 +0100 +++ b/pru_rtaudio.p Thu May 28 14:35:55 2015 -0400 @@ -31,7 +31,8 @@ #define GPIO_CLEARDATAOUT 0x190 #define GPIO_SETDATAOUT 0x194 -#define PRU0_ARM_INTERRUPT 19 +#define PRU0_ARM_INTERRUPT 19 // Interrupt signalling we're done +#define PRU1_ARM_INTERRUPT 20 // Interrupt signalling a block is ready #define C_ADC_DAC_MEM C24 // PRU0 mem #ifdef DBOX_CAPE @@ -990,7 +991,8 @@ // Notify ARM of buffer swap AND r2, reg_flags, (1 << FLAG_BIT_BUFFER1) // Mask out every but low bit SBBO r2, reg_comm_addr, COMM_CURRENT_BUFFER, 4 - + MOV R31.b0, PRU1_ARM_INTERRUPT + 16 // Interrupt to host loop + // Increment the frame count in the comm buffer (for status monitoring) LBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4 ADD r2, r2, reg_frame_total