sample looping!

master
zetaPRIME 2021-11-10 02:13:51 -05:00
parent b57974e066
commit 39f5966c0f
6 changed files with 224 additions and 7 deletions

View File

@ -64,6 +64,11 @@ QCborMap Sample::toCbor() const {
m[qs("channels")] = ch;
}
if (loopStart >= 0) { // only store if there is a loop point
m[qs("loopStart")] = loopStart;
m[qs("loopEnd")] = loopEnd;
}
return m;
}
@ -84,6 +89,9 @@ std::shared_ptr<Sample> Sample::fromCbor(const QCborMap& m, QUuid uuid) {
memcpy(smp->data[i].data(), c.constData(), bs);
}
smp->loopStart = static_cast<int>(m.value("loopStart").toInteger(-1));
smp->loopEnd = static_cast<int>(m.value("loopEnd").toInteger(-1));
return smp;
}
std::shared_ptr<Sample> Sample::fromCbor(const QCborValue& m, QUuid uuid) { return fromCbor(m.toMap(), uuid); }

View File

@ -26,6 +26,9 @@ namespace Xybrid::Data {
std::array<std::vector<float>, 2> data;
int loopStart = -1;
int loopEnd = -1;
inline AudioFrame operator[] (size_t at) const {
if (data[1].empty()) return {data[0][at]};
return {data[0][at], data[1][at]};

View File

@ -371,6 +371,29 @@ MainWindow::MainWindow(QWidget *parent) :
connect(ui->sampleList->selectionModel(), &QItemSelectionModel::currentChanged, this, [this, mdl](const QModelIndex& ind, const QModelIndex& old [[maybe_unused]]) {
selectSampleForEditing(mdl->itemAt(ind));
});
// edit pane
connect(ui->groupSampleLoop, &QGroupBox::toggled, this, [this](bool on) {
if (editingSample) {
if (on) {
editingSample->loopStart = ui->spinSampleLoopStart->value();
editingSample->loopEnd = ui->spinSampleLoopEnd->value();
} else {
editingSample->loopStart = -1;
editingSample->loopEnd = -1;
}
}
});
connect(ui->spinSampleLoopStart, qOverload<int>(&QSpinBox::valueChanged), this, [this](int v) {
if (editingSample && ui->groupSampleLoop->isChecked()) {
editingSample->loopStart = v;
}
});
connect(ui->spinSampleLoopEnd, qOverload<int>(&QSpinBox::valueChanged), this, [this](int v) {
if (editingSample && ui->groupSampleLoop->isChecked()) {
editingSample->loopEnd = v;
}
});
}
// Set up signaling from project to UI
@ -626,20 +649,40 @@ bool MainWindow::selectPatternForEditing(Pattern* pattern) {
void MainWindow::selectSampleForEditing(std::shared_ptr<Xybrid::Data::Sample> smp) {
if (!smp || smp->project != project.get()) { // no valid sample selected
editingSample = nullptr;
ui->sampleViewPane->setEnabled(false);
ui->sampleInfo->setText(qs("(no sample selected)"));
ui->groupSampleLoop->setChecked(false);
ui->spinSampleLoopStart->setValue(0);
ui->spinSampleLoopEnd->setValue(0);
} else {
editingSample = smp;
ui->sampleViewPane->setEnabled(true);
ui->sampleInfo->setText(
qs("%1 // %2\n%3 %4Hz, %5 frames (%6)")
.arg(smp->name.section('/', -1, -1))
.arg(smp->uuid.toString())
.arg(smp->numChannels() == 2 ? qs("Stereo") : qs("Mono"))
.arg(smp->name.section('/', -1, -1),
smp->uuid.toString(),
smp->numChannels() == 2 ? qs("Stereo") : qs("Mono"))
.arg(smp->sampleRate)
.arg(smp->length())
.arg(Util::sampleLength(smp->sampleRate, smp->length()))
);
ui->spinSampleLoopStart->setRange(0, smp->length());
ui->spinSampleLoopEnd->setRange(0, smp->length());
if (smp->loopStart < 0) { // loop disabled
ui->groupSampleLoop->setChecked(false);
ui->spinSampleLoopStart->setValue(0);
ui->spinSampleLoopEnd->setValue(smp->length());
} else {
ui->groupSampleLoop->setChecked(true);
ui->spinSampleLoopStart->setValue(smp->loopStart);
ui->spinSampleLoopEnd->setValue(smp->loopEnd);
}
}
ui->waveformPreview->setSample(smp);
}

View File

@ -27,6 +27,7 @@ namespace Xybrid {
UISocket* socket;
std::shared_ptr<Data::Project> project;
std::shared_ptr<Data::Pattern> editingPattern;
std::shared_ptr<Data::Sample> editingSample;
QUndoStack* undoStack;

View File

@ -33,7 +33,7 @@
<enum>Qt::NoFocus</enum>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<property name="documentMode">
<bool>true</bool>
@ -687,6 +687,161 @@
</property>
</widget>
</item>
<item>
<widget class="QWidget" name="sampleLoopRow" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="groupSampleLoop">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Loop</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelSampleLoopStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinSampleLoopStart">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelSampleLoopEnd">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinSampleLoopEnd">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>4</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">

View File

@ -13,15 +13,22 @@ namespace Xybrid::NodeLib {
extern const std::array<std::array<double, LUT_TAPS>, LUT_STEPS> resamplerLUT;
inline Data::AudioFrame resamp(Data::Sample* smp, double pos) {
auto loop = smp->loopStart >= 0;
auto len = static_cast<ptrdiff_t>(smp->length());
auto ls = static_cast<ptrdiff_t>(smp->loopStart);
auto le = static_cast<ptrdiff_t>(smp->loopEnd);
auto ll = le - ls;
double ip = std::floor(pos);
auto& pt = NodeLib::resamplerLUT[static_cast<size_t>((pos - ip)*NodeLib::LUT_STEPS) % NodeLib::LUT_STEPS];
Data::AudioFrame out(0.0);
auto ii = static_cast<ptrdiff_t>(ip) - 3;
for (size_t i = 0; i < 8; i++) {
auto si = ii+static_cast<ptrdiff_t>(i);
if (si >= 0 && si < static_cast<ptrdiff_t>(smp->length())) out += (*smp)[static_cast<size_t>(si)] * pt[i];
for (uint8_t i = 0; i < 8; i++) {
if (loop && ii > le) ii = (ii - ls) % ll + ls;
if (ii >= 0 && ii < len) out += (*smp)[static_cast<size_t>(ii)] * pt[i];
ii++;
}
return out;