final fixes

This commit is contained in:
AlexandreRouma 2023-01-01 03:20:49 +01:00
parent 5c690c9753
commit 8771e4bf09
5 changed files with 140 additions and 104 deletions

View File

@ -7,6 +7,7 @@
#include <dsp/sink/handler_sink.h> #include <dsp/sink/handler_sink.h>
#include <dsp/routing/splitter.h> #include <dsp/routing/splitter.h>
#include <dsp/audio/volume.h> #include <dsp/audio/volume.h>
#include <dsp/convert/stereo_to_mono.h>
#include <thread> #include <thread>
#include <ctime> #include <ctime>
#include <gui/gui.h> #include <gui/gui.h>
@ -41,15 +42,15 @@ public:
root = (std::string)core::args["root"]; root = (std::string)core::args["root"];
// Define option lists // Define option lists
formats.define("WAV", wav::FORMAT_WAV); containers.define("WAV", wav::FORMAT_WAV);
// formats.define("RF64", wav::FORMAT_RF64); // Disabled for now // containers.define("RF64", wav::FORMAT_RF64); // Disabled for now
sampleTypes.define(wav::SAMP_TYPE_UINT8, "Uint8", wav::SAMP_TYPE_UINT8); sampleTypes.define(wav::SAMP_TYPE_UINT8, "Uint8", wav::SAMP_TYPE_UINT8);
sampleTypes.define(wav::SAMP_TYPE_INT16, "Int16", wav::SAMP_TYPE_INT16); sampleTypes.define(wav::SAMP_TYPE_INT16, "Int16", wav::SAMP_TYPE_INT16);
sampleTypes.define(wav::SAMP_TYPE_INT32, "Int32", wav::SAMP_TYPE_INT32); sampleTypes.define(wav::SAMP_TYPE_INT32, "Int32", wav::SAMP_TYPE_INT32);
sampleTypes.define(wav::SAMP_TYPE_FLOAT32, "Float32", wav::SAMP_TYPE_FLOAT32); sampleTypes.define(wav::SAMP_TYPE_FLOAT32, "Float32", wav::SAMP_TYPE_FLOAT32);
// Load default config for option lists // Load default config for option lists
formatId = formats.valueId(wav::FORMAT_WAV); containerId = containers.valueId(wav::FORMAT_WAV);
sampleTypeId = sampleTypes.valueId(wav::SAMP_TYPE_INT16); sampleTypeId = sampleTypes.valueId(wav::SAMP_TYPE_INT16);
// Load config // Load config
@ -60,8 +61,8 @@ public:
if (config.conf[name].contains("recPath")) { if (config.conf[name].contains("recPath")) {
folderSelect.setPath(config.conf[name]["recPath"]); folderSelect.setPath(config.conf[name]["recPath"]);
} }
if (config.conf[name].contains("format") && formats.keyExists(config.conf[name]["format"])) { if (config.conf[name].contains("format") && containers.keyExists(config.conf[name]["format"])) {
formatId = formats.keyId(config.conf[name]["format"]); containerId = containers.keyId(config.conf[name]["format"]);
} }
if (config.conf[name].contains("sampleType") && sampleTypes.keyExists(config.conf[name]["sampleType"])) { if (config.conf[name].contains("sampleType") && sampleTypes.keyExists(config.conf[name]["sampleType"])) {
sampleTypeId = sampleTypes.keyId(config.conf[name]["sampleType"]); sampleTypeId = sampleTypes.keyId(config.conf[name]["sampleType"]);
@ -82,11 +83,12 @@ public:
splitter.init(&volume.out); splitter.init(&volume.out);
splitter.bindStream(&meterStream); splitter.bindStream(&meterStream);
meter.init(&meterStream); meter.init(&meterStream);
s2m.init(NULL);
// Init sinks // Init sinks
basebandSink.init(NULL, complexHandler, this); basebandSink.init(NULL, complexHandler, this);
stereoSink.init(NULL, stereoHandler, this); stereoSink.init(NULL, stereoHandler, this);
monoSink.init(NULL, monoHandler, this); monoSink.init(&s2m.out, monoHandler, this);
gui::menu.registerEntry(name, menuHandler, this); gui::menu.registerEntry(name, menuHandler, this);
} }
@ -142,7 +144,7 @@ public:
else { else {
samplerate = sigpath::iqFrontEnd.getSampleRate(); samplerate = sigpath::iqFrontEnd.getSampleRate();
} }
writer.setFormat(formats[formatId]); writer.setFormat(containers[containerId]);
writer.setChannels((recMode == RECORDER_MODE_AUDIO && !stereo) ? 1 : 2); writer.setChannels((recMode == RECORDER_MODE_AUDIO && !stereo) ? 1 : 2);
writer.setSampleType(sampleTypes[sampleTypeId]); writer.setSampleType(sampleTypes[sampleTypeId]);
writer.setSamplerate(samplerate); writer.setSamplerate(samplerate);
@ -159,9 +161,16 @@ public:
if (recMode == RECORDER_MODE_AUDIO) { if (recMode == RECORDER_MODE_AUDIO) {
// TODO: Select the stereo to mono converter if needed // TODO: Select the stereo to mono converter if needed
stereoStream = sigpath::sinkManager.bindStream(selectedStreamName); stereoStream = sigpath::sinkManager.bindStream(selectedStreamName);
if (stereo) {
stereoSink.setInput(stereoStream); stereoSink.setInput(stereoStream);
stereoSink.start(); stereoSink.start();
} }
else {
s2m.setInput(stereoStream);
s2m.start();
monoSink.start();
}
}
else { else {
// Create and bind IQ stream // Create and bind IQ stream
basebandStream = new dsp::stream<dsp::complex_t>(); basebandStream = new dsp::stream<dsp::complex_t>();
@ -179,8 +188,10 @@ public:
// Close audio stream or baseband // Close audio stream or baseband
if (recMode == RECORDER_MODE_AUDIO) { if (recMode == RECORDER_MODE_AUDIO) {
// TODO: HAS TO BE DONE PROPERLY // NOTE: Has to be done before the unbind since the stream is deleted...
monoSink.stop();
stereoSink.stop(); stereoSink.stop();
s2m.stop();
sigpath::sinkManager.unbindStream(selectedStreamName, stereoStream); sigpath::sinkManager.unbindStream(selectedStreamName, stereoStream);
} }
else { else {
@ -231,11 +242,11 @@ private:
} }
} }
ImGui::LeftLabel("WAV Format"); ImGui::LeftLabel("Container");
ImGui::FillWidth(); ImGui::FillWidth();
if (ImGui::Combo(CONCAT("##_recorder_wav_fmt_", _this->name), &_this->formatId, _this->formats.txt)) { if (ImGui::Combo(CONCAT("##_recorder_container_", _this->name), &_this->containerId, _this->containers.txt)) {
config.acquire(); config.acquire();
config.conf[_this->name]["format"] = _this->formats.key(_this->formatId); config.conf[_this->name]["container"] = _this->containers.key(_this->containerId);
config.release(true); config.release(true);
} }
@ -431,12 +442,12 @@ private:
bool enabled = true; bool enabled = true;
std::string root; std::string root;
OptionList<std::string, wav::Format> formats; OptionList<std::string, wav::Format> containers;
OptionList<int, wav::SampleType> sampleTypes; OptionList<int, wav::SampleType> sampleTypes;
FolderSelect folderSelect; FolderSelect folderSelect;
int recMode = RECORDER_MODE_AUDIO; int recMode = RECORDER_MODE_AUDIO;
int formatId; int containerId;
int sampleTypeId; int sampleTypeId;
bool stereo = true; bool stereo = true;
std::string selectedStreamName = ""; std::string selectedStreamName = "";
@ -460,6 +471,7 @@ private:
dsp::routing::Splitter<dsp::stereo_t> splitter; dsp::routing::Splitter<dsp::stereo_t> splitter;
dsp::stream<dsp::stereo_t> meterStream; dsp::stream<dsp::stereo_t> meterStream;
dsp::bench::PeakLevelMeter<dsp::stereo_t> meter; dsp::bench::PeakLevelMeter<dsp::stereo_t> meter;
dsp::convert::StereoToMono s2m;
uint64_t samplerate = 48000; uint64_t samplerate = 48000;

View File

@ -2,28 +2,64 @@
#include <stdexcept> #include <stdexcept>
namespace riff { namespace riff {
bool Writer::open(std::string path, char form[4]) { const char* RIFF_SIGNATURE = "RIFF";
// TODO: Open file const char* LIST_SIGNATURE = "LIST";
const size_t RIFF_LABEL_SIZE = 4;
bool Writer::open(std::string path, const char form[4]) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Open file
file = std::ofstream(path, std::ios::out | std::ios::binary);
if (!file.is_open()) { return false; }
// Begin RIFF chunk
beginRIFF(form); beginRIFF(form);
return false; return true;
} }
bool Writer::isOpen() { bool Writer::isOpen() {
std::lock_guard<std::recursive_mutex> lck(mtx);
return false; return file.is_open();
} }
void Writer::close() { void Writer::close() {
std::lock_guard<std::recursive_mutex> lck(mtx);
if (!isOpen()) { return; } if (!isOpen()) { return; }
// Finalize RIFF chunk
endRIFF(); endRIFF();
// Close file
file.close(); file.close();
} }
void Writer::beginChunk(char id[4]) { void Writer::beginList(const char id[4]) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Create chunk with the LIST ID and write id
beginChunk(LIST_SIGNATURE);
write((uint8_t*)id, RIFF_LABEL_SIZE);
}
void Writer::endList() {
std::lock_guard<std::recursive_mutex> lck(mtx);
if (chunks.empty()) {
throw std::runtime_error("No chunk to end");
}
if (memcmp(chunks.top().hdr.id, LIST_SIGNATURE, RIFF_LABEL_SIZE)) {
throw std::runtime_error("Top chunk not LIST chunk");
}
endChunk();
}
void Writer::beginChunk(const char id[4]) {
std::lock_guard<std::recursive_mutex> lck(mtx);
// Create and write header // Create and write header
ChunkDesc desc; ChunkDesc desc;
desc.pos = file.tellp(); desc.pos = file.tellp();
@ -36,7 +72,9 @@ namespace riff {
} }
void Writer::endChunk() { void Writer::endChunk() {
if (!chunks.empty()) { std::lock_guard<std::recursive_mutex> lck(mtx);
if (chunks.empty()) {
throw std::runtime_error("No chunk to end"); throw std::runtime_error("No chunk to end");
} }
@ -46,7 +84,9 @@ namespace riff {
// Write size // Write size
auto pos = file.tellp(); auto pos = file.tellp();
file.seekp(desc.pos + 4); auto npos = desc.pos;
npos += 4;
file.seekp(npos);
file.write((char*)&desc.hdr.size, sizeof(desc.hdr.size)); file.write((char*)&desc.hdr.size, sizeof(desc.hdr.size));
file.seekp(pos); file.seekp(pos);
@ -56,31 +96,38 @@ namespace riff {
} }
} }
void Writer::write(void* data, size_t len) { void Writer::write(const uint8_t* data, size_t len) {
if (!chunks.empty()) { std::lock_guard<std::recursive_mutex> lck(mtx);
if (chunks.empty()) {
throw std::runtime_error("No chunk to write into"); throw std::runtime_error("No chunk to write into");
} }
file.write((char*)data, len); file.write((char*)data, len);
chunks.top().hdr.size += len; chunks.top().hdr.size += len;
} }
void Writer::beginRIFF(char form[4]) { void Writer::beginRIFF(const char form[4]) {
std::lock_guard<std::recursive_mutex> lck(mtx);
if (!chunks.empty()) { if (!chunks.empty()) {
throw std::runtime_error("Can't create RIFF chunk on an existing RIFF file"); throw std::runtime_error("Can't create RIFF chunk on an existing RIFF file");
} }
// Create chunk with RIFF ID and write form // Create chunk with RIFF ID and write form
beginChunk("RIFF"); beginChunk(RIFF_SIGNATURE);
write(form, sizeof(form)); write((uint8_t*)form, RIFF_LABEL_SIZE);
} }
void Writer::endRIFF() { void Writer::endRIFF() {
if (!chunks.empty()) { std::lock_guard<std::recursive_mutex> lck(mtx);
if (chunks.empty()) {
throw std::runtime_error("No chunk to end"); throw std::runtime_error("No chunk to end");
} }
if (memcmp(chunks.top().hdr.id, "RIFF", 4)) { if (memcmp(chunks.top().hdr.id, RIFF_SIGNATURE, RIFF_LABEL_SIZE)) {
throw std::runtime_error("Top chunk not RIFF chunk"); throw std::runtime_error("Top chunk not RIFF chunk");
} }
endChunk(); endChunk();
} }
} }

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <mutex>
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <stack> #include <stack>
@ -19,22 +20,23 @@ namespace riff {
class Writer { class Writer {
public: public:
bool open(std::string path, char form[4]); bool open(std::string path, const char form[4]);
bool isOpen(); bool isOpen();
void close(); void close();
void beginList(); void beginList(const char id[4]);
void endList(); void endList();
void beginChunk(char id[4]); void beginChunk(const char id[4]);
void endChunk(); void endChunk();
void write(void* data, size_t len); void write(const uint8_t* data, size_t len);
private: private:
void beginRIFF(char form[4]); void beginRIFF(const char form[4]);
void endRIFF(); void endRIFF();
std::recursive_mutex mtx;
std::ofstream file; std::ofstream file;
std::stack<ChunkDesc> chunks; std::stack<ChunkDesc> chunks;
}; };

View File

@ -6,7 +6,6 @@
#include <map> #include <map>
namespace wav { namespace wav {
const char* RIFF_SIGNATURE = "RIFF";
const char* WAVE_FILE_TYPE = "WAVE"; const char* WAVE_FILE_TYPE = "WAVE";
const char* FORMAT_MARKER = "fmt "; const char* FORMAT_MARKER = "fmt ";
const char* DATA_MARKER = "data"; const char* DATA_MARKER = "data";
@ -30,13 +29,6 @@ namespace wav {
_samplerate = samplerate; _samplerate = samplerate;
_format = format; _format = format;
_type = type; _type = type;
// Initialize header with constants
memcpy(hdr.signature, RIFF_SIGNATURE, 4);
memcpy(hdr.fileType, WAVE_FILE_TYPE, 4);
memcpy(hdr.formatMarker, FORMAT_MARKER, 4);
hdr.formatHeaderLength = FORMAT_HEADER_LEN;
memcpy(hdr.dataMarker, DATA_MARKER, 4);
} }
Writer::~Writer() { close(); } Writer::~Writer() { close(); }
@ -44,14 +36,21 @@ namespace wav {
bool Writer::open(std::string path) { bool Writer::open(std::string path) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Close previous file // Close previous file
if (_isOpen) { close(); } if (rw.isOpen()) { close(); }
// Reset work values // Reset work values
samplesWritten = 0; samplesWritten = 0;
// Fill header
bytesPerSamp = (SAMP_BITS[_type] / 8) * _channels;
hdr.codec = (_type == SAMP_TYPE_FLOAT32) ? CODEC_FLOAT : CODEC_PCM;
hdr.channelCount = _channels;
hdr.sampleRate = _samplerate;
hdr.bitDepth = SAMP_BITS[_type];
hdr.bytesPerSample = bytesPerSamp;
hdr.bytesPerSecond = bytesPerSamp * _samplerate;
// Precompute sizes and allocate buffers // Precompute sizes and allocate buffers
// TODO: Get number of bits for each sample type
bytesPerSamp = (SAMP_BITS[_type] / 8) * _channels; // THIS IS WRONG
switch (_type) { switch (_type) {
case SAMP_TYPE_UINT8: case SAMP_TYPE_UINT8:
bufU8 = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE * _channels); bufU8 = dsp::buffer::alloc<uint8_t>(STREAM_BUFFER_SIZE * _channels);
@ -69,36 +68,37 @@ namespace wav {
break; break;
} }
// Open new file // Open file
file.open(path, std::ios::out | std::ios::binary); if (!rw.open(path, WAVE_FILE_TYPE)) { return false; }
if (!file.is_open()) { return false; }
// Skip header, it'll be written when finalizing the file // Write format chunk
uint8_t dummy[sizeof(Header)]; rw.beginChunk(FORMAT_MARKER);
memset(dummy, 0, sizeof(dummy)); rw.write((uint8_t*)&hdr, sizeof(FormatHeader));
file.write((char*)dummy, sizeof(dummy)); rw.endChunk();
// Begin data chunk
rw.beginChunk(DATA_MARKER);
_isOpen = true;
return true; return true;
} }
bool Writer::isOpen() { bool Writer::isOpen() {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
return _isOpen; return rw.isOpen();
} }
void Writer::close() { void Writer::close() {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Do nothing if the file is not open // Do nothing if the file is not open
if (!_isOpen) { return; } if (!rw.isOpen()) { return; }
// Finilize wav // Finish data chunk
finalize(); rw.endChunk();
// Close the file // Close the file
file.close(); rw.close();
// Destroy buffers // Free buffers
if (bufU8) { if (bufU8) {
dsp::buffer::free(bufU8); dsp::buffer::free(bufU8);
bufU8 = NULL; bufU8 = NULL;
@ -111,15 +111,12 @@ namespace wav {
dsp::buffer::free(bufI32); dsp::buffer::free(bufI32);
bufI32 = NULL; bufI32 = NULL;
} }
// Mark as closed
_isOpen = false;
} }
void Writer::setChannels(int channels) { void Writer::setChannels(int channels) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Do not allow settings to change while open // Do not allow settings to change while open
if (_isOpen) { throw std::runtime_error("Cannot change parameters while file is open"); } if (rw.isOpen()) { throw std::runtime_error("Cannot change parameters while file is open"); }
// Validate channel count // Validate channel count
if (channels < 1) { throw std::runtime_error("Channel count must be greater or equal to 1"); } if (channels < 1) { throw std::runtime_error("Channel count must be greater or equal to 1"); }
@ -129,7 +126,7 @@ namespace wav {
void Writer::setSamplerate(uint64_t samplerate) { void Writer::setSamplerate(uint64_t samplerate) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Do not allow settings to change while open // Do not allow settings to change while open
if (_isOpen) { throw std::runtime_error("Cannot change parameters while file is open"); } if (rw.isOpen()) { throw std::runtime_error("Cannot change parameters while file is open"); }
// Validate samplerate // Validate samplerate
if (!samplerate) { throw std::runtime_error("Samplerate must be non-zero"); } if (!samplerate) { throw std::runtime_error("Samplerate must be non-zero"); }
@ -138,42 +135,42 @@ namespace wav {
void Writer::setFormat(Format format) { void Writer::setFormat(Format format) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Do not allow settings to change while open // Do not allow settings to change while open
if (_isOpen) { throw std::runtime_error("Cannot change parameters while file is open"); } if (rw.isOpen()) { throw std::runtime_error("Cannot change parameters while file is open"); }
_format = format; _format = format;
} }
void Writer::setSampleType(SampleType type) { void Writer::setSampleType(SampleType type) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
// Do not allow settings to change while open // Do not allow settings to change while open
if (_isOpen) { throw std::runtime_error("Cannot change parameters while file is open"); } if (rw.isOpen()) { throw std::runtime_error("Cannot change parameters while file is open"); }
_type = type; _type = type;
} }
void Writer::write(float* samples, int count) { void Writer::write(float* samples, int count) {
std::lock_guard<std::recursive_mutex> lck(mtx); std::lock_guard<std::recursive_mutex> lck(mtx);
if (!_isOpen) { return; } if (!rw.isOpen()) { return; }
// Select different writer function depending on the chose depth // Select different writer function depending on the chose depth
int tcount; int tcount = count * _channels;
int tbytes = count * bytesPerSamp;
switch (_type) { switch (_type) {
case SAMP_TYPE_UINT8: case SAMP_TYPE_UINT8:
tcount = count * _channels; // Volk doesn't support unsigned ints yet :/
// Volk doesn't support unsigned ints :/
for (int i = 0; i < tcount; i++) { for (int i = 0; i < tcount; i++) {
bufU8[i] = (samples[i] * 127.0f) + 128.0f; bufU8[i] = (samples[i] * 127.0f) + 128.0f;
} }
file.write((char*)bufU8, count * bytesPerSamp); rw.write(bufU8, tbytes);
break; break;
case SAMP_TYPE_INT16: case SAMP_TYPE_INT16:
volk_32f_s32f_convert_16i(bufI16, samples, 32767.0f, count * _channels); volk_32f_s32f_convert_16i(bufI16, samples, 32767.0f, tcount);
file.write((char*)bufI16, count * bytesPerSamp); rw.write((uint8_t*)bufI16, tbytes);
break; break;
case SAMP_TYPE_INT32: case SAMP_TYPE_INT32:
volk_32f_s32f_convert_32i(bufI32, samples, 2147483647.0f, count * _channels); volk_32f_s32f_convert_32i(bufI32, samples, 2147483647.0f, tcount);
file.write((char*)bufI32, count * bytesPerSamp); rw.write((uint8_t*)bufI32, tbytes);
break; break;
case SAMP_TYPE_FLOAT32: case SAMP_TYPE_FLOAT32:
file.write((char*)samples, count * bytesPerSamp); rw.write((uint8_t*)samples, tbytes);
break; break;
default: default:
break; break;
@ -182,17 +179,4 @@ namespace wav {
// Increment sample counter // Increment sample counter
samplesWritten += count; samplesWritten += count;
} }
void Writer::finalize() {
file.seekp(file.beg);
hdr.codec = (_type == SAMP_TYPE_FLOAT32) ? CODEC_FLOAT : CODEC_PCM;
hdr.channelCount = _channels;
hdr.sampleRate = _samplerate;
hdr.bitDepth = SAMP_BITS[_type];
hdr.bytesPerSample = bytesPerSamp;
hdr.bytesPerSecond = bytesPerSamp * _samplerate;
hdr.dataSize = samplesWritten * bytesPerSamp;
hdr.fileSize = hdr.dataSize + sizeof(hdr) - 8;
file.write((char*)&hdr, sizeof(hdr));
}
} }

View File

@ -3,23 +3,17 @@
#include <fstream> #include <fstream>
#include <stdint.h> #include <stdint.h>
#include <mutex> #include <mutex>
#include "riff.h"
namespace wav { namespace wav {
#pragma pack(push, 1) #pragma pack(push, 1)
struct Header { struct FormatHeader {
char signature[4]; // "RIFF" uint16_t codec;
uint32_t fileSize; // data bytes + sizeof(WavHeader_t) - 8
char fileType[4]; // "WAVE"
char formatMarker[4]; // "fmt "
uint32_t formatHeaderLength; // Always 16
uint16_t codec; // PCM (1)
uint16_t channelCount; uint16_t channelCount;
uint32_t sampleRate; uint32_t sampleRate;
uint32_t bytesPerSecond; uint32_t bytesPerSecond;
uint16_t bytesPerSample; uint16_t bytesPerSample;
uint16_t bitDepth; uint16_t bitDepth;
char dataMarker[4]; // "data"
uint32_t dataSize;
}; };
#pragma pack(pop) #pragma pack(pop)
@ -59,12 +53,9 @@ namespace wav {
void write(float* samples, int count); void write(float* samples, int count);
private: private:
void finalize();
std::recursive_mutex mtx; std::recursive_mutex mtx;
std::ofstream file; FormatHeader hdr;
Header hdr; riff::Writer rw;
bool _isOpen = false;
int _channels; int _channels;
uint64_t _samplerate; uint64_t _samplerate;