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..1fc24c3 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 += *reinterpret_cast(&data[i << 1]); 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.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); - 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,96 +126,66 @@ 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[0], 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) +unsigned 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) + break; + 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); - if (FD_ISSET(STDIN_FILENO, &fds)) { + if (FD_ISSET(STDIN_FILENO, &fds)) FD_CLR(STDIN_FILENO, &fds); - } } } - if (bytes == 0) { - data.resize(bytesRead); - 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..88b5e2f 100644 --- a/wave_reader.hpp +++ b/wave_reader.hpp @@ -79,10 +79,10 @@ 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; - unsigned dataOffset, headerOffset, currentDataOffset; + unsigned dataOffset; int fileDescriptor; };