diff --git a/notes b/notes index e1037cf..7a683ac 100644 --- a/notes +++ b/notes @@ -35,14 +35,14 @@ TODO { about-license info } + fix overrun-by-1 on sample preview + + templatize svfilter and have mono version for synth use + 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 - uh... maybe save/load samples as s16 instead of f32 - automation node { listens for one specific param supports tweening @@ -58,6 +58,8 @@ TODO { editing song info should probably be an UndoStack action 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 buffer helper akin to what quicklevel does { diff --git a/xybrid/audio/audioengine.cpp b/xybrid/audio/audioengine.cpp index 0bdaa52..a4ec127 100644 --- a/xybrid/audio/audioengine.cpp +++ b/xybrid/audio/audioengine.cpp @@ -406,8 +406,8 @@ qint64 AudioEngine::readData(char *data, qint64 maxlen) { // convert non-interleaved floating point into interleaved int16 int16_t* l = reinterpret_cast(data); int16_t* r = reinterpret_cast(data+smp); - *l = static_cast(std::clamp(buffer[0][bufPos], -1.0f, 1.0f) * 32767); - *r = static_cast(std::clamp(buffer[1][bufPos], -1.0f, 1.0f) * 32767); + *l = static_cast(std::clamp(buffer[0][bufPos] * 32768.0f, -32767.0f, 32767.0f)); + *r = static_cast(std::clamp(buffer[1][bufPos] * 32768.0f, -32767.0f, 32767.0f)); bufPos++; data += stride; diff --git a/xybrid/data/sample.cpp b/xybrid/data/sample.cpp index b847767..404ffb4 100644 --- a/xybrid/data/sample.cpp +++ b/xybrid/data/sample.cpp @@ -48,17 +48,35 @@ std::array Sample::plotBetween(size_t ch, size_t start, size_t end) co 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(PCM_MiB_THRESHOLD * (1024*1024) / sizeof(float)); QCborMap Sample::toCbor() const { QCborMap m; m[qs("name")] = name; m[qs("rate")] = sampleRate; + + QString fmt = qs("f32"); + if (numChannels() * length() > PCM_THRESHOLD) fmt = qs("s16"); + m[qs("fmt")] = fmt; + { QCborArray ch; auto n = static_cast(numChannels()); - for (size_t i = 0; i < n; i++) { - ch[static_cast(i)] = QByteArray(reinterpret_cast(data[i].data()), static_cast(data[i].size() * sizeof(data[i][0]))); + if (fmt == qs("f32")) { + for (size_t i = 0; i < n; i++) { + ch[static_cast(i)] = QByteArray(reinterpret_cast(data[i].data()), static_cast(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(sz * 2), static_cast(0)); + + for (size_t j = 0; j < sz; j++) *reinterpret_cast(dat.data() + j*2) = static_cast(std::clamp(static_cast(data[i][j]) * 32768.0, -32767.0, 32767.0)); + ch[static_cast(i)] = dat; + } } m[qs("channels")] = ch; @@ -82,14 +100,29 @@ std::shared_ptr Sample::fromCbor(const QCborMap& m, QUuid uuid) { smp->sampleRate = static_cast(m.value("rate").toInteger(48000)); + auto fmt = m.value("fmt").toString(qs("f32")); + auto ch = m.value("channels").toArray(); auto s = static_cast(ch.size()); - for (size_t i = 0; i < s; i++) { - auto c = ch[static_cast(i)].toByteArray(); - auto bs = static_cast(c.size()); - smp->data[i].resize(bs / sizeof(*smp->data[i].begin())); - memcpy(smp->data[i].data(), c.constData(), bs); + if (fmt == qs("f32")) { + for (size_t i = 0; i < s; i++) { + auto c = ch[static_cast(i)].toByteArray(); + auto bs = static_cast(c.size()); + 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(i)].toByteArray(); + auto bs = static_cast(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(static_cast(*reinterpret_cast(c.data()+j*2))/32768.0); + } + //memcpy(smp->data[i].data(), c.constData(), bs); + } } smp->loopStart = static_cast(m.value("loopStart").toInteger(-1)); diff --git a/xybrid/fileops.cpp b/xybrid/fileops.cpp index 6dacd18..9133052 100644 --- a/xybrid/fileops.cpp +++ b/xybrid/fileops.cpp @@ -35,7 +35,7 @@ namespace { + (static_cast(minor)<<16) + (static_cast(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); }