From d1dc1fed84238feab58d4b3fcf5f880b649cfa71 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Wed, 10 May 2023 02:38:33 +0200 Subject: [PATCH 1/3] Visual Studio 2019 project added --- fm_transmitter.vcxproj | 265 ++++++++++++++++++++++++++++++++++++ fm_transmitter.vcxproj.user | 4 + mailbox.c => mailbox.cpp | 0 mailbox.h => mailbox.hpp | 0 makefile | 4 +- 5 files changed, 271 insertions(+), 2 deletions(-) create mode 100644 fm_transmitter.vcxproj create mode 100644 fm_transmitter.vcxproj.user rename mailbox.c => mailbox.cpp (100%) rename mailbox.h => mailbox.hpp (100%) diff --git a/fm_transmitter.vcxproj b/fm_transmitter.vcxproj new file mode 100644 index 0000000..ab39dee --- /dev/null +++ b/fm_transmitter.vcxproj @@ -0,0 +1,265 @@ + + + + + Debug + ARM + + + Release + ARM + + + Debug + ARM64 + + + Release + ARM64 + + + Debug + x86 + + + Release + x86 + + + Debug + x64 + + + Release + x64 + + + + {f1c523ba-9fe5-413c-8a54-8c7563f63eb6} + Linux + fm_transmitter + 15.0 + Linux + 1.0 + Generic + {D51BCBC9-82E9-4017-911E-C93873C4EA2B} + + + + true + + + false + + + true + + + false + + + true + + + false + + + false + + + true + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6"; + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions) + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + None + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6"; + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions) + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6"; + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions) + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6"; + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + + + + + EXECUTABLE="fm_transmitter";VERSION="0.9.6";NDEBUG;%(PreprocessorDefinitions) + /opt/vc/include;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + /opt/vc/lib;%(Link.AdditionalLibraryDirectories) + + + pthread;bcm_host;%(LibraryDependencies) + false + false + + + + + \ No newline at end of file diff --git a/fm_transmitter.vcxproj.user b/fm_transmitter.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/fm_transmitter.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/mailbox.c b/mailbox.cpp similarity index 100% rename from mailbox.c rename to mailbox.cpp diff --git a/mailbox.h b/mailbox.hpp similarity index 100% rename from mailbox.h rename to mailbox.hpp diff --git a/makefile b/makefile index 66777fa..5cba90e 100644 --- a/makefile +++ b/makefile @@ -9,8 +9,8 @@ endif all: fm_transmitter.o mailbox.o wave_reader.o transmitter.o g++ -o $(EXECUTABLE) fm_transmitter.o mailbox.o wave_reader.o transmitter.o -L/opt/vc/lib -lm -lpthread -lbcm_host -mailbox.o: mailbox.c mailbox.h - g++ $(FLAGS) -c mailbox.c +mailbox.o: mailbox.cpp mailbox.hpp + g++ $(FLAGS) -c mailbox.cpp wave_reader.o: wave_reader.cpp wave_reader.hpp g++ $(FLAGS) -c wave_reader.cpp From 019f39c22020f66473e71ef9cec80ffd84954b2f Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Sun, 21 May 2023 01:21:38 +0200 Subject: [PATCH 2/3] Conditional variable support added --- fm_transmitter.cpp | 9 +-- transmitter.cpp | 135 ++++++++++++++++++++++++++------------------- transmitter.hpp | 7 ++- wave_reader.cpp | 38 ++++++++----- wave_reader.hpp | 7 ++- 5 files changed, 117 insertions(+), 79 deletions(-) diff --git a/fm_transmitter.cpp b/fm_transmitter.cpp index b5b158a..32946e4 100644 --- a/fm_transmitter.cpp +++ b/fm_transmitter.cpp @@ -36,7 +36,8 @@ #include #include -bool stop = false; +std::mutex mtx; +bool enable = true; Transmitter *transmitter = nullptr; void sigIntHandler(int sigNum) @@ -44,7 +45,7 @@ void sigIntHandler(int sigNum) if (transmitter != nullptr) { std::cout << "Stopping..." << std::endl; transmitter->Stop(); - stop = true; + enable = false; } } @@ -97,14 +98,14 @@ int main(int argc, char** argv) if ((optind == argc) && loop) { optind = filesOffset; } - WaveReader reader(filename != "-" ? filename : std::string(), stop); + WaveReader reader(filename != "-" ? filename : std::string(), enable, mtx); WaveHeader header = reader.GetHeader(); std::cout << "Playing: " << reader.GetFilename() << ", " << header.sampleRate << " Hz, " << header.bitsPerSample << " bits, " << ((header.channels > 0x01) ? "stereo" : "mono") << std::endl; transmitter->Transmit(reader, frequency, bandwidth, dmaChannel, optind < argc); - } while (!stop && (optind < argc)); + } while (enable && (optind < argc)); } catch (std::exception &catched) { std::cout << "Error: " << catched.what() << std::endl; result = EXIT_FAILURE; diff --git a/transmitter.cpp b/transmitter.cpp index ca1aeff..d7f7497 100644 --- a/transmitter.cpp +++ b/transmitter.cpp @@ -32,7 +32,7 @@ */ #include "transmitter.hpp" -#include "mailbox.h" +#include "mailbox.hpp" #include #include #include @@ -354,7 +354,7 @@ class DMAController : public Device }; Transmitter::Transmitter() - : output(nullptr), stop(true) + : output(nullptr), enable(false) { } @@ -366,7 +366,10 @@ Transmitter::~Transmitter() { void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier) { - stop = false; + { + std::lock_guard lock(mtx); + enable = true; + } WaveHeader header = reader.GetHeader(); unsigned bufferSize = static_cast(static_cast(header.sampleRate) * BUFFER_TIME / 1000000); @@ -399,50 +402,64 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, void Transmitter::Stop() { - stop = true; + std::unique_lock lock(mtx); + enable = false; + lock.unlock(); + cv.notify_all(); } void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange) { - std::vector samples = reader.GetSamples(bufferSize, stop); - if (samples.empty()) { - return; - } - + std::vector samples; unsigned sampleOffset = 0; - bool eof = samples.size() < bufferSize, txStop = false; - std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &txStop); - std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2)); + bool eof = false, stop = false, start = true; + + std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop); auto finally = [&]() { { - std::lock_guard lock(access); - txStop = true; + std::lock_guard lock(mtx); + stop = true; + cv.notify_one(); } transmitterThread.join(); samples.clear(); - stop = true; + enable = false; }; + try { - while (!eof && !stop) { - { - std::lock_guard lock(access); - if (txStop) { - throw std::runtime_error("Transmitter thread has unexpectedly exited"); - } - if (samples.empty()) { - if (!reader.SetSampleOffset(sampleOffset + bufferSize)) { - break; - } - samples = reader.GetSamples(bufferSize, stop); - if (samples.empty()) { - break; - } - eof = samples.size() < bufferSize; - } + while (!eof) { + std::unique_lock lock(mtx); + if (!start) { + cv.wait(lock, [&]() -> bool { + return samples.empty() || !enable || stop; + }); + } else { + start = false; + } + if (!enable) { + break; + } + if (stop) { + throw std::runtime_error("Transmitter thread has unexpectedly exited"); + } + if (samples.empty()) { + if (!reader.SetSampleOffset(sampleOffset + bufferSize)) { + break; + } + lock.unlock(); + samples = reader.GetSamples(bufferSize, enable, mtx); + lock.lock(); + if (samples.empty()) { + break; + } + eof = samples.size() < bufferSize; + lock.unlock(); + cv.notify_one(); + } else { + lock.unlock(); } - std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2)); } } catch (...) { finally(); @@ -459,7 +476,7 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign AllocatedMemory allocated(sizeof(uint32_t) * bufferSize + sizeof(DMAControllBlock) * (2 * bufferSize) + sizeof(uint32_t)); - std::vector samples = reader.GetSamples(bufferSize, stop); + std::vector samples = reader.GetSamples(bufferSize, enable, mtx); if (samples.empty()) { return; } @@ -509,11 +526,18 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign std::this_thread::sleep_for(std::chrono::milliseconds(1)); } samples.clear(); - stop = true; + std::lock_guard lock(mtx); + enable = false; }; try { - while (!eof && !stop) { - samples = reader.GetSamples(bufferSize, stop); + while (!eof) { + { + std::lock_guard lock(mtx); + if (!enable) { + break; + } + } + samples = reader.GetSamples(bufferSize, enable, mtx); if (!samples.size()) { break; } @@ -542,28 +566,25 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, volatile TimerRegisters *timer = reinterpret_cast(peripherals.GetVirtualAddress(TIMER_BASE_OFFSET)); uint64_t current = *(reinterpret_cast(&timer->low)); - uint64_t playbackStart = current; + uint64_t playbackStart = current, start = current; while (true) { std::vector loadedSamples; - while (true) { - { - std::lock_guard lock(instance->access); - if (*stop) { - return; - } - loadedSamples = std::move(*samples); - current = *(reinterpret_cast(&timer->low)); - if (!loadedSamples.empty()) { - *sampleOffset = (current - playbackStart) * sampleRate / 1000000; - break; - } - } - std::this_thread::sleep_for(std::chrono::microseconds(1)); - }; - uint64_t start = current; - unsigned offset = (current - start) * sampleRate / 1000000; + std::unique_lock lock(instance->mtx); + instance->cv.wait(lock, [&]() -> bool { + return !samples->empty() || *stop; + }); + if (*stop) { + break; + } + start = current = *(reinterpret_cast(&timer->low)); + *sampleOffset = (current - playbackStart) * sampleRate / 1000000; + loadedSamples = std::move(*samples); + lock.unlock(); + instance->cv.notify_one(); + + unsigned offset = 0; while (true) { if (offset >= loadedSamples.size()) { @@ -573,14 +594,16 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, float value = loadedSamples[offset].GetMonoValue(); instance->output->SetDivisor(clockDivisor - static_cast(round(value * divisorRange))); while (offset == prevOffset) { - std::this_thread::sleep_for(std::chrono::microseconds(1)); // asm("nop"); + std::this_thread::yield(); // asm("nop"); current = *(reinterpret_cast(&timer->low));; offset = (current - start) * sampleRate / 1000000; } } } } catch (...) { - std::lock_guard lock(instance->access); + std::unique_lock lock(instance->mtx); *stop = true; + lock.unlock(); + instance->cv.notify_one(); } } diff --git a/transmitter.hpp b/transmitter.hpp index 74a2e77..e5649bc 100644 --- a/transmitter.hpp +++ b/transmitter.hpp @@ -34,7 +34,7 @@ #pragma once #include "wave_reader.hpp" -#include +#include class ClockOutput; @@ -53,7 +53,8 @@ class Transmitter void TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel); static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector *samples, bool *stop); + std::condition_variable cv; ClockOutput *output; - std::mutex access; - bool stop; + std::mutex mtx; + bool enable; }; diff --git a/wave_reader.cpp b/wave_reader.cpp index 72eca11..0ac441f 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -65,7 +65,7 @@ float Sample::GetMonoValue() const return value; } -WaveReader::WaveReader(const std::string &filename, bool &stop) : +WaveReader::WaveReader(const std::string &filename, bool &enable, std::mutex &mtx) : filename(filename), headerOffset(0), currentDataOffset(0) { if (!filename.empty()) { @@ -80,12 +80,12 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) : } try { - ReadData(sizeof(WaveHeader::chunkID) + sizeof(WaveHeader::chunkSize) + sizeof(WaveHeader::format), true, stop); + ReadData(sizeof(WaveHeader::chunkID) + sizeof(WaveHeader::chunkSize) + sizeof(WaveHeader::format), true, enable, mtx); if ((std::string(reinterpret_cast(header.chunkID), 4) != std::string("RIFF")) || (std::string(reinterpret_cast(header.format), 4) != std::string("WAVE"))) { throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", WAVE file expected")); } - ReadData(sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), true, stop); + ReadData(sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), true, enable, mtx); unsigned subchunk1MinSize = sizeof(WaveHeader::audioFormat) + sizeof(WaveHeader::channels) + sizeof(WaveHeader::sampleRate) + sizeof(WaveHeader::byteRate) + sizeof(WaveHeader::blockAlign) + sizeof(WaveHeader::bitsPerSample); @@ -93,7 +93,7 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) : throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted")); } - ReadData(header.subchunk1Size, true, stop); + ReadData(header.subchunk1Size, true, enable, mtx); if ((header.audioFormat != WAVE_FORMAT_PCM) || (header.byteRate != (header.bitsPerSample >> 3) * header.channels * header.sampleRate) || (header.blockAlign != (header.bitsPerSample >> 3) * header.channels) || @@ -101,7 +101,7 @@ WaveReader::WaveReader(const std::string &filename, bool &stop) : throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", unsupported WAVE format")); } - ReadData(sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), true, stop); + ReadData(sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), true, enable, mtx); if (std::string(reinterpret_cast(header.subchunk2ID), 4) != std::string("data")) { throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted")); } @@ -134,7 +134,7 @@ const WaveHeader &WaveReader::GetHeader() const return header; } -std::vector WaveReader::GetSamples(unsigned quantity, bool &stop) { +std::vector WaveReader::GetSamples(unsigned quantity, bool &enable, std::mutex &mtx) { unsigned bytesPerSample = (header.bitsPerSample >> 3) * header.channels; unsigned bytesToRead = quantity * bytesPerSample; unsigned bytesLeft = header.subchunk2Size - currentDataOffset; @@ -143,7 +143,7 @@ std::vector WaveReader::GetSamples(unsigned quantity, bool &stop) { quantity = bytesToRead / bytesPerSample; } - std::vector data = std::move(ReadData(bytesToRead, false, stop)); + std::vector data = std::move(ReadData(bytesToRead, false, enable, mtx)); if (data.size() < bytesToRead) { quantity = data.size() / bytesPerSample; } @@ -166,12 +166,18 @@ bool WaveReader::SetSampleOffset(unsigned offset) { return true; } -std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &stop) +std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx) { unsigned bytesRead = 0; std::vector data; data.resize(bytesToRead); - while ((bytesRead < bytesToRead) && !stop) { + while (bytesRead < bytesToRead) { + { + std::lock_guard lock(mtx); + if (!enable) { + break; + } + } int bytes = read(fileDescriptor, &data[bytesRead], bytesToRead - bytesRead); if (((bytes == -1) && ((fileDescriptor != STDIN_FILENO) || (errno != EAGAIN))) || ((static_cast(bytes) < bytesToRead) && headerBytes && (fileDescriptor != STDIN_FILENO))) { @@ -191,14 +197,20 @@ std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes } if (headerBytes) { - if (stop) { - throw std::runtime_error("Cannot obtain header, program interrupted"); + { + std::lock_guard lock(mtx); + if (!enable) { + throw std::runtime_error("Cannot obtain header, program interrupted"); + } } std::memcpy(&(reinterpret_cast(&header))[headerOffset], data.data(), bytesRead); headerOffset += bytesRead; } else { - if (stop) { - data.resize(bytesRead); + { + std::lock_guard lock(mtx); + if (!enable) { + data.resize(bytesRead); + } } currentDataOffset += bytesRead; } diff --git a/wave_reader.hpp b/wave_reader.hpp index 2e2a54c..060f10a 100644 --- a/wave_reader.hpp +++ b/wave_reader.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #define WAVE_FORMAT_PCM 0x0001 @@ -68,17 +69,17 @@ protected: class WaveReader { public: - WaveReader(const std::string &filename, bool &stop); + WaveReader(const std::string &filename, bool &enable, std::mutex &mtx); virtual ~WaveReader(); WaveReader(const WaveReader &) = delete; WaveReader(WaveReader &&) = delete; WaveReader &operator=(const WaveReader &) = delete; std::string GetFilename() const; const WaveHeader &GetHeader() const; - std::vector GetSamples(unsigned quantity, bool &stop); + std::vector GetSamples(unsigned quantity, bool &enable, std::mutex &mtx); bool SetSampleOffset(unsigned offset); private: - std::vector ReadData(unsigned bytesToRead, bool headerBytes, bool &stop); + std::vector ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx); std::string filename; WaveHeader header; From ddfcf6a53c4c0a8cdf6558da2ad728a84b3f9653 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Mon, 14 Aug 2023 02:11:23 +0200 Subject: [PATCH 3/3] Fixed ARM64 bug, when not using DMA --- mailbox.cpp | 2 +- transmitter.cpp | 159 +++++++++++++++++++++++------------------------- transmitter.hpp | 2 + wave_reader.cpp | 11 +++- 4 files changed, 88 insertions(+), 86 deletions(-) diff --git a/mailbox.cpp b/mailbox.cpp index 5d78045..537c550 100644 --- a/mailbox.cpp +++ b/mailbox.cpp @@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include "mailbox.h" +#include "mailbox.hpp" #define PAGE_SIZE (4*1024) diff --git a/transmitter.cpp b/transmitter.cpp index d7f7497..b8dc0f0 100644 --- a/transmitter.cpp +++ b/transmitter.cpp @@ -51,7 +51,6 @@ #define BCM2711_PLLD_FREQ 750 #define GPIO_BASE_OFFSET 0x00200000 -#define TIMER_BASE_OFFSET 0x00003000 #define CLK0_BASE_OFFSET 0x00101070 #define CLK1_BASE_OFFSET 0x00101078 @@ -100,16 +99,6 @@ #define BUFFER_TIME 1000000 #define PAGE_SIZE 4096 -struct TimerRegisters { - uint32_t ctlStatus; - uint32_t low; - uint32_t high; - uint32_t c0; - uint32_t c1; - uint32_t c2; - uint32_t c3; -}; - struct ClockRegisters { uint32_t ctl; uint32_t div; @@ -359,6 +348,10 @@ Transmitter::Transmitter() } Transmitter::~Transmitter() { + std::unique_lock lock(mtx); + cv.wait(lock, [&]() -> bool { + return !txThread.joinable() && !enable; + }); if (output != nullptr) { delete output; } @@ -386,6 +379,11 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, delete output; output = nullptr; } + { + std::lock_guard lock(mtx); + enable = false; + } + cv.notify_all(); }; try { if (dmaChannel != 0xff) { @@ -408,66 +406,6 @@ void Transmitter::Stop() cv.notify_all(); } -void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange) -{ - std::vector samples; - unsigned sampleOffset = 0; - - bool eof = false, stop = false, start = true; - - std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop); - - auto finally = [&]() { - { - std::lock_guard lock(mtx); - stop = true; - cv.notify_one(); - } - transmitterThread.join(); - samples.clear(); - enable = false; - }; - - try { - while (!eof) { - std::unique_lock lock(mtx); - if (!start) { - cv.wait(lock, [&]() -> bool { - return samples.empty() || !enable || stop; - }); - } else { - start = false; - } - if (!enable) { - break; - } - if (stop) { - throw std::runtime_error("Transmitter thread has unexpectedly exited"); - } - if (samples.empty()) { - if (!reader.SetSampleOffset(sampleOffset + bufferSize)) { - break; - } - lock.unlock(); - samples = reader.GetSamples(bufferSize, enable, mtx); - lock.lock(); - if (samples.empty()) { - break; - } - eof = samples.size() < bufferSize; - lock.unlock(); - cv.notify_one(); - } else { - lock.unlock(); - } - } - } catch (...) { - finally(); - throw; - } - finally(); -} - void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel) { if (dmaChannel > 15) { @@ -526,8 +464,6 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign std::this_thread::sleep_for(std::chrono::milliseconds(1)); } samples.clear(); - std::lock_guard lock(mtx); - enable = false; }; try { while (!eof) { @@ -559,14 +495,69 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign finally(); } +void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange) +{ + std::vector samples; + unsigned sampleOffset = 0; + + bool eof = false, stop = false, start = true; + + txThread = std::thread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &stop); + + auto finally = [&]() { + { + std::lock_guard lock(mtx); + stop = true; + } + cv.notify_all(); + txThread.join(); + samples.clear(); + }; + + try { + while (!eof) { + std::unique_lock lock(mtx); + if (!start) { + cv.wait(lock, [&]() -> bool { + return samples.empty() || !enable || stop; + }); + } + if (!enable) { + break; + } + if (stop) { + throw std::runtime_error("Transmitter thread has unexpectedly exited"); + } + if (samples.empty()) { + if (!reader.SetSampleOffset(sampleOffset + (start ? 0 : bufferSize))) { + break; + } + lock.unlock(); + samples = reader.GetSamples(bufferSize, enable, mtx); + lock.lock(); + if (samples.empty()) { + break; + } + eof = samples.size() < bufferSize; + lock.unlock(); + cv.notify_all(); + } else { + lock.unlock(); + } + start = false; + } + } catch (...) { + finally(); + throw; + } + finally(); +} + void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector *samples, bool *stop) { try { - Peripherals &peripherals = Peripherals::GetInstance(); - - volatile TimerRegisters *timer = reinterpret_cast(peripherals.GetVirtualAddress(TIMER_BASE_OFFSET)); - uint64_t current = *(reinterpret_cast(&timer->low)); - uint64_t playbackStart = current, start = current; + auto playbackStart = std::chrono::system_clock::now(); + std::chrono::system_clock::time_point current, start; while (true) { std::vector loadedSamples; @@ -578,11 +569,11 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, if (*stop) { break; } - start = current = *(reinterpret_cast(&timer->low)); - *sampleOffset = (current - playbackStart) * sampleRate / 1000000; + start = current = std::chrono::system_clock::now(); + *sampleOffset = std::chrono::duration_cast(current - playbackStart).count() * sampleRate / 1000000; loadedSamples = std::move(*samples); lock.unlock(); - instance->cv.notify_one(); + instance->cv.notify_all(); unsigned offset = 0; @@ -595,8 +586,8 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, instance->output->SetDivisor(clockDivisor - static_cast(round(value * divisorRange))); while (offset == prevOffset) { std::this_thread::yield(); // asm("nop"); - current = *(reinterpret_cast(&timer->low));; - offset = (current - start) * sampleRate / 1000000; + current = std::chrono::system_clock::now(); + offset = std::chrono::duration_cast(current - start).count() * sampleRate / 1000000; } } } @@ -604,6 +595,6 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, std::unique_lock lock(instance->mtx); *stop = true; lock.unlock(); - instance->cv.notify_one(); + instance->cv.notify_all(); } } diff --git a/transmitter.hpp b/transmitter.hpp index e5649bc..92ffc3b 100644 --- a/transmitter.hpp +++ b/transmitter.hpp @@ -35,6 +35,7 @@ #include "wave_reader.hpp" #include +#include class ClockOutput; @@ -54,6 +55,7 @@ class Transmitter static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector *samples, bool *stop); std::condition_variable cv; + std::thread txThread; ClockOutput *output; std::mutex mtx; bool enable; diff --git a/wave_reader.cpp b/wave_reader.cpp index 0ac441f..1494f8c 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -171,6 +171,10 @@ std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes unsigned bytesRead = 0; std::vector data; data.resize(bytesToRead); + timeval timeout = { + .tv_sec = 1, + }; + fd_set fds; while (bytesRead < bytesToRead) { { std::lock_guard lock(mtx); @@ -191,7 +195,12 @@ std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes data.resize(bytesRead); break; } else { - std::this_thread::sleep_for(std::chrono::microseconds(1)); + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout); + if (FD_ISSET(STDIN_FILENO, &fds)) { + FD_CLR(STDIN_FILENO, &fds); + } } } }