From e2b5905aefdb5f43937e3c2f0c495e2a4c620d62 Mon Sep 17 00:00:00 2001 From: v1993 Date: Tue, 31 Jul 2018 15:53:09 +0300 Subject: [PATCH 01/26] Improve project structure Add mamespace isolation --- sound.h | 44 ---- soundDefines.h | 58 ++++++ soundMixer.cpp | 469 +++++++++++++++++++++--------------------- soundMixer.h | 112 +++++----- soundProvider.cpp | 38 ++-- soundProvider.h | 53 ++--- soundProviderTask.cpp | 54 ++--- soundProviderTask.h | 45 ++-- 8 files changed, 446 insertions(+), 427 deletions(-) create mode 100644 soundDefines.h diff --git a/sound.h b/sound.h index 36607ee..4b1ca3a 100644 --- a/sound.h +++ b/sound.h @@ -1,48 +1,4 @@ #pragma once -#include -#include -#include - -#include -#include -#include -#include - -#define SOUND_FREQ_TO_DELAY(f) (1000000/f) - -class SoundProvider; - -typedef unsigned int SoundChNum; -typedef uint8_t SoundData; -typedef long unsigned int SoundPos; -typedef unsigned int SoundVolume; - -enum SoundState { - STOPPED, - PLAYING, - PAUSED -}; - -enum SoundProviderControl { - END, - FAILURE -}; - -enum SoundEvent { - STOP, - START, - PAUSE, - RESUME, - VOLSET -}; - -struct SoundControl { - SoundEvent event; - SoundChNum channel; - SoundProvider *provider; - SoundVolume vol; -}; - #include #include diff --git a/soundDefines.h b/soundDefines.h new file mode 100644 index 0000000..734a0b6 --- /dev/null +++ b/soundDefines.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +} +#endif + +#define USING_NS_SOUND using namespace Sound + +namespace Sound { + class SoundProvider; + class SoundMixer; + + using SoundChNum = unsigned int; + using SoundData = uint8_t; + using SoundPos = long unsigned int; + using SoundVolume = unsigned int; + + enum SoundState { + STOPPED, + PLAYING, + PAUSED + }; + + enum SoundProviderControl { + END, + FAILURE + }; + + enum SoundEvent { + STOP, + START, + PAUSE, + RESUME, + VOLSET + }; + + struct SoundControl { + SoundEvent event; + SoundChNum channel; + SoundProvider *provider; + SoundVolume vol; + }; +} + diff --git a/soundMixer.cpp b/soundMixer.cpp index 39f89bd..028c5b9 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -1,4 +1,10 @@ -#include +#include "soundMixer.h" +#include "soundProvider.h" + +#define SOUND_FREQ_TO_DELAY(f) (1000000/f) +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif static int gcd(int a, int b) { while(true) { @@ -15,283 +21,284 @@ static int lcm(int a, int b) { return temp ? (a / temp * b) : 0; } -bool SoundMixer::handleQueue() { - xSemaphoreTake(mutex, portMAX_DELAY); - SoundControl ctrl; - bool upd = false; // Should we recalculate anything? - while(xQueueReceive(queue, &ctrl, 0) == pdTRUE) { // Handle all events without blocking - SoundChNum channel = ctrl.channel; - SoundProvider *sound; - if (ctrl.event == START) { - sound = ctrl.provider; - } else { - sound = chSound[channel]; +namespace Sound { + bool SoundMixer::handleQueue() { + xSemaphoreTake(mutex, portMAX_DELAY); + SoundControl ctrl; + bool upd = false; // Should we recalculate anything? + while(xQueueReceive(queue, &ctrl, 0) == pdTRUE) { // Handle all events without blocking + SoundChNum channel = ctrl.channel; + SoundProvider *sound; + if (ctrl.event == START) { + sound = ctrl.provider; + } else { + sound = chSound[channel]; + } + SoundVolume vol = ctrl.vol; + switch(ctrl.event) { + case STOP: + if (chActive[channel]) {upd = true; decSound();} + if (chActive[channel] || chPaused[channel]) { + chActive[channel] = false; + chPaused[channel] = false; + sound->provider_stop(); + } + break; + case START: // I'm sure that channel is free + upd = true; + incSound(); + chSound[channel] = sound; + chActive[channel] = true; + sound->provider_start(); + sound->actual = 0; + break; + case PAUSE: + if (chActive[channel]) { + upd = true; + decSound(); + chActive[channel] = false; + chPaused[channel] = true; + sound->provider_pause(); + } + break; + case RESUME: + if (chPaused[channel]) { + upd = true; + incSound(); + chActive[channel] = true; + chPaused[channel] = false; + sound->provider_resume(); + } + break; + case VOLSET: + chVolume[channel] = vol; + break; + } } - SoundVolume vol = ctrl.vol; - switch(ctrl.event) { - case STOP: - if (chActive[channel]) {upd = true; decSound();} - if (chActive[channel] || chPaused[channel]) { - chActive[channel] = false; - chPaused[channel] = false; - sound->provider_stop(); - } - break; - case START: // I'm sure that channel is free - upd = true; - incSound(); - chSound[channel] = sound; - chActive[channel] = true; - sound->provider_start(); - sound->actual = 0; - break; - case PAUSE: - if (chActive[channel]) { - upd = true; - decSound(); - chActive[channel] = false; - chPaused[channel] = true; - sound->provider_pause(); - } - break; - case RESUME: - if (chPaused[channel]) { - upd = true; - incSound(); - chActive[channel] = true; - chPaused[channel] = false; - sound->provider_resume(); - } - break; - case VOLSET: - chVolume[channel] = vol; + xSemaphoreGive(mutex); + return upd; + } + + void SoundMixer::setupTimer() { + SoundChNum activeCount = uxSemaphoreGetCount(chActiveCount); + counterMax = 1; + if (activeCount == 1) { // Only one sound + for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + chSound[i]->divisor = 1; + esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); break; + }} + } else { + SoundChNum n = 0; + unsigned long int freqArr[activeCount]; + for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + freqArr[n++] = chSound[i]->getFrequency(); + }} + + int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[activeCount]), freqArr[0], lcm); + for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + SoundProvider *sound = chSound[i]; + sound->divisor = freqLcm / sound->getFrequency(); + counterMax = lcm(counterMax, sound->divisor); + }} + esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(freqLcm)); } } - xSemaphoreGive(mutex); - return upd; -} -void SoundMixer::setupTimer() { - SoundChNum activeCount = uxSemaphoreGetCount(chActiveCount); - counterMax = 1; - if (activeCount == 1) { // Only one sound - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { - chSound[i]->divisor = 1; - esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); - break; - }} - } else { - SoundChNum n = 0; - unsigned long int freqArr[activeCount]; - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { - freqArr[n++] = chSound[i]->getFrequency(); - }} + void SoundMixer::soundCallback() { + bool upd = handleQueue(); + if (upd) { + esp_timer_stop(timer); // It will work OK anyway + if (uxSemaphoreGetCount(chActiveCount) == 0) { // If nothing to play + xSemaphoreGive(timerMutex); + return; + } + setupTimer(); // TODO: implement that function + counter = 0; // Only for later ++ + } + + counter++; + if (counter > counterMax) counter = 1; - int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[activeCount]), freqArr[0], lcm); + unsigned int out = 0; for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { SoundProvider *sound = chSound[i]; - sound->divisor = freqLcm / sound->getFrequency(); - counterMax = lcm(counterMax, sound->divisor); + if ((counter % sound->divisor) == 0) { + SoundData sample; + if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) { + sound->actual = sample; + } + } + out += sound->actual * chVolume[i]; + SoundProviderControl ctrl; + while(xQueueReceive(sound->controlQueue, &ctrl, 0) == pdTRUE) { + switch(ctrl) { + case END: + if (sound->repeat) { + sound->provider_restart(); + } else { + stop(i); + } + break; + case FAILURE: + stop(i); + break; + } + } }} - esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(freqLcm)); + + out = out / 255 / chCount; + dac_output_voltage(dacCh, min(out, 255)); // Do NOT overload } -} -void SoundMixer::soundCallback() { - bool upd = handleQueue(); - if (upd) { - esp_timer_stop(timer); // It will work OK anyway - if (uxSemaphoreGetCount(chActiveCount) == 0) { // If nothing to play - xSemaphoreGive(timerMutex); - return; - } - setupTimer(); // TODO: implement that function - counter = 0; // Only for later ++ + void SoundMixer::incSound() { + xSemaphoreGive(chActiveCount); } - counter++; - if (counter > counterMax) counter = 1; - - unsigned int out = 0; - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { - SoundProvider *sound = chSound[i]; - if ((counter % sound->divisor) == 0) { - SoundData sample; - if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) { - sound->actual = sample; - } - } - out += sound->actual * chVolume[i]; - SoundProviderControl ctrl; - while(xQueueReceive(sound->controlQueue, &ctrl, 0) == pdTRUE) { - switch(ctrl) { - case END: - if (sound->repeat) { - sound->provider_restart(); - } else { - stop(i); - } - break; - case FAILURE: - stop(i); - break; - } - } - }} + void SoundMixer::decSound() { + xSemaphoreTake(chActiveCount, portMAX_DELAY); + } - out = out / 255 / chCount; - dac_output_voltage(dacCh, min(out, 255)); // Do NOT overload -} + void SoundMixer::addEvent(SoundControl event) { + xQueueSendToBack(queue, &event, portMAX_DELAY); + } -void SoundMixer::incSound() { - xSemaphoreGive(chActiveCount); -} + SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac) { + chCount = normal_channels + auto_channels; + chFirstAuto = normal_channels; // It isn't mistake, but looks strange + assert(chCount <= CONFIG_SND_MAX_CHANNELS); + dacCh = dac; -void SoundMixer::decSound() { - xSemaphoreTake(chActiveCount, portMAX_DELAY); -} + dac_output_enable(dacCh); + esp_timer_create_args_t timer_args; -void SoundMixer::addEvent(SoundControl event) { - xQueueSendToBack(queue, &event, portMAX_DELAY); -} + timer_args.callback = reinterpret_cast(&SoundMixer::soundCallback); + timer_args.arg = this; + timer_args.dispatch_method = ESP_TIMER_TASK; + timer_args.name = "Sound timer"; -SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac) { - chCount = normal_channels + auto_channels; - chFirstAuto = normal_channels; // It isn't mistake, but looks strange - assert(chCount <= CONFIG_SND_MAX_CHANNELS); - dacCh = dac; + esp_timer_create(&timer_args, &timer); - dac_output_enable(dacCh); - esp_timer_create_args_t timer_args; + mutex = xSemaphoreCreateMutex(); + timerMutex = xSemaphoreCreateCounting(1, 1); + chActiveCount = xSemaphoreCreateCounting(chCount, 0); + queue = xQueueCreate(CONFIG_SND_CONTROL_QUEUE_SIZE, sizeof(SoundControl)); - timer_args.callback = reinterpret_cast(&SoundMixer::soundCallback); - timer_args.arg = this; - timer_args.dispatch_method = ESP_TIMER_TASK; - timer_args.name = "Sound timer"; + for (SoundChNum i = 0; i < chCount; i++) { // Set defaults + chActive[i] = false; + chPaused[i] = false; + chVolume[i] = 255; + chSound[i] = NULL; + } + } - esp_timer_create(&timer_args, &timer); + SoundMixer::~SoundMixer() { + esp_timer_stop(timer); + esp_timer_delete(timer); - mutex = xSemaphoreCreateMutex(); - timerMutex = xSemaphoreCreateCounting(1, 1); - chActiveCount = xSemaphoreCreateCounting(chCount, 0); - queue = xQueueCreate(CONFIG_SND_CONTROL_QUEUE_SIZE, sizeof(SoundControl)); + vSemaphoreDelete(mutex); + vSemaphoreDelete(timerMutex); + vSemaphoreDelete(chActiveCount); + vQueueDelete(queue); + } - for (SoundChNum i = 0; i < chCount; i++) { // Set defaults - chActive[i] = false; - chPaused[i] = false; - chVolume[i] = 255; - chSound[i] = NULL; + void SoundMixer::checkTimer() { + if (xSemaphoreTake(timerMutex, 0) == pdTRUE) { // If timer isn't active + esp_timer_start_once(timer, 0); // Activate one-shot handler + } } -} -SoundMixer::~SoundMixer() { - esp_timer_stop(timer); - esp_timer_delete(timer); + void SoundMixer::play(SoundChNum channel, SoundProvider *sound) { + stop(channel); - vSemaphoreDelete(mutex); - vSemaphoreDelete(timerMutex); - vSemaphoreDelete(chActiveCount); - vQueueDelete(queue); -} + SoundControl ctrl; + ctrl.event = START; + ctrl.channel = channel; + ctrl.provider = sound; + addEvent(ctrl); + + checkTimer(); + } -void SoundMixer::checkTimer() { - if (xSemaphoreTake(timerMutex, 0) == pdTRUE) { // If timer isn't active - esp_timer_start_once(timer, 0); // Activate one-shot handler + void SoundMixer::stop(SoundChNum channel) { + if (uxSemaphoreGetCount(timerMutex) == 0) { + SoundControl ctrl; + ctrl.event = STOP; + ctrl.channel = channel; + addEvent(ctrl); + } } -} -void SoundMixer::play(SoundChNum channel, SoundProvider *sound) { - stop(channel); + void SoundMixer::stopAll() { + for (SoundChNum i = 0; i < chCount; i++) { + stop(i); + } + } - SoundControl ctrl; - ctrl.event = START; - ctrl.channel = channel; - ctrl.provider = sound; - addEvent(ctrl); + void SoundMixer::pause(SoundChNum channel) { + if (uxSemaphoreGetCount(timerMutex) == 0) { + SoundControl ctrl; + ctrl.event = PAUSE; + ctrl.channel = channel; + addEvent(ctrl); + } + } - checkTimer(); -} + void SoundMixer::pauseAll() { + for (SoundChNum i = 0; i < chCount; i++) { + pause(i); + } + } -void SoundMixer::stop(SoundChNum channel) { - if (uxSemaphoreGetCount(timerMutex) == 0) { + void SoundMixer::resume(SoundChNum channel) { SoundControl ctrl; - ctrl.event = STOP; + ctrl.event = RESUME; ctrl.channel = channel; addEvent(ctrl); + + checkTimer(); } -} -void SoundMixer::stopAll() { - for (SoundChNum i = 0; i < chCount; i++) { - stop(i); + void SoundMixer::resumeAll() { + for (SoundChNum i = 0; i < chCount; i++) { + resume(i); + } } -} -void SoundMixer::pause(SoundChNum channel) { - if (uxSemaphoreGetCount(timerMutex) == 0) { + void SoundMixer::setVolume(SoundChNum channel, SoundVolume vol) { SoundControl ctrl; - ctrl.event = PAUSE; + ctrl.event = VOLSET; ctrl.channel = channel; - addEvent(ctrl); + ctrl.vol = vol; + addEvent(ctrl); // We don't call checkTimer because this event can be handled later } -} -void SoundMixer::pauseAll() { - for (SoundChNum i = 0; i < chCount; i++) { - pause(i); + SoundVolume SoundMixer::getVolume(SoundChNum channel) { + SoundVolume vol; + xSemaphoreTake(mutex, portMAX_DELAY); + vol = chVolume[channel]; + xSemaphoreGive(mutex); + return vol; } -} - -void SoundMixer::resume(SoundChNum channel) { - SoundControl ctrl; - ctrl.event = RESUME; - ctrl.channel = channel; - addEvent(ctrl); - checkTimer(); -} - - -void SoundMixer::resumeAll() { - for (SoundChNum i = 0; i < chCount; i++) { - resume(i); + SoundState SoundMixer::state(SoundChNum channel) { + xSemaphoreTake(mutex, portMAX_DELAY); + SoundState s; + if (chActive[channel]) s = PLAYING; + else if (chPaused[channel]) s = PAUSED; + else s = STOPPED; + xSemaphoreGive(mutex); + return s; } -} - -void SoundMixer::setVolume(SoundChNum channel, SoundVolume vol) { - SoundControl ctrl; - ctrl.event = VOLSET; - ctrl.channel = channel; - ctrl.vol = vol; - addEvent(ctrl); // We don't call checkTimer because this event can be handled later -} -SoundVolume SoundMixer::getVolume(SoundChNum channel) { - SoundVolume vol; - xSemaphoreTake(mutex, portMAX_DELAY); - vol = chVolume[channel]; - xSemaphoreGive(mutex); - return vol; -} - -SoundState SoundMixer::state(SoundChNum channel) { - xSemaphoreTake(mutex, portMAX_DELAY); - SoundState s; - if (chActive[channel]) s = PLAYING; - else if (chPaused[channel]) s = PAUSED; - else s = STOPPED; - xSemaphoreGive(mutex); - return s; -} - -SoundChNum SoundMixer::playAuto(SoundProvider *sound, SoundVolume vol) { - for (SoundChNum i = chFirstAuto; i < chCount; i++) { - if (state(i) == STOPPED) { // We found free channel, setting up - setVolume(i, vol); - play(i, sound); - return i; + SoundChNum SoundMixer::playAuto(SoundProvider *sound, SoundVolume vol) { + for (SoundChNum i = chFirstAuto; i < chCount; i++) { + if (state(i) == STOPPED) { // We found free channel, setting up + setVolume(i, vol); + play(i, sound); + return i; + } } + return chCount; // No free channels } - return chCount; // No free channels } diff --git a/soundMixer.h b/soundMixer.h index 9a85e51..e99dbb2 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -1,61 +1,53 @@ #pragma once - -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif - -#include -#include -#include - -class SoundProvider; - -class SoundMixer { - protected: - esp_timer_handle_t timer = NULL; // Timer for this instance - SemaphoreHandle_t mutex = NULL; // Mutex for this instance - SemaphoreHandle_t timerMutex = NULL; // Mutex for timer control - QueueHandle_t queue = NULL; // Queue for this instance - dac_channel_t dacCh; // DAC channel for sound - - unsigned long int counter = 1; // Only for callback - unsigned long int counterMax = 1; - - SoundChNum chCount; // Total channels count, <= CONFIG_SND_MAX_CHANNELS - SoundChNum chFirstAuto; // Number of first "auto" channel - SemaphoreHandle_t chActiveCount = NULL; // Count of active channels (to control timer) - SoundProvider* chSound[CONFIG_SND_MAX_CHANNELS]; // Sound provider pointers (you should carry about memory by youself) - bool chActive[CONFIG_SND_MAX_CHANNELS]; // Active channels, UNSAFE - bool chPaused[CONFIG_SND_MAX_CHANNELS]; // Paused channels, UNSAFE - - SoundVolume chVolume[CONFIG_SND_MAX_CHANNELS]; // Volume map - - void incSound(); // Increment counter - void decSound(); // Decrement counter - - void soundCallback(); // Play one step - bool handleQueue(); // Handle suspended events (SAFE) - void setupTimer(); // Set divisors and start timer - - void addEvent(SoundControl event); - - void checkTimer(); // Start one-shot "promo-"timer if isn't active - public: - - SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac); // Setup SoundMixer - ~SoundMixer(); // Destroy extra stuff - - void play(SoundChNum channel, SoundProvider *sound); - void stop(SoundChNum channel); - void pause(SoundChNum channel); - void resume(SoundChNum channel); - void setVolume(SoundChNum channel, SoundVolume vol); - SoundVolume getVolume(SoundChNum channel); - SoundState state(SoundChNum channel); // SAFE - - SoundChNum playAuto(SoundProvider *sound, SoundVolume vol); // Auto select channel and play sound on it (if no aviable, count of channels will be returned) - - void stopAll(); - void pauseAll(); - void resumeAll(); -}; +#include "soundDefines.h" + +namespace Sound { + class SoundMixer { + protected: + esp_timer_handle_t timer = NULL; // Timer for this instance + SemaphoreHandle_t mutex = NULL; // Mutex for this instance + SemaphoreHandle_t timerMutex = NULL; // Mutex for timer control + QueueHandle_t queue = NULL; // Queue for this instance + dac_channel_t dacCh; // DAC channel for sound + + unsigned long int counter = 1; // Only for callback + unsigned long int counterMax = 1; + + SoundChNum chCount; // Total channels count, <= CONFIG_SND_MAX_CHANNELS + SoundChNum chFirstAuto; // Number of first "auto" channel + SemaphoreHandle_t chActiveCount = NULL; // Count of active channels (to control timer) + SoundProvider* chSound[CONFIG_SND_MAX_CHANNELS]; // Sound provider pointers (you should carry about memory by youself) + bool chActive[CONFIG_SND_MAX_CHANNELS]; // Active channels, UNSAFE + bool chPaused[CONFIG_SND_MAX_CHANNELS]; // Paused channels, UNSAFE + + SoundVolume chVolume[CONFIG_SND_MAX_CHANNELS]; // Volume map + + void incSound(); // Increment counter + void decSound(); // Decrement counter + + void soundCallback(); // Play one step + bool handleQueue(); // Handle suspended events (SAFE) + void setupTimer(); // Set divisors and start timer + + void addEvent(SoundControl event); + + void checkTimer(); // Start one-shot "promo-"timer if isn't active + public: + SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac); // Setup SoundMixer + ~SoundMixer(); // Destroy extra stuff + + void play(SoundChNum channel, SoundProvider *sound); + void stop(SoundChNum channel); + void pause(SoundChNum channel); + void resume(SoundChNum channel); + void setVolume(SoundChNum channel, SoundVolume vol); + SoundVolume getVolume(SoundChNum channel); + SoundState state(SoundChNum channel); // SAFE + + SoundChNum playAuto(SoundProvider *sound, SoundVolume vol); // Auto select channel and play sound on it (if no aviable, count of channels will be returned) + + void stopAll(); + void pauseAll(); + void resumeAll(); + }; +} diff --git a/soundProvider.cpp b/soundProvider.cpp index e4ab165..d16165b 100644 --- a/soundProvider.cpp +++ b/soundProvider.cpp @@ -1,25 +1,27 @@ #include -SoundProvider::SoundProvider() { - queue = xQueueCreate(CONFIG_SND_PROVIDER_MAIN_QUEUE_SIZE, sizeof(SoundData)); - assert(queue != NULL); - controlQueue = xQueueCreate(CONFIG_SND_PROVIDER_CONTROL_QUEUE_SIZE, sizeof(SoundProviderControl)); - assert(controlQueue != NULL); -} +namespace Sound { + SoundProvider::SoundProvider() { + queue = xQueueCreate(CONFIG_SND_PROVIDER_MAIN_QUEUE_SIZE, sizeof(SoundData)); + assert(queue != NULL); + controlQueue = xQueueCreate(CONFIG_SND_PROVIDER_CONTROL_QUEUE_SIZE, sizeof(SoundProviderControl)); + assert(controlQueue != NULL); + } -SoundProvider::~SoundProvider() { - vQueueDelete(queue); - vQueueDelete(controlQueue); -} + SoundProvider::~SoundProvider() { + vQueueDelete(queue); + vQueueDelete(controlQueue); + } -void SoundProvider::postSample(SoundData sample) { - xQueueSendToBack(queue, &sample, portMAX_DELAY); -} + void SoundProvider::postSample(SoundData sample) { + xQueueSendToBack(queue, &sample, portMAX_DELAY); + } -void SoundProvider::postControl(SoundProviderControl ctrl) { - xQueueSendToBack(controlQueue, &ctrl, portMAX_DELAY); -} + void SoundProvider::postControl(SoundProviderControl ctrl) { + xQueueSendToBack(controlQueue, &ctrl, portMAX_DELAY); + } -void SoundProvider::queueReset() { - xQueueReset(queue); + void SoundProvider::queueReset() { + xQueueReset(queue); + } } diff --git a/soundProvider.h b/soundProvider.h index b88c066..d5d7fcf 100644 --- a/soundProvider.h +++ b/soundProvider.h @@ -1,37 +1,38 @@ #pragma once +#include "soundDefines.h" -class SoundMixer; +namespace Sound { + class SoundProvider { // Abstract interface for sound providers. Include queues initialisation/deinitialisation. + protected: + QueueHandle_t queue = NULL; // Read from here + QueueHandle_t controlQueue = NULL; // Read controlling data from here -class SoundProvider { // Abstract interface for sound providers. Include queues initialisation/deinitialisation. - protected: - QueueHandle_t queue = NULL; // Read from here - QueueHandle_t controlQueue = NULL; // Read controlling data from here + // PROVIDER CONTROL INTERFACE START + virtual void provider_start() = 0; // Start filling (should be ok if started) + virtual void provider_pause() {}; // Optional methods for extra optimisation + virtual void provider_resume() {}; + virtual void provider_stop() = 0; // Stop filling (should be ok if isn't started) - // PROVIDER CONTROL INTERFACE START - virtual void provider_start() = 0; // Start filling (should be ok if started) - virtual void provider_pause() {}; // Optional methods for extra optimisation - virtual void provider_resume() {}; - virtual void provider_stop() = 0; // Stop filling (should be ok if isn't started) + virtual void provider_restart() {provider_stop(); provider_start();} // This one calls if track repeats (default implementation should work, but it is better to write your own) + // PROVIDER CONTROL INTERFACE END - virtual void provider_restart() {provider_stop(); provider_start();} // This one calls if track repeats (default implementation should work, but it is better to write your own) - // PROVIDER CONTROL INTERFACE END + void postSample(SoundData sample); + void postControl(SoundProviderControl ctrl); - void postSample(SoundData sample); - void postControl(SoundProviderControl ctrl); + void queueReset(); - void queueReset(); + virtual unsigned long int getFrequency() = 0; // Frequency in Hz, should be constant - virtual unsigned long int getFrequency() = 0; // Frequency in Hz, should be constant + private: + unsigned int divisor = 1; // For different frequencies in same mixer + SoundData actual = 0; - private: - unsigned int divisor = 1; // For different frequencies in same mixer - SoundData actual = 0; + public: + SoundProvider(); + virtual ~SoundProvider(); - public: - SoundProvider(); - virtual ~SoundProvider(); + bool repeat = false; // Implementation souldn't use any optimisations based on this - bool repeat = false; // Implementation souldn't use any optimisations based on this - - friend SoundMixer; -}; + friend SoundMixer; + }; +} diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 9f71936..5fdb7a2 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -1,36 +1,38 @@ #include #include -SoundProviderTask::SoundProviderTask() {} -SoundProviderTask::~SoundProviderTask() {} +namespace Sound { + SoundProviderTask::SoundProviderTask() {} + SoundProviderTask::~SoundProviderTask() {} -void SoundProviderTask::provider_start() { - if (taskHandle == NULL) { - task_prestart(); - xTaskCreate(reinterpret_cast(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle); - task_poststart(); + void SoundProviderTask::provider_start() { + if (taskHandle == NULL) { + task_prestart(); + xTaskCreate(reinterpret_cast(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle); + task_poststart(); + } } -} -void SoundProviderTask::provider_stop() { - if (taskHandle != NULL) { - task_prestop(); - vTaskDelete(taskHandle); - taskHandle = NULL; - task_poststop(); - queueReset(); + void SoundProviderTask::provider_stop() { + if (taskHandle != NULL) { + task_prestop(); + vTaskDelete(taskHandle); + taskHandle = NULL; + task_poststop(); + queueReset(); + } } -} -void SoundProviderTask::stopFromTask() { - TaskHandle_t handle = taskHandle; - taskHandle = NULL; - vTaskDelete(handle); - while(true) {}; -} + void SoundProviderTask::stopFromTask() { + TaskHandle_t handle = taskHandle; + taskHandle = NULL; + vTaskDelete(handle); + while(true) {}; + } -void SoundProviderTask::taskProviderCode() { - task_code(); - postControl(END); - stopFromTask(); + void SoundProviderTask::taskProviderCode() { + task_code(); + postControl(END); + stopFromTask(); + } } diff --git a/soundProviderTask.h b/soundProviderTask.h index 1a97325..4d1de55 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -1,28 +1,29 @@ #pragma once - #include -class SoundProviderTask: public SoundProvider { - protected: - // TASK PROVIDER INTERFACE START - virtual void task_prestart() {}; - virtual void task_poststart() {}; - virtual void task_code() = 0; - virtual void task_prestop() {}; - virtual void task_poststop() {}; - // TASK PROVIDER INTERFACE END +namespace Sound { + class SoundProviderTask: public SoundProvider { + protected: + // TASK PROVIDER INTERFACE START + virtual void task_prestart() {}; + virtual void task_poststart() {}; + virtual void task_code() = 0; + virtual void task_prestop() {}; + virtual void task_poststop() {}; + // TASK PROVIDER INTERFACE END - // PROVIDER CONTROL INTERFACE START - void provider_start(); // Start filling (should be ok if started) - void provider_stop(); // Stop filling (should be ok if isn't started) - // PROVIDER CONTROL INTERFACE END + // PROVIDER CONTROL INTERFACE START + void provider_start(); // Start filling (should be ok if started) + void provider_stop(); // Stop filling (should be ok if isn't started) + // PROVIDER CONTROL INTERFACE END - void taskProviderCode(); - void stopFromTask(); - TaskHandle_t taskHandle = NULL; + void taskProviderCode(); + void stopFromTask(); + TaskHandle_t taskHandle = NULL; - size_t stackSize = 2048; - public: - SoundProviderTask(); - virtual ~SoundProviderTask(); -}; + size_t stackSize = 2048; + public: + SoundProviderTask(); + virtual ~SoundProviderTask(); + }; +} From 639d49fe57987b6a13c1340d8cb8e0b0237134a6 Mon Sep 17 00:00:00 2001 From: v1993 Date: Wed, 1 Aug 2018 17:53:14 +0300 Subject: [PATCH 02/26] Use smart pointers Begin using C++ specific things vs C ones --- soundDefines.h | 3 ++- soundMixer.cpp | 53 +++++++++++++++++++++++++++----------------------- soundMixer.h | 18 ++++++++++++----- 3 files changed, 44 insertions(+), 30 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index 734a0b6..b7b707c 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -3,6 +3,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -51,7 +52,7 @@ namespace Sound { struct SoundControl { SoundEvent event; SoundChNum channel; - SoundProvider *provider; + std::shared_ptr provider; SoundVolume vol; }; } diff --git a/soundMixer.cpp b/soundMixer.cpp index 028c5b9..9e5e7d0 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -26,15 +26,15 @@ namespace Sound { xSemaphoreTake(mutex, portMAX_DELAY); SoundControl ctrl; bool upd = false; // Should we recalculate anything? - while(xQueueReceive(queue, &ctrl, 0) == pdTRUE) { // Handle all events without blocking + std::lock_guard queueLock(queueMutex); + while(not queue.empty()) { // Handle all events without blocking + SoundControl ctrl = queue.front(); + queue.pop(); SoundChNum channel = ctrl.channel; - SoundProvider *sound; if (ctrl.event == START) { - sound = ctrl.provider; - } else { - sound = chSound[channel]; + chSound[channel] = std::move(ctrl.provider); } - SoundVolume vol = ctrl.vol; + std::shared_ptr& sound = chSound[channel]; switch(ctrl.event) { case STOP: if (chActive[channel]) {upd = true; decSound();} @@ -42,12 +42,12 @@ namespace Sound { chActive[channel] = false; chPaused[channel] = false; sound->provider_stop(); + chSound[channel] = nullptr; // Release pointer } break; case START: // I'm sure that channel is free upd = true; incSound(); - chSound[channel] = sound; chActive[channel] = true; sound->provider_start(); sound->actual = 0; @@ -71,7 +71,7 @@ namespace Sound { } break; case VOLSET: - chVolume[channel] = vol; + chVolume[channel] = ctrl.vol; break; } } @@ -97,7 +97,7 @@ namespace Sound { int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[activeCount]), freqArr[0], lcm); for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { - SoundProvider *sound = chSound[i]; + std::shared_ptr sound = chSound[i]; sound->divisor = freqLcm / sound->getFrequency(); counterMax = lcm(counterMax, sound->divisor); }} @@ -113,7 +113,7 @@ namespace Sound { xSemaphoreGive(timerMutex); return; } - setupTimer(); // TODO: implement that function + setupTimer(); counter = 0; // Only for later ++ } @@ -122,7 +122,8 @@ namespace Sound { unsigned int out = 0; for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { - SoundProvider *sound = chSound[i]; + std::shared_ptr& sound = chSound[i]; + //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; if ((counter % sound->divisor) == 0) { SoundData sample; if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) { @@ -159,8 +160,9 @@ namespace Sound { xSemaphoreTake(chActiveCount, portMAX_DELAY); } - void SoundMixer::addEvent(SoundControl event) { - xQueueSendToBack(queue, &event, portMAX_DELAY); + void SoundMixer::addEvent(const SoundControl& event) { + std::lock_guard queueLock(queueMutex); + queue.push(event); } SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac) { @@ -182,7 +184,6 @@ namespace Sound { mutex = xSemaphoreCreateMutex(); timerMutex = xSemaphoreCreateCounting(1, 1); chActiveCount = xSemaphoreCreateCounting(chCount, 0); - queue = xQueueCreate(CONFIG_SND_CONTROL_QUEUE_SIZE, sizeof(SoundControl)); for (SoundChNum i = 0; i < chCount; i++) { // Set defaults chActive[i] = false; @@ -199,7 +200,6 @@ namespace Sound { vSemaphoreDelete(mutex); vSemaphoreDelete(timerMutex); vSemaphoreDelete(chActiveCount); - vQueueDelete(queue); } void SoundMixer::checkTimer() { @@ -208,15 +208,20 @@ namespace Sound { } } - void SoundMixer::play(SoundChNum channel, SoundProvider *sound) { - stop(channel); + void SoundMixer::play(SoundChNum channel, const std::shared_ptr& sound) { + { + auto copy = sound; + stop(channel); + + SoundControl ctrl; + ctrl.event = START; + ctrl.channel = channel; + ctrl.provider = std::move(copy); // Copy + std::cout << sound.use_count() << std::endl; + addEvent(ctrl); + } + std::cout << sound.use_count() << std::endl; - SoundControl ctrl; - ctrl.event = START; - ctrl.channel = channel; - ctrl.provider = sound; - addEvent(ctrl); - checkTimer(); } @@ -291,7 +296,7 @@ namespace Sound { return s; } - SoundChNum SoundMixer::playAuto(SoundProvider *sound, SoundVolume vol) { + SoundChNum SoundMixer::playAuto(const std::shared_ptr& sound, SoundVolume vol) { for (SoundChNum i = chFirstAuto; i < chCount; i++) { if (state(i) == STOPPED) { // We found free channel, setting up setVolume(i, vol); diff --git a/soundMixer.h b/soundMixer.h index e99dbb2..cb8f73c 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -1,5 +1,9 @@ #pragma once #include "soundDefines.h" +#include +#include +#include +#include namespace Sound { class SoundMixer { @@ -7,7 +11,11 @@ namespace Sound { esp_timer_handle_t timer = NULL; // Timer for this instance SemaphoreHandle_t mutex = NULL; // Mutex for this instance SemaphoreHandle_t timerMutex = NULL; // Mutex for timer control - QueueHandle_t queue = NULL; // Queue for this instance + + std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers + // Refer to https://stackoverflow.com/q/51632219/5697743 + std::mutex queueMutex; // Mutex for queue + dac_channel_t dacCh; // DAC channel for sound unsigned long int counter = 1; // Only for callback @@ -16,7 +24,7 @@ namespace Sound { SoundChNum chCount; // Total channels count, <= CONFIG_SND_MAX_CHANNELS SoundChNum chFirstAuto; // Number of first "auto" channel SemaphoreHandle_t chActiveCount = NULL; // Count of active channels (to control timer) - SoundProvider* chSound[CONFIG_SND_MAX_CHANNELS]; // Sound provider pointers (you should carry about memory by youself) + std::array, CONFIG_SND_MAX_CHANNELS> chSound; // Sound provider pointers bool chActive[CONFIG_SND_MAX_CHANNELS]; // Active channels, UNSAFE bool chPaused[CONFIG_SND_MAX_CHANNELS]; // Paused channels, UNSAFE @@ -29,14 +37,14 @@ namespace Sound { bool handleQueue(); // Handle suspended events (SAFE) void setupTimer(); // Set divisors and start timer - void addEvent(SoundControl event); + void addEvent(const SoundControl& event); void checkTimer(); // Start one-shot "promo-"timer if isn't active public: SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac); // Setup SoundMixer ~SoundMixer(); // Destroy extra stuff - void play(SoundChNum channel, SoundProvider *sound); + void play(SoundChNum channel, const std::shared_ptr& sound); void stop(SoundChNum channel); void pause(SoundChNum channel); void resume(SoundChNum channel); @@ -44,7 +52,7 @@ namespace Sound { SoundVolume getVolume(SoundChNum channel); SoundState state(SoundChNum channel); // SAFE - SoundChNum playAuto(SoundProvider *sound, SoundVolume vol); // Auto select channel and play sound on it (if no aviable, count of channels will be returned) + SoundChNum playAuto(const std::shared_ptr& sound, SoundVolume vol); // Auto select channel and play sound on it (if no aviable, count of channels will be returned) void stopAll(); void pauseAll(); From c23e81b07064ca5f3d4d806e60e2fed5335956b2 Mon Sep 17 00:00:00 2001 From: v1993 Date: Wed, 1 Aug 2018 19:11:28 +0300 Subject: [PATCH 03/26] Various fixes of bugs and potential bugs --- soundDefines.h | 1 + soundMixer.cpp | 42 ++++++++++++++++++++++++++++++------------ soundMixer.h | 10 ++++++---- soundProvider.cpp | 4 ++-- soundProvider.h | 6 +++--- soundProviderTask.cpp | 24 +++++++++++++++++++----- soundProviderTask.h | 9 ++++++--- 7 files changed, 67 insertions(+), 29 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index b7b707c..33cfadc 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -44,6 +44,7 @@ namespace Sound { enum SoundEvent { STOP, START, + RESTART, PAUSE, RESUME, VOLSET diff --git a/soundMixer.cpp b/soundMixer.cpp index 9e5e7d0..069d0de 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -61,6 +61,11 @@ namespace Sound { sound->provider_pause(); } break; + case RESTART: + if (chActive[channel]) { + sound->provider_restart(); + } + break; case RESUME: if (chPaused[channel]) { upd = true; @@ -136,7 +141,7 @@ namespace Sound { switch(ctrl) { case END: if (sound->repeat) { - sound->provider_restart(); + restart(i); } else { stop(i); } @@ -189,7 +194,7 @@ namespace Sound { chActive[i] = false; chPaused[i] = false; chVolume[i] = 255; - chSound[i] = NULL; + chSound[i] = nullptr; } } @@ -209,17 +214,15 @@ namespace Sound { } void SoundMixer::play(SoundChNum channel, const std::shared_ptr& sound) { - { - auto copy = sound; - stop(channel); + stop(channel); + + SoundControl ctrl; + ctrl.event = START; + ctrl.channel = channel; + ctrl.provider = sound; // Copy + std::cout << sound.use_count() << std::endl; + addEvent(ctrl); - SoundControl ctrl; - ctrl.event = START; - ctrl.channel = channel; - ctrl.provider = std::move(copy); // Copy - std::cout << sound.use_count() << std::endl; - addEvent(ctrl); - } std::cout << sound.use_count() << std::endl; checkTimer(); @@ -255,6 +258,21 @@ namespace Sound { } } + void SoundMixer::restart(SoundChNum channel) { + if (uxSemaphoreGetCount(timerMutex) == 0) { + SoundControl ctrl; + ctrl.event = RESTART; + ctrl.channel = channel; + addEvent(ctrl); + } + } + + void SoundMixer::restartAll() { + for (SoundChNum i = 0; i < chCount; i++) { + restart(i); + } + } + void SoundMixer::resume(SoundChNum channel) { SoundControl ctrl; ctrl.event = RESUME; diff --git a/soundMixer.h b/soundMixer.h index cb8f73c..4920ce2 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -8,9 +8,9 @@ namespace Sound { class SoundMixer { protected: - esp_timer_handle_t timer = NULL; // Timer for this instance - SemaphoreHandle_t mutex = NULL; // Mutex for this instance - SemaphoreHandle_t timerMutex = NULL; // Mutex for timer control + esp_timer_handle_t timer = nullptr; // Timer for this instance + SemaphoreHandle_t mutex = nullptr; // Mutex for this instance + SemaphoreHandle_t timerMutex = nullptr; // Mutex for timer control std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers // Refer to https://stackoverflow.com/q/51632219/5697743 @@ -23,7 +23,7 @@ namespace Sound { SoundChNum chCount; // Total channels count, <= CONFIG_SND_MAX_CHANNELS SoundChNum chFirstAuto; // Number of first "auto" channel - SemaphoreHandle_t chActiveCount = NULL; // Count of active channels (to control timer) + SemaphoreHandle_t chActiveCount = nullptr; // Count of active channels (to control timer) std::array, CONFIG_SND_MAX_CHANNELS> chSound; // Sound provider pointers bool chActive[CONFIG_SND_MAX_CHANNELS]; // Active channels, UNSAFE bool chPaused[CONFIG_SND_MAX_CHANNELS]; // Paused channels, UNSAFE @@ -47,6 +47,7 @@ namespace Sound { void play(SoundChNum channel, const std::shared_ptr& sound); void stop(SoundChNum channel); void pause(SoundChNum channel); + void restart(SoundChNum channel); void resume(SoundChNum channel); void setVolume(SoundChNum channel, SoundVolume vol); SoundVolume getVolume(SoundChNum channel); @@ -56,6 +57,7 @@ namespace Sound { void stopAll(); void pauseAll(); + void restartAll(); void resumeAll(); }; } diff --git a/soundProvider.cpp b/soundProvider.cpp index d16165b..4cbdf46 100644 --- a/soundProvider.cpp +++ b/soundProvider.cpp @@ -3,9 +3,9 @@ namespace Sound { SoundProvider::SoundProvider() { queue = xQueueCreate(CONFIG_SND_PROVIDER_MAIN_QUEUE_SIZE, sizeof(SoundData)); - assert(queue != NULL); + assert(queue != nullptr); controlQueue = xQueueCreate(CONFIG_SND_PROVIDER_CONTROL_QUEUE_SIZE, sizeof(SoundProviderControl)); - assert(controlQueue != NULL); + assert(controlQueue != nullptr); } SoundProvider::~SoundProvider() { diff --git a/soundProvider.h b/soundProvider.h index d5d7fcf..8dbb3c6 100644 --- a/soundProvider.h +++ b/soundProvider.h @@ -4,8 +4,8 @@ namespace Sound { class SoundProvider { // Abstract interface for sound providers. Include queues initialisation/deinitialisation. protected: - QueueHandle_t queue = NULL; // Read from here - QueueHandle_t controlQueue = NULL; // Read controlling data from here + QueueHandle_t queue = nullptr; // Read from here + QueueHandle_t controlQueue = nullptr; // Read controlling data from here // PROVIDER CONTROL INTERFACE START virtual void provider_start() = 0; // Start filling (should be ok if started) @@ -13,7 +13,7 @@ namespace Sound { virtual void provider_resume() {}; virtual void provider_stop() = 0; // Stop filling (should be ok if isn't started) - virtual void provider_restart() {provider_stop(); provider_start();} // This one calls if track repeats (default implementation should work, but it is better to write your own) + virtual void provider_restart() {provider_stop(); provider_start();}; // This one calls if track repeats (default implementation MAY NOT work) // PROVIDER CONTROL INTERFACE END void postSample(SoundData sample); diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 5fdb7a2..12f6e0a 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -5,33 +5,47 @@ namespace Sound { SoundProviderTask::SoundProviderTask() {} SoundProviderTask::~SoundProviderTask() {} + void SoundProviderTask::unconditionalStart() { + xTaskCreate(reinterpret_cast(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle); + } + void SoundProviderTask::provider_start() { - if (taskHandle == NULL) { + if (taskHandle == nullptr) { task_prestart(); - xTaskCreate(reinterpret_cast(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle); + unconditionalStart(); task_poststart(); } } void SoundProviderTask::provider_stop() { - if (taskHandle != NULL) { + if (taskHandle != nullptr) { task_prestop(); vTaskDelete(taskHandle); - taskHandle = NULL; + taskHandle = nullptr; task_poststop(); queueReset(); } } + void SoundProviderTask::provider_restart() { + if (taskHandle != nullptr) { + task_prestop(); + vTaskDelete(taskHandle); + task_poststop(); + unconditionalStart(); + } + } + void SoundProviderTask::stopFromTask() { TaskHandle_t handle = taskHandle; - taskHandle = NULL; + taskHandle = nullptr; vTaskDelete(handle); while(true) {}; } void SoundProviderTask::taskProviderCode() { task_code(); + while (uxQueueMessagesWaiting(queue) > 0) vTaskDelay(1); postControl(END); stopFromTask(); } diff --git a/soundProviderTask.h b/soundProviderTask.h index 4d1de55..2e0369a 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -13,15 +13,18 @@ namespace Sound { // TASK PROVIDER INTERFACE END // PROVIDER CONTROL INTERFACE START - void provider_start(); // Start filling (should be ok if started) - void provider_stop(); // Stop filling (should be ok if isn't started) + virtual void provider_start() final; // Start filling (should be ok if started) + virtual void provider_stop() final; // Stop filling (should be ok if isn't started) + virtual void provider_restart(); // This one calls if track repeats (default implementation MAY NOT work) // PROVIDER CONTROL INTERFACE END void taskProviderCode(); void stopFromTask(); - TaskHandle_t taskHandle = NULL; + TaskHandle_t taskHandle = nullptr; size_t stackSize = 2048; + private: + void unconditionalStart(); public: SoundProviderTask(); virtual ~SoundProviderTask(); From d5f0e6c4ae9380bf97bb41fe05a55d8db0b9ec98 Mon Sep 17 00:00:00 2001 From: v1993 Date: Wed, 1 Aug 2018 22:35:25 +0300 Subject: [PATCH 04/26] Remove unused setting --- Kconfig | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Kconfig b/Kconfig index 0310f9e..442046b 100644 --- a/Kconfig +++ b/Kconfig @@ -7,14 +7,6 @@ config SND_MAX_CHANNELS help Maximal count of channels to be mixed with SoundMixer. -config SND_CONTROL_QUEUE_SIZE - int "Size of mixer's queue" - default 128 - range 32 512 - help - In most cases default value will work fine. Try changing it in case of blocking. - Keep it bigger than SND_MAX_CHANNELS. - menu "Provider configuration" config SND_PROVIDER_MAIN_QUEUE_SIZE From a605eb32e8ca9d9bc38fb1306f5c7ee747eec9b5 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 01:16:23 +0300 Subject: [PATCH 05/26] Use C++ primitives instead of C ones --- soundMixer.cpp | 19 ++++++------------- soundMixer.h | 6 +++--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/soundMixer.cpp b/soundMixer.cpp index 069d0de..ef6cc7e 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -23,9 +23,9 @@ static int lcm(int a, int b) { namespace Sound { bool SoundMixer::handleQueue() { - xSemaphoreTake(mutex, portMAX_DELAY); SoundControl ctrl; bool upd = false; // Should we recalculate anything? + std::lock_guard lock(); std::lock_guard queueLock(queueMutex); while(not queue.empty()) { // Handle all events without blocking SoundControl ctrl = queue.front(); @@ -80,7 +80,6 @@ namespace Sound { break; } } - xSemaphoreGive(mutex); return upd; } @@ -186,7 +185,6 @@ namespace Sound { esp_timer_create(&timer_args, &timer); - mutex = xSemaphoreCreateMutex(); timerMutex = xSemaphoreCreateCounting(1, 1); chActiveCount = xSemaphoreCreateCounting(chCount, 0); @@ -202,7 +200,6 @@ namespace Sound { esp_timer_stop(timer); esp_timer_delete(timer); - vSemaphoreDelete(mutex); vSemaphoreDelete(timerMutex); vSemaphoreDelete(chActiveCount); } @@ -298,20 +295,16 @@ namespace Sound { SoundVolume SoundMixer::getVolume(SoundChNum channel) { SoundVolume vol; - xSemaphoreTake(mutex, portMAX_DELAY); + std::lock_guard lock(); vol = chVolume[channel]; - xSemaphoreGive(mutex); return vol; } SoundState SoundMixer::state(SoundChNum channel) { - xSemaphoreTake(mutex, portMAX_DELAY); - SoundState s; - if (chActive[channel]) s = PLAYING; - else if (chPaused[channel]) s = PAUSED; - else s = STOPPED; - xSemaphoreGive(mutex); - return s; + std::lock_guard lock(); + if (chActive[channel]) return PLAYING; + else if (chPaused[channel]) return PAUSED; + else return STOPPED; } SoundChNum SoundMixer::playAuto(const std::shared_ptr& sound, SoundVolume vol) { diff --git a/soundMixer.h b/soundMixer.h index 4920ce2..6e88088 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -9,7 +9,7 @@ namespace Sound { class SoundMixer { protected: esp_timer_handle_t timer = nullptr; // Timer for this instance - SemaphoreHandle_t mutex = nullptr; // Mutex for this instance + std::mutex mutex; // Mutex for this instance SemaphoreHandle_t timerMutex = nullptr; // Mutex for timer control std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers @@ -25,8 +25,8 @@ namespace Sound { SoundChNum chFirstAuto; // Number of first "auto" channel SemaphoreHandle_t chActiveCount = nullptr; // Count of active channels (to control timer) std::array, CONFIG_SND_MAX_CHANNELS> chSound; // Sound provider pointers - bool chActive[CONFIG_SND_MAX_CHANNELS]; // Active channels, UNSAFE - bool chPaused[CONFIG_SND_MAX_CHANNELS]; // Paused channels, UNSAFE + std::array chActive; // Active channels, UNSAFE + std::array chPaused; // Paused channels, UNSAFE SoundVolume chVolume[CONFIG_SND_MAX_CHANNELS]; // Volume map From a5ea4427146e318964b080472f733c6f4b161265 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 13:48:26 +0300 Subject: [PATCH 06/26] Fix bugs, use more C++ primitives --- soundMixer.cpp | 40 +++++++++++++++++----------------------- soundMixer.h | 7 ++++--- 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/soundMixer.cpp b/soundMixer.cpp index ef6cc7e..9b3959d 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -25,7 +25,6 @@ namespace Sound { bool SoundMixer::handleQueue() { SoundControl ctrl; bool upd = false; // Should we recalculate anything? - std::lock_guard lock(); std::lock_guard queueLock(queueMutex); while(not queue.empty()) { // Handle all events without blocking SoundControl ctrl = queue.front(); @@ -84,9 +83,8 @@ namespace Sound { } void SoundMixer::setupTimer() { - SoundChNum activeCount = uxSemaphoreGetCount(chActiveCount); counterMax = 1; - if (activeCount == 1) { // Only one sound + if (chActiveCount == 1) { // Only one sound for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { chSound[i]->divisor = 1; esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); @@ -94,13 +92,13 @@ namespace Sound { }} } else { SoundChNum n = 0; - unsigned long int freqArr[activeCount]; - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + std::vector freqArr(chActiveCount); + for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { freqArr[n++] = chSound[i]->getFrequency(); }} - int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[activeCount]), freqArr[0], lcm); - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[chActiveCount]), freqArr[0], lcm); + for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { std::shared_ptr sound = chSound[i]; sound->divisor = freqLcm / sound->getFrequency(); counterMax = lcm(counterMax, sound->divisor); @@ -110,11 +108,12 @@ namespace Sound { } void SoundMixer::soundCallback() { + std::lock_guard lock(mutex); bool upd = handleQueue(); if (upd) { esp_timer_stop(timer); // It will work OK anyway - if (uxSemaphoreGetCount(chActiveCount) == 0) { // If nothing to play - xSemaphoreGive(timerMutex); + if (chActiveCount == 0) { // If nothing to play + timerActive = false; return; } setupTimer(); @@ -157,11 +156,11 @@ namespace Sound { } void SoundMixer::incSound() { - xSemaphoreGive(chActiveCount); + chActiveCount++; } void SoundMixer::decSound() { - xSemaphoreTake(chActiveCount, portMAX_DELAY); + chActiveCount--; } void SoundMixer::addEvent(const SoundControl& event) { @@ -185,9 +184,6 @@ namespace Sound { esp_timer_create(&timer_args, &timer); - timerMutex = xSemaphoreCreateCounting(1, 1); - chActiveCount = xSemaphoreCreateCounting(chCount, 0); - for (SoundChNum i = 0; i < chCount; i++) { // Set defaults chActive[i] = false; chPaused[i] = false; @@ -199,13 +195,11 @@ namespace Sound { SoundMixer::~SoundMixer() { esp_timer_stop(timer); esp_timer_delete(timer); - - vSemaphoreDelete(timerMutex); - vSemaphoreDelete(chActiveCount); } void SoundMixer::checkTimer() { - if (xSemaphoreTake(timerMutex, 0) == pdTRUE) { // If timer isn't active + if (not timerActive) { // If timer isn't active + timerActive = true; esp_timer_start_once(timer, 0); // Activate one-shot handler } } @@ -226,7 +220,7 @@ namespace Sound { } void SoundMixer::stop(SoundChNum channel) { - if (uxSemaphoreGetCount(timerMutex) == 0) { + if (timerActive) { SoundControl ctrl; ctrl.event = STOP; ctrl.channel = channel; @@ -241,7 +235,7 @@ namespace Sound { } void SoundMixer::pause(SoundChNum channel) { - if (uxSemaphoreGetCount(timerMutex) == 0) { + if (timerActive) { SoundControl ctrl; ctrl.event = PAUSE; ctrl.channel = channel; @@ -256,7 +250,7 @@ namespace Sound { } void SoundMixer::restart(SoundChNum channel) { - if (uxSemaphoreGetCount(timerMutex) == 0) { + if (timerActive) { SoundControl ctrl; ctrl.event = RESTART; ctrl.channel = channel; @@ -295,13 +289,13 @@ namespace Sound { SoundVolume SoundMixer::getVolume(SoundChNum channel) { SoundVolume vol; - std::lock_guard lock(); + std::lock_guard lock(mutex); vol = chVolume[channel]; return vol; } SoundState SoundMixer::state(SoundChNum channel) { - std::lock_guard lock(); + std::lock_guard lock(mutex); if (chActive[channel]) return PLAYING; else if (chPaused[channel]) return PAUSED; else return STOPPED; diff --git a/soundMixer.h b/soundMixer.h index 6e88088..fe16287 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -4,13 +4,14 @@ #include #include #include +#include namespace Sound { class SoundMixer { protected: esp_timer_handle_t timer = nullptr; // Timer for this instance - std::mutex mutex; // Mutex for this instance - SemaphoreHandle_t timerMutex = nullptr; // Mutex for timer control + std::recursive_mutex mutex; // Mutex for this instance + std::atomic timerActive = {false}; // Is timer active? std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers // Refer to https://stackoverflow.com/q/51632219/5697743 @@ -23,7 +24,7 @@ namespace Sound { SoundChNum chCount; // Total channels count, <= CONFIG_SND_MAX_CHANNELS SoundChNum chFirstAuto; // Number of first "auto" channel - SemaphoreHandle_t chActiveCount = nullptr; // Count of active channels (to control timer) + std::atomic chActiveCount = {0}; // Count of active channels (to control timer) std::array, CONFIG_SND_MAX_CHANNELS> chSound; // Sound provider pointers std::array chActive; // Active channels, UNSAFE std::array chPaused; // Paused channels, UNSAFE From ab8e1812c28e18a8afe778b8270022d90cc08a73 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 13:57:46 +0300 Subject: [PATCH 07/26] Use prefix ++/-- when possible --- soundMixer.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/soundMixer.cpp b/soundMixer.cpp index 9b3959d..4d25e28 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -85,7 +85,7 @@ namespace Sound { void SoundMixer::setupTimer() { counterMax = 1; if (chActiveCount == 1) { // Only one sound - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { chSound[i]->divisor = 1; esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); break; @@ -120,11 +120,11 @@ namespace Sound { counter = 0; // Only for later ++ } - counter++; + ++counter; if (counter > counterMax) counter = 1; unsigned int out = 0; - for (SoundChNum i = 0; i < chCount; i++) { if (chActive[i]) { + for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { std::shared_ptr& sound = chSound[i]; //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; if ((counter % sound->divisor) == 0) { @@ -156,11 +156,11 @@ namespace Sound { } void SoundMixer::incSound() { - chActiveCount++; + ++chActiveCount; } void SoundMixer::decSound() { - chActiveCount--; + --chActiveCount; } void SoundMixer::addEvent(const SoundControl& event) { @@ -184,7 +184,7 @@ namespace Sound { esp_timer_create(&timer_args, &timer); - for (SoundChNum i = 0; i < chCount; i++) { // Set defaults + for (SoundChNum i = 0; i < chCount; ++i) { // Set defaults chActive[i] = false; chPaused[i] = false; chVolume[i] = 255; @@ -229,7 +229,7 @@ namespace Sound { } void SoundMixer::stopAll() { - for (SoundChNum i = 0; i < chCount; i++) { + for (SoundChNum i = 0; i < chCount; ++i) { stop(i); } } @@ -244,7 +244,7 @@ namespace Sound { } void SoundMixer::pauseAll() { - for (SoundChNum i = 0; i < chCount; i++) { + for (SoundChNum i = 0; i < chCount; ++i) { pause(i); } } @@ -259,7 +259,7 @@ namespace Sound { } void SoundMixer::restartAll() { - for (SoundChNum i = 0; i < chCount; i++) { + for (SoundChNum i = 0; i < chCount; ++i) { restart(i); } } @@ -274,7 +274,7 @@ namespace Sound { } void SoundMixer::resumeAll() { - for (SoundChNum i = 0; i < chCount; i++) { + for (SoundChNum i = 0; i < chCount; ++i) { resume(i); } } @@ -302,7 +302,7 @@ namespace Sound { } SoundChNum SoundMixer::playAuto(const std::shared_ptr& sound, SoundVolume vol) { - for (SoundChNum i = chFirstAuto; i < chCount; i++) { + for (SoundChNum i = chFirstAuto; i < chCount; ++i) { if (state(i) == STOPPED) { // We found free channel, setting up setVolume(i, vol); play(i, sound); From 5c2787c2040b4aea56bfc4445cf6d93f9cb9773b Mon Sep 17 00:00:00 2001 From: Valera Date: Thu, 2 Aug 2018 14:03:03 +0300 Subject: [PATCH 08/26] Go to MIT license --- LICENSE | 222 ++++++-------------------------------------------------- 1 file changed, 21 insertions(+), 201 deletions(-) diff --git a/LICENSE b/LICENSE index 261eeb9..c88d2ec 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +MIT License + +Copyright (c) 2018 Valeri Ochinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 18a3c1ddf7a04324a1fbdffacd986c50b94f19d1 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 15:33:36 +0300 Subject: [PATCH 09/26] Improve thread safety --- component.mk | 1 + soundMixer.cpp | 20 ++++++++++++++------ soundMixer.h | 3 ++- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/component.mk b/component.mk index 0e1bdac..209b496 100644 --- a/component.mk +++ b/component.mk @@ -1,2 +1,3 @@ COMPONENT_SRCDIRS:=. COMPONENT_ADD_INCLUDEDIRS:=. +CPPFLAGS += -std=c++14 diff --git a/soundMixer.cpp b/soundMixer.cpp index 4d25e28..b2d9c98 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -108,17 +108,19 @@ namespace Sound { } void SoundMixer::soundCallback() { - std::lock_guard lock(mutex); + std::unique_lock lock(mutex); bool upd = handleQueue(); if (upd) { esp_timer_stop(timer); // It will work OK anyway if (chActiveCount == 0) { // If nothing to play timerActive = false; + dac_output_voltage(dacCh, 0); // Reduce energy usage return; } setupTimer(); counter = 0; // Only for later ++ } + lock.unlock(); // We leave critical area ++counter; if (counter > counterMax) counter = 1; @@ -198,6 +200,7 @@ namespace Sound { } void SoundMixer::checkTimer() { + std::shared_lock lock(mutex); if (not timerActive) { // If timer isn't active timerActive = true; esp_timer_start_once(timer, 0); // Activate one-shot handler @@ -220,7 +223,9 @@ namespace Sound { } void SoundMixer::stop(SoundChNum channel) { + std::shared_lock lock(mutex); if (timerActive) { + lock.unlock(); SoundControl ctrl; ctrl.event = STOP; ctrl.channel = channel; @@ -235,7 +240,9 @@ namespace Sound { } void SoundMixer::pause(SoundChNum channel) { + std::shared_lock lock(mutex); if (timerActive) { + lock.unlock(); SoundControl ctrl; ctrl.event = PAUSE; ctrl.channel = channel; @@ -250,6 +257,7 @@ namespace Sound { } void SoundMixer::restart(SoundChNum channel) { + std::shared_lock lock(mutex); if (timerActive) { SoundControl ctrl; ctrl.event = RESTART; @@ -269,7 +277,7 @@ namespace Sound { ctrl.event = RESUME; ctrl.channel = channel; addEvent(ctrl); - + checkTimer(); } @@ -289,16 +297,16 @@ namespace Sound { SoundVolume SoundMixer::getVolume(SoundChNum channel) { SoundVolume vol; - std::lock_guard lock(mutex); + std::shared_lock lock(mutex); vol = chVolume[channel]; return vol; } SoundState SoundMixer::state(SoundChNum channel) { - std::lock_guard lock(mutex); - if (chActive[channel]) return PLAYING; + std::shared_lock lock(mutex); + if (chActive[channel]) return PLAYING; else if (chPaused[channel]) return PAUSED; - else return STOPPED; + else return STOPPED; } SoundChNum SoundMixer::playAuto(const std::shared_ptr& sound, SoundVolume vol) { diff --git a/soundMixer.h b/soundMixer.h index fe16287..6e2987b 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -4,13 +4,14 @@ #include #include #include +#include #include namespace Sound { class SoundMixer { protected: esp_timer_handle_t timer = nullptr; // Timer for this instance - std::recursive_mutex mutex; // Mutex for this instance + std::shared_timed_mutex mutex; // Mutex for this instance std::atomic timerActive = {false}; // Is timer active? std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers From 20d78f872abcf49504a2aefde5d9a475b0502803 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 15:58:51 +0300 Subject: [PATCH 10/26] Remove debug strings --- soundDefines.h | 1 - soundMixer.cpp | 3 --- soundMixer.h | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index 33cfadc..e62e629 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include diff --git a/soundMixer.cpp b/soundMixer.cpp index b2d9c98..4448bbb 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -214,11 +214,8 @@ namespace Sound { ctrl.event = START; ctrl.channel = channel; ctrl.provider = sound; // Copy - std::cout << sound.use_count() << std::endl; addEvent(ctrl); - std::cout << sound.use_count() << std::endl; - checkTimer(); } diff --git a/soundMixer.h b/soundMixer.h index 6e2987b..647e5cd 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -36,7 +36,7 @@ namespace Sound { void decSound(); // Decrement counter void soundCallback(); // Play one step - bool handleQueue(); // Handle suspended events (SAFE) + bool handleQueue(); // Handle suspended events (SEMI-SAFE) void setupTimer(); // Set divisors and start timer void addEvent(const SoundControl& event); From ffa370a93397c6ebb7ec837795e3d176875a60c9 Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 17:02:16 +0300 Subject: [PATCH 11/26] Add `override` specifier --- soundProviderTask.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/soundProviderTask.h b/soundProviderTask.h index 2e0369a..4097df2 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -13,9 +13,9 @@ namespace Sound { // TASK PROVIDER INTERFACE END // PROVIDER CONTROL INTERFACE START - virtual void provider_start() final; // Start filling (should be ok if started) - virtual void provider_stop() final; // Stop filling (should be ok if isn't started) - virtual void provider_restart(); // This one calls if track repeats (default implementation MAY NOT work) + virtual void provider_start() override final; // Start filling (should be ok if started) + virtual void provider_stop() override final; // Stop filling (should be ok if isn't started) + virtual void provider_restart() override; // This one calls if track repeats (default implementation MAY NOT work) // PROVIDER CONTROL INTERFACE END void taskProviderCode(); From 7287de548d4cb7f5b25752657430dc742470f62d Mon Sep 17 00:00:00 2001 From: v1993 Date: Thu, 2 Aug 2018 17:42:18 +0300 Subject: [PATCH 12/26] =?UTF-8?q?CPPFLAGS=20=E2=86=92=20CXXFLAGS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- component.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component.mk b/component.mk index 209b496..522e76e 100644 --- a/component.mk +++ b/component.mk @@ -1,3 +1,3 @@ COMPONENT_SRCDIRS:=. COMPONENT_ADD_INCLUDEDIRS:=. -CPPFLAGS += -std=c++14 +CXXFLAGS += -std=c++14 From b58f70cc868acaa24d8304ac7da5c1827d5f2433 Mon Sep 17 00:00:00 2001 From: v1993 Date: Fri, 3 Aug 2018 18:08:54 +0300 Subject: [PATCH 13/26] Make unconditionalStart protected vs private --- soundProviderTask.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/soundProviderTask.h b/soundProviderTask.h index 4097df2..8184618 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -20,11 +20,10 @@ namespace Sound { void taskProviderCode(); void stopFromTask(); + void unconditionalStart(); TaskHandle_t taskHandle = nullptr; size_t stackSize = 2048; - private: - void unconditionalStart(); public: SoundProviderTask(); virtual ~SoundProviderTask(); From 6e209721a004ba69a0ea99e18fd07a74d62c6760 Mon Sep 17 00:00:00 2001 From: v1993 Date: Fri, 3 Aug 2018 18:16:36 +0300 Subject: [PATCH 14/26] Restart will start playing anyway --- soundProviderTask.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 12f6e0a..1beb48c 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -32,8 +32,8 @@ namespace Sound { task_prestop(); vTaskDelete(taskHandle); task_poststop(); - unconditionalStart(); } + unconditionalStart(); } void SoundProviderTask::stopFromTask() { From a08144cdd25dfbeb6cb2afd851d5b298a0c8955b Mon Sep 17 00:00:00 2001 From: v1993 Date: Fri, 3 Aug 2018 18:42:06 +0300 Subject: [PATCH 15/26] Disable auto-end --- soundProviderTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 1beb48c..292e22d 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -46,7 +46,6 @@ namespace Sound { void SoundProviderTask::taskProviderCode() { task_code(); while (uxQueueMessagesWaiting(queue) > 0) vTaskDelay(1); - postControl(END); stopFromTask(); } } From 37a52710f438e343add586b0bfa284cc7671f0d7 Mon Sep 17 00:00:00 2001 From: v1993 Date: Fri, 3 Aug 2018 20:35:09 +0300 Subject: [PATCH 16/26] Remove useless line --- soundProviderTask.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 292e22d..0a8d174 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -45,7 +45,6 @@ namespace Sound { void SoundProviderTask::taskProviderCode() { task_code(); - while (uxQueueMessagesWaiting(queue) > 0) vTaskDelay(1); stopFromTask(); } } From 86b28f2a6a5747c254b88700f0d8e78b88ddef24 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sat, 4 Aug 2018 00:29:30 +0300 Subject: [PATCH 17/26] Add option for changing bitrate during playing --- soundDefines.h | 1 + soundMixer.cpp | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index e62e629..e27c13b 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -36,6 +36,7 @@ namespace Sound { }; enum SoundProviderControl { + FREQUENCY_UPDATE, END, FAILURE }; diff --git a/soundMixer.cpp b/soundMixer.cpp index 4448bbb..af0cb31 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -126,6 +126,7 @@ namespace Sound { if (counter > counterMax) counter = 1; unsigned int out = 0; + upd = false; for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { std::shared_ptr& sound = chSound[i]; //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; @@ -139,6 +140,9 @@ namespace Sound { SoundProviderControl ctrl; while(xQueueReceive(sound->controlQueue, &ctrl, 0) == pdTRUE) { switch(ctrl) { + case FREQUENCY_UPDATE: + upd = true; + break; case END: if (sound->repeat) { restart(i); @@ -155,6 +159,13 @@ namespace Sound { out = out / 255 / chCount; dac_output_voltage(dacCh, min(out, 255)); // Do NOT overload + + if (upd) { // If someone have changed frequency + lock.lock(); + esp_timer_stop(timer); + setupTimer(); + lock.unlock(); // If I'll add some code later + } } void SoundMixer::incSound() { @@ -170,9 +181,10 @@ namespace Sound { queue.push(event); } - SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac) { - chCount = normal_channels + auto_channels; - chFirstAuto = normal_channels; // It isn't mistake, but looks strange + SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac): + chCount(normal_channels + auto_channels), + chFirstAuto(normal_channels) // It isn't mistake, but looks strange + { assert(chCount <= CONFIG_SND_MAX_CHANNELS); dacCh = dac; From e1e3faef7c541ac2f1fba1d8d151f66b7da8c1c0 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 01:14:01 +0300 Subject: [PATCH 18/26] Add IRAM_ATTR to all functions which can be called in timer --- soundDefines.h | 5 +---- soundMixer.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index e27c13b..621cafe 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -4,9 +4,7 @@ #include #include -#ifdef __cplusplus extern "C" { -#endif #include #include #include @@ -14,9 +12,8 @@ extern "C" { #include #include #include -#ifdef __cplusplus +#include } -#endif #define USING_NS_SOUND using namespace Sound diff --git a/soundMixer.cpp b/soundMixer.cpp index af0cb31..bdee426 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -6,7 +6,7 @@ #define min(a,b) ((a)<(b)?(a):(b)) #endif -static int gcd(int a, int b) { +static int IRAM_ATTR gcd(int a, int b) { while(true) { if (a == 0) return b; b %= a; @@ -15,14 +15,14 @@ static int gcd(int a, int b) { } } -static int lcm(int a, int b) { +static int IRAM_ATTR lcm(int a, int b) { int temp = gcd(a, b); return temp ? (a / temp * b) : 0; } namespace Sound { - bool SoundMixer::handleQueue() { + bool IRAM_ATTR SoundMixer::handleQueue() { SoundControl ctrl; bool upd = false; // Should we recalculate anything? std::lock_guard queueLock(queueMutex); @@ -82,7 +82,7 @@ namespace Sound { return upd; } - void SoundMixer::setupTimer() { + void IRAM_ATTR SoundMixer::setupTimer() { counterMax = 1; if (chActiveCount == 1) { // Only one sound for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { @@ -107,7 +107,7 @@ namespace Sound { } } - void SoundMixer::soundCallback() { + void IRAM_ATTR SoundMixer::soundCallback() { std::unique_lock lock(mutex); bool upd = handleQueue(); if (upd) { @@ -168,15 +168,15 @@ namespace Sound { } } - void SoundMixer::incSound() { + void IRAM_ATTR SoundMixer::incSound() { ++chActiveCount; } - void SoundMixer::decSound() { + void IRAM_ATTR SoundMixer::decSound() { --chActiveCount; } - void SoundMixer::addEvent(const SoundControl& event) { + void IRAM_ATTR SoundMixer::addEvent(const SoundControl& event) { std::lock_guard queueLock(queueMutex); queue.push(event); } From 2134ccc7072aa42feab3680c641b6caaa4deb793 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 01:31:36 +0300 Subject: [PATCH 19/26] Make using IRAM optional --- Kconfig | 7 +++++++ soundMixer.cpp | 26 ++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Kconfig b/Kconfig index 442046b..6043a09 100644 --- a/Kconfig +++ b/Kconfig @@ -7,6 +7,13 @@ config SND_MAX_CHANNELS help Maximal count of channels to be mixed with SoundMixer. +config SND_USE_IRAM + bool "Move timer functions to IRAM" + default true + help + This generally allow extra optimisation. + If you need more space in IRAM, turn it off. + menu "Provider configuration" config SND_PROVIDER_MAIN_QUEUE_SIZE diff --git a/soundMixer.cpp b/soundMixer.cpp index bdee426..b88e56c 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -6,7 +6,13 @@ #define min(a,b) ((a)<(b)?(a):(b)) #endif -static int IRAM_ATTR gcd(int a, int b) { +#if CONFIG_SND_USE_IRAM +#define TIMER_ATTRIBUTE IRAM_ATTR +#else +#define TIMER_ATTRIBUTE +#end + +static int TIMER_ATTRIBUTE gcd(int a, int b) { while(true) { if (a == 0) return b; b %= a; @@ -15,14 +21,14 @@ static int IRAM_ATTR gcd(int a, int b) { } } -static int IRAM_ATTR lcm(int a, int b) { +static int TIMER_ATTRIBUTE lcm(int a, int b) { int temp = gcd(a, b); return temp ? (a / temp * b) : 0; } namespace Sound { - bool IRAM_ATTR SoundMixer::handleQueue() { + bool TIMER_ATTRIBUTE SoundMixer::handleQueue() { SoundControl ctrl; bool upd = false; // Should we recalculate anything? std::lock_guard queueLock(queueMutex); @@ -82,7 +88,7 @@ namespace Sound { return upd; } - void IRAM_ATTR SoundMixer::setupTimer() { + void TIMER_ATTRIBUTE SoundMixer::setupTimer() { counterMax = 1; if (chActiveCount == 1) { // Only one sound for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { @@ -107,7 +113,7 @@ namespace Sound { } } - void IRAM_ATTR SoundMixer::soundCallback() { + void TIMER_ATTRIBUTE SoundMixer::soundCallback() { std::unique_lock lock(mutex); bool upd = handleQueue(); if (upd) { @@ -168,15 +174,15 @@ namespace Sound { } } - void IRAM_ATTR SoundMixer::incSound() { + void TIMER_ATTRIBUTE SoundMixer::incSound() { ++chActiveCount; } - void IRAM_ATTR SoundMixer::decSound() { + void TIMER_ATTRIBUTE SoundMixer::decSound() { --chActiveCount; } - void IRAM_ATTR SoundMixer::addEvent(const SoundControl& event) { + void TIMER_ATTRIBUTE SoundMixer::addEvent(const SoundControl& event) { std::lock_guard queueLock(queueMutex); queue.push(event); } @@ -231,7 +237,7 @@ namespace Sound { checkTimer(); } - void SoundMixer::stop(SoundChNum channel) { + void TIMER_ATTRIBUTE SoundMixer::stop(SoundChNum channel) { std::shared_lock lock(mutex); if (timerActive) { lock.unlock(); @@ -265,7 +271,7 @@ namespace Sound { } } - void SoundMixer::restart(SoundChNum channel) { + void TIMER_ATTRIBUTE SoundMixer::restart(SoundChNum channel) { std::shared_lock lock(mutex); if (timerActive) { SoundControl ctrl; From 51c59895ee43e4e622a4a63cd01bc118934f4127 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 09:59:19 +0300 Subject: [PATCH 20/26] Fix typo --- soundMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soundMixer.cpp b/soundMixer.cpp index b88e56c..138074e 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -10,7 +10,7 @@ #define TIMER_ATTRIBUTE IRAM_ATTR #else #define TIMER_ATTRIBUTE -#end +#endif static int TIMER_ATTRIBUTE gcd(int a, int b) { while(true) { From 9f3a76a61a40193f64503ad9394eeac5474a2d64 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 10:06:53 +0300 Subject: [PATCH 21/26] Use std::min() instead of macro --- soundMixer.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/soundMixer.cpp b/soundMixer.cpp index 138074e..e4135bf 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -1,10 +1,9 @@ #include "soundMixer.h" #include "soundProvider.h" +#include + #define SOUND_FREQ_TO_DELAY(f) (1000000/f) -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif #if CONFIG_SND_USE_IRAM #define TIMER_ATTRIBUTE IRAM_ATTR @@ -164,7 +163,7 @@ namespace Sound { }} out = out / 255 / chCount; - dac_output_voltage(dacCh, min(out, 255)); // Do NOT overload + dac_output_voltage(dacCh, std::min(out, 255U)); // Do NOT overload if (upd) { // If someone have changed frequency lock.lock(); From e20ca217294060df4f696162778dbef2f1a2afb4 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 10:18:32 +0300 Subject: [PATCH 22/26] Add `waitQueueEmpty` method --- soundProviderTask.h | 1 + 1 file changed, 1 insertion(+) diff --git a/soundProviderTask.h b/soundProviderTask.h index 8184618..398de5c 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -21,6 +21,7 @@ namespace Sound { void taskProviderCode(); void stopFromTask(); void unconditionalStart(); + void waitQueueEmpty() { while (uxQueueMessagesWaiting(queue) > 0) vTaskDelay(1); }; // Idiomatic TaskHandle_t taskHandle = nullptr; size_t stackSize = 2048; From a711d552fc2355d35b92dd9ca6d4e39e84c60419 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 10:21:22 +0300 Subject: [PATCH 23/26] Fix typo --- Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Kconfig b/Kconfig index 6043a09..e4ce59e 100644 --- a/Kconfig +++ b/Kconfig @@ -1,7 +1,7 @@ menu "Sound module configuration" config SND_MAX_CHANNELS - int "Maximun count of mixed channels" + int "Maximal count of mixed channels" default 64 range 1 255 help From 21300ee462b9ca1851bb46616830b0c87269036a Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 5 Aug 2018 16:48:12 +0300 Subject: [PATCH 24/26] Fix default option --- Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Kconfig b/Kconfig index e4ce59e..2647c99 100644 --- a/Kconfig +++ b/Kconfig @@ -9,7 +9,7 @@ config SND_MAX_CHANNELS config SND_USE_IRAM bool "Move timer functions to IRAM" - default true + default y help This generally allow extra optimisation. If you need more space in IRAM, turn it off. From c939e54b4a3e3182b18d1e8d94093f1ae9b5b92e Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 12 Aug 2018 16:53:48 +0300 Subject: [PATCH 25/26] Fix warnings Autoformat sources --- soundDefines.h | 8 +- soundMixer.cpp | 172 +++++++++++++++++++++++------------------- soundMixer.h | 7 +- soundProvider.h | 18 +++-- soundProviderTask.cpp | 16 ++-- soundProviderTask.h | 27 +++++-- 6 files changed, 151 insertions(+), 97 deletions(-) diff --git a/soundDefines.h b/soundDefines.h index 621cafe..1e2431f 100644 --- a/soundDefines.h +++ b/soundDefines.h @@ -48,10 +48,10 @@ namespace Sound { }; struct SoundControl { - SoundEvent event; - SoundChNum channel; - std::shared_ptr provider; - SoundVolume vol; + SoundEvent event; + SoundChNum channel; + std::shared_ptr provider; + SoundVolume vol; }; } diff --git a/soundMixer.cpp b/soundMixer.cpp index e4135bf..6c3ab44 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -11,42 +11,53 @@ #define TIMER_ATTRIBUTE #endif -static int TIMER_ATTRIBUTE gcd(int a, int b) { - while(true) { - if (a == 0) return b; - b %= a; - if (b == 0) return a; - a %= b; +#define forceinline inline __attribute__((always_inline)) + +extern "C" { + static int TIMER_ATTRIBUTE gcd(int a, int b) { + while(true) { + if (a == 0) return b; + b %= a; + if (b == 0) return a; + a %= b; + } } -} -static int TIMER_ATTRIBUTE lcm(int a, int b) { - int temp = gcd(a, b); + static int TIMER_ATTRIBUTE lcm(int a, int b) { + int temp = gcd(a, b); + + return temp ? (a / temp * b) : 0; + } - return temp ? (a / temp * b) : 0; + void TIMER_ATTRIBUTE sound_timer_callback_function(void* arg) { + static_cast(arg)->soundCallback(); + } } namespace Sound { - bool TIMER_ATTRIBUTE SoundMixer::handleQueue() { + bool TIMER_ATTRIBUTE forceinline SoundMixer::handleQueue() { SoundControl ctrl; bool upd = false; // Should we recalculate anything? std::lock_guard queueLock(queueMutex); while(not queue.empty()) { // Handle all events without blocking - SoundControl ctrl = queue.front(); + SoundControl ctrl = std::move(queue.front()); queue.pop(); - SoundChNum channel = ctrl.channel; + SoundChNum& channel = ctrl.channel; if (ctrl.event == START) { chSound[channel] = std::move(ctrl.provider); } std::shared_ptr& sound = chSound[channel]; switch(ctrl.event) { case STOP: - if (chActive[channel]) {upd = true; decSound();} + if (chActive[channel]) { + upd = true; + decSound(); + } if (chActive[channel] || chPaused[channel]) { - chActive[channel] = false; - chPaused[channel] = false; - sound->provider_stop(); - chSound[channel] = nullptr; // Release pointer + chActive[channel] = false; + chPaused[channel] = false; + sound->provider_stop(); + chSound[channel] = nullptr; // Release pointer } break; case START: // I'm sure that channel is free @@ -58,25 +69,25 @@ namespace Sound { break; case PAUSE: if (chActive[channel]) { - upd = true; - decSound(); - chActive[channel] = false; - chPaused[channel] = true; - sound->provider_pause(); + upd = true; + decSound(); + chActive[channel] = false; + chPaused[channel] = true; + sound->provider_pause(); } break; case RESTART: if (chActive[channel]) { - sound->provider_restart(); + sound->provider_restart(); } break; case RESUME: if (chPaused[channel]) { - upd = true; - incSound(); - chActive[channel] = true; - chPaused[channel] = false; - sound->provider_resume(); + upd = true; + incSound(); + chActive[channel] = true; + chPaused[channel] = false; + sound->provider_resume(); } break; case VOLSET: @@ -90,29 +101,35 @@ namespace Sound { void TIMER_ATTRIBUTE SoundMixer::setupTimer() { counterMax = 1; if (chActiveCount == 1) { // Only one sound - for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { - chSound[i]->divisor = 1; - esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); - break; - }} + for (SoundChNum i = 0; i < chCount; ++i) { + if (chActive[i]) { + chSound[i]->divisor = 1; + esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(chSound[i]->getFrequency())); + break; + } + } } else { SoundChNum n = 0; std::vector freqArr(chActiveCount); - for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { - freqArr[n++] = chSound[i]->getFrequency(); - }} + for (SoundChNum i = 0; i < chCount; ++i) { + if (chActive[i]) { + freqArr[n++] = chSound[i]->getFrequency(); + } + } int freqLcm = std::accumulate(&(freqArr[1]), &(freqArr[chActiveCount]), freqArr[0], lcm); - for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { - std::shared_ptr sound = chSound[i]; - sound->divisor = freqLcm / sound->getFrequency(); - counterMax = lcm(counterMax, sound->divisor); - }} + for (SoundChNum i = 0; i < chCount; ++i) { + if (chActive[i]) { + std::shared_ptr sound = chSound[i]; + sound->divisor = freqLcm / sound->getFrequency(); + counterMax = lcm(counterMax, sound->divisor); + } + } esp_timer_start_periodic(timer, SOUND_FREQ_TO_DELAY(freqLcm)); } } - void TIMER_ATTRIBUTE SoundMixer::soundCallback() { + void TIMER_ATTRIBUTE forceinline SoundMixer::soundCallback() { std::unique_lock lock(mutex); bool upd = handleQueue(); if (upd) { @@ -132,35 +149,37 @@ namespace Sound { unsigned int out = 0; upd = false; - for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { - std::shared_ptr& sound = chSound[i]; - //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; - if ((counter % sound->divisor) == 0) { - SoundData sample; - if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) { - sound->actual = sample; - } - } - out += sound->actual * chVolume[i]; - SoundProviderControl ctrl; - while(xQueueReceive(sound->controlQueue, &ctrl, 0) == pdTRUE) { - switch(ctrl) { - case FREQUENCY_UPDATE: - upd = true; - break; - case END: - if (sound->repeat) { - restart(i); - } else { + for (SoundChNum i = 0; i < chCount; ++i) { + if (chActive[i]) { + std::shared_ptr& sound = chSound[i]; + //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; + if ((counter % sound->divisor) == 0) { + SoundData sample; + if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) { + sound->actual = sample; + } + } + out += sound->actual * chVolume[i]; + SoundProviderControl ctrl; + while(xQueueReceive(sound->controlQueue, &ctrl, 0) == pdTRUE) { + switch(ctrl) { + case FREQUENCY_UPDATE: + upd = true; + break; + case END: + if (sound->repeat) { + restart(i); + } else { + stop(i); + } + break; + case FAILURE: stop(i); - } - break; - case FAILURE: - stop(i); - break; + break; + } } } - }} + } out = out / 255 / chCount; dac_output_voltage(dacCh, std::min(out, 255U)); // Do NOT overload @@ -173,11 +192,11 @@ namespace Sound { } } - void TIMER_ATTRIBUTE SoundMixer::incSound() { + void TIMER_ATTRIBUTE forceinline SoundMixer::incSound() { ++chActiveCount; } - void TIMER_ATTRIBUTE SoundMixer::decSound() { + void TIMER_ATTRIBUTE forceinline SoundMixer::decSound() { --chActiveCount; } @@ -186,9 +205,8 @@ namespace Sound { queue.push(event); } - SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac): - chCount(normal_channels + auto_channels), - chFirstAuto(normal_channels) // It isn't mistake, but looks strange + SoundMixer::SoundMixer(SoundChNum normal_channels, SoundChNum auto_channels, dac_channel_t dac) : + chCount(normal_channels + auto_channels), chFirstAuto(normal_channels) // It isn't mistake, but looks strange { assert(chCount <= CONFIG_SND_MAX_CHANNELS); dacCh = dac; @@ -196,7 +214,7 @@ namespace Sound { dac_output_enable(dacCh); esp_timer_create_args_t timer_args; - timer_args.callback = reinterpret_cast(&SoundMixer::soundCallback); + timer_args.callback = sound_timer_callback_function; timer_args.arg = this; timer_args.dispatch_method = ESP_TIMER_TASK; timer_args.name = "Sound timer"; @@ -318,9 +336,9 @@ namespace Sound { SoundState SoundMixer::state(SoundChNum channel) { std::shared_lock lock(mutex); - if (chActive[channel]) return PLAYING; + if (chActive[channel]) return PLAYING; else if (chPaused[channel]) return PAUSED; - else return STOPPED; + else return STOPPED; } SoundChNum SoundMixer::playAuto(const std::shared_ptr& sound, SoundVolume vol) { diff --git a/soundMixer.h b/soundMixer.h index 647e5cd..31e5c7d 100644 --- a/soundMixer.h +++ b/soundMixer.h @@ -7,6 +7,8 @@ #include #include +extern "C" void sound_timer_callback_function(void* arg); + namespace Sound { class SoundMixer { protected: @@ -16,6 +18,7 @@ namespace Sound { std::queue> queue; // Queue for this instance, not FreeRTOS due to smart pointers // Refer to https://stackoverflow.com/q/51632219/5697743 + std::mutex queueMutex; // Mutex for queue dac_channel_t dacCh; // DAC channel for sound @@ -34,7 +37,7 @@ namespace Sound { void incSound(); // Increment counter void decSound(); // Decrement counter - + void soundCallback(); // Play one step bool handleQueue(); // Handle suspended events (SEMI-SAFE) void setupTimer(); // Set divisors and start timer @@ -61,5 +64,7 @@ namespace Sound { void pauseAll(); void restartAll(); void resumeAll(); + + friend void ::sound_timer_callback_function(void* arg); }; } diff --git a/soundProvider.h b/soundProvider.h index 8dbb3c6..3f37ea2 100644 --- a/soundProvider.h +++ b/soundProvider.h @@ -9,12 +9,20 @@ namespace Sound { // PROVIDER CONTROL INTERFACE START virtual void provider_start() = 0; // Start filling (should be ok if started) - virtual void provider_pause() {}; // Optional methods for extra optimisation - virtual void provider_resume() {}; + virtual void provider_pause() { + } + ; // Optional methods for extra optimisation + virtual void provider_resume() { + } + ; virtual void provider_stop() = 0; // Stop filling (should be ok if isn't started) - virtual void provider_restart() {provider_stop(); provider_start();}; // This one calls if track repeats (default implementation MAY NOT work) - // PROVIDER CONTROL INTERFACE END + virtual void provider_restart() { + provider_stop(); + provider_start(); + } + ; // This one calls if track repeats (default implementation MAY NOT work) + // PROVIDER CONTROL INTERFACE END void postSample(SoundData sample); void postControl(SoundProviderControl ctrl); @@ -33,6 +41,6 @@ namespace Sound { bool repeat = false; // Implementation souldn't use any optimisations based on this - friend SoundMixer; + friend SoundMixer; }; } diff --git a/soundProviderTask.cpp b/soundProviderTask.cpp index 0a8d174..3cdf47f 100644 --- a/soundProviderTask.cpp +++ b/soundProviderTask.cpp @@ -1,12 +1,17 @@ #include -#include + +extern "C" void sound_provider_start(void* arg) { + static_cast(arg)->taskProviderCode(); +} namespace Sound { - SoundProviderTask::SoundProviderTask() {} - SoundProviderTask::~SoundProviderTask() {} + SoundProviderTask::SoundProviderTask() { + } + SoundProviderTask::~SoundProviderTask() { + } void SoundProviderTask::unconditionalStart() { - xTaskCreate(reinterpret_cast(&SoundProviderTask::taskProviderCode), "SProvTask", stackSize, this, 10, &taskHandle); + xTaskCreate(sound_provider_start, "SProvTask", stackSize, this, 10, &taskHandle); } void SoundProviderTask::provider_start() { @@ -40,7 +45,8 @@ namespace Sound { TaskHandle_t handle = taskHandle; taskHandle = nullptr; vTaskDelete(handle); - while(true) {}; + while(true) { + }; } void SoundProviderTask::taskProviderCode() { diff --git a/soundProviderTask.h b/soundProviderTask.h index 398de5c..0a732b6 100644 --- a/soundProviderTask.h +++ b/soundProviderTask.h @@ -1,15 +1,25 @@ #pragma once #include +extern "C" void sound_provider_start(void* arg); + namespace Sound { class SoundProviderTask: public SoundProvider { protected: // TASK PROVIDER INTERFACE START - virtual void task_prestart() {}; - virtual void task_poststart() {}; + virtual void task_prestart() { + } + ; + virtual void task_poststart() { + } + ; virtual void task_code() = 0; - virtual void task_prestop() {}; - virtual void task_poststop() {}; + virtual void task_prestop() { + } + ; + virtual void task_poststop() { + } + ; // TASK PROVIDER INTERFACE END // PROVIDER CONTROL INTERFACE START @@ -21,12 +31,19 @@ namespace Sound { void taskProviderCode(); void stopFromTask(); void unconditionalStart(); - void waitQueueEmpty() { while (uxQueueMessagesWaiting(queue) > 0) vTaskDelay(1); }; // Idiomatic + void waitQueueEmpty() { + while(uxQueueMessagesWaiting(queue) > 0) + vTaskDelay(1); + } + ; // Idiomatic TaskHandle_t taskHandle = nullptr; size_t stackSize = 2048; + public: SoundProviderTask(); virtual ~SoundProviderTask(); + + friend void ::sound_provider_start(void* arg); }; } From 8b1e79bb478ff7f25e8b38667c4ddc613076c2d0 Mon Sep 17 00:00:00 2001 From: v1993 Date: Sun, 26 May 2019 00:36:31 +0300 Subject: [PATCH 26/26] Add CMake support, close #9 --- CMakeLists.txt | 14 ++++++++++++++ component.mk | 4 ++++ soundMixer.cpp | 6 +++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7a40905 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,14 @@ +# PLEASE, UPDATE THIS FILE WHEN ADDING ANY NEW C SOURCES + +set(COMPONENT_SRCS + # Cross-platform stuff + "soundMixer.cpp" + "soundProvider.cpp" + "soundProviderTask.cpp" + ) +set(COMPONENT_ADD_INCLUDEDIRS ".") + +register_component() + +#target_compile_features(${COMPONENT_TARGET} PUBLIC cxx_std_14) +#set_property(TARGET ${COMPONENT_TARGET} PROPERTY CXX_STANDARD 14) diff --git a/component.mk b/component.mk index 522e76e..33aa4ce 100644 --- a/component.mk +++ b/component.mk @@ -1,3 +1,7 @@ COMPONENT_SRCDIRS:=. COMPONENT_ADD_INCLUDEDIRS:=. CXXFLAGS += -std=c++14 + +ifeq ($(CONFIG_OPTIMIZATION_LEVEL_DEBUG),y) + CFLAGS += -fno-inline-functions +endif \ No newline at end of file diff --git a/soundMixer.cpp b/soundMixer.cpp index 6c3ab44..29864d0 100644 --- a/soundMixer.cpp +++ b/soundMixer.cpp @@ -11,7 +11,12 @@ #define TIMER_ATTRIBUTE #endif +#ifdef CONFIG_OPTIMIZATION_LEVEL_RELEASE #define forceinline inline __attribute__((always_inline)) +#else +#define forceinline // Turn off on debug builds to prevent debugger crash. +// Refer to https://github.com/espressif/esp-idf/issues/2343 +#endif extern "C" { static int TIMER_ATTRIBUTE gcd(int a, int b) { @@ -152,7 +157,6 @@ namespace Sound { for (SoundChNum i = 0; i < chCount; ++i) { if (chActive[i]) { std::shared_ptr& sound = chSound[i]; - //if ((rand() % 1000) == 0) std::cout << sound.use_count() << std::endl; if ((counter % sound->divisor) == 0) { SoundData sample; if (xQueueReceive(sound->queue, &sample, 0) == pdTRUE) {