Marcin Kondej 2025-04-09 02:35:07 +00:00 zatwierdzone przez GitHub
commit d6688a0766
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B5690EEEBB952194
4 zmienionych plików z 76 dodań i 128 usunięć

Wyświetl plik

@ -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() << ", "

Wyświetl plik

@ -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,10 +349,9 @@ 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<unsigned>(round(Peripherals::GetClockFrequency() * (0x01 << 12) / frequency));
unsigned divisorRange = clockDivisor - static_cast<unsigned>(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<Sample> 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<std::mutex> 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<int>(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<std::chrono::microseconds>(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<int>(round(value * divisorRange)));

Wyświetl plik

@ -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<int16_t *>(&data[i << 1]);
break;
case 1:
channelValues[i] = (static_cast<int16_t>(data[i]) - 0x80) << 8;
sum += (static_cast<int16_t>(data[i]) - 0x80) << 8;
break;
}
sum += channelValues[i];
}
value = 2 * sum / (static_cast<float>(USHRT_MAX) * channels);
delete[] channelValues;
value = sum / (-static_cast<float>(SHRT_MIN) * channels);
}
float Sample::GetMonoValue() const
@ -66,63 +63,55 @@ 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<char *>(header.chunkID), 4) != std::string("RIFF")) || (std::string(reinterpret_cast<char *>(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<char *>(header.chunkID), 4) != std::string("RIFF")) || (std::string(reinterpret_cast<char *>(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<char *>(header.subchunk1ID), 4) != std::string("fmt ")) || (header.subchunk1Size < subchunk1MinSize)) {
if ((std::string(reinterpret_cast<char *>(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<char *>(header.subchunk2ID), 4) != std::string("data")) {
ReadData(&header.subchunk2ID, sizeof(WaveHeader::subchunk2ID) + sizeof(WaveHeader::subchunk2Size), enable, mtx);
if (std::string(reinterpret_cast<char *>(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<Sample> 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<uint8_t> data = std::move(ReadData(bytesToRead, false, enable, mtx));
if (data.size() < bytesToRead) {
std::vector<uint8_t> data(bytesToRead);
data.resize(ReadData(&data[0], bytesToRead, enable, mtx));
if (data.size() < bytesToRead)
quantity = data.size() / bytesPerSample;
}
std::vector<Sample> 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<uint8_t> 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<uint8_t> data;
data.resize(bytesToRead);
timeval timeout = {
.tv_sec = 1,
};
fd_set fds;
while (bytesRead < bytesToRead) {
{
std::lock_guard<std::mutex> 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<unsigned>(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<uint8_t *>(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<std::mutex> lock(mtx);
if (!enable) {
throw std::runtime_error("Cannot obtain header, program interrupted");
}
}
std::memcpy(&(reinterpret_cast<uint8_t *>(&header))[headerOffset], data.data(), bytesRead);
headerOffset += bytesRead;
} else {
{
std::lock_guard<std::mutex> lock(mtx);
if (!enable) {
data.resize(bytesRead);
}
}
currentDataOffset += bytesRead;
}
return data;
return bytesRead;
}

Wyświetl plik

@ -79,10 +79,10 @@ class WaveReader
std::vector<Sample> GetSamples(unsigned quantity, bool &enable, std::mutex &mtx);
bool SetSampleOffset(unsigned offset);
private:
std::vector<uint8_t> 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;
};