Resampler class added, in future will be performing FMX related tasks

resampler
Marcin Kondej 2022-12-02 14:47:02 +01:00
rodzic 26776f577c
commit 40eb9065e5
6 zmienionych plików z 153 dodań i 311 usunięć

Wyświetl plik

@ -1,36 +1,3 @@
/*
FM Transmitter - use Raspberry Pi as FM transmitter
Copyright (c) 2021, 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 "transmitter.hpp"
#include <iostream>
#include <csignal>
@ -52,11 +19,14 @@ int main(int argc, char** argv)
{
float frequency = 100.f, bandwidth = 200.f;
uint16_t dmaChannel = 0;
bool showUsage = true, loop = false;
bool showUsage = true, loop = false, resample = false;
int opt, filesOffset;
while ((opt = getopt(argc, argv, "rf:d:b:v")) != -1) {
while ((opt = getopt(argc, argv, "srf:d:b:v")) != -1) {
switch (opt) {
case 's':
resample = true;
break;
case 'r':
loop = true;
break;
@ -103,7 +73,7 @@ int main(int argc, char** argv)
<< header.sampleRate << " Hz, "
<< header.bitsPerSample << " bits, "
<< ((header.channels > 0x01) ? "stereo" : "mono") << std::endl;
transmitter->Transmit(reader, frequency, bandwidth, dmaChannel, optind < argc);
transmitter->Transmit(reader, frequency, bandwidth, dmaChannel, resample, optind < argc);
} while (!stop && (optind < argc));
} catch (std::exception &catched) {
std::cout << "Error: " << catched.what() << std::endl;

Wyświetl plik

@ -1,5 +1,5 @@
EXECUTABLE = fm_transmitter
VERSION = 0.9.6
VERSION = 0.9.7
FLAGS = -Wall -O3 -std=c++11
TRANSMITTER = -fno-strict-aliasing -I/opt/vc/include
ifeq ($(GPIO21), 1)

Wyświetl plik

@ -1,36 +1,3 @@
/*
FM Transmitter - use Raspberry Pi as FM transmitter
Copyright (c) 2022, 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 "transmitter.hpp"
#include "mailbox.h"
#include <bcm_host.h>
@ -97,7 +64,8 @@
#define DMA_TI_DEST_DREQ (0x01 << 6)
#define DMA_TI_WAIT_RESP (0x01 << 3)
#define BUFFER_TIME 1000000
#define RESAMPLER_FREQUENCY 44100
#define BUFFER_TIME 100000
#define PAGE_SIZE 4096
struct TimerRegisters {
@ -178,13 +146,13 @@ class Peripherals
}
private:
Peripherals() {
int memFd;
if ((memFd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
int mem;
if ((mem = 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);
peripherals = mmap(nullptr, GetSize(), PROT_READ | PROT_WRITE, MAP_SHARED, mem, GetVirtualBaseAddress());
close(mem);
if (peripherals == MAP_FAILED) {
throw std::runtime_error("Cannot obtain access to peripherals (mmap error)");
}
@ -204,42 +172,38 @@ class AllocatedMemory
{
public:
AllocatedMemory() = delete;
AllocatedMemory(unsigned size) {
mBoxFd = mbox_open();
memSize = size;
if (memSize % PAGE_SIZE) {
memSize = (memSize / PAGE_SIZE + 1) * PAGE_SIZE;
AllocatedMemory(std::size_t size) {
mBox = mbox_open();
this->size = (size % PAGE_SIZE) ? (size / PAGE_SIZE + 1) * PAGE_SIZE : size;
handle = mem_alloc(mBox, static_cast<unsigned>(this->size), PAGE_SIZE, (Peripherals::GetVirtualBaseAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2711_MEM_FLAG);
if (!handle) {
mbox_close(mBox);
throw std::runtime_error("Cannot allocate memory (" + std::to_string(this->size) + " bytes)");
}
memHandle = mem_alloc(mBoxFd, size, PAGE_SIZE, (Peripherals::GetVirtualBaseAddress() == BCM2835_PERI_VIRT_BASE) ? BCM2835_MEM_FLAG : BCM2711_MEM_FLAG);
if (!memHandle) {
mbox_close(mBoxFd);
memSize = 0;
throw std::runtime_error("Cannot allocate memory (" + std::to_string(size) + " bytes)");
}
memAddress = mem_lock(mBoxFd, memHandle);
memAllocated = mapmem(memAddress & ~0xc0000000, memSize);
address = mem_lock(mBox, handle);
allocated = mapmem(address & ~0xc0000000, this->size);
}
virtual ~AllocatedMemory() {
unmapmem(memAllocated, memSize);
mem_unlock(mBoxFd, memHandle);
mem_free(mBoxFd, memHandle);
mbox_close(mBoxFd);
memSize = 0;
unmapmem(allocated, size);
mem_unlock(mBox, handle);
mem_free(mBox, handle);
mbox_close(mBox);
}
AllocatedMemory(const AllocatedMemory &) = delete;
AllocatedMemory(AllocatedMemory &&) = delete;
AllocatedMemory &operator=(const AllocatedMemory &) = delete;
uintptr_t GetPhysicalAddress(volatile void *object) const {
return (memSize) ? memAddress + (reinterpret_cast<uintptr_t>(object) - reinterpret_cast<uintptr_t>(memAllocated)) : 0x00000000;
return (size) ? address + (reinterpret_cast<uintptr_t>(object) - reinterpret_cast<uintptr_t>(allocated)) : 0x00000000;
}
uintptr_t GetBaseAddress() const {
return reinterpret_cast<uintptr_t>(memAllocated);
return reinterpret_cast<uintptr_t>(allocated);
}
private:
unsigned memSize, memHandle;
uintptr_t memAddress;
void *memAllocated;
int mBoxFd;
std::size_t size;
unsigned handle;
uintptr_t address;
void *allocated;
int mBox;
};
class Device
@ -353,6 +317,42 @@ class DMAController : public Device
volatile DMARegisters *dma;
};
class Resampler
{
public:
Resampler(WaveReader *reader, unsigned sampleRate, unsigned range) : reader(reader), sampleRate(sampleRate), range(range) {
if (sampleRate < reader->GetHeader().sampleRate) {
throw std::runtime_error("Downsampling not supported");
}
}
std::vector<int> GetFrames(std::size_t quantity, bool &stop) {
int value = 0;
WaveHeader header = reader->GetHeader();
auto source = reader->GetFrames(static_cast<std::size_t>(static_cast<unsigned long long>(quantity) * header.sampleRate / sampleRate), stop);
std::vector<int> samples(static_cast<std::size_t>(static_cast<unsigned long long>(source.size()) * sampleRate / header.sampleRate));
std::size_t prev = SIZE_MAX;
for (std::size_t i = 0; i < samples.size(); i++) {
std::size_t offset = static_cast<std::size_t>(static_cast<unsigned long long>(i) * source.size() / samples.size());
if (prev != offset) {
int sum = (source[offset].size() == 2) ? (source[offset][0] + source[offset][1]) / 2 : source[offset][0];
value = static_cast<int>(round(static_cast<float>(static_cast<int>(range) * sum) / SHRT_MAX));
prev = offset;
}
samples[i] = value;
}
return samples;
}
bool SetFrameOffset(std::size_t offset) {
return reader->SetFrameOffset(static_cast<std::size_t>(static_cast<unsigned long long>(offset) * reader->GetHeader().sampleRate / sampleRate));
}
unsigned GetSampleRate() const {
return sampleRate;
}
protected:
WaveReader *reader;
unsigned sampleRate, range;
};
Transmitter::Transmitter()
: output(nullptr), stop(true)
{
@ -364,12 +364,11 @@ Transmitter::~Transmitter() {
}
}
void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier)
void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool resample, bool preserveCarrier)
{
stop = false;
WaveHeader header = reader.GetHeader();
unsigned bufferSize = static_cast<unsigned>(static_cast<unsigned long long>(header.sampleRate) * BUFFER_TIME / 1000000);
unsigned clockDivisor = static_cast<unsigned>(round(Peripherals::GetClockFrequency() * (0x01 << 12) / frequency));
unsigned divisorRange = clockDivisor - static_cast<unsigned>(round(Peripherals::GetClockFrequency() * (0x01 << 12) / (frequency + 0.0005f * bandwidth)));
@ -386,9 +385,11 @@ void Transmitter::Transmit(WaveReader &reader, float frequency, float bandwidth,
};
try {
if (dmaChannel != 0xff) {
TransmitViaDma(reader, *output, header.sampleRate, bufferSize, clockDivisor, divisorRange, dmaChannel);
Resampler resampler(&reader, resample ? RESAMPLER_FREQUENCY : header.sampleRate, divisorRange);
TransmitViaDma(resampler, *output, clockDivisor, dmaChannel);
} else {
TransmitViaCpu(reader, *output, header.sampleRate, bufferSize, clockDivisor, divisorRange);
Resampler resampler(&reader, header.sampleRate, divisorRange);
TransmitViaCpu(resampler, *output, clockDivisor);
}
} catch (...) {
finally();
@ -402,16 +403,17 @@ void Transmitter::Stop()
stop = true;
}
void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange)
void Transmitter::TransmitViaCpu(Resampler &resampler, ClockOutput &output, unsigned clockDivisor)
{
std::vector<Sample> samples = reader.GetSamples(bufferSize, stop);
if (samples.empty()) {
std::vector<int> samples = resampler.GetFrames(static_cast<std::size_t>(static_cast<unsigned long long>(resampler.GetSampleRate()) * BUFFER_TIME / 1000000), stop);
std::size_t bufferSize = samples.size();
if (!bufferSize) {
return;
}
unsigned sampleOffset = 0;
bool eof = samples.size() < bufferSize, txStop = false;
std::thread transmitterThread(Transmitter::TransmitterThread, this, &output, sampleRate, clockDivisor, divisorRange, &sampleOffset, &samples, &txStop);
bool txStop = false;
std::size_t frame = 0;
std::thread txThread(Transmitter::TransmitterThread, this, &output, resampler.GetSampleRate(), clockDivisor, &frame, &samples, &txStop);
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
@ -420,26 +422,25 @@ void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsign
std::lock_guard<std::mutex> lock(access);
txStop = true;
}
transmitterThread.join();
txThread.join();
samples.clear();
stop = true;
};
try {
while (!eof && !stop) {
while (!stop) {
{
std::lock_guard<std::mutex> lock(access);
if (txStop) {
throw std::runtime_error("Transmitter thread has unexpectedly exited");
}
if (samples.empty()) {
if (!reader.SetSampleOffset(sampleOffset + bufferSize)) {
if (!resampler.SetFrameOffset(frame + bufferSize)) {
break;
}
samples = reader.GetSamples(bufferSize, stop);
samples = resampler.GetFrames(bufferSize, stop);
if (samples.empty()) {
break;
}
eof = samples.size() < bufferSize;
}
}
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 2));
@ -451,36 +452,29 @@ void Transmitter::TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsign
finally();
}
void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel)
void Transmitter::TransmitViaDma(Resampler &resampler, ClockOutput &output, unsigned clockDivisor, unsigned dmaChannel)
{
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<Sample> samples = reader.GetSamples(bufferSize, stop);
if (samples.empty()) {
std::vector<int> samples = resampler.GetFrames(static_cast<std::size_t>(static_cast<unsigned long long>(resampler.GetSampleRate()) * BUFFER_TIME / 1000000), stop);
std::size_t bufferSize = samples.size();
if (!bufferSize) {
return;
}
bool eof = false;
if (samples.size() < bufferSize) {
bufferSize = samples.size();
eof = true;
}
PWMController pwm(sampleRate);
Peripherals &peripherals = Peripherals::GetInstance();
AllocatedMemory allocated(sizeof(uint32_t) * bufferSize + sizeof(DMAControllBlock) * (2 * bufferSize) + sizeof(uint32_t));
PWMController pwm(resampler.GetSampleRate());
unsigned cbOffset = 0;
std::size_t cbOffset = 0;
volatile DMAControllBlock *dmaCb = reinterpret_cast<DMAControllBlock *>(allocated.GetBaseAddress());
volatile uint32_t *clkDiv = reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(dmaCb) + 2 * sizeof(DMAControllBlock) * bufferSize);
volatile uint32_t *pwmFifoData = reinterpret_cast<uint32_t *>(reinterpret_cast<uintptr_t>(clkDiv) + sizeof(uint32_t) * bufferSize);
for (unsigned i = 0; i < bufferSize; i++) {
float value = samples[i].GetMonoValue();
clkDiv[i] = CLK_PASSWORD | (0xffffff & (clockDivisor - static_cast<int32_t>(round(value * divisorRange))));
clkDiv[i] = CLK_PASSWORD | (0xffffff & (clockDivisor - samples[i]));
dmaCb[cbOffset].transferInfo = DMA_TI_NO_WIDE_BURST | DMA_TI_WAIT_RESP;;
dmaCb[cbOffset].srcAddress = allocated.GetPhysicalAddress(&clkDiv[i]);
dmaCb[cbOffset].dstAddress = peripherals.GetPhysicalAddress(&output.GetDivisor());
@ -501,30 +495,30 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
DMAController dma(allocated.GetPhysicalAddress(dmaCb), dmaChannel);
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 10));
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 100));
auto finally = [&]() {
dmaCb[(cbOffset < 2 * bufferSize) ? cbOffset : 0].nextCbAddress = 0x00000000;
while (dma.GetControllBlockAddress() != 0x00000000) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
samples.clear();
stop = true;
};
try {
while (!eof && !stop) {
samples = reader.GetSamples(bufferSize, stop);
if (!samples.size()) {
break;
}
bool end = false;
while (!stop && !end) {
cbOffset = 0;
eof = samples.size() < bufferSize;
for (std::size_t i = 0; i < samples.size(); i++) {
float value = samples[i].GetMonoValue();
samples = resampler.GetFrames(bufferSize, stop);
for (std::size_t i = 0; i < bufferSize; i++) {
int sample = (i < samples.size()) ? samples[i] : 0;
while (i == ((dma.GetControllBlockAddress() - allocated.GetPhysicalAddress(dmaCb)) / (2 * sizeof(DMAControllBlock)))) {
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 10));
std::this_thread::sleep_for(std::chrono::microseconds(BUFFER_TIME / 100));
}
clkDiv[i] = CLK_PASSWORD | (0xffffff & (clockDivisor - sample));
if (i >= samples.size()) {
end = true;
break;
}
clkDiv[i] = CLK_PASSWORD | (0xffffff & (clockDivisor - static_cast<int>(round(value * divisorRange))));
cbOffset += 2;
}
}
@ -535,7 +529,7 @@ void Transmitter::TransmitViaDma(WaveReader &reader, ClockOutput &output, unsign
finally();
}
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop)
void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, std::size_t *frame, std::vector<int> *samples, bool *stop)
{
try {
Peripherals &peripherals = Peripherals::GetInstance();
@ -545,17 +539,17 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
uint64_t playbackStart = current;
while (true) {
std::vector<Sample> loadedSamples;
std::vector<int> acquired;
while (true) {
{
std::lock_guard<std::mutex> lock(instance->access);
if (*stop) {
return;
}
loadedSamples = std::move(*samples);
acquired = std::move(*samples);
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
if (!loadedSamples.empty()) {
*sampleOffset = (current - playbackStart) * sampleRate / 1000000;
if (!acquired.empty()) {
*frame = static_cast<std::size_t>((current - playbackStart) * sampleRate / 1000000);
break;
}
}
@ -563,19 +557,18 @@ void Transmitter::TransmitterThread(Transmitter *instance, ClockOutput *output,
};
uint64_t start = current;
unsigned offset = (current - start) * sampleRate / 1000000;
std::size_t offset = static_cast<std::size_t>((current - start) * sampleRate / 1000000);
while (true) {
if (offset >= loadedSamples.size()) {
if (offset >= acquired.size()) {
break;
}
unsigned prevOffset = offset;
float value = loadedSamples[offset].GetMonoValue();
instance->output->SetDivisor(clockDivisor - static_cast<int>(round(value * divisorRange)));
while (offset == prevOffset) {
std::size_t prev = offset;
instance->output->SetDivisor(clockDivisor - acquired[offset]);
while (offset == prev) {
std::this_thread::sleep_for(std::chrono::microseconds(1)); // asm("nop");
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));;
offset = (current - start) * sampleRate / 1000000;
current = *(reinterpret_cast<volatile uint64_t *>(&timer->low));
offset = static_cast<std::size_t>((current - start) * sampleRate / 1000000);
}
}
}

Wyświetl plik

@ -1,42 +1,10 @@
/*
FM Transmitter - use Raspberry Pi as FM transmitter
Copyright (c) 2021, 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.
*/
#pragma once
#include "wave_reader.hpp"
#include <mutex>
class ClockOutput;
class Resampler;
class Transmitter
{
@ -46,12 +14,12 @@ class Transmitter
Transmitter(const Transmitter &) = delete;
Transmitter(Transmitter &&) = delete;
Transmitter &operator=(const Transmitter &) = delete;
void Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool preserveCarrier);
void Transmit(WaveReader &reader, float frequency, float bandwidth, unsigned dmaChannel, bool resample, bool preserveCarrier);
void Stop();
private:
void TransmitViaCpu(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange);
void TransmitViaDma(WaveReader &reader, ClockOutput &output, unsigned sampleRate, unsigned bufferSize, unsigned clockDivisor, unsigned divisorRange, unsigned dmaChannel);
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, unsigned divisorRange, unsigned *sampleOffset, std::vector<Sample> *samples, bool *stop);
void TransmitViaCpu(Resampler &resampler, ClockOutput &output, unsigned clockDivisor);
void TransmitViaDma(Resampler &resampler, ClockOutput &output, unsigned clockDivisor, unsigned dmaChannel);
static void TransmitterThread(Transmitter *instance, ClockOutput *output, unsigned sampleRate, unsigned clockDivisor, std::size_t *frame, std::vector<int> *samples, bool *stop);
ClockOutput *output;
std::mutex access;

Wyświetl plik

@ -1,36 +1,3 @@
/*
FM Transmitter - use Raspberry Pi as FM transmitter
Copyright (c) 2021, 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 "wave_reader.hpp"
#include <stdexcept>
#include <cstring>
@ -38,32 +5,6 @@
#include <chrono>
#include <unistd.h>
#include <fcntl.h>
#include <climits>
Sample::Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel)
: value(0.f)
{
int sum = 0;
int16_t *channelValues = new int16_t[channels];
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];
break;
case 1:
channelValues[i] = (static_cast<int16_t>(data[i]) - 0x80) << 8;
break;
}
sum += channelValues[i];
}
value = 2 * sum / (static_cast<float>(USHRT_MAX) * channels);
delete[] channelValues;
}
float Sample::GetMonoValue() const
{
return value;
}
WaveReader::WaveReader(const std::string &filename, bool &stop) :
filename(filename), headerOffset(0), currentDataOffset(0)
@ -134,29 +75,41 @@ const WaveHeader &WaveReader::GetHeader() const
return header;
}
std::vector<Sample> WaveReader::GetSamples(unsigned quantity, bool &stop) {
unsigned bytesPerSample = (header.bitsPerSample >> 3) * header.channels;
unsigned bytesToRead = quantity * bytesPerSample;
unsigned bytesLeft = header.subchunk2Size - currentDataOffset;
std::vector<std::vector<int16_t>> WaveReader::GetFrames(std::size_t quantity, bool &stop) {
uint16_t bytesPerSample = (header.bitsPerSample >> 3);
uint16_t bytesPerFrame = bytesPerSample * header.channels;
std::size_t bytesToRead = quantity * bytesPerFrame;
std::size_t bytesLeft = header.subchunk2Size - currentDataOffset;
if (bytesToRead > bytesLeft) {
bytesToRead = bytesLeft - bytesLeft % bytesPerSample;
quantity = bytesToRead / bytesPerSample;
bytesToRead = bytesLeft - bytesLeft % bytesPerFrame;
quantity = bytesToRead / bytesPerFrame;
}
std::vector<uint8_t> data = std::move(ReadData(bytesToRead, false, stop));
if (data.size() < bytesToRead) {
quantity = data.size() / bytesPerSample;
quantity = data.size() / bytesPerFrame;
}
std::vector<Sample> samples;
samples.reserve(quantity);
for (unsigned i = 0; i < quantity; i++) {
samples.push_back(Sample(&data[bytesPerSample * i], header.channels, header.bitsPerSample));
std::vector<std::vector<int16_t>> frames;
frames.reserve(quantity);
for (std::size_t i = 0; i < quantity; i++) {
std::vector<int16_t> channels(header.channels, 0x0000);
for (int16_t j = 0; j < header.channels; j++) {
switch (bytesPerSample) {
case 2:
channels[j] = (data[i * bytesPerFrame + j * bytesPerSample + 1] << 8) | data[i * bytesPerFrame + j * bytesPerSample];
break;
case 1:
channels[j] = (static_cast<int16_t>(data[i * bytesPerFrame]) - 0x80) << 8;
break;
}
}
frames.push_back(channels);
}
return samples;
return frames;
}
bool WaveReader::SetSampleOffset(unsigned offset) {
bool WaveReader::SetFrameOffset(std::size_t offset) {
if (fileDescriptor != STDIN_FILENO) {
currentDataOffset = offset * (header.bitsPerSample >> 3) * header.channels;
if (lseek(fileDescriptor, dataOffset + currentDataOffset, SEEK_SET) == -1) {
@ -166,9 +119,9 @@ bool WaveReader::SetSampleOffset(unsigned offset) {
return true;
}
std::vector<uint8_t> WaveReader::ReadData(unsigned bytesToRead, bool headerBytes, bool &stop)
std::vector<uint8_t> WaveReader::ReadData(std::size_t bytesToRead, bool headerBytes, bool &stop)
{
unsigned bytesRead = 0;
std::size_t bytesRead = 0;
std::vector<uint8_t> data;
data.resize(bytesToRead);
while ((bytesRead < bytesToRead) && !stop) {

Wyświetl plik

@ -1,36 +1,3 @@
/*
FM Transmitter - use Raspberry Pi as FM transmitter
Copyright (c) 2021, 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.
*/
#pragma once
#include <cstdint>
@ -56,15 +23,6 @@ struct WaveHeader
uint32_t subchunk2Size;
};
class Sample
{
public:
Sample(uint8_t *data, unsigned channels, unsigned bitsPerChannel);
float GetMonoValue() const;
protected:
float value;
};
class WaveReader
{
public:
@ -75,13 +33,13 @@ class WaveReader
WaveReader &operator=(const WaveReader &) = delete;
std::string GetFilename() const;
const WaveHeader &GetHeader() const;
std::vector<Sample> GetSamples(unsigned quantity, bool &stop);
bool SetSampleOffset(unsigned offset);
std::vector<std::vector<int16_t>> GetFrames(std::size_t quantity, bool &stop);
bool SetFrameOffset(std::size_t offset);
private:
std::vector<uint8_t> ReadData(unsigned bytesToRead, bool headerBytes, bool &stop);
std::vector<uint8_t> ReadData(std::size_t bytesToRead, bool headerBytes, bool &stop);
std::string filename;
WaveHeader header;
unsigned dataOffset, headerOffset, currentDataOffset;
std::size_t dataOffset, headerOffset, currentDataOffset;
int fileDescriptor;
};