sample base note support

master
zetaPRIME 2021-11-11 00:47:37 -05:00
parent 14af2ffeb7
commit d4aa622fa6
6 changed files with 123 additions and 14 deletions

13
notes
View File

@ -32,22 +32,11 @@ parameters {
TODO {
immediate frontburner {
... resampling matches modplug-xmms with kaiser window, not modplug's polyphase
fiddling with things, figured out much better sounding cases {
rate<1.0, omit window
rate>1.0, omit window AND USE HALF RATIO SINC???
rate near 1.0, windowed is probably best
triple-lut this?? fade to windowed near 1.0, pick one of the other two for main
maybe only double, windowed with a=5.5 sounds good for rate<1.0
}
???
distortion effect
single-selection sampler
- global (default) pan (PXX) for InstrumentCore
add ,XX support to global tempo
}

View File

@ -69,6 +69,9 @@ QCborMap Sample::toCbor() const {
m[qs("loopEnd")] = loopEnd;
}
m[qs("note")] = baseNote;
m[qs("subNote")] = subNote;
return m;
}
@ -92,6 +95,9 @@ std::shared_ptr<Sample> Sample::fromCbor(const QCborMap& m, QUuid uuid) {
smp->loopStart = static_cast<int>(m.value("loopStart").toInteger(-1));
smp->loopEnd = static_cast<int>(m.value("loopEnd").toInteger(-1));
smp->baseNote = static_cast<int>(m.value("note").toInteger(60));
smp->subNote = m.value("subNote").toDouble(0.0);
return smp;
}
std::shared_ptr<Sample> Sample::fromCbor(const QCborValue& m, QUuid uuid) { return fromCbor(m.toMap(), uuid); }

View File

@ -29,6 +29,9 @@ namespace Xybrid::Data {
int loopStart = -1;
int loopEnd = -1;
int baseNote = 60;
double subNote = 0.0;
inline AudioFrame operator[] (size_t at) const {
if (data[1].empty()) return {data[0][at]};
return {data[0][at], data[1][at]};
@ -43,6 +46,8 @@ namespace Xybrid::Data {
inline int length() const { return static_cast<int>(data[0].size()); }
std::array<float, 2> plotBetween(size_t ch, size_t start, size_t end) const;
inline double getNote() const { return static_cast<double>(baseNote) + subNote; }
QCborMap toCbor() const;
static std::shared_ptr<Sample> fromCbor(const QCborMap&, QUuid);
static std::shared_ptr<Sample> fromCbor(const QCborValue&, QUuid);

View File

@ -397,6 +397,21 @@ MainWindow::MainWindow(QWidget *parent) :
ui->waveformPreview->update();
}
});
connect(ui->spinSampleNote, qOverload<int>(&QSpinBox::valueChanged), this, [this](int v) {
auto sp = ui->spinSampleNote;
sp->setSuffix(")");
sp->setPrefix(qs("%1 (").arg(Util::noteName(static_cast<int16_t>(v))));
if (editingSample) editingSample->baseNote = v;
});
connect(ui->spinSampleNoteSub, qOverload<double>(&QDoubleSpinBox::valueChanged), this, [this](double v) {
auto sp = ui->spinSampleNoteSub;
sp->setPrefix(v >= 0.0 ? qs("+") : QString());
if (editingSample) editingSample->subNote = v;
});
emit ui->spinSampleNoteSub->valueChanged(0.0); // force refresh
}
// Set up signaling from project to UI
@ -659,6 +674,9 @@ void MainWindow::selectSampleForEditing(std::shared_ptr<Xybrid::Data::Sample> sm
ui->groupSampleLoop->setChecked(false);
ui->spinSampleLoopStart->setValue(0);
ui->spinSampleLoopEnd->setValue(0);
ui->spinSampleNote->setValue(60);
ui->spinSampleNoteSub->setValue(0.0);
} else {
editingSample = nullptr;
ui->sampleViewPane->setEnabled(true);
@ -685,6 +703,9 @@ void MainWindow::selectSampleForEditing(std::shared_ptr<Xybrid::Data::Sample> sm
ui->spinSampleLoopEnd->setValue(smp->loopEnd);
}
ui->spinSampleNote->setValue(smp->baseNote);
ui->spinSampleNoteSub->setValue(smp->subNote);
editingSample = smp;
}

View File

@ -688,7 +688,7 @@
</widget>
</item>
<item>
<widget class="QWidget" name="sampleLoopRow" native="true">
<widget class="QWidget" name="sampleEditRow" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>2</number>
@ -826,6 +826,93 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupSampleNote">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Base Note</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<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_3" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_11">
<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="QSpinBox" name="spinSampleNote">
<property name="maximum">
<number>119</number>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="spinSampleNoteSub">
<property name="minimum">
<double>-1.000000000000000</double>
</property>
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">

View File

@ -81,13 +81,14 @@ void Capaxitor::init() {
if (!smp) return core.deleteNote(note);
double rate = static_cast<double>(smp->sampleRate) / static_cast<double>(audioEngine->curSampleRate());
double baseNote = smp->getNote();
size_t ts = p->size;
for (size_t i = 0; i < ts; i++) {
core.advanceNote(note);
double n = note.effectiveNote();
double fr = std::pow(SEMI, n - 12*5);
double fr = std::pow(SEMI, n - baseNote);
// actual sample pos
double sp = data.sampleTime * rate;