sample formats, threshold for saving as s16, slightly more "correct" playback output

master
zetaPRIME 2022-03-22 22:18:57 -04:00
parent cbce51744c
commit 0a14aec9e5
4 changed files with 49 additions and 14 deletions

10
notes
View File

@ -35,14 +35,14 @@ TODO {
about-license info about-license info
} }
fix overrun-by-1 on sample preview
templatize svfilter and have mono version for synth use
revert-to-saved menu action revert-to-saved menu action
- splice-between behavior when trying to hook up a command input to something with both a command input and output while already connected
distortion effect distortion effect
uh... maybe save/load samples as s16 instead of f32
automation node { automation node {
listens for one specific param listens for one specific param
supports tweening supports tweening
@ -58,6 +58,8 @@ TODO {
editing song info should probably be an UndoStack action editing song info should probably be an UndoStack action
editing song *tempo* ABSOLUTELY should editing song *tempo* ABSOLUTELY should
maybe retool rendering to feed f32 (or even f64) to ffmpeg
figure out what to actually do with directory config figure out what to actually do with directory config
buffer helper akin to what quicklevel does { buffer helper akin to what quicklevel does {

View File

@ -406,8 +406,8 @@ qint64 AudioEngine::readData(char *data, qint64 maxlen) {
// convert non-interleaved floating point into interleaved int16 // convert non-interleaved floating point into interleaved int16
int16_t* l = reinterpret_cast<int16_t*>(data); int16_t* l = reinterpret_cast<int16_t*>(data);
int16_t* r = reinterpret_cast<int16_t*>(data+smp); int16_t* r = reinterpret_cast<int16_t*>(data+smp);
*l = static_cast<int16_t>(std::clamp(buffer[0][bufPos], -1.0f, 1.0f) * 32767); *l = static_cast<int16_t>(std::clamp(buffer[0][bufPos] * 32768.0f, -32767.0f, 32767.0f));
*r = static_cast<int16_t>(std::clamp(buffer[1][bufPos], -1.0f, 1.0f) * 32767); *r = static_cast<int16_t>(std::clamp(buffer[1][bufPos] * 32768.0f, -32767.0f, 32767.0f));
bufPos++; bufPos++;
data += stride; data += stride;

View File

@ -48,17 +48,35 @@ std::array<float, 2> Sample::plotBetween(size_t ch, size_t start, size_t end) co
return {mn, mx}; return {mn, mx};
} }
// threshold in MiB-stored-as-float for saving long samples as s16 instead
const constexpr double PCM_MiB_THRESHOLD = 5;
const constexpr int PCM_THRESHOLD = static_cast<int>(PCM_MiB_THRESHOLD * (1024*1024) / sizeof(float));
QCborMap Sample::toCbor() const { QCborMap Sample::toCbor() const {
QCborMap m; QCborMap m;
m[qs("name")] = name; m[qs("name")] = name;
m[qs("rate")] = sampleRate; m[qs("rate")] = sampleRate;
QString fmt = qs("f32");
if (numChannels() * length() > PCM_THRESHOLD) fmt = qs("s16");
m[qs("fmt")] = fmt;
{ {
QCborArray ch; QCborArray ch;
auto n = static_cast<size_t>(numChannels()); auto n = static_cast<size_t>(numChannels());
for (size_t i = 0; i < n; i++) { if (fmt == qs("f32")) {
ch[static_cast<qsizetype>(i)] = QByteArray(reinterpret_cast<const char*>(data[i].data()), static_cast<int>(data[i].size() * sizeof(data[i][0]))); for (size_t i = 0; i < n; i++) {
ch[static_cast<qsizetype>(i)] = QByteArray(reinterpret_cast<const char*>(data[i].data()), static_cast<int>(data[i].size() * sizeof(data[i][0])));
}
} else if (fmt == qs("s16")) {
for (size_t i = 0; i < n; i++) {
auto sz = data[i].size();
QByteArray dat(static_cast<int>(sz * 2), static_cast<char>(0));
for (size_t j = 0; j < sz; j++) *reinterpret_cast<int16_t*>(dat.data() + j*2) = static_cast<int16_t>(std::clamp(static_cast<double>(data[i][j]) * 32768.0, -32767.0, 32767.0));
ch[static_cast<qsizetype>(i)] = dat;
}
} }
m[qs("channels")] = ch; m[qs("channels")] = ch;
@ -82,14 +100,29 @@ std::shared_ptr<Sample> Sample::fromCbor(const QCborMap& m, QUuid uuid) {
smp->sampleRate = static_cast<int>(m.value("rate").toInteger(48000)); smp->sampleRate = static_cast<int>(m.value("rate").toInteger(48000));
auto fmt = m.value("fmt").toString(qs("f32"));
auto ch = m.value("channels").toArray(); auto ch = m.value("channels").toArray();
auto s = static_cast<size_t>(ch.size()); auto s = static_cast<size_t>(ch.size());
for (size_t i = 0; i < s; i++) { if (fmt == qs("f32")) {
auto c = ch[static_cast<qint64>(i)].toByteArray(); for (size_t i = 0; i < s; i++) {
auto bs = static_cast<size_t>(c.size()); auto c = ch[static_cast<qint64>(i)].toByteArray();
smp->data[i].resize(bs / sizeof(*smp->data[i].begin())); auto bs = static_cast<size_t>(c.size());
memcpy(smp->data[i].data(), c.constData(), bs); smp->data[i].resize(bs / sizeof(*smp->data[i].begin()));
memcpy(smp->data[i].data(), c.constData(), bs);
}
} else if (fmt == qs("s16")) {
for (size_t i = 0; i < s; i++) {
auto c = ch[static_cast<qint64>(i)].toByteArray();
auto bs = static_cast<size_t>(c.size());
auto sz = bs / 2;
smp->data[i].resize(sz);
for (size_t j = 0; j < sz; j++) {
smp->data[i][j] = static_cast<float>(static_cast<double>(*reinterpret_cast<int16_t*>(c.data()+j*2))/32768.0);
}
//memcpy(smp->data[i].data(), c.constData(), bs);
}
} }
smp->loopStart = static_cast<int>(m.value("loopStart").toInteger(-1)); smp->loopStart = static_cast<int>(m.value("loopStart").toInteger(-1));

View File

@ -35,7 +35,7 @@ namespace {
+ (static_cast<uint32_t>(minor)<<16) + (static_cast<uint32_t>(minor)<<16)
+ (static_cast<uint32_t>(major)<<24); + (static_cast<uint32_t>(major)<<24);
} }
constexpr const uint32_t XYBRID_VERSION = packedVersion(0,0,0,1); constexpr const uint32_t XYBRID_VERSION = packedVersion(0,0,0,2);
constexpr const QSize dlgSize(700, 500); constexpr const QSize dlgSize(700, 500);
} }