diff --git a/platform/drivers/audio/inputStream_GDx.c b/platform/drivers/audio/inputStream_GDx.c deleted file mode 100644 index 4d9038f7..00000000 --- a/platform/drivers/audio/inputStream_GDx.c +++ /dev/null @@ -1,55 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include - -streamId inputStream_start(const enum AudioSource source, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t bufLength, - const enum BufMode mode, - const uint32_t sampleRate) -{ - (void) source; - (void) prio; - (void) buf; - (void) bufLength; - (void) mode; - (void) sampleRate; - - return -1; -} - -dataBlock_t inputStream_getData(streamId id) -{ - (void) id; - - dataBlock_t block; - block.data = NULL; - block.len = 0; - return block; -} - -void inputStream_stop(streamId id) -{ - (void) id; -} diff --git a/platform/drivers/audio/inputStream_MDx.cpp b/platform/drivers/audio/inputStream_MDx.cpp deleted file mode 100644 index 8e252ecf..00000000 --- a/platform/drivers/audio/inputStream_MDx.cpp +++ /dev/null @@ -1,292 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace miosix; - - -static bool inUse = false; // Flag to determine if the input stream is already open. -static Thread *sWaiting = 0; // Thread waiting on interrupt. -static stream_sample_t *bufAddr = 0; // Start address of data buffer, fixed. -static stream_sample_t *bufCurr = 0; // Buffer address to be returned to application. -static size_t bufLen = 0; // Buffer length. -static uint8_t bufMode = BUF_LINEAR; // Buffer management mode. - -void __attribute__((used)) DmaHandlerImpl() -{ - if(DMA2->LISR & (DMA_LISR_TCIF2 | DMA_LISR_HTIF2)) - { - switch(bufMode) - { - case BUF_LINEAR: - // Finish, stop DMA and ADC - DMA2_Stream2->CR &= ~DMA_SxCR_EN; - ADC2->CR2 &= ~ADC_CR2_ADON; - break; - - case BUF_CIRC_DOUBLE: - // Return half of the buffer but do not stop the DMA - if(DMA2->LISR & DMA_LISR_HTIF2) - bufCurr = bufAddr; // Return first half - else - bufCurr = bufAddr + (bufLen / 2); // Return second half - break; - - default: - break; - } - - // Wake up the thread - if(sWaiting != 0) - { - sWaiting->IRQwakeup(); - Priority prio = sWaiting->IRQgetPriority(); - if(prio > Thread::IRQgetCurrentThread()->IRQgetPriority()) - Scheduler::IRQfindNextThread(); - sWaiting = 0; - } - } - - DMA2->LIFCR |= DMA_LIFCR_CTEIF2 // Clear transfer error flag (not handled) - | DMA_LIFCR_CHTIF2 // Clear half transfer flag - | DMA_LIFCR_CTCIF2; // Clear transfer completed flag -} - -void __attribute__((naked)) DMA2_Stream2_IRQHandler() -{ - saveContext(); - asm volatile("bl _Z14DmaHandlerImplv"); - restoreContext(); -} - - -streamId inputStream_start(const enum AudioSource source, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t bufLength, - const enum BufMode mode, - const uint32_t sampleRate) -{ - (void) prio; // TODO: input stream does not have priority - - // Check if buffer is in CCM area or not, since DMA cannot access CCM RAM - if(reinterpret_cast< uint32_t >(buf) < 0x20000000) return -1; - - /* - * Critical section for inUse flag management, makes the code below - * thread-safe. - */ - { - FastInterruptDisableLock dLock; - if(inUse) return -1; - inUse = true; - } - - bufMode = mode; - bufAddr = buf; - bufLen = bufLength; - - RCC->APB2ENR |= RCC_APB2ENR_ADC2EN; // Enable ADC - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Enable conv. timebase timer - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable DMA - __DSB(); - - /* - * TIM2 for conversion triggering via TIM2_TRGO, that is counter reload. - * AP1 frequency is 42MHz but timer runs at 84MHz, tick rate is 1MHz, - * reload register is configured based on desired sample rate. - */ - tim_setUpdateFreqency(TIM2, sampleRate, 84000000); - - TIM2->CNT = 0; - TIM2->EGR = TIM_EGR_UG; - TIM2->CR2 = TIM_CR2_MMS_1; - TIM2->CR1 = TIM_CR1_CEN; - - /* DMA2 Stream 2 common configuration: - * - channel 1: ADC2 - * - high priority - * - half-word transfer, both memory and peripheral - * - increment memory - * - peripheral-to-memory transfer - */ - DMA2_Stream2->PAR = reinterpret_cast< uint32_t >(&(ADC2->DR)); - DMA2_Stream2->M0AR = reinterpret_cast< uint32_t >(buf); - DMA2_Stream2->NDTR = bufLength; - DMA2_Stream2->CR = DMA_SxCR_CHSEL_0 // Channel 1 - | DMA_SxCR_MSIZE_0 // Memory size: 16 bit - | DMA_SxCR_PSIZE_0 // Peripheral size: 16 bit - | DMA_SxCR_PL_1 // High priority - | DMA_SxCR_MINC; // Increment memory - - /* - * Configure DMA and memory pointers according to buffer management mode. - * In linear and circular mode all the buffer is returned, in double circular - * buffer mode the buffer pointer is managed inside the DMA ISR. - */ - switch(mode) - { - case BUF_LINEAR: - DMA2_Stream2->CR |= DMA_SxCR_TCIE; // Interrupt on transfer end - bufCurr = bufAddr; // Return all the buffer - break; - - case BUF_CIRC_DOUBLE: - DMA2_Stream2->CR |= DMA_SxCR_CIRC // Circular mode - | DMA_SxCR_HTIE // Interrupt on half transfer - | DMA_SxCR_TCIE; // Interrupt on transfer end - break; - - default: - inUse = false; // Invalid setting, release flag and return error. - return -1; - break; - } - - // Configure NVIC interrupt - NVIC_ClearPendingIRQ(DMA2_Stream2_IRQn); - NVIC_SetPriority(DMA2_Stream2_IRQn, 10); - NVIC_EnableIRQ(DMA2_Stream2_IRQn); - - /* - * ADC2 configuration. - * - * ADC clock is APB2 frequency divided by 4, giving 21MHz. - * Channel sample time set to 84 cycles, total conversion time is 100 - * cycles: this leads to a maximum sampling frequency of 210kHz. - * Convert one channel only, no overrun interrupt, 12-bit resolution, - * no analog watchdog, discontinuous mode, no end of conversion interrupts. - */ - ADC->CCR |= ADC_CCR_ADCPRE_0; - ADC2->SMPR2 = ADC_SMPR2_SMP2_2 - | ADC_SMPR2_SMP1_2; - ADC2->SQR1 = 0; // Convert one channel - ADC2->CR1 |= ADC_CR1_DISCEN; - ADC2->CR2 |= ADC_CR2_EXTEN_0 // Trigger on rising edge - | ADC_CR2_EXTSEL_1 - | ADC_CR2_EXTSEL_2 // 0b0110 TIM2_TRGO trig. source - | ADC_CR2_DDS // Enable DMA data transfer - | ADC_CR2_DMA; - - /* - * Select ADC channel according to signal source: - * - CH3, mic input on PA3 (vox level) - * - CH13, audio from RTX on PC13 - */ - switch(source) - { - case SOURCE_MIC: - gpio_setMode(GPIOA, 3, INPUT_ANALOG); - ADC2->SQR3 = 3; - break; - - case SOURCE_RTX: - gpio_setMode(GPIOC, 3, INPUT_ANALOG); - ADC2->SQR3 = 13; - break; - - default: - inUse = false; // Unsupported source, release flag and return error. - return -1; - break; - } - - if(mode == BUF_CIRC_DOUBLE) - { - DMA2_Stream2->CR |= DMA_SxCR_EN; // Enable DMA - ADC2->CR2 |= ADC_CR2_ADON; // Enable ADC - } - - return 0; -} - -dataBlock_t inputStream_getData(streamId id) -{ - dataBlock_t block; - block.data = NULL; - block.len = 0; - - // Invalid stream ID, return an empty data block - if(id < 0) return block; - - if(bufMode == BUF_LINEAR) - { - // Reload DMA configuration then start DMA and ADC, stopped in ISR - DMA2_Stream2->PAR = reinterpret_cast< uint32_t >(&(ADC2->DR)); - DMA2_Stream2->M0AR = reinterpret_cast< uint32_t >(bufAddr); - DMA2_Stream2->NDTR = bufLen; - DMA2_Stream2->CR |= DMA_SxCR_EN; - ADC2->CR2 |= ADC_CR2_ADON; - } - - /* - * Put the calling thread in waiting status until data is ready. - */ - { - FastInterruptDisableLock dLock; - sWaiting = Thread::IRQgetCurrentThread(); - do - { - Thread::IRQwait(); - { - FastInterruptEnableLock eLock(dLock); - Thread::yield(); - } - - }while(sWaiting); - } - - block.data = bufCurr; - block.len = bufLen; - if(bufMode == BUF_CIRC_DOUBLE) block.len /= 2; - - return block; -} - -void inputStream_stop(streamId id) -{ - if(id < 0) return; - - TIM2->CR1 &= ~TIM_CR1_CEN; // Shut down timebase - ADC2->CR2 &= ~ADC_CR2_ADON; // Shut down ADC - DMA2_Stream2->CR &= ~DMA_SxCR_EN; // Shut down DMA transfer - - RCC->APB2ENR &= ~RCC_APB2ENR_ADC2EN; // Disable ADC - RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; // Disable conv. timebase timer - __DSB(); - - // Critical section: release inUse flag, invalidate (partial) data, wake up - // thread. - { - FastInterruptDisableLock dLock; - inUse = false; - bufCurr = 0; - bufLen = 0; - if(sWaiting != 0) sWaiting->IRQwakeup(); - } -} diff --git a/platform/drivers/audio/inputStream_Mod17.cpp b/platform/drivers/audio/inputStream_Mod17.cpp deleted file mode 100644 index 5e8662cd..00000000 --- a/platform/drivers/audio/inputStream_Mod17.cpp +++ /dev/null @@ -1,290 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace miosix; - - -static bool inUse = false; // Flag to determine if the input stream is already open. -static Thread *sWaiting = 0; // Thread waiting on interrupt. -static stream_sample_t *bufAddr = 0; // Start address of data buffer, fixed. -static stream_sample_t *bufCurr = 0; // Buffer address to be returned to application. -static size_t bufLen = 0; // Buffer length. -static uint8_t bufMode = BUF_LINEAR; // Buffer management mode. - -void __attribute__((used)) DmaHandlerImpl() -{ - if(DMA2->LISR & (DMA_LISR_TCIF2 | DMA_LISR_HTIF2)) - { - switch(bufMode) - { - case BUF_LINEAR: - // Finish, stop DMA and ADC - DMA2_Stream2->CR &= ~DMA_SxCR_EN; - ADC2->CR2 &= ~ADC_CR2_ADON; - break; - - case BUF_CIRC_DOUBLE: - // Return half of the buffer but do not stop the DMA - if(DMA2->LISR & DMA_LISR_HTIF2) - bufCurr = bufAddr; // Return first half - else - bufCurr = bufAddr + (bufLen / 2); // Return second half - break; - - default: - break; - } - - // Wake up the thread - if(sWaiting != 0) - { - sWaiting->IRQwakeup(); - Priority prio = sWaiting->IRQgetPriority(); - if(prio > Thread::IRQgetCurrentThread()->IRQgetPriority()) - Scheduler::IRQfindNextThread(); - sWaiting = 0; - } - } - - DMA2->LIFCR |= DMA_LIFCR_CTEIF2 // Clear transfer error flag (not handled) - | DMA_LIFCR_CHTIF2 // Clear half transfer flag - | DMA_LIFCR_CTCIF2; // Clear transfer completed flag -} - -void __attribute__((naked)) DMA2_Stream2_IRQHandler() -{ - saveContext(); - asm volatile("bl _Z14DmaHandlerImplv"); - restoreContext(); -} - - -streamId inputStream_start(const enum AudioSource source, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t bufLength, - const enum BufMode mode, - const uint32_t sampleRate) -{ - (void) prio; // TODO: input stream does not have priority - - // Check if buffer is in CCM area or not, since DMA cannot access CCM RAM - if(reinterpret_cast< uint32_t >(buf) < 0x20000000) return -1; - - /* - * Critical section for inUse flag management, makes the code below - * thread-safe. - */ - { - FastInterruptDisableLock dLock; - if(inUse) return -1; - inUse = true; - } - - bufMode = mode; - bufAddr = buf; - bufLen = bufLength; - - RCC->APB2ENR |= RCC_APB2ENR_ADC2EN; // Enable ADC - RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Enable conv. timebase timer - RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN; // Enable DMA - __DSB(); - - /* - * TIM2 for conversion triggering via TIM2_TRGO, that is counter reload. - * AP1 frequency is 42MHz but timer runs at 84MHz, tick rate is 1MHz, - * reload register is configured based on desired sample rate. - */ - tim_setUpdateFreqency(TIM2, sampleRate, 84000000); - - TIM2->CNT = 0; - TIM2->EGR = TIM_EGR_UG; - TIM2->CR2 = TIM_CR2_MMS_1; - TIM2->CR1 = TIM_CR1_CEN; - - /* DMA2 Stream 2 common configuration: - * - channel 1: ADC2 - * - high priority - * - half-word transfer, both memory and peripheral - * - increment memory - * - peripheral-to-memory transfer - */ - DMA2_Stream2->PAR = reinterpret_cast< uint32_t >(&(ADC2->DR)); - DMA2_Stream2->M0AR = reinterpret_cast< uint32_t >(buf); - DMA2_Stream2->NDTR = bufLength; - DMA2_Stream2->CR = DMA_SxCR_CHSEL_0 // Channel 1 - | DMA_SxCR_MSIZE_0 // Memory size: 16 bit - | DMA_SxCR_PSIZE_0 // Peripheral size: 16 bit - | DMA_SxCR_PL_1 // High priority - | DMA_SxCR_MINC; // Increment memory - - /* - * Configure DMA and memory pointers according to buffer management mode. - * In linear and circular mode all the buffer is returned, in double circular - * buffer mode the buffer pointer is managed inside the DMA ISR. - */ - switch(mode) - { - case BUF_LINEAR: - DMA2_Stream2->CR |= DMA_SxCR_TCIE; // Interrupt on transfer end - bufCurr = bufAddr; // Return all the buffer - break; - - case BUF_CIRC_DOUBLE: - DMA2_Stream2->CR |= DMA_SxCR_CIRC // Circular mode - | DMA_SxCR_HTIE // Interrupt on half transfer - | DMA_SxCR_TCIE; // Interrupt on transfer end - break; - - default: - inUse = false; // Invalid setting, release flag and return error. - return -1; - break; - } - - // Configure NVIC interrupt - NVIC_ClearPendingIRQ(DMA2_Stream2_IRQn); - NVIC_SetPriority(DMA2_Stream2_IRQn, 10); - NVIC_EnableIRQ(DMA2_Stream2_IRQn); - - /* - * ADC2 configuration. - * - * ADC clock is APB2 frequency divided by 4, giving 21MHz. - * Channel sample time set to 84 cycles, total conversion time is 100 - * cycles: this leads to a maximum sampling frequency of 210kHz. - * Convert one channel only, no overrun interrupt, 12-bit resolution, - * no analog watchdog, discontinuous mode, no end of conversion interrupts. - */ - ADC->CCR |= ADC_CCR_ADCPRE_0; - ADC2->SMPR2 = ADC_SMPR2_SMP2_2 - | ADC_SMPR2_SMP1_2; - ADC2->SQR1 = 0; // Convert one channel - ADC2->CR1 |= ADC_CR1_DISCEN; - ADC2->CR2 |= ADC_CR2_EXTEN_0 // Trigger on rising edge - | ADC_CR2_EXTSEL_1 - | ADC_CR2_EXTSEL_2 // 0b0110 TIM2_TRGO trig. source - | ADC_CR2_DDS // Enable DMA data transfer - | ADC_CR2_DMA; - - /* - * Select ADC channel according to signal source: - * - CH1, audio from RTX on PA1 - * - CH2, audio from microphone on PA2 - */ - switch(source) - { - case SOURCE_MIC: - gpio_setMode(AUDIO_MIC, INPUT_ANALOG); - ADC2->SQR3 = 2; - break; - - case SOURCE_RTX: - gpio_setMode(BASEBAND_RX, INPUT_ANALOG); - ADC2->SQR3 = 1; - break; - - default: - inUse = false; // Unsupported source, release flag and return error. - return -1; - break; - } - - if(mode == BUF_CIRC_DOUBLE) - { - DMA2_Stream2->CR |= DMA_SxCR_EN; // Enable DMA - ADC2->CR2 |= ADC_CR2_ADON; // Enable ADC - } - - return 0; -} - -dataBlock_t inputStream_getData(streamId id) -{ - dataBlock_t block; - block.data = NULL; - block.len = 0; - - // Invalid stream ID, return an empty data block - if(id < 0) return block; - - if(bufMode == BUF_LINEAR) - { - // Reload DMA configuration then start DMA and ADC, stopped in ISR - DMA2_Stream2->PAR = reinterpret_cast< uint32_t >(&(ADC2->DR)); - DMA2_Stream2->M0AR = reinterpret_cast< uint32_t >(bufAddr); - DMA2_Stream2->NDTR = bufLen; - DMA2_Stream2->CR |= DMA_SxCR_EN; - ADC2->CR2 |= ADC_CR2_ADON; - } - - /* - * Put the calling thread in waiting status until data is ready. - */ - { - FastInterruptDisableLock dLock; - sWaiting = Thread::IRQgetCurrentThread(); - do - { - Thread::IRQwait(); - { - FastInterruptEnableLock eLock(dLock); - Thread::yield(); - } - - }while((sWaiting != 0) && (inUse == true)); - } - - block.data = bufCurr; - block.len = bufLen; - if(bufMode == BUF_CIRC_DOUBLE) block.len /= 2; - - return block; -} - -void inputStream_stop(streamId id) -{ - if(id < 0) return; - - TIM2->CR1 = 0; // Shut down timebase - ADC2->CR2 = 0; // Shut down ADC - DMA2_Stream2->CR = 0; // Shut down DMA transfer - - RCC->APB2ENR &= ~RCC_APB2ENR_ADC2EN; // Disable ADC - RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN; // Disable conv. timebase timer - RCC->AHB1ENR &= ~RCC_AHB1ENR_DMA2EN; // Disable DMA - __DSB(); - - // Critical section: release inUse flag and invalidate (partial) data. - // Releasing the "inUse" flag cause the wake up of pending threads. - FastInterruptDisableLock dLock; - bufCurr = 0; - bufLen = 0; - inUse = false; -} diff --git a/platform/drivers/audio/inputStream_linux.cpp b/platform/drivers/audio/inputStream_linux.cpp deleted file mode 100644 index 189e6c18..00000000 --- a/platform/drivers/audio/inputStream_linux.cpp +++ /dev/null @@ -1,366 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2022 - 2023 by Alain Carlucci * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -streamId gNextAvailableStreamId = 0; - -class InputStream -{ - public: - InputStream(enum AudioSource source, - enum AudioPriority priority, - stream_sample_t* buf, - size_t bufLength, - enum BufMode mode, - uint32_t sampleRate) - : m_run_thread(true), m_func_running(false) - { - if (bufLength % 2) - { - fprintf(stderr, "InputStream error: invalid bufLength %lu\n", - bufLength); - return; - } - - m_db_ready[0] = m_db_ready[1] = false; - - std::string sourceString; - switch (source) - { - case SOURCE_MIC: - sourceString = "MIC"; - break; - case SOURCE_MCU: - sourceString = "MCU"; - break; - case SOURCE_RTX: - sourceString = "RTX"; - break; - default: - break; - } - - m_fp = fopen((sourceString + ".raw").c_str(), "rb"); - if (!m_fp) - { - fprintf(stderr, "InputStream error: cannot open: %s.raw\n", - sourceString.c_str()); - return; - } - - fseek(m_fp, 0, SEEK_END); - m_size = ftell(m_fp); - fseek(m_fp, 0, SEEK_SET); - if (m_size % 2 || m_size == 0) - { - fprintf(stderr, "InputStream error: invalid file: %s.raw\n", - sourceString.c_str()); - return; - } - - m_valid = true; - - changeId(); - setStreamData(priority, buf, bufLength, mode, sampleRate); - } - - bool isValid() const - { - return m_valid; - } - - ~InputStream() - { - stopThread(); - - if (m_fp) fclose(m_fp); - } - - dataBlock_t getDataBlock() - { - if (!m_valid) return {nullptr, 0}; - - switch (m_mode) - { - case BufMode::BUF_LINEAR: - { - // With this mode, just sleep for the right amount of time - // and return the buffer content - if (!fillBuffer(m_buf, m_bufLength)) return {NULL, 0}; - - return {m_buf, m_bufLength}; - } - case BufMode::BUF_CIRC_DOUBLE: - { - // If this mode is selected, wait for the readiness of the - // current slice and return it - - int id = m_db_curread; - - // Wait for `m_buf` to be ready - while (!m_db_ready[id] && m_run_thread) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - if (!m_run_thread) return {NULL, 0}; - - // Return the buffer contents - auto* pos = m_buf + id * (m_bufLength / 2); - m_db_ready[id] = 0; - - // Update the read buffer - m_db_curread = (id + 1) % 2; - return {pos, m_bufLength / 2}; - } - default: - return {NULL, 0}; - } - } - - AudioPriority priority() const - { - return m_prio; - } - - streamId id() const - { - return m_id; - } - - void changeId() - { - m_id = gNextAvailableStreamId; - gNextAvailableStreamId += 1; - } - - void setStreamData(AudioPriority priority, - stream_sample_t* buf, - size_t bufLength, - BufMode mode, - uint32_t sampleRate) - { - if (!m_valid) return; - - stopThread(); - m_run_thread = true; // set it as runnable again - - // HERE stop thread - m_prio = priority; - m_buf = buf; - m_bufLength = bufLength; - m_mode = mode; - m_sampleRate = sampleRate; - - switch (m_mode) - { - case BufMode::BUF_LINEAR: - // TODO: stop a running thread - break; - case BufMode::BUF_CIRC_DOUBLE: - m_thread = - std::thread(std::bind(&InputStream::threadFunc, this)); - // TODO: start thread - break; - } - } - - private: - bool m_valid = false; - FILE* m_fp = nullptr; - uint64_t m_size = 0; - - streamId m_id; - AudioPriority m_prio; - BufMode m_mode; - uint32_t m_sampleRate = 0; - - stream_sample_t* m_buf = nullptr; - size_t m_bufLength = 0; - - size_t m_db_curwrite = 0; - size_t m_db_curread = 0; - std::atomic m_db_ready[2]; - std::atomic m_run_thread; - std::atomic m_func_running; - std::thread m_thread; - - // Emulate an ADC that reads to the circular buffer - void threadFunc() - { - m_db_ready[0] = m_db_ready[1] = false; - while (m_run_thread) - { - m_db_ready[0] = false; - m_db_curwrite = 0; - fillBuffer(m_buf, m_bufLength / 2); - m_db_ready[0] = true; - if (!m_run_thread) break; - - m_db_curwrite = 1; - m_db_ready[1] = false; - fillBuffer(m_buf + m_bufLength / 2, m_bufLength / 2); - m_db_ready[1] = true; - } - } - - // This is a blocking function that emulates an ADC writing to the - // specified memory region. It takes the same time that an ADC would take - // to sample the same quantity of data. - bool fillBuffer(stream_sample_t* dest, size_t sz) - { - size_t i = 0; - if (!m_run_thread) return false; - - assert(m_func_running == false); - m_func_running = true; - - auto reset_func_running = [&]() - { - assert(m_func_running == true); - m_func_running = false; - }; - - using std::chrono::microseconds; - - if (m_sampleRate > 0) - { - // Do a piecewise-sleep so that it's easily interruptible - uint64_t microsec = sz * 1000000 / m_sampleRate; - while (microsec > 10000) - { - if (!m_run_thread) - { - // Early exit if the class is being deallocated - reset_func_running(); - return false; - } - - std::this_thread::sleep_for(microseconds(10000)); - microsec -= 10000; - } - std::this_thread::sleep_for(microseconds(microsec)); - } - - if (!m_run_thread) - { - // Early exit if the class is being deallocated - reset_func_running(); - return false; - } - - // Fill the buffer - while (i < sz) - { - auto n = fread(dest + i, 2, sz - i, m_fp); - if (n < (sz - i)) fseek(m_fp, 0, SEEK_SET); - i += n; - } - - assert(i == sz); - reset_func_running(); - return true; - } - - void stopThread() - { - m_run_thread = false; - - while (m_func_running) - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - - if (m_thread.joinable()) m_thread.join(); - } -}; - -std::map> gOpenStreams; - -streamId inputStream_start(const enum AudioSource source, - const enum AudioPriority priority, - stream_sample_t* const buf, - const size_t bufLength, - const enum BufMode mode, - const uint32_t sampleRate) -{ - auto it = gOpenStreams.find(source); - if (it != gOpenStreams.end()) - { - auto& inputStream = it->second; - if (inputStream->priority() >= priority) return -1; - - inputStream->changeId(); - inputStream->setStreamData(priority, buf, bufLength, mode, sampleRate); - - return inputStream->id(); - } - - auto stream = std::make_unique(source, priority, buf, - bufLength, mode, sampleRate); - - if (!stream->isValid()) return -1; - - const auto id = stream->id(); - - // New stream, move it into the map - gOpenStreams[source] = std::move(stream); - - return id; -} - -dataBlock_t inputStream_getData(streamId id) -{ - InputStream* stream = nullptr; - for (auto& i : gOpenStreams) - if (i.second->id() == id) - { - stream = i.second.get(); - break; - } - - if (stream == nullptr) return dataBlock_t{NULL, 0}; - - return stream->getDataBlock(); -} - -void inputStream_stop(streamId id) -{ - AudioSource src; - bool found = false; - for (auto& i : gOpenStreams) - if (i.second->id() == id) - { - found = true; - src = i.first; - break; - } - - if (!found) return; - - gOpenStreams.erase(src); -} diff --git a/platform/drivers/audio/outputStream_GDx.c b/platform/drivers/audio/outputStream_GDx.c deleted file mode 100644 index 1b597939..00000000 --- a/platform/drivers/audio/outputStream_GDx.c +++ /dev/null @@ -1,63 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include - -streamId outputStream_start(const enum AudioSink destination, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t length, - const enum BufMode mode, - const uint32_t sampleRate) -{ - (void) destination; - (void) prio; - (void) buf; - (void) length; - (void) mode; - (void) sampleRate; - - return -1; -} - -stream_sample_t *outputStream_getIdleBuffer(const streamId id) -{ - (void) id; - - return NULL; -} - -bool outputStream_sync(const streamId id, const bool bufChanged) -{ - (void) id; - (void) bufChanged; - - return false; -} - -void outputStream_stop(const streamId id) -{ - (void) id; -} - -void outputStream_terminate(const streamId id) -{ - (void) id; -} diff --git a/platform/drivers/audio/outputStream_MDx.cpp b/platform/drivers/audio/outputStream_MDx.cpp deleted file mode 100644 index b75b553f..00000000 --- a/platform/drivers/audio/outputStream_MDx.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2022 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include - -static int priority = PRIO_BEEP; -static bool running = false; // Stream is running -static bool circularMode = false; // Circular mode enabled -static bool reqFinish = false; // Pending termination request -static size_t bufLen = 0; // Buffer length -static stream_sample_t *bufAddr = 0; // Start address of data buffer, fixed. -static stream_sample_t *idleBuf = 0; - -using namespace miosix; -static Thread *dmaWaiting = 0; - - -/** - * \internal - * Stop an ongoing transfer, deactivating timers and DMA stream. - */ -static inline void stopTransfer() -{ - TIM7->CR1 = 0; // Stop TIM7 - DMA1_Stream2->CR &= ~DMA_SxCR_EN; // Stop DMA transfer - TIM3->CCER &= ~TIM_CCER_CC3E; // Turn off compare channel - RCC->APB1ENR &= ~RCC_APB1ENR_TIM7EN; // Turn off TIM7 APB clock - __DSB(); - - // Re-activate "beeps" - toneGen_unlockBeep(); - - // Finally, clear flags and restore priority level - running = false; - reqFinish = false; - circularMode = false; - priority = PRIO_BEEP; -} - -/** - * \internal - * Actual implementation of DMA1 Stream2 interrupt handler. - */ -void __attribute__((used)) DMA_Handler() -{ - if(DMA1->LISR & DMA_LISR_HTIF2) - idleBuf = bufAddr; - else - idleBuf = bufAddr + (bufLen / 2); - - // Stop transfer for linear buffer mode or pending termination request. - if((circularMode == false) || (reqFinish == true)) - { - stopTransfer(); - } - - // Clear interrupt flags - DMA1->LIFCR = DMA_LIFCR_CTCIF2 - | DMA_LIFCR_CHTIF2 - | DMA_LIFCR_CTEIF2; - - // Finally, wake up eventual pending threads - if(dmaWaiting == 0) return; - dmaWaiting->IRQwakeup(); - if(dmaWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) - Scheduler::IRQfindNextThread(); - dmaWaiting = 0; -} - -void __attribute__((naked)) DMA1_Stream2_IRQHandler() -{ - saveContext(); - asm volatile("bl _Z11DMA_Handlerv"); - restoreContext(); -} - -streamId outputStream_start(const enum AudioSink destination, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t length, - const enum BufMode mode, - const uint32_t sampleRate) -{ - // Sanity check - if((buf == NULL) || (length == 0) || (sampleRate == 0)) return -1; - - // This device cannot sink to buffers - if(destination == SINK_MCU) return -1; - - // Check if an output stream is already opened and, in case, handle priority. - if(running) - { - if(prio < priority) return -1; // Lower priority, reject. - if(prio > priority) stopTransfer(); // Higher priority, takes over. - while(running) ; // Same priority, wait. - } - - // Thread-safe block: assign priority, set stream as running and lock "beeps" - __disable_irq(); - priority = prio; - running = true; - toneGen_lockBeep(); - __enable_irq(); - - /* - * Convert buffer elements from int16_t to unsigned 8 bit values, as - * required by tone generator. Processing can be done in-place because the - * API mandates that the function caller does not modify the buffer content - * once this function has been called. - */ - S16toU8(buf, length); - bufAddr = buf; - bufLen = length; - idleBuf = bufAddr + (bufLen / 2); - - RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; - RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; - __DSB(); - - /* - * Timebase for triggering of DMA transfers. - * Bus frequency for TIM7 is 84MHz. - */ - tim_setUpdateFreqency(TIM7, sampleRate, 84000000); - TIM7->CNT = 0; - TIM7->EGR = TIM_EGR_UG; - TIM7->DIER = TIM_DIER_UDE; - - /* - * DMA stream for sample transfer, destination is TIM3 CCR3 - */ - DMA1_Stream2->NDTR = length; - DMA1_Stream2->PAR = reinterpret_cast< uint32_t >(&(TIM3->CCR3)); - DMA1_Stream2->M0AR = reinterpret_cast< uint32_t >(buf); - DMA1_Stream2->CR = DMA_SxCR_CHSEL_0 // Channel 1 - | DMA_SxCR_PL // Very high priority - | DMA_SxCR_MSIZE_0 // 16 bit source size - | DMA_SxCR_PSIZE_0 // 16 bit destination size - | DMA_SxCR_MINC // Increment source pointer - | DMA_SxCR_DIR_0 // Memory to peripheral - | DMA_SxCR_TCIE // Transfer complete interrupt - | DMA_SxCR_TEIE; // Transfer error interrupt - - if(mode == BUF_CIRC_DOUBLE) - { - DMA1_Stream2->CR |= DMA_SxCR_CIRC // Circular buffer mode - | DMA_SxCR_HTIE; // Half transfer interrupt - circularMode = true; - } - - DMA1_Stream2->CR |= DMA_SxCR_EN; // Enable transfer - - // Enable DMA interrupts - NVIC_ClearPendingIRQ(DMA1_Stream2_IRQn); - NVIC_SetPriority(DMA1_Stream2_IRQn, 10); - NVIC_EnableIRQ(DMA1_Stream2_IRQn); - - // Enable compare channel - TIM3->CCR3 = buf[0]; - TIM3->CCER |= TIM_CCER_CC3E; - TIM3->CR1 |= TIM_CR1_CEN; - - // Start timer for DMA transfer triggering - TIM7->CR1 = TIM_CR1_CEN; - - return 0; -} - -stream_sample_t *outputStream_getIdleBuffer(const streamId id) -{ - (void) id; - - if(!circularMode) return nullptr; - - return idleBuf; -} - -bool outputStream_sync(const streamId id, const bool bufChanged) -{ - (void) id; - - if(circularMode && bufChanged) - { - stream_sample_t *ptr = outputStream_getIdleBuffer(id); - S16toU8(ptr, bufLen/2); - } - - // Enter in critical section until the end of the function - FastInterruptDisableLock dLock; - - Thread *curThread = Thread::IRQgetCurrentThread(); - if((dmaWaiting != 0) && (dmaWaiting != curThread)) return false; - dmaWaiting = curThread; - - do - { - Thread::IRQwait(); - { - // Re-enable interrupts while waiting for IRQ - FastInterruptEnableLock eLock(dLock); - Thread::yield(); - } - } - while((dmaWaiting != 0) && (running == true)); - - dmaWaiting = 0; - - return true; -} - -void outputStream_stop(const streamId id) -{ - (void) id; - - reqFinish = true; -} - -void outputStream_terminate(const streamId id) -{ - (void) id; - - __disable_irq(); - - stopTransfer(); - - DMA1->LIFCR = DMA_LIFCR_CTCIF2 - | DMA_LIFCR_CHTIF2 - | DMA_LIFCR_CTEIF2; - - __enable_irq(); -} diff --git a/platform/drivers/audio/outputStream_Mod17.cpp b/platform/drivers/audio/outputStream_Mod17.cpp deleted file mode 100644 index a74b1cd1..00000000 --- a/platform/drivers/audio/outputStream_Mod17.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -static int priority = PRIO_BEEP; -static bool running = false; // Stream is running -static bool circularMode = false; // Circular mode enabled -static bool reqFinish = false; // Pending termination request -static size_t bufLen = 0; // Buffer length -static stream_sample_t *bufAddr = 0; // Start address of data buffer, fixed. -static stream_sample_t *idleBuf = 0; - -using namespace miosix; -static Thread *dmaWaiting = 0; - - -/** - * \internal - * Stop an ongoing transfer, deactivating timers and DMA stream. - */ -static inline void stopTransfer() -{ - // Stop DMA transfers - DMA1_Stream5->CR = 0; - DMA1_Stream6->CR = 0; - - TIM7->CR1 = 0; // Shutdown timer - DAC->SR = 0; // Clear status flags - DAC->CR = DAC_CR_EN1; // Keep only channel 1 active - DAC->DHR12R1 = 1365; // Set channel 1 (RTX) to about 1.1V when idle - - // Clear flags and restore priority level - running = false; - reqFinish = false; - circularMode = false; - priority = PRIO_BEEP; -} - -/** - * \internal - * Actual implementation of DMA interrupt handler. - */ -void __attribute__((used)) DMA_Handler() -{ - // Manage half transfer interrupt - if((DMA1->HISR & DMA_HISR_HTIF5) || (DMA1->HISR & DMA_HISR_HTIF6)) - idleBuf = bufAddr; - else - idleBuf = bufAddr + (bufLen / 2); - - // Stop transfer for linear buffer mode or pending termination request. - if((circularMode == false) || (reqFinish == true)) - { - stopTransfer(); - } - - // Clear interrupt flags for stream 5 and 6 - uint32_t mask = DMA_HISR_TEIF5 - | DMA_HISR_TCIF5 - | DMA_HISR_HTIF5 - | DMA_HISR_TEIF6 - | DMA_HISR_TCIF6 - | DMA_HISR_HTIF6; - - DMA1->HIFCR = DMA1->HISR & mask; - - // Finally, wake up eventual pending threads - if(dmaWaiting == 0) return; - dmaWaiting->IRQwakeup(); - if(dmaWaiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority()) - Scheduler::IRQfindNextThread(); - dmaWaiting = 0; -} - -// DMA 1, Stream 5: data transfer for RTX sink -void __attribute__((used)) DMA1_Stream5_IRQHandler() -{ - saveContext(); - asm volatile("bl _Z11DMA_Handlerv"); - restoreContext(); -} - -// DMA 1, Stream 6: data transfer for speaker sink -void __attribute__((used)) DMA1_Stream6_IRQHandler() -{ - saveContext(); - asm volatile("bl _Z11DMA_Handlerv"); - restoreContext(); -} - - -streamId outputStream_start(const enum AudioSink destination, - const enum AudioPriority prio, - stream_sample_t * const buf, - const size_t length, - const enum BufMode mode, - const uint32_t sampleRate) -{ - // Sanity check - if((buf == NULL) || (length == 0) || (sampleRate == 0)) return -1; - - // This device cannot sink to buffers - if(destination == SINK_MCU) return -1; - - // Check if an output stream is already opened and, in case, handle priority. - if(running) - { - if(prio < priority) return -1; // Lower priority, reject. - if(prio > priority) stopTransfer(); // Higher priority, takes over. - while(running) ; // Same priority, wait. - } - - // Thread-safe block: assign priority, set stream as running and lock "beeps" - __disable_irq(); - priority = prio; - running = true; - __enable_irq(); - - /* - * Convert buffer elements from int16_t to unsigned 8 bit values, as - * required by tone generator. Processing can be done in-place because the - * API mandates that the function caller does not modify the buffer content - * once this function has been called. - */ - S16toU12(buf, length); - bufAddr = buf; - bufLen = length; - idleBuf = bufAddr + (bufLen / 2); - - // Configure GPIOs - gpio_setMode(BASEBAND_TX, INPUT_ANALOG); /* Baseband TX */ - gpio_setMode(AUDIO_SPK, INPUT_ANALOG); /* Spk output */ - - /* - * Enable peripherals - */ - RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN; - RCC->APB1ENR |= RCC_APB1ENR_DACEN - | RCC_APB1ENR_TIM7EN; - __DSB(); - - /* - * Configure DAC and DMA stream - */ - uint32_t circular = 0; - if(mode == BUF_CIRC_DOUBLE) - { - circular = DMA_SxCR_CIRC // Circular buffer mode - | DMA_SxCR_HTIE; // Half transfer interrupt - circularMode = true; - } - - if(destination == SINK_RTX) - { - DAC->CR |= DAC_CR_DMAEN1 // Enable DMA mode - | DAC_CR_TSEL1_1 // TIM7 TRGO as trigger source - | DAC_CR_TEN1 // Enable trigger input - | DAC_CR_EN1; // Enable DAC - - DMA1_Stream5->NDTR = length; - DMA1_Stream5->PAR = reinterpret_cast< uint32_t >(&(DAC->DHR12R1)); - DMA1_Stream5->M0AR = reinterpret_cast< uint32_t >(buf); - DMA1_Stream5->CR = DMA_SxCR_CHSEL // Channel 7 - | DMA_SxCR_PL // Very high priority - | DMA_SxCR_MSIZE_0 // 16 bit source size - | DMA_SxCR_PSIZE_0 // 16 bit destination size - | DMA_SxCR_MINC // Increment source pointer - | DMA_SxCR_TCIE // Transfer complete interrupt - | DMA_SxCR_TEIE // Transfer error interrupt - | DMA_SxCR_DIR_0 // Memory to peripheral - | circular // Circular mode - | DMA_SxCR_EN; // Start transfer - - NVIC_ClearPendingIRQ(DMA1_Stream5_IRQn); - NVIC_SetPriority(DMA1_Stream5_IRQn, 10); - NVIC_EnableIRQ(DMA1_Stream5_IRQn); - } - else - { - DAC->CR |= DAC_CR_DMAEN2 // Enable DMA mode - | DAC_CR_TSEL2_1 // TIM7 TRGO as trigger source - | DAC_CR_TEN2 // Enable trigger input - | DAC_CR_EN2; // Enable DAC - - DMA1_Stream6->NDTR = length; - DMA1_Stream6->PAR = reinterpret_cast< uint32_t >(&(DAC->DHR12R2)); - DMA1_Stream6->M0AR = reinterpret_cast< uint32_t >(buf); - DMA1_Stream6->CR = DMA_SxCR_CHSEL // Channel 7 - | DMA_SxCR_PL // Very high priority - | DMA_SxCR_MSIZE_0 // 16 bit source size - | DMA_SxCR_PSIZE_0 // 16 bit destination size - | DMA_SxCR_MINC // Increment source pointer - | DMA_SxCR_TCIE // Transfer complete interrupt - | DMA_SxCR_TEIE // Transfer error interrupt - | DMA_SxCR_DIR_0 // Memory to peripheral - | circular // Circular mode - | DMA_SxCR_EN; // Start transfer - - NVIC_ClearPendingIRQ(DMA1_Stream6_IRQn); - NVIC_SetPriority(DMA1_Stream6_IRQn, 10); - NVIC_EnableIRQ(DMA1_Stream6_IRQn); - } - - /* - * TIM7 for conversion triggering via TIM7_TRGO, that is counter reload. - * APB1 frequency is 42MHz but timer runs at 84MHz, tick rate is 1MHz, - * reload register is configured based on desired sample rate. - */ - tim_setUpdateFreqency(TIM7, sampleRate, 84000000); - TIM7->CNT = 0; - TIM7->EGR = TIM_EGR_UG; - TIM7->CR2 = TIM_CR2_MMS_1; - TIM7->CR1 = TIM_CR1_CEN; - - return 0; -} - -stream_sample_t *outputStream_getIdleBuffer(const streamId id) -{ - (void) id; - - if(!circularMode) return nullptr; - - return idleBuf; -} - -bool outputStream_sync(const streamId id, const bool bufChanged) -{ - (void) id; - - if(circularMode && bufChanged) - { - stream_sample_t *ptr = outputStream_getIdleBuffer(id); - S16toU12(ptr, bufLen/2); - } - - // Enter in critical section until the end of the function - FastInterruptDisableLock dLock; - - Thread *curThread = Thread::IRQgetCurrentThread(); - if((dmaWaiting != 0) && (dmaWaiting != curThread)) return false; - dmaWaiting = curThread; - - do - { - Thread::IRQwait(); - { - // Re-enable interrupts while waiting for IRQ - FastInterruptEnableLock eLock(dLock); - Thread::yield(); - } - } - while((dmaWaiting != 0) && (running == true)); - - dmaWaiting = 0; - - return true; -} - -void outputStream_stop(const streamId id) -{ - (void) id; - - reqFinish = true; -} - -void outputStream_terminate(const streamId id) -{ - (void) id; - - FastInterruptDisableLock dLock; - - stopTransfer(); - - DMA1->HIFCR = DMA_HIFCR_CTEIF5 - | DMA_HIFCR_CTCIF5 - | DMA_HIFCR_CHTIF5; - - DMA1->HIFCR = DMA_HIFCR_CTEIF6 - | DMA_HIFCR_CTCIF6 - | DMA_HIFCR_CHTIF6; -} diff --git a/platform/drivers/audio/outputStream_linux.c b/platform/drivers/audio/outputStream_linux.c deleted file mode 100644 index 60ca23dc..00000000 --- a/platform/drivers/audio/outputStream_linux.c +++ /dev/null @@ -1,272 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2021 - 2023 by Federico Amedeo Izzo IU2NUO, * - * Niccolò Izzo IU2KIN * - * Frederik Saraci IU2NRO * - * Silvano Seva IU2KWO * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 3 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, see * - ***************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Expand opaque pa_simple struct -struct pa_simple -{ - pa_threaded_mainloop *mainloop; - pa_context *context; - pa_stream *stream; - pa_stream_direction_t direction; - const void *read_data; - size_t read_index; - size_t read_length; - int operation_success; -}; - -static enum BufMode bufMode; // Buffer operation mode -static enum AudioPriority priority = PRIO_BEEP; // Priority level -static bool running = false; // Stream is running -static size_t bufLen = 0; // Total buffer length -static stream_sample_t *playBuf = NULL; // Buffer being reproduced -static stream_sample_t *idleBuf = NULL; // Idle buffer available to be filled -static pa_simple *paInstance = NULL; // Pulseaudio instance -static size_t remaining = 0; -static pthread_cond_t barrier; -static pthread_mutex_t mutex; - -static void buf_circ_write_cb(pa_stream* s, size_t length, void* userdata) -{ - (void) userdata; - - if((s == NULL) || (length <= 0)) - return; - - if(length > remaining) - { - // We can play all the rest of the buffer - pa_stream_write(s, playBuf, remaining * sizeof(stream_sample_t), - NULL, 0, PA_SEEK_RELATIVE); - remaining = 0; - } - else - { - pa_stream_write(s, playBuf, length, NULL, 0, PA_SEEK_RELATIVE); - - if (remaining > length) - remaining -= length; - else - remaining = 0; - } - - // All data in playBuffer has been sent - if(remaining == 0) - { - // Reload counter - remaining = bufLen/2; - - pthread_mutex_lock(&mutex); - - // Swap idle and play buffers - stream_sample_t *tmp = idleBuf; - playBuf = idleBuf; - idleBuf = tmp; - - // Unlock waiting threads - pthread_cond_signal(&barrier); - pthread_mutex_unlock(&mutex); - } -} - -streamId outputStream_start(const enum AudioSink destination, - const enum AudioPriority prio, - stream_sample_t* const buffer, - const size_t length, - const enum BufMode mode, - const uint32_t sampleRate) -{ - - if(destination != SINK_SPK) - return -1; - - // Check if an output stream is already opened and, in case, handle - // priority. - if(running) - { - if(prio < priority) return -1; // Lower priority, reject. - if(prio > priority) outputStream_stop(0); // Higher priority, takes over. - while(running) ; // Same priority, wait. - } - - // Assign priority and set stream as running - running = true; - priority = prio; - bufMode = mode; - playBuf = buffer; - idleBuf = buffer + (length/2); - bufLen = length; - remaining = length/2; - - int paError = 0; - bool success = true; - - if(paInstance == NULL) - { - // Stream data sample format - static pa_sample_spec spec; - spec.format = PA_SAMPLE_S16LE; - spec.rate = 0; - spec.channels = 1; - spec.rate = sampleRate; - - paInstance = pa_simple_new(NULL, "OpenRTX", PA_STREAM_PLAYBACK, NULL, - "Audio out", &spec, NULL, NULL, &paError); - - if(paInstance == NULL) - { - fprintf(stderr, __FILE__ ": pa_simple_new() failed: %s\n", - pa_strerror(paError)); - - success = false; - } - else - { - pthread_mutex_init(&mutex, NULL); - pthread_cond_init(&barrier, NULL); - } - } - - switch(mode) - { - case BUF_LINEAR: - if(pa_simple_write(paInstance, buffer, length, &paError) < 0) - success = false; - break; - - case BUF_CIRC_DOUBLE: - { - if(paInstance->stream == NULL) - { - success = false; - break; - } - - // Register write callback - pa_stream_set_write_callback(paInstance->stream, buf_circ_write_cb, - NULL); - - // Set minimal prebuffering - const pa_buffer_attr attr = - { - .fragsize = -1, - .maxlength = -1, - .minreq = -1, - .prebuf = 320, - .tlength = -1, - }; - - pa_stream_set_buffer_attr(paInstance->stream, &attr, NULL, NULL); - - // Get maximum pulse buffer size - size_t wsize = pa_stream_writable_size(paInstance->stream); - if(wsize > (length / 2)) - wsize = length / 2; - - // Start writing loop - pa_stream_write(paInstance->stream, playBuf, wsize, NULL, 0, - PA_SEEK_RELATIVE); - } - break; - } - - if(success == false) - { - running = false; - priority = PRIO_BEEP; - return -1; - } - - return 0; -} - -stream_sample_t *outputStream_getIdleBuffer(const streamId id) -{ - (void) id; - - stream_sample_t *ptr = NULL; - - if(bufMode == BUF_CIRC_DOUBLE) - { - pthread_mutex_lock(&mutex); - ptr = idleBuf; - pthread_mutex_unlock(&mutex); - } - - return ptr; -} - -bool outputStream_sync(const streamId id, const bool bufChanged) -{ - (void) id; - (void) bufChanged; - - if(bufMode == BUF_CIRC_DOUBLE) - { - pthread_mutex_lock(&mutex); - pthread_cond_wait(&barrier, &mutex); - pthread_mutex_unlock(&mutex); - } - - // - // TODO syncronisation barrrier also for linear buffer mode - // - - return true; -} - -void outputStream_stop(const streamId id) -{ - (void) id; - - int error = 0; - if (pa_simple_flush(paInstance, &error) < 0) - { - fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", - pa_strerror(error)); - } - - running = false; - priority = PRIO_BEEP; -} - -void outputStream_terminate(const streamId id) -{ - (void) id; - - running = false; - priority = PRIO_BEEP; - - if(paInstance != NULL) - { - pa_simple_free(paInstance); - pthread_mutex_destroy(&mutex); - pthread_cond_destroy(&barrier); - } -} diff --git a/platform/drivers/stubs/audio_stub.c b/platform/drivers/stubs/audio_stub.c index 1ef06715..cb8d0c2f 100644 --- a/platform/drivers/stubs/audio_stub.c +++ b/platform/drivers/stubs/audio_stub.c @@ -18,9 +18,21 @@ * along with this program; if not, see * ***************************************************************************/ -#include #include +const struct audioDevice outputDevices[] = +{ + {NULL, 0, 0, SINK_MCU}, + {NULL, 0, 0, SINK_RTX}, + {NULL, 0, 0, SINK_SPK}, +}; + +const struct audioDevice inputDevices[] = +{ + {NULL, 0, 0, SINK_MCU}, + {NULL, 0, 0, SINK_RTX}, + {NULL, 0, 0, SINK_SPK}, +}; void audio_init() {