From 01fe1815163383f4ba10c8ce46e95cb1f5ca00c4 Mon Sep 17 00:00:00 2001 From: Marcin Kondej Date: Fri, 20 Sep 2019 00:53:36 +0200 Subject: [PATCH] C++11 Update --- error_reporter.cpp | 9 +- error_reporter.h => error_reporter.hpp | 16 +- main.cpp | 30 +- makefile | 19 +- pcm_wave_header.h => pcm_wave_header.hpp | 114 ++--- preemp.cpp | 58 --- preemp.h | 48 -- sample.cpp | 36 +- sample.h => sample.hpp | 27 +- transmitter.cpp | 612 ++++++++++++----------- transmitter.h => transmitter.hpp | 152 +++--- wave_reader.cpp | 64 +-- wave_reader.h => wave_reader.hpp | 127 +++-- 13 files changed, 621 insertions(+), 691 deletions(-) rename error_reporter.h => error_reporter.hpp (88%) rename pcm_wave_header.h => pcm_wave_header.hpp (78%) delete mode 100644 preemp.cpp delete mode 100644 preemp.h rename sample.h => sample.hpp (81%) rename transmitter.h => transmitter.hpp (51%) rename wave_reader.h => wave_reader.hpp (72%) diff --git a/error_reporter.cpp b/error_reporter.cpp index 04988d2..3b91227 100644 --- a/error_reporter.cpp +++ b/error_reporter.cpp @@ -31,9 +31,14 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "error_reporter.h" +#include "error_reporter.hpp" -ErrorReporter::ErrorReporter(string message) : +ErrorReporter::ErrorReporter() : + errorMessage(std::string()) +{ +} + +ErrorReporter::ErrorReporter(std::string message) : errorMessage(message) { } diff --git a/error_reporter.h b/error_reporter.hpp similarity index 88% rename from error_reporter.h rename to error_reporter.hpp index dd0f59d..7f174c3 100644 --- a/error_reporter.h +++ b/error_reporter.hpp @@ -31,24 +31,22 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef ERROR_REPORTER_H -#define ERROR_REPORTER_H +#ifndef ERROR_REPORTER_HPP +#define ERROR_REPORTER_HPP #include #include -using std::exception; -using std::string; - -class ErrorReporter : public exception +class ErrorReporter : public std::exception { public: - explicit ErrorReporter(string message); + ErrorReporter(); + ErrorReporter(std::string message); virtual ~ErrorReporter() throw(); virtual const char *what() const throw(); protected: - string errorMessage; + std::string errorMessage; }; -#endif // ERROR_REPORTER_H +#endif // ERROR_REPORTER_HPP diff --git a/main.cpp b/main.cpp index 20661c0..fdfd74c 100644 --- a/main.cpp +++ b/main.cpp @@ -31,21 +31,19 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "transmitter.h" +#include "transmitter.hpp" #include #include #include #include -using namespace std; - bool play = true; Transmitter *transmitter = NULL; void sigIntHandler(int sigNum) { if (transmitter != NULL) { - cout << "Stopping..." << endl; + std::cout << "Stopping..." << std::endl; transmitter->stop(); play = false; } @@ -55,9 +53,9 @@ int main(int argc, char** argv) { double frequency = 100.0; double bandwidth = 100.0; - unsigned short dmaChannel = 0; + uint16_t dmaChannel = 0; bool loop = false; - string filename; + std::string filename; bool showUsage = true; int opt, filesOffset; @@ -76,7 +74,7 @@ int main(int argc, char** argv) bandwidth = ::atof(optarg); break; case 'v': - cout << EXECUTABLE << " version: " << VERSION << endl; + std::cout << EXECUTABLE << " version: " << VERSION << std::endl; return 0; } } @@ -85,7 +83,7 @@ int main(int argc, char** argv) showUsage = false; } if (showUsage) { - cout << "Usage: " << EXECUTABLE << " [-f ] [-b ] [-d ] [-r] " << endl; + std::cout << "Usage: " << EXECUTABLE << " [-f ] [-b ] [-d ] [-r] " << std::endl; return 0; } @@ -99,18 +97,18 @@ int main(int argc, char** argv) if ((optind == argc) && loop) { optind = filesOffset; } - WaveReader reader(filename != "-" ? filename : string(), play); + WaveReader reader(filename != "-" ? filename : std::string(), play); PCMWaveHeader header = reader.getHeader(); - cout << "Broadcasting at " << frequency << " MHz with " - << bandwidth << " kHz bandwidth" << endl; - cout << "Playing: " << reader.getFilename() << ", " + std::cout << "Broadcasting at " << frequency << " MHz with " + << bandwidth << " kHz bandwidth" << std::endl; + std::cout << "Playing: " << reader.getFilename() << ", " << header.sampleRate << " Hz, " << header.bitsPerSample << " bits, " - << ((header.channels > 0x01) ? "stereo" : "mono") << endl; - transmitter->play(reader, frequency, bandwidth, dmaChannel, optind < argc); + << ((header.channels > 0x01) ? "stereo" : "mono") << std::endl; + transmitter->transmit(reader, frequency, bandwidth, dmaChannel, optind < argc); } while (play && (optind < argc)); - } catch (exception &error) { - cout << "Error: " << error.what() << endl; + } catch (std::exception &catched) { + std::cout << "Error: " << catched.what() << std::endl; return 1; } diff --git a/makefile b/makefile index 3c69076..995f67a 100644 --- a/makefile +++ b/makefile @@ -1,26 +1,23 @@ EXECUTABLE = fm_transmitter -VERSION = 0.9.1 -FLAGS = -Wall -O3 +VERSION = 0.9.3 +FLAGS = -Wall -O3 -std=c++11 -all: main.o mailbox.o error_reporter.o sample.o preemp.o wave_reader.o transmitter.o - g++ -L/opt/vc/lib -lm -lpthread -lbcm_host -o $(EXECUTABLE) main.o mailbox.o sample.o preemp.o error_reporter.o wave_reader.o transmitter.o +all: main.o mailbox.o error_reporter.o sample.o wave_reader.o transmitter.o + g++ -L/opt/vc/lib -lm -lpthread -lbcm_host -o $(EXECUTABLE) main.o mailbox.o sample.o error_reporter.o wave_reader.o transmitter.o mailbox.o: mailbox.c mailbox.h g++ $(FLAGS) -c mailbox.c -error_reporter.o: error_reporter.cpp error_reporter.h +error_reporter.o: error_reporter.cpp error_reporter.hpp g++ $(FLAGS) -c error_reporter.cpp -sample.o: sample.cpp sample.h +sample.o: sample.cpp sample.hpp g++ $(FLAGS) -c sample.cpp -preemp.o: preemp.cpp preemp.h - g++ $(FLAGS) -c preemp.cpp - -wave_reader.o: wave_reader.cpp wave_reader.h +wave_reader.o: wave_reader.cpp wave_reader.hpp g++ $(FLAGS) -c wave_reader.cpp -transmitter.o: transmitter.cpp transmitter.h +transmitter.o: transmitter.cpp transmitter.hpp g++ $(FLAGS) -fno-strict-aliasing -I/opt/vc/include -c transmitter.cpp main.o: main.cpp diff --git a/pcm_wave_header.h b/pcm_wave_header.hpp similarity index 78% rename from pcm_wave_header.h rename to pcm_wave_header.hpp index 869e3d9..9ad7a53 100644 --- a/pcm_wave_header.h +++ b/pcm_wave_header.hpp @@ -1,56 +1,58 @@ -/* - fm_transmitter - use Raspberry Pi as FM transmitter - - Copyright (c) 2019, Marcin Kondej - All rights reserved. - - See https://github.com/markondej/fm_transmitter - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef PCM_WAVE_HEADER_H -#define PCM_WAVE_HEADER_H - -#define WAVE_FORMAT_PCM 0x0001 - -struct PCMWaveHeader -{ - char chunkID[4]; - unsigned chunkSize; - char format[4]; - char subchunk1ID[4]; - unsigned subchunk1Size; - unsigned short audioFormat; - unsigned short channels; - unsigned sampleRate; - unsigned byteRate; - unsigned short blockAlign; - unsigned short bitsPerSample; - char subchunk2ID[4]; - unsigned subchunk2Size; -}; - -#endif // PCM_WAVE_HEADER +/* + fm_transmitter - use Raspberry Pi as FM transmitter + + Copyright (c) 2019, Marcin Kondej + All rights reserved. + + See https://github.com/markondej/fm_transmitter + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef PCM_WAVE_HEADER_HPP +#define PCM_WAVE_HEADER_HPP + +#include + +#define WAVE_FORMAT_PCM 0x0001 + +struct PCMWaveHeader +{ + int8_t chunkID[4]; + uint32_t chunkSize; + int8_t format[4]; + int8_t subchunk1ID[4]; + uint32_t subchunk1Size; + uint16_t audioFormat; + uint16_t channels; + uint32_t sampleRate; + uint32_t byteRate; + uint16_t blockAlign; + uint16_t bitsPerSample; + int8_t subchunk2ID[4]; + uint32_t subchunk2Size; +}; + +#endif // PCM_WAVE_HEADER_HPP diff --git a/preemp.cpp b/preemp.cpp deleted file mode 100644 index fb30123..0000000 --- a/preemp.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - fm_transmitter - use Raspberry Pi as FM transmitter - - Copyright (c) 2019, Marcin Kondej - All rights reserved. - - See https://github.com/markondej/fm_transmitter - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "preemp.h" - -PreEmp::PreEmp(unsigned sampleRate) - : timeConst(sampleRate * 75.0e-6), prevValue(0.0) -{ -} - -PreEmp::PreEmp(const PreEmp &source) - : timeConst(source.timeConst), prevValue(source.prevValue) -{ -} - -PreEmp &PreEmp::operator=(const PreEmp &source) -{ - timeConst = source.timeConst; - prevValue = source.prevValue; - return *this; -} - -float PreEmp::filter(float value) -{ - prevValue = value; - value = value + (prevValue - value) / (1.0 - timeConst); - return value; -} diff --git a/preemp.h b/preemp.h deleted file mode 100644 index e44515a..0000000 --- a/preemp.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - fm_transmitter - use Raspberry Pi as FM transmitter - - Copyright (c) 2019, Marcin Kondej - All rights reserved. - - See https://github.com/markondej/fm_transmitter - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef PREEMP_H -#define PREEMP_H - -class PreEmp -{ - public: - PreEmp(unsigned sampleRate); - PreEmp(const PreEmp &source); - PreEmp &operator=(const PreEmp &source); - float filter(float value); - protected: - float timeConst, prevValue; -}; - -#endif // PREEMP_H diff --git a/sample.cpp b/sample.cpp index 1a3ade1..00fa99b 100644 --- a/sample.cpp +++ b/sample.cpp @@ -31,38 +31,42 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "sample.h" +#include "sample.hpp" +#include -Sample::Sample(char *data, unsigned short channels, unsigned short bitsPerChannel) - : value(0) +Sample::Sample(uint8_t *data, uint16_t channels, uint16_t bitsPerChannel) + : value(0.) { - int sum = 0; - short *channelValues = new short[channels]; - short multiplier = bitsPerChannel >> 3; - for (unsigned i = 0; i < channels; i++) { + int32_t sum = 0; + int16_t *channelValues = new int16_t[channels]; + int16_t multiplier = bitsPerChannel >> 3; + for (uint32_t i = 0; i < channels; i++) { if (multiplier > 1) { channelValues[i] = (data[(i + 1) * multiplier - 1] << 8) | data[(i + 1) * multiplier - 2]; } else { - channelValues[i] = ((short)(unsigned char)data[i] - 0x80) << 8; + channelValues[i] = ((int16_t)data[i] - 0x80) << 8; } sum += channelValues[i]; } - value = sum / channels; + value = 2 * (sum / channels) / (double)USHRT_MAX; delete[] channelValues; } -Sample::Sample(const Sample &source) - : value(source.value) +double Sample::getMonoValue() { + return value; } -Sample &Sample::operator=(const Sample &source) +#ifndef NO_PREEMP +PreEmphasis::PreEmphasis(uint32_t sampleRate) + : timeConst(sampleRate * 75.0e-6), prevValue(0.0) { - value = source.value; - return *this; } -float Sample::getMonoValue() +double PreEmphasis::filter(double value) { - return value / 32768.0; + prevValue = value; + value = value + (prevValue - value) / (1.0 - timeConst); + return value; } +#endif \ No newline at end of file diff --git a/sample.h b/sample.hpp similarity index 81% rename from sample.h rename to sample.hpp index bec89c0..241fd4b 100644 --- a/sample.h +++ b/sample.hpp @@ -31,18 +31,29 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef SAMPLE_H -#define SAMPLE_H +#ifndef SAMPLE_HPP +#define SAMPLE_HPP + +#include class Sample { public: - Sample(char *data, unsigned short channels, unsigned short bitsPerChannel); - Sample(const Sample &source); - Sample &operator=(const Sample &source); - float getMonoValue(); + Sample(uint8_t *data, uint16_t channels, uint16_t bitsPerChannel); + double getMonoValue(); + protected: + double value; +}; + +#ifndef NO_PREEMP +class PreEmphasis +{ + public: + PreEmphasis(uint32_t sampleRate); + double filter(double value); protected: - short value; + double timeConst, prevValue; }; +#endif -#endif // SAMPLE_H +#endif // SAMPLE_HPP diff --git a/transmitter.cpp b/transmitter.cpp index 7acfdf4..f35586b 100644 --- a/transmitter.cpp +++ b/transmitter.cpp @@ -31,19 +31,15 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "transmitter.h" -#include "preemp.h" -#include "error_reporter.h" +#include "transmitter.hpp" +#include "error_reporter.hpp" #include "mailbox.h" #include -#include +#include #include #include #include -using std::ostringstream; -using std::vector; - #define PERIPHERALS_PHYS_BASE 0x7E000000 #define BCM2835_PERI_VIRT_BASE 0x20000000 #define BCM2838_PERI_VIRT_BASE 0xFE000000 @@ -67,70 +63,78 @@ using std::vector; #define PAGE_SIZE 4096 struct TimerRegisters { - unsigned ctlStatus; - unsigned low; - unsigned high; - unsigned c0; - unsigned c1; - unsigned c2; - unsigned c3; + uint32_t ctlStatus; + uint32_t low; + uint32_t high; + uint32_t c0; + uint32_t c1; + uint32_t c2; + uint32_t c3; }; struct ClockRegisters { - unsigned ctl; - unsigned div; + uint32_t ctl; + uint32_t div; }; struct PWMRegisters { - unsigned ctl; - unsigned status; - unsigned dmaConf; - unsigned reserved0; - unsigned chn1Range; - unsigned chn1Data; - unsigned fifoIn; - unsigned reserved1; - unsigned chn2Range; - unsigned chn2Data; + uint32_t ctl; + uint32_t status; + uint32_t dmaConf; + uint32_t reserved0; + uint32_t chn1Range; + uint32_t chn1Data; + uint32_t fifoIn; + uint32_t reserved1; + uint32_t chn2Range; + uint32_t chn2Data; }; struct DMAControllBlock { - unsigned transferInfo; - unsigned srcAddress; - unsigned dstAddress; - unsigned transferLen; - unsigned stride; - unsigned nextCbAddress; - unsigned reserved0; - unsigned reserved1; + uint32_t transferInfo; + uint32_t srcAddress; + uint32_t dstAddress; + uint32_t transferLen; + uint32_t stride; + uint32_t nextCbAddress; + uint32_t reserved0; + uint32_t reserved1; }; struct DMARegisters { - unsigned ctlStatus; - unsigned cbAddress; - unsigned transferInfo; - unsigned srcAddress; - unsigned dstAddress; - unsigned transferLen; - unsigned stride; - unsigned nextCbAddress; - unsigned debug; + uint32_t ctlStatus; + uint32_t cbAddress; + uint32_t transferInfo; + uint32_t srcAddress; + uint32_t dstAddress; + uint32_t transferLen; + uint32_t stride; + uint32_t nextCbAddress; + uint32_t debug; +}; + +struct AllocatedMemory { + uint32_t handle, size, physicalBase, virtualBase; + int mBoxFd; }; +void *Transmitter::peripherals = nullptr; bool Transmitter::transmitting = false; -bool Transmitter::clockInitialized = false; -bool Transmitter::preserveCarrier = false; -void *Transmitter::peripherals = NULL; +uint32_t Transmitter::sampleOffset = 0; +uint32_t Transmitter::clockDivisor = 0; +uint32_t Transmitter::divisorRange = 0; +uint32_t Transmitter::sampleRate = 0; +volatile ClockRegisters *Transmitter::output = nullptr; +std::vector *Transmitter::buffer = nullptr; Transmitter::Transmitter() - : memSize(0) { int memFd; if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) { throw ErrorReporter("Cannot open /dev/mem (permission denied)"); } - peripherals = mmap(NULL, getPeripheralSize(), PROT_READ | PROT_WRITE, MAP_SHARED, memFd, getPeripheralVirtAddress()); + peripherals = mmap(nullptr, getPeripheralsSize(), PROT_READ | PROT_WRITE, MAP_SHARED, memFd, getPeripheralsVirtBaseAddress()); close(memFd); if (peripherals == MAP_FAILED) { throw ErrorReporter("Cannot obtain access to peripherals (mmap error)"); @@ -139,7 +143,7 @@ Transmitter::Transmitter() Transmitter::~Transmitter() { - munmap(peripherals, getPeripheralSize()); + munmap(peripherals, getPeripheralsSize()); } Transmitter &Transmitter::getInstance() @@ -148,319 +152,345 @@ Transmitter &Transmitter::getInstance() return instance; } -bool Transmitter::allocateMemory(unsigned size) +uint32_t Transmitter::getPeripheralsVirtBaseAddress() { - if (memSize) { - return false; - } - mBoxFd = mbox_open(); - memSize = size; - if (memSize % PAGE_SIZE) { - memSize = (memSize / PAGE_SIZE + 1) * PAGE_SIZE; - } - memHandle = mem_alloc(mBoxFd, size, PAGE_SIZE, (getPeripheralVirtAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2838_MEM_FLAG); - if (!memHandle) { - mbox_close(mBoxFd); - memSize = 0; - return false; + return (bcm_host_get_peripheral_size() == BCM2838_PERI_VIRT_BASE) ? BCM2838_PERI_VIRT_BASE : bcm_host_get_peripheral_address(); +} + +uint32_t Transmitter::getPeripheralsSize() +{ + uint32_t size = bcm_host_get_peripheral_size(); + if (size == BCM2838_PERI_VIRT_BASE) { + size = 0x01000000; } - memAddress = mem_lock(mBoxFd, memHandle); - memAllocated = mapmem(memAddress & ~0xC0000000, memSize); - return true; + return size; } -void Transmitter::freeMemory() +double Transmitter::getSourceFreq() { - unmapmem(memAllocated, memSize); - mem_unlock(mBoxFd, memHandle); - mem_free(mBoxFd, memHandle); + return (getPeripheralsVirtBaseAddress() == BCM2838_PERI_VIRT_BASE) ? BCM2838_PLLD_FREQ : BCM2835_PLLD_FREQ; +} + +uint32_t Transmitter::getPeripheralPhysAddress(volatile void *object) { + return PERIPHERALS_PHYS_BASE + ((uint32_t)object - (uint32_t)peripherals); +} - mbox_close(mBoxFd); - memSize = 0; +uint32_t Transmitter::getPeripheralVirtAddress(uint32_t offset) { + return (uint32_t)peripherals + offset; } -unsigned Transmitter::getPeripheralVirtAddress() +uint32_t Transmitter::getMemoryPhysAddress(AllocatedMemory &memory, volatile void *object) { - return (bcm_host_get_peripheral_size() == BCM2838_PERI_VIRT_BASE) ? BCM2838_PERI_VIRT_BASE : bcm_host_get_peripheral_address(); + return memory.physicalBase + ((uint32_t)object - memory.virtualBase); } -unsigned Transmitter::getPeripheralSize() +AllocatedMemory Transmitter::allocateMemory(uint32_t size) { - unsigned size = bcm_host_get_peripheral_size(); - if (size == BCM2838_PERI_VIRT_BASE) { - size = 0x01000000; + AllocatedMemory memory; + memory.size = 0x00000000; + memory.mBoxFd = mbox_open(); + if (size % PAGE_SIZE) { + size = (size / PAGE_SIZE + 1) * PAGE_SIZE; } - return size; + memory.handle = mem_alloc(memory.mBoxFd, size, PAGE_SIZE, (getPeripheralsVirtBaseAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2838_MEM_FLAG); + if (!memory.handle) { + mbox_close(memory.mBoxFd); + return memory; + } + memory.physicalBase = mem_lock(memory.mBoxFd, memory.handle); + memory.virtualBase = (uint32_t)mapmem(memory.physicalBase & ~0xC0000000, size); + memory.size = size; + return memory; +} + +void Transmitter::freeMemory(AllocatedMemory &memory) +{ + unmapmem((void *)memory.virtualBase, memory.size); + mem_unlock(memory.mBoxFd, memory.handle); + mem_free(memory.mBoxFd, memory.handle); + mbox_close(memory.mBoxFd); + memory.size = 0x00000000; +} + +volatile PWMRegisters *Transmitter::initPwmController() +{ + volatile ClockRegisters *pwmClk = (ClockRegisters *)getPeripheralVirtAddress(PWMCLK_BASE_OFFSET); + double pwmClkFreq = PWM_WRITES_PER_SAMPLE * PWM_CHANNEL_RANGE * sampleRate / 1000000; + pwmClk->ctl = (0x5A << 24) | 0x06; + usleep(1000); + pwmClk->div = (0x5A << 24) | (uint32_t)(getSourceFreq() * (0x01 << 12) / pwmClkFreq); + pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06; + + volatile PWMRegisters *pwm = (PWMRegisters *)getPeripheralVirtAddress(PWM_BASE_OFFSET); + pwm->ctl = 0x00000000; + usleep(1000); + pwm->status = 0x01FC; + pwm->ctl = (0x01 << 6); + usleep(1000); + pwm->chn1Range = PWM_CHANNEL_RANGE; + pwm->dmaConf = (0x01 << 31) | 0x0707; + pwm->ctl = (0x01 << 5) | (0x01 << 2) | 0x01; + return pwm; +} + +void Transmitter::closePwmController(volatile PWMRegisters *pwm) +{ + pwm->ctl = 0x00000000; } -float Transmitter::getSrcClkFreq() +volatile DMARegisters *Transmitter::startDma(AllocatedMemory &memory, volatile DMAControllBlock *dmaCb, uint8_t dmaChannel) { - return (getPeripheralVirtAddress() == BCM2838_PERI_VIRT_BASE) ? BCM2838_PLLD_FREQ : BCM2835_PLLD_FREQ; + volatile DMARegisters *dma = (DMARegisters *)getPeripheralVirtAddress((dmaChannel < 15) ? DMA0_BASE_OFFSET + dmaChannel * 0x100 : DMA15_BASE_OFFSET); + dma->ctlStatus = (0x01 << 31); + usleep(1000); + dma->ctlStatus = (0x01 << 2) | (0x01 << 1); + dma->cbAddress = getMemoryPhysAddress(memory, dmaCb); + dma->ctlStatus = (0xFF << 16) | 0x01; + return dma; } -unsigned Transmitter::getMemoryAddress(volatile void *object) +void Transmitter::closeDma(volatile DMARegisters *dma) { - return (memSize) ? memAddress + ((unsigned)object - (unsigned)memAllocated) : 0x00000000; + dma->ctlStatus = (0x01 << 31); } -unsigned Transmitter::getPeripheralAddress(volatile void *object) { - return PERIPHERALS_PHYS_BASE + ((unsigned)object - (unsigned)peripherals); +volatile ClockRegisters *Transmitter::initClockOutput() +{ + volatile ClockRegisters *clock = (ClockRegisters *)getPeripheralVirtAddress(CLK0_BASE_OFFSET); + volatile uint32_t *gpio = (uint32_t *)getPeripheralVirtAddress(GPIO_BASE_OFFSET); + clock->ctl = (0x5A << 24) | 0x06; + usleep(1000); + clock->div = (0x5A << 24) | clockDivisor; + clock->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06; + *gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14); + return clock; } -void *Transmitter::getPeripheral(unsigned offset) { - return (void *)((unsigned)peripherals + offset); +void Transmitter::closeClockOutput(volatile ClockRegisters *clock) +{ + clock->ctl = (0x5A << 24) | 0x06; } -void Transmitter::play(WaveReader &reader, double frequency, double bandwidth, unsigned char dmaChannel, bool preserveCarrierOnExit) +void Transmitter::transmit(WaveReader &reader, double frequency, double bandwidth, uint8_t dmaChannel, bool preserveCarrierOnExit) { if (transmitting) { throw ErrorReporter("Cannot play, transmitter already in use"); } transmitting = true; - preserveCarrier = preserveCarrierOnExit; PCMWaveHeader header = reader.getHeader(); - unsigned bufferSize = (unsigned)((unsigned long long)header.sampleRate * BUFFER_TIME / 1000000); - vector *samples = reader.getSamples(bufferSize, transmitting); - if (samples == NULL) { - return; - } - bool eof = samples->size() < bufferSize; - - unsigned clockDivisor = (unsigned)round(getSrcClkFreq() * (0x01 << 12) / frequency); - unsigned divisorRange = clockDivisor - (unsigned)round(getSrcClkFreq() * (0x01 << 12) / (frequency + 0.0005 * bandwidth)); - bool isError = false; - string errorMessage; + uint32_t bufferSize = (uint32_t)((uint64_t)header.sampleRate * BUFFER_TIME / 1000000); - if (dmaChannel != 0xFF) { - if (dmaChannel > 15) { - delete samples; - throw ErrorReporter("DMA channel number out of range (0 - 15)"); - } + preserveCarrier = preserveCarrierOnExit; + clockDivisor = (uint32_t)round(getSourceFreq() * (0x01 << 12) / frequency); + divisorRange = clockDivisor - (uint32_t)round(getSourceFreq() * (0x01 << 12) / (frequency + 0.0005 * bandwidth)); + sampleRate = header.sampleRate; - if (!allocateMemory(sizeof(unsigned) * (bufferSize + 1) + sizeof(DMAControllBlock) * (2 * bufferSize))) { - delete samples; - throw ErrorReporter("Cannot allocate memory"); - } + if (!clockInitialized) { + output = initClockOutput(); + clockInitialized = true; + } - volatile ClockRegisters *clk0 = (ClockRegisters *)getPeripheral(CLK0_BASE_OFFSET); - volatile unsigned *gpio = (unsigned *)getPeripheral(GPIO_BASE_OFFSET); - if (!clockInitialized) { - clk0->ctl = (0x5A << 24) | 0x06; - usleep(1000); - clk0->div = (0x5A << 24) | clockDivisor; - clk0->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06; - *gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14); - clockInitialized = true; + try { + if (dmaChannel != 0xFF) { + transmitViaDma(reader, bufferSize, dmaChannel); + } else { + transmitViaCpu(reader, bufferSize); } + } catch (ErrorReporter &catched) { + closeClockOutput(output); + throw catched; + } - volatile ClockRegisters *pwmClk = (ClockRegisters *)getPeripheral(PWMCLK_BASE_OFFSET); - float pwmClkFreq = PWM_WRITES_PER_SAMPLE * PWM_CHANNEL_RANGE * header.sampleRate / 1000000; - pwmClk->ctl = (0x5A << 24) | 0x06; - usleep(1000); - pwmClk->div = (0x5A << 24) | (unsigned)(getSrcClkFreq() * (0x01 << 12) / pwmClkFreq); - pwmClk->ctl = (0x5A << 24) | (0x01 << 4) | 0x06; - - volatile PWMRegisters *pwm = (PWMRegisters *)getPeripheral(PWM_BASE_OFFSET); - pwm->ctl = 0x00; - usleep(1000); - pwm->status = 0x01FC; - pwm->ctl = (0x01 << 6); - usleep(1000); - pwm->chn1Range = PWM_CHANNEL_RANGE; - pwm->dmaConf = (0x01 << 31) | 0x0707; - pwm->ctl = (0x01 << 5) | (0x01 << 2) | 0x01; + if (!preserveCarrier) { + closeClockOutput(output); + } +} - float value; - unsigned i, cbOffset = 0; -#ifndef NO_PREEMP - PreEmp preEmp(header.sampleRate); -#endif +void Transmitter::transmitViaCpu(WaveReader &reader, uint32_t bufferSize) +{ + std::vector *samples = reader.getSamples(bufferSize, transmitting); + if (samples == nullptr) { + return; + } - volatile DMAControllBlock *dmaCb = (DMAControllBlock *)memAllocated; - volatile unsigned *clkDiv = (unsigned *)((unsigned)dmaCb + 2 * sizeof(DMAControllBlock) * bufferSize); - volatile unsigned *pwmFifoData = (unsigned *)((unsigned)clkDiv + sizeof(unsigned) * bufferSize); - for (i = 0; i < bufferSize; i++) { - value = (*samples)[i].getMonoValue(); -#ifndef NO_PREEMP - value = preEmp.filter(value); -#endif - clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange)); - dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x01 << 3); - dmaCb[cbOffset].srcAddress = getMemoryAddress(&clkDiv[i]); - dmaCb[cbOffset].dstAddress = getPeripheralAddress(&clk0->div); - dmaCb[cbOffset].transferLen = sizeof(unsigned); - dmaCb[cbOffset].stride = 0; - dmaCb[cbOffset].nextCbAddress = getMemoryAddress(&dmaCb[cbOffset + 1]); - cbOffset++; - - dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x05 << 16) | (0x01 << 6) | (0x01 << 3); - dmaCb[cbOffset].srcAddress = getMemoryAddress(pwmFifoData); - dmaCb[cbOffset].dstAddress = getPeripheralAddress(&pwm->fifoIn); - dmaCb[cbOffset].transferLen = sizeof(unsigned) * PWM_WRITES_PER_SAMPLE; - dmaCb[cbOffset].stride = 0; - dmaCb[cbOffset].nextCbAddress = getMemoryAddress((i < bufferSize - 1) ? &dmaCb[cbOffset + 1] : dmaCb); - cbOffset++; - } - *pwmFifoData = 0x00; - delete samples; + sampleOffset = 0; + buffer = samples; - volatile DMARegisters *dma = (DMARegisters *)getPeripheral((dmaChannel < 15) ? DMA0_BASE_OFFSET + dmaChannel * 0x100 : DMA15_BASE_OFFSET); - dma->ctlStatus = (0x01 << 31); - usleep(1000); - dma->ctlStatus = (0x01 << 2) | (0x01 << 1); - dma->cbAddress = getMemoryAddress(dmaCb); - dma->ctlStatus = (0xFF << 16) | 0x01; + bool eof = samples->size() < bufferSize, exceptionCatched = false; + std::thread txThread(Transmitter::transmitThread); + ErrorReporter exception; - usleep(BUFFER_TIME / 4); + usleep(BUFFER_TIME / 2); - try { - while (!eof && transmitting) { + try { + while (!eof && transmitting) { + if (buffer == nullptr) { + if (!reader.setSampleOffset(sampleOffset + bufferSize)) { + break; + } samples = reader.getSamples(bufferSize, transmitting); - if (samples == NULL) { + if (samples == nullptr) { break; } eof = samples->size() < bufferSize; - cbOffset = 0; - for (i = 0; i < samples->size(); i++) { - value = (*samples)[i].getMonoValue(); -#ifndef NO_PREEMP - value = preEmp.filter(value); -#endif - while (i == ((dma->cbAddress - getMemoryAddress(dmaCb)) / (2 * sizeof(DMAControllBlock)))) { - usleep(1000); - } - clkDiv[i] = (0x5A << 24) | (clockDivisor - (int)round(value * divisorRange)); - cbOffset += 2; - } - delete samples; + buffer = samples; } - } catch (ErrorReporter &error) { - preserveCarrier = false; - errorMessage = error.what(); - isError = true; + usleep(BUFFER_TIME / 2); } + } catch (ErrorReporter &catched) { + exceptionCatched = true; + exception = catched; + } + transmitting = false; + txThread.join(); + if (buffer != nullptr) { + delete buffer; + } + if (exceptionCatched) { + throw exception; + } +} - cbOffset -= 2; - dmaCb[cbOffset].nextCbAddress = 0x00; - while (dma->cbAddress != 0x00) { - usleep(1000); - } +void Transmitter::transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_t dmaChannel) +{ + if (dmaChannel > 15) { + throw ErrorReporter("DMA channel number out of range (0 - 15)"); + } + + std::vector *samples = reader.getSamples(bufferSize, transmitting); + if (samples == nullptr) { + return; + } + bool eof = false; + if (samples->size() < bufferSize) { + bufferSize = samples->size(); + eof = true; + } - dma->ctlStatus = (0x01 << 31); - pwm->ctl = 0x00; + AllocatedMemory dmaMemory = allocateMemory(sizeof(uint32_t) * (bufferSize + 1) + sizeof(DMAControllBlock) * (2 * bufferSize)); + if (!dmaMemory.size) { + delete samples; + throw ErrorReporter("Cannot allocate memory"); + } - freeMemory(); - transmitting = false; + volatile PWMRegisters *pwm = initPwmController(); - if (!preserveCarrier) { - clk0->ctl = (0x5A << 24) | 0x06; - } - } else { - unsigned sampleOffset = 0; - vector *buffer = samples; - - void *transmitterParams[5] = { - (void *)&buffer, - (void *)&sampleOffset, - (void *)&clockDivisor, - (void *)&divisorRange, - (void *)&header.sampleRate - }; - - pthread_t thread; - int returnCode = pthread_create(&thread, NULL, &Transmitter::transmit, (void *)transmitterParams); - if (returnCode) { - delete samples; - ostringstream oss; - oss << "Cannot create transmitter thread (code: " << returnCode << ")"; - throw ErrorReporter(oss.str()); - } + double value; + uint32_t i, cbOffset = 0; +#ifndef NO_PREEMP + PreEmphasis preEmphasis(sampleRate); +#endif - usleep(BUFFER_TIME / 2); - - try { - while (!eof && transmitting) { - if (buffer == NULL) { - if (!reader.setSampleOffset(sampleOffset + bufferSize)) { - break; - } - samples = reader.getSamples(bufferSize, transmitting); - if (samples == NULL) { - break; - } - eof = samples->size() < bufferSize; - buffer = samples; + volatile DMAControllBlock *dmaCb = (DMAControllBlock *)dmaMemory.virtualBase; + volatile uint32_t *clkDiv = (uint32_t *)((uint32_t)dmaCb + 2 * sizeof(DMAControllBlock) * bufferSize); + volatile uint32_t *pwmFifoData = (uint32_t *)((uint32_t)clkDiv + sizeof(uint32_t) * bufferSize); + for (i = 0; i < bufferSize; i++) { + value = (*samples)[i].getMonoValue(); +#ifndef NO_PREEMP + value = preEmphasis.filter(value); +#endif + clkDiv[i] = (0x5A << 24) | (clockDivisor - (int32_t)round(value * divisorRange)); + dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x01 << 3); + dmaCb[cbOffset].srcAddress = getMemoryPhysAddress(dmaMemory, &clkDiv[i]); + dmaCb[cbOffset].dstAddress = getPeripheralPhysAddress(&output->div); + dmaCb[cbOffset].transferLen = sizeof(uint32_t); + dmaCb[cbOffset].stride = 0; + dmaCb[cbOffset].nextCbAddress = getMemoryPhysAddress(dmaMemory, &dmaCb[cbOffset + 1]); + cbOffset++; + + dmaCb[cbOffset].transferInfo = (0x01 << 26) | (0x05 << 16) | (0x01 << 6) | (0x01 << 3); + dmaCb[cbOffset].srcAddress = getMemoryPhysAddress(dmaMemory, pwmFifoData); + dmaCb[cbOffset].dstAddress = getPeripheralPhysAddress(&pwm->fifoIn); + dmaCb[cbOffset].transferLen = sizeof(uint32_t) * PWM_WRITES_PER_SAMPLE; + dmaCb[cbOffset].stride = 0; + dmaCb[cbOffset].nextCbAddress = getMemoryPhysAddress(dmaMemory, (i < bufferSize - 1) ? &dmaCb[cbOffset + 1] : dmaCb); + cbOffset++; + } + *pwmFifoData = 0x00000000; + delete samples; + + volatile DMARegisters *dma = startDma(dmaMemory, dmaCb, dmaChannel); + bool exceptionCatched = false; + ErrorReporter exception; + usleep(BUFFER_TIME / 4); + + try { + while (!eof && transmitting) { + samples = reader.getSamples(bufferSize, transmitting); + if (samples == nullptr) { + break; + } + cbOffset = 0; + eof = samples->size() < bufferSize; + for (i = 0; i < samples->size(); i++) { + value = (*samples)[i].getMonoValue(); +#ifndef NO_PREEMP + value = preEmphasis.filter(value); +#endif + while (i == ((dma->cbAddress - getMemoryPhysAddress(dmaMemory, dmaCb)) / (2 * sizeof(DMAControllBlock)))) { + usleep(1000); } - usleep(BUFFER_TIME / 2); + clkDiv[i] = (0x5A << 24) | (clockDivisor - (int32_t)round(value * divisorRange)); + cbOffset += 2; } + delete samples; } - catch (ErrorReporter &error) { - preserveCarrier = false; - errorMessage = error.what(); - isError = true; - } - transmitting = false; - pthread_join(thread, NULL); - if (buffer != NULL) { - delete buffer; - } + } catch (ErrorReporter &catched) { + exceptionCatched = true; + exception = catched; + } + + cbOffset = (cbOffset < 2 * bufferSize) ? cbOffset : 0; + dmaCb[cbOffset].nextCbAddress = 0x00000000; + while (dma->cbAddress != 0x00000000) { + usleep(1000); } - if (isError) { - throw ErrorReporter(errorMessage); + + closeDma(dma); + closePwmController(pwm); + + freeMemory(dmaMemory); + transmitting = false; + + if (exceptionCatched) { + throw exception; } } -void *Transmitter::transmit(void *params) +void Transmitter::transmitThread() { - vector **buffer = (vector **)((void **)params)[0]; - unsigned *sampleOffset = (unsigned *)((void **)params)[1]; - unsigned *clockDivisor = (unsigned *)((void **)params)[2]; - unsigned *divisorRange = (unsigned *)((void **)params)[3]; - unsigned *sampleRate = (unsigned *)((void **)params)[4]; - - unsigned offset, length, prevOffset; - vector *samples = NULL; - unsigned long long start; - float value; + uint32_t offset, length, prevOffset; + std::vector *samples = nullptr; + uint64_t start; + double value; #ifndef NO_PREEMP - PreEmp preEmp(*sampleRate); + PreEmphasis preEmphasis(sampleRate); #endif - volatile ClockRegisters *clk0 = (ClockRegisters *)getPeripheral(CLK0_BASE_OFFSET); - volatile unsigned *gpio = (unsigned *)getPeripheral(GPIO_BASE_OFFSET); - if (!clockInitialized) { - clk0->ctl = (0x5A << 24) | 0x06; - usleep(1000); - clk0->div = (0x5A << 24) | *clockDivisor; - clk0->ctl = (0x5A << 24) | (0x01 << 9) | (0x01 << 4) | 0x06; - *gpio = (*gpio & 0xFFFF8FFF) | (0x01 << 14); - clockInitialized = true; - } - - volatile TimerRegisters *timer = (TimerRegisters *)getPeripheral(TIMER_BASE_OFFSET); - unsigned long long current = *(unsigned long long *)&timer->low; - unsigned long long playbackStart = current; + volatile TimerRegisters *timer = (TimerRegisters *)getPeripheralVirtAddress(TIMER_BASE_OFFSET); + uint64_t current = *(uint64_t *)&timer->low; + uint64_t playbackStart = current; while (transmitting) { start = current; - while ((*buffer == NULL) && transmitting) { + while ((buffer == nullptr) && transmitting) { usleep(1); - current = *(unsigned long long *)&timer->low; + current = *(uint64_t *)&timer->low; } if (!transmitting) { break; } - samples = *buffer; + samples = buffer; length = samples->size(); - *buffer = NULL; + buffer = nullptr; - *sampleOffset = (current - playbackStart) * (*sampleRate) / 1000000; - offset = (current - start) * (*sampleRate) / 1000000; + sampleOffset = (current - playbackStart) * sampleRate / 1000000; + offset = (current - start) * sampleRate / 1000000; while (true) { if (offset >= length) { @@ -469,23 +499,17 @@ void *Transmitter::transmit(void *params) prevOffset = offset; value = (*samples)[offset].getMonoValue(); #ifndef NO_PREEMP - value = preEmp.filter(value); + value = preEmphasis.filter(value); #endif - clk0->div = (0x5A << 24) | (*clockDivisor - (int)round(value * (*divisorRange))); + output->div = (0x5A << 24) | (clockDivisor - (int32_t)round(value * divisorRange)); while (offset == prevOffset) { usleep(1); // asm("nop"); - current = *(unsigned long long *)&timer->low; - offset = (current - start) * (*sampleRate) / 1000000; + current = *(uint64_t *)&timer->low; + offset = (current - start) * sampleRate / 1000000; } } delete samples; } - - if (!preserveCarrier) { - clk0->ctl = (0x5A << 24) | 0x06; - } - - return NULL; } void Transmitter::stop() diff --git a/transmitter.h b/transmitter.hpp similarity index 51% rename from transmitter.h rename to transmitter.hpp index de2a626..3c9d6d5 100644 --- a/transmitter.h +++ b/transmitter.hpp @@ -1,69 +1,83 @@ -/* - fm_transmitter - use Raspberry Pi as FM transmitter - - Copyright (c) 2019, Marcin Kondej - All rights reserved. - - See https://github.com/markondej/fm_transmitter - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef TRANSMITTER_H -#define TRANSMITTER_H - -#include "wave_reader.h" - -using std::string; - -class Transmitter -{ - public: - virtual ~Transmitter(); - static Transmitter &getInstance(); - void play(WaveReader &reader, double frequency, double bandwidth, unsigned char dmaChannel, bool preserveCarrierOnExit); - void stop(); - private: - Transmitter(); - Transmitter(const Transmitter &source); - Transmitter &operator=(const Transmitter &source); - bool allocateMemory(unsigned size); - void freeMemory(); - float getSrcClkFreq(); - unsigned getPeripheralVirtAddress(); - unsigned getPeripheralSize(); - unsigned getMemoryAddress(volatile void *object); - unsigned getPeripheralAddress(volatile void *object); - static void *getPeripheral(unsigned offset); - static void *transmit(void *params); - - static void *peripherals; - static bool transmitting, clockInitialized, preserveCarrier; - unsigned memSize, memAddress, memHandle; - void *memAllocated; - int mBoxFd; -}; - -#endif // TRANSMITTER_H +/* + fm_transmitter - use Raspberry Pi as FM transmitter + + Copyright (c) 2019, Marcin Kondej + All rights reserved. + + See https://github.com/markondej/fm_transmitter + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef TRANSMITTER_HPP +#define TRANSMITTER_HPP + +#include "wave_reader.hpp" + +struct AllocatedMemory; +struct PWMRegisters; +struct DMARegisters; +struct DMAControllBlock; +struct ClockRegisters; + +class Transmitter +{ + public: + virtual ~Transmitter(); + Transmitter(const Transmitter &source) = delete; + Transmitter &operator=(const Transmitter &source) = delete; + static Transmitter &getInstance(); + void transmit(WaveReader &reader, double frequency, double bandwidth, uint8_t dmaChannel, bool preserveCarrierOnExit); + void stop(); + private: + Transmitter(); + uint32_t getPeripheralsVirtBaseAddress(); + uint32_t getPeripheralsSize(); + double getSourceFreq(); + uint32_t getPeripheralPhysAddress(volatile void *object); + static uint32_t getPeripheralVirtAddress(uint32_t offset); + uint32_t getMemoryPhysAddress(AllocatedMemory &memory, volatile void *object); + AllocatedMemory allocateMemory(uint32_t size); + void freeMemory(AllocatedMemory &memory); + volatile PWMRegisters *initPwmController(); + void closePwmController(volatile PWMRegisters *pwm); + volatile DMARegisters *startDma(AllocatedMemory &memory, volatile DMAControllBlock *dmaCb, uint8_t dmaChannel); + void closeDma(volatile DMARegisters *dma); + volatile ClockRegisters *initClockOutput(); + void closeClockOutput(volatile ClockRegisters *clock); + void transmitViaCpu(WaveReader &reader, uint32_t bufferSize); + void transmitViaDma(WaveReader &reader, uint32_t bufferSize, uint8_t dmaChannel); + static void transmitThread(); + + bool clockInitialized, preserveCarrier; + + static void *peripherals; + static bool transmitting; + static uint32_t sampleOffset, clockDivisor, divisorRange, sampleRate; + static volatile ClockRegisters *output; + static std::vector *buffer; +}; + +#endif // TRANSMITTER_HPP diff --git a/wave_reader.cpp b/wave_reader.cpp index 749f953..35f3804 100644 --- a/wave_reader.cpp +++ b/wave_reader.cpp @@ -31,21 +31,15 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "wave_reader.h" -#include "error_reporter.h" -#include +#include "wave_reader.hpp" +#include "error_reporter.hpp" #include #include #include -using std::ostringstream; -using std::exception; - -WaveReader::WaveReader(string filename, bool &continueFlag) : +WaveReader::WaveReader(std::string filename, bool &continueFlag) : filename(filename), headerOffset(0), currentDataOffset(0) { - ostringstream oss; - if (!filename.empty()) { fileDescriptor = open(filename.c_str(), O_RDONLY); } else { @@ -54,24 +48,21 @@ WaveReader::WaveReader(string filename, bool &continueFlag) : } if (fileDescriptor == -1) { - oss << "Cannot open " << getFilename() << ", file does not exist"; - throw ErrorReporter(oss.str()); + throw ErrorReporter(std::string("Cannot open ") + getFilename() + std::string(", file does not exist")); } try { readData(sizeof(PCMWaveHeader::chunkID) + sizeof(PCMWaveHeader::chunkSize) + sizeof(PCMWaveHeader::format), true, continueFlag); - if ((string(header.chunkID, 4) != string("RIFF")) || (string(header.format, 4) != string("WAVE"))) { - oss << "Error while opening " << getFilename() << ", WAVE file expected"; - throw ErrorReporter(oss.str()); + if ((std::string((char *)header.chunkID, 4) != std::string("RIFF")) || (std::string((char *)header.format, 4) != std::string("WAVE"))) { + throw ErrorReporter(std::string("Error while opening ") + getFilename() + std::string(", WAVE file expected")); } readData(sizeof(PCMWaveHeader::subchunk1ID) + sizeof(PCMWaveHeader::subchunk1Size), true, continueFlag); - unsigned subchunk1MinSize = sizeof(PCMWaveHeader::audioFormat) + sizeof(PCMWaveHeader::channels) + + uint32_t subchunk1MinSize = sizeof(PCMWaveHeader::audioFormat) + sizeof(PCMWaveHeader::channels) + sizeof(PCMWaveHeader::sampleRate) + sizeof(PCMWaveHeader::byteRate) + sizeof(PCMWaveHeader::blockAlign) + sizeof(PCMWaveHeader::bitsPerSample); - if ((string(header.subchunk1ID, 4) != string("fmt ")) || (header.subchunk1Size < subchunk1MinSize)) { - oss << "Error while opening " << getFilename() << ", data corrupted"; - throw ErrorReporter(oss.str()); + if ((std::string((char *)header.subchunk1ID, 4) != std::string("fmt ")) || (header.subchunk1Size < subchunk1MinSize)) { + throw ErrorReporter(std::string("Error while opening ") + getFilename() + std::string(", data corrupted")); } readData(header.subchunk1Size, true, continueFlag); @@ -79,14 +70,12 @@ WaveReader::WaveReader(string filename, bool &continueFlag) : (header.byteRate != (header.bitsPerSample >> 3) * header.channels * header.sampleRate) || (header.blockAlign != (header.bitsPerSample >> 3) * header.channels) || (((header.bitsPerSample >> 3) != 1) && ((header.bitsPerSample >> 3) != 2))) { - oss << "Error while opening " << getFilename() << ", unsupported WAVE format"; - throw ErrorReporter(oss.str()); + throw ErrorReporter(std::string("Error while opening ") + getFilename() + std::string(", unsupported WAVE format")); } readData(sizeof(PCMWaveHeader::subchunk2ID) + sizeof(PCMWaveHeader::subchunk2Size), true, continueFlag); - if (string(header.subchunk2ID, 4) != string("data")) { - oss << "Error while opening " << getFilename() << ", data corrupted"; - throw ErrorReporter(oss.str()); + if (std::string((char *)header.subchunk2ID, 4) != std::string("data")) { + throw ErrorReporter(std::string("Error while opening ") + getFilename() + std::string(", data corrupted")); } } catch (ErrorReporter &error) { if (fileDescriptor != STDIN_FILENO) { @@ -107,21 +96,18 @@ WaveReader::~WaveReader() } } -vector *WaveReader::readData(unsigned bytesToRead, bool headerBytes, bool &continueFlag) +std::vector *WaveReader::readData(uint32_t bytesToRead, bool headerBytes, bool &continueFlag) { - unsigned bytesRead = 0; - vector *data = new vector(); + uint32_t bytesRead = 0; + std::vector *data = new std::vector(); data->resize(bytesToRead); while ((bytesRead < bytesToRead) && continueFlag) { int bytes = read(fileDescriptor, &(*data)[bytesRead], bytesToRead - bytesRead); if (((bytes == -1) && ((fileDescriptor != STDIN_FILENO) || (errno != EAGAIN))) || - (((unsigned)bytes < bytesToRead) && headerBytes && (fileDescriptor != STDIN_FILENO))) { + (((uint32_t)bytes < bytesToRead) && headerBytes && (fileDescriptor != STDIN_FILENO))) { delete data; - - ostringstream oss; - oss << "Error while reading " << getFilename() << ", file is corrupted"; - throw ErrorReporter(oss.str()); + throw ErrorReporter(std::string("Error while opening ") + getFilename() + std::string(", data corrupted")); } if (bytes > 0) { bytesRead += bytes; @@ -144,7 +130,7 @@ vector *WaveReader::readData(unsigned bytesToRead, bool headerBytes, bool if (data == NULL) { throw ErrorReporter("Cannot obtain header, program interrupted"); } - memcpy(&((char *)&header)[headerOffset], &(*data)[0], bytesRead); + memcpy(&((uint8_t *)&header)[headerOffset], &(*data)[0], bytesRead); headerOffset += bytesRead; delete data; data = NULL; @@ -155,10 +141,10 @@ vector *WaveReader::readData(unsigned bytesToRead, bool headerBytes, bool return data; } -vector *WaveReader::getSamples(unsigned quantity, bool &continueFlag) { - unsigned bytesToRead, bytesLeft, bytesPerSample; - vector *samples = new vector(); - vector *data; +std::vector *WaveReader::getSamples(uint32_t quantity, bool &continueFlag) { + uint32_t bytesToRead, bytesLeft, bytesPerSample; + std::vector *samples = new std::vector(); + std::vector *data; bytesPerSample = (header.bitsPerSample >> 3) * header.channels; bytesToRead = quantity * bytesPerSample; @@ -182,7 +168,7 @@ vector *WaveReader::getSamples(unsigned quantity, bool &continueFlag) { quantity = data->size() / bytesPerSample; } - for (unsigned i = 0; i < quantity; i++) { + for (uint32_t i = 0; i < quantity; i++) { samples->push_back(Sample(&(*data)[bytesPerSample * i], header.channels, header.bitsPerSample)); } @@ -190,7 +176,7 @@ vector *WaveReader::getSamples(unsigned quantity, bool &continueFlag) { return samples; } -bool WaveReader::setSampleOffset(unsigned offset) { +bool WaveReader::setSampleOffset(uint32_t offset) { if (fileDescriptor != STDIN_FILENO) { currentDataOffset = offset * (header.bitsPerSample >> 3) * header.channels; if (lseek(fileDescriptor, dataOffset + currentDataOffset, SEEK_SET) == -1) { @@ -200,7 +186,7 @@ bool WaveReader::setSampleOffset(unsigned offset) { return true; } -string WaveReader::getFilename() +std::string WaveReader::getFilename() { return fileDescriptor != STDIN_FILENO ? filename : "STDIN"; } diff --git a/wave_reader.h b/wave_reader.hpp similarity index 72% rename from wave_reader.h rename to wave_reader.hpp index ec57417..6810841 100644 --- a/wave_reader.h +++ b/wave_reader.hpp @@ -1,65 +1,62 @@ -/* - fm_transmitter - use Raspberry Pi as FM transmitter - - Copyright (c) 2019, Marcin Kondej - All rights reserved. - - See https://github.com/markondej/fm_transmitter - - Redistribution and use in source and binary forms, with or without modification, are - permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list - of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright notice, this - list of conditions and the following disclaimer in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its contributors may be - used to endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY - EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT - SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#ifndef WAVE_READER_H -#define WAVE_READER_H - -#include "pcm_wave_header.h" -#include "sample.h" -#include -#include - -using std::vector; -using std::string; - -class WaveReader -{ - public: - WaveReader(string filename, bool &continueFlag); - virtual ~WaveReader(); - string getFilename(); - PCMWaveHeader getHeader(); - vector *getSamples(unsigned quantity, bool &continueFlag); - bool setSampleOffset(unsigned offset); - private: - vector *readData(unsigned bytesToRead, bool headerBytes, bool &continueFlag); - WaveReader(const WaveReader &source); - WaveReader &operator=(const WaveReader &source); - - string filename; - PCMWaveHeader header; - unsigned dataOffset, headerOffset, currentDataOffset; - int fileDescriptor; -}; - -#endif // WAVE_READER_H +/* + fm_transmitter - use Raspberry Pi as FM transmitter + + Copyright (c) 2019, Marcin Kondej + All rights reserved. + + See https://github.com/markondej/fm_transmitter + + Redistribution and use in source and binary forms, with or without modification, are + permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list + of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef WAVE_READER_HPP +#define WAVE_READER_HPP + +#include "pcm_wave_header.hpp" +#include "sample.hpp" +#include +#include + +class WaveReader +{ + public: + WaveReader(std::string filename, bool &continueFlag); + virtual ~WaveReader(); + WaveReader(const WaveReader &source) = delete; + WaveReader &operator=(const WaveReader &source) = delete; + std::string getFilename(); + PCMWaveHeader getHeader(); + std::vector *getSamples(uint32_t quantity, bool &continueFlag); + bool setSampleOffset(uint32_t offset); + private: + std::vector *readData(uint32_t bytesToRead, bool headerBytes, bool &continueFlag); + + std::string filename; + PCMWaveHeader header; + uint32_t dataOffset, headerOffset, currentDataOffset; + int fileDescriptor; +}; + +#endif // WAVE_READER_HPP