From a8c604405fb0ed7beea4151ac97c0481fe8b9b78 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Wed, 9 Apr 2025 03:33:23 +0200 Subject: [PATCH 1/4] Performance fixes --- fm_transmitter.cpp | 4 +- transmitter.cpp | 71 ++++++++++++--------------- wave_reader.cpp | 118 +++++++++++++++------------------------------ wave_reader.hpp | 2 +- 4 files changed, 72 insertions(+), 123 deletions(-) diff --git a/fm_transmitter.cpp b/fm_transmitter.cpp index 51db29b..c3a950b 100644 --- a/fm_transmitter.cpp +++ b/fm_transmitter.cpp @@ -95,9 +95,9 @@ int main(int argc, char** argv) << bandwidth << " kHz bandwidth" << std::endl; do { std::string filename = argv[optind++]; - if ((optind == argc) && loop) { + if ((optind == argc) && loop) optind = filesOffset; - } + WaveReader reader(filename != "-" ? filename : std::string(), enable, mtx); WaveHeader header = reader.GetHeader(); std::cout << "Playing: " << reader.GetFilename() << ", " diff --git a/transmitter.cpp b/transmitter.cpp index d786e7f..a7b2dc0 100644 --- a/transmitter.cpp +++ b/transmitter.cpp @@ -169,21 +169,18 @@ class Peripherals private: Peripherals() { int memFd; - if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { + if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) throw std::runtime_error("Cannot open /dev/mem file (permission denied)"); - } peripherals = mmap(nullptr, GetSize(), PROT_READ | PROT_WRITE, MAP_SHARED, memFd, GetVirtualBaseAddress()); close(memFd); - if (peripherals == MAP_FAILED) { + if (peripherals == MAP_FAILED) throw std::runtime_error("Cannot obtain access to peripherals (mmap error)"); - } } unsigned GetSize() { unsigned size = bcm_host_get_peripheral_size(); - if (size == BCM2711_PERI_VIRT_BASE) { + if (size == BCM2711_PERI_VIRT_BASE) size = 0x01000000; - } return size; } @@ -197,9 +194,8 @@ class AllocatedMemory AllocatedMemory(unsigned size) { mBoxFd = mbox_open(); memSize = size; - if (memSize % PAGE_SIZE) { + if (memSize % PAGE_SIZE) memSize = (memSize / PAGE_SIZE + 1) * PAGE_SIZE; - } memHandle = mem_alloc(mBoxFd, size, PAGE_SIZE, (Peripherals::GetVirtualBaseAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2711_MEM_FLAG); if (!memHandle) { mbox_close(mBoxFd); @@ -353,9 +349,8 @@ Transmitter::~Transmitter() { cv.wait(lock, [&]() -> bool { return !enable; }); - if (output) { + if (output) delete output; - } } void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier) @@ -383,15 +378,13 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned clockDivisor = static_cast(round(Peripherals::GetClockFrequency() * (0x01 << 12) / frequency)); unsigned divisorRange = clockDivisor - static_cast(round(Peripherals::GetClockFrequency() * (0x01 << 12) / (frequency + 0.0005f * bandwidth))); - if (!output) { + if (!output) output = new ClockOutput(clockDivisor); - } - if (dmaChannel != 0xff) { + if (dmaChannel != 0xff) TxViaDma(reader, header.sampleRate, bufferSize, clockDivisor, divisorRange, dmaChannel); - } else { + else TxViaCpu(reader, header.sampleRate, bufferSize, clockDivisor, divisorRange); - } } catch (...) { finally(); throw; @@ -409,16 +402,14 @@ void Transmitter::Stop() void Transmitter::TxViaDma(WaveReader &reader, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel) { - if (dmaChannel > 15) { + if (dmaChannel > 15) throw std::runtime_error("DMA channel number out of range (0 - 15)"); - } AllocatedMemory allocated(sizeof(uint32_t) * bufferSize + sizeof(DMAControllBlock) * (2 * bufferSize) + sizeof(uint32_t)); std::vector samples = reader.GetSamples(bufferSize, enable, mtx); - if (samples.empty()) { + if (samples.empty()) return; - } bool eof = false; if (samples.size() < bufferSize) { @@ -461,30 +452,27 @@ void Transmitter::TxViaDma(WaveReader &reader, unsigned sampleRate, unsigned buf auto finally = [&]() { dmaCb[(cbOffset < 2 * bufferSize) ? cbOffset : 0].nextCbAddress = 0x00000000; - while (dma.GetControllBlockAddress() != 0x00000000) { + while (dma.GetControllBlockAddress() != 0x00000000) std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } samples.clear(); }; try { while (!eof) { { std::lock_guard lock(mtx); - if (!enable) { + if (!enable) break; - } } samples = reader.GetSamples(bufferSize, enable, mtx); - if (!samples.size()) { + if (!samples.size()) break; - } + cbOffset = 0; eof = samples.size() < bufferSize; for (std::size_t i = 0; i < samples.size(); i++) { float value = samples[i].GetMonoValue(); - while (i == ((dma.GetControllBlockAddress() - allocated.GetPhysicalAddress(dmaCb)) / (2 * sizeof(DMAControllBlock)))) { + while (i == ((dma.GetControllBlockAddress() - allocated.GetPhysicalAddress(dmaCb)) / (2 * sizeof(DMAControllBlock)))) std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 10)); - } clkDiv[i] = CLK_PASSWORD | (0xffffff & (clockDivisor - static_cast(round(value * divisorRange)))); cbOffset += 2; } @@ -523,28 +511,28 @@ void Transmitter::TxViaCpu(WaveReader &reader, unsigned sampleRate, unsigned buf return samples.empty() || !enable || stop; }); } - if (!enable) { + + if (!enable) break; - } - if (stop) { + + if (stop) throw std::runtime_error("Transmitter thread has unexpectedly exited"); - } + if (samples.empty()) { - if (!reader.SetSampleOffset(sampleOffset + (start ? 0 : bufferSize))) { + if (!reader.SetSampleOffset(sampleOffset + (start ? 0 : bufferSize))) break; - } lock.unlock(); samples = reader.GetSamples(bufferSize, enable, mtx); lock.lock(); - if (samples.empty()) { + if (samples.empty()) break; - } + eof = samples.size() < bufferSize; lock.unlock(); cv.notify_all(); - } else { + } else lock.unlock(); - } + start = false; } } catch (...) { @@ -567,9 +555,10 @@ void Transmitter::CpuTxThread(unsigned sampleRate, unsigned clockDivisor, unsign cv.wait(lock, [&]() -> bool { return !samples->empty() || *stop; }); - if (*stop) { + + if (*stop) break; - } + start = current = std::chrono::system_clock::now(); *sampleOffset = std::chrono::duration_cast(current - playbackStart).count() * sampleRate / 1000000; loadedSamples = std::move(*samples); @@ -579,9 +568,9 @@ void Transmitter::CpuTxThread(unsigned sampleRate, unsigned clockDivisor, unsign unsigned offset = 0; while (true) { - if (offset >= loadedSamples.size()) { + if (offset >= loadedSamples.size()) break; - } + unsigned prevOffset = offset; float value = loadedSamples[offset].GetMonoValue(); output->SetDivisor(clockDivisor - static_cast(round(value * divisorRange))); diff --git a/wave_reader.cpp b/wave_reader.cpp index b77374d..1ad8238 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -43,21 +43,18 @@ Sample::Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel) : value(0.f) { - int sum = 0; - int16_t *channelValues = new int16_t[channels]; + int32_t sum = 0; for (unsigned i = 0; i < channels; i++) { switch (bitsPerChannel >> 3) { case 2: - channelValues[i] = (data[((i + 1) << 1) - 1] << 8) | data[((i + 1) << 1) - 2]; + sum += (data[((i + 1) << 1) - 1] << 8) | data[((i + 1) << 1) - 2]; break; case 1: - channelValues[i] = (static_cast(data[i]) - 0x80) << 8; + sum += (static_cast(data[i]) - 0x80) << 8; break; } - sum += channelValues[i]; } - value = 2 * sum / (static_cast(USHRT_MAX) * channels); - delete[] channelValues; + value = sum / (-static_cast(SHRT_MIN) * channels); } float Sample::GetMonoValue() const @@ -66,62 +63,54 @@ float Sample::GetMonoValue() const } WaveReader::WaveReader(const std::string &filename, bool &enable, std::mutex &mtx) : - filename(filename), headerOffset(0), currentDataOffset(0) + filename(filename) { - if (!filename.empty()) { + if (!filename.empty()) fileDescriptor = open(filename.c_str(), O_RDONLY); - } else { + else { fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK); fileDescriptor = STDIN_FILENO; } - if (fileDescriptor == -1) { - throw std::runtime_error(std::string("Cannot open ") + GetFilename() + std::string(", file does not exist")); - } + if (fileDescriptor == -1) + throw std::runtime_error(std::string("Cannot open ") + GetFilename()); try { - 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"))) { + ReadData(&header.chunkID, sizeof(WaveHeader::chunkID) + sizeof(WaveHeader::chunkSize) + sizeof(WaveHeader::format), 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, enable, mtx); + ReadData(&header.subchunkID, sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), enable, mtx); unsigned subchunk1MinSize = sizeof(WaveHeader::audioFormat) + sizeof(WaveHeader::channels) + sizeof(WaveHeader::sampleRate) + sizeof(WaveHeader::byteRate) + sizeof(WaveHeader::blockAlign) + sizeof(WaveHeader::bitsPerSample); - if ((std::string(reinterpret_cast(header.subchunk1ID), 4) != std::string("fmt ")) || (header.subchunk1Size < subchunk1MinSize)) { + if ((std::string(reinterpret_cast(header.subchunk1ID), 4) != std::string("fmt ")) || (header.subchunk1Size < subchunk1MinSize)) throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted")); - } - ReadData(header.subchunk1Size, true, enable, mtx); + ReadData(&header.audioFormat, header.subchunk1Size, enable, mtx); if ((header.audioFormat != WAVE_FORMAT_PCM) || (header.byteRate != (header.bitsPerSample >> 3) * header.channels * header.sampleRate) || (header.blockAlign != (header.bitsPerSample >> 3) * header.channels) || - (((header.bitsPerSample >> 3) != 1) && ((header.bitsPerSample >> 3) != 2))) { + (((header.bitsPerSample >> 3) != 1) && ((header.bitsPerSample >> 3) != 2))) throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", unsupported WAVE format")); - } - ReadData(sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), true, enable, mtx); - if (std::string(reinterpret_cast(header.subchunk2ID), 4) != std::string("data")) { + ReadData(&header.subchunk2ID, sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), 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")); - } } catch (...) { - if (fileDescriptor != STDIN_FILENO) { + if (fileDescriptor != STDIN_FILENO) close(fileDescriptor); - } throw; } - if (fileDescriptor != STDIN_FILENO) { + if (fileDescriptor != STDIN_FILENO) dataOffset = lseek(fileDescriptor, 0, SEEK_CUR); - } } WaveReader::~WaveReader() { - if (fileDescriptor != STDIN_FILENO) { + if (fileDescriptor != STDIN_FILENO) close(fileDescriptor); - } } std::string WaveReader::GetFilename() const @@ -137,59 +126,52 @@ const WaveHeader &WaveReader::GetHeader() const 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; + unsigned bytesLeft = (fileDescriptor != STDIN_FILENO) ? + header.subchunk2Size - lseek(fileDescriptor, 0, SEEK_CUR) : bytesToRead; if (bytesToRead > bytesLeft) { bytesToRead = bytesLeft - bytesLeft % bytesPerSample; quantity = bytesToRead / bytesPerSample; } - std::vector data = std::move(ReadData(bytesToRead, false, enable, mtx)); - if (data.size() < bytesToRead) { + std::vector data(bytesToRead); + data.resize(ReadData(data.data(), bytesToRead, enable, mtx)); + if (data.size() < bytesToRead) quantity = data.size() / bytesPerSample; - } std::vector samples; samples.reserve(quantity); - for (unsigned i = 0; i < quantity; i++) { + for (unsigned i = 0; i < quantity; i++) samples.push_back(Sample(&data[bytesPerSample * i], header.channels, header.bitsPerSample)); - } + return samples; } bool WaveReader::SetSampleOffset(unsigned offset) { - if (fileDescriptor != STDIN_FILENO) { - currentDataOffset = offset * (header.bitsPerSample >> 3) * header.channels; - if (lseek(fileDescriptor, dataOffset + currentDataOffset, SEEK_SET) == -1) { + if (fileDescriptor != STDIN_FILENO) + if (lseek(fileDescriptor, dataOffset + offset * (header.bitsPerSample >> 3) * header.channels, SEEK_SET) == -1) return false; - } - } return true; } -std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx) +int WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, std::mutex &mtx) { 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); - if (!enable) { + 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))) { - throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", data corrupted")); - } - if (bytes > 0) { + int bytes = read(fileDescriptor, &(reinterpret_cast(buffer)[bytesRead]), bytesToRead - bytesRead); + if ((bytes == -1) && ((fileDescriptor != STDIN_FILENO) || (errno != EAGAIN))) + throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", cannot read file")); + if (bytes > 0) bytesRead += bytes; - } if (bytesRead < bytesToRead) { if (fileDescriptor != STDIN_FILENO) { data.resize(bytesRead); @@ -198,35 +180,13 @@ std::vector WaveReader::ReadData(unsigned bytesToRead, bool headerBytes FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout); - if (FD_ISSET(STDIN_FILENO, &fds)) { + if (FD_ISSET(STDIN_FILENO, &fds)) FD_CLR(STDIN_FILENO, &fds); - } } } - if (bytes == 0) { - data.resize(bytesRead); + if (bytes == 0) break; - } } - if (headerBytes) { - { - 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 { - { - std::lock_guard lock(mtx); - if (!enable) { - data.resize(bytesRead); - } - } - currentDataOffset += bytesRead; - } - - return data; + return bytesRead; } diff --git a/wave_reader.hpp b/wave_reader.hpp index 060f10a..a1470af 100644 --- a/wave_reader.hpp +++ b/wave_reader.hpp @@ -83,6 +83,6 @@ class WaveReader std::string filename; WaveHeader header; - unsigned dataOffset, headerOffset, currentDataOffset; + unsigned dataOffset; int fileDescriptor; }; From 3aa9cdec49ced8e80fe0228c3d612f2bf42c9b4f Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Wed, 9 Apr 2025 03:59:18 +0200 Subject: [PATCH 2/4] Code fixes --- wave_reader.cpp | 13 +++++-------- wave_reader.hpp | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/wave_reader.cpp b/wave_reader.cpp index 1ad8238..0b6a0f4 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -80,7 +80,7 @@ WaveReader::WaveReader(const std::string &filename, bool &enable, std::mutex &mt 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(&header.subchunkID, sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), enable, mtx); + ReadData(&header.subchunk1ID, sizeof(WaveHeader::subchunk1ID) + sizeof(WaveHeader::subchunk1Size), enable, mtx); unsigned subchunk1MinSize = sizeof(WaveHeader::audioFormat) + sizeof(WaveHeader::channels) + sizeof(WaveHeader::sampleRate) + sizeof(WaveHeader::byteRate) + sizeof(WaveHeader::blockAlign) + sizeof(WaveHeader::bitsPerSample); @@ -134,7 +134,7 @@ std::vector WaveReader::GetSamples(unsigned quantity, bool &enable, std: } std::vector data(bytesToRead); - data.resize(ReadData(data.data(), bytesToRead, enable, mtx)); + data.resize(ReadData(&data[0], bytesToRead, enable, mtx)); if (data.size() < bytesToRead) quantity = data.size() / bytesPerSample; @@ -153,7 +153,7 @@ bool WaveReader::SetSampleOffset(unsigned offset) { return true; } -int WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, std::mutex &mtx) +unsigned WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, std::mutex &mtx) { unsigned bytesRead = 0; timeval timeout = { @@ -173,10 +173,9 @@ int WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, std:: if (bytes > 0) bytesRead += bytes; if (bytesRead < bytesToRead) { - if (fileDescriptor != STDIN_FILENO) { - data.resize(bytesRead); + if (fileDescriptor != STDIN_FILENO) break; - } else { + else { FD_ZERO(&fds); FD_SET(STDIN_FILENO, &fds); select(STDIN_FILENO + 1, &fds, nullptr, nullptr, &timeout); @@ -184,8 +183,6 @@ int WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, std:: FD_CLR(STDIN_FILENO, &fds); } } - if (bytes == 0) - break; } return bytesRead; diff --git a/wave_reader.hpp b/wave_reader.hpp index a1470af..88b5e2f 100644 --- a/wave_reader.hpp +++ b/wave_reader.hpp @@ -79,7 +79,7 @@ class WaveReader std::vector GetSamples(unsigned quantity, bool &enable, std::mutex &mtx); bool SetSampleOffset(unsigned offset); private: - std::vector ReadData(unsigned bytesToRead, bool headerBytes, bool &enable, std::mutex &mtx); + unsigned ReadData(void *buffer, unsigned bytesToRead, bool &enable, std::mutex &mtx); std::string filename; WaveHeader header; From aac3b26f654a23ca7517355302bc1e42145cf6c7 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Wed, 9 Apr 2025 04:14:59 +0200 Subject: [PATCH 3/4] Casting fixed --- wave_reader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wave_reader.cpp b/wave_reader.cpp index 0b6a0f4..5ef4d96 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -47,7 +47,7 @@ Sample::Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel) for (unsigned i = 0; i < channels; i++) { switch (bitsPerChannel >> 3) { case 2: - sum += (data[((i + 1) << 1) - 1] << 8) | data[((i + 1) << 1) - 2]; + sum += *reinterpret_cast(&data[i << 1]); break; case 1: sum += (static_cast(data[i]) - 0x80) << 8; From 6dfa17b97fb5b814e2d0583e2b3714c2ff280065 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Wed, 9 Apr 2025 04:32:27 +0200 Subject: [PATCH 4/4] Revoked STDIN bug fix --- wave_reader.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wave_reader.cpp b/wave_reader.cpp index 5ef4d96..1fc24c3 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -170,6 +170,8 @@ unsigned WaveReader::ReadData(void *buffer, unsigned bytesToRead, bool &enable, int bytes = read(fileDescriptor, &(reinterpret_cast(buffer)[bytesRead]), bytesToRead - bytesRead); if ((bytes == -1) && ((fileDescriptor != STDIN_FILENO) || (errno != EAGAIN))) throw std::runtime_error(std::string("Error while opening ") + GetFilename() + std::string(", cannot read file")); + if (bytes == 0) + break; if (bytes > 0) bytesRead += bytes; if (bytesRead < bytesToRead) {