Skip to content

Commit 288fec2

Browse files
committed
add option for resampling to take place on input
1 parent 97b0504 commit 288fec2

File tree

10 files changed

+295
-148
lines changed

10 files changed

+295
-148
lines changed

bungee/Bungee.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
#pragma once
55

6+
#include "Modes.h"
7+
68
#ifndef BUNGEE_VISIBILITY
79
# define BUNGEE_VISIBILITY
810
#endif
@@ -36,9 +38,12 @@ struct Request
3638

3739
// Set to have the stretcher forget all previous grains and restart on this grain.
3840
bool reset;
41+
42+
// How resampling should be applied to this grain.
43+
enum ResampleMode resampleMode;
3944
};
4045

41-
// Information to describe a chunk of audio that the audio stretcher requires as input for the current grain.
46+
// Information to describe a chunk of audio that the audio stretcher requires as input for the current grain.
4247
// Note that input chunks of consecutive grains often overlap and are usually centred on the grain's
4348
// Request::position.
4449
struct InputChunk

bungee/CommandLine.h

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,28 @@ struct Options :
4646
("s,speed", "output speed as multiple of input speed", cxxopts::value<double>()->default_value("1")) //
4747
("p,pitch", "output pitch shift in semitones", cxxopts::value<double>()->default_value("0")) //
4848
;
49-
add_options("Developer / Debug") //
49+
auto optionAdder = add_options(helpGroups.emplace_back("Processing"));
50+
51+
#define X_BEGIN(Type, type) \
52+
{ \
53+
std::string names, d, a = "[";
54+
#define X_END(Type, type) \
55+
optionAdder(#type, names + "]", cxxopts::value<std::string>()->default_value(d)); \
56+
}
57+
#define X_ITEM(Type, type, mode, description) \
58+
names += a + #mode; \
59+
a = "|"; \
60+
if (Request{}.type##Mode == type##Mode_##mode) \
61+
d = #mode;
62+
63+
BUNGEE_MODES
64+
65+
#undef X_BEGIN
66+
#undef X_END
67+
#undef X_ITEM
68+
//
69+
;
70+
add_options(helpGroups.emplace_back("Developer")) //
5071
("grain", "increases [+1] or decreases [-1] grain duration by a factor of two", cxxopts::value<int>()->default_value("0")) //
5172
("push", "input chunk size (0 for pull operation, negative for random push chunk size)", cxxopts::value<int>()->default_value("0")) //
5273
("instrumentation", "report useful diagnostic information to system log") //
@@ -95,6 +116,32 @@ struct Parameters :
95116

96117
if ((*this)["push"].as<int>() && request.speed < 0.)
97118
fail("speed not greater than zero in 'push' mode");
119+
120+
#define X_BEGIN(Type, type) \
121+
{ \
122+
const auto s = (*this)[#type].as<std::string>(); \
123+
if (false) \
124+
{ \
125+
}
126+
127+
#define X_ITEM(Type, type, mode, description) \
128+
else if (s == #mode) \
129+
{ \
130+
request.type##Mode = type##Mode_##mode; \
131+
}
132+
133+
#define X_END(Type, type) \
134+
else \
135+
{ \
136+
Bungee::CommandLine::fail("Unrecognised value for --" #type); \
137+
} \
138+
}
139+
140+
BUNGEE_MODES
141+
142+
#undef X_BEGIN
143+
#undef X_ITEM
144+
#undef X_END
98145
}
99146
};
100147

bungee/Modes.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (C) 2020-2025 Parabola Research Limited
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
#pragma once
5+
6+
#define BUNGEE_MODES_RESAMPLE \
7+
X_BEGIN(Resample, resample) \
8+
X_ITEM(Resample, resample, autoOut, "output resampling, activated as needed") \
9+
X_ITEM(Resample, resample, autoIn, "input resampling, activated as needed") \
10+
X_ITEM(Resample, resample, autoInOut, "input resampling on input when downsampling and on output when upsampling") \
11+
X_ITEM(Resample, resample, forceOut, "output resampling, always active") \
12+
X_ITEM(Resample, resample, forceIn, "input resampling, always active") \
13+
X_END(Resample, resample)
14+
15+
#define BUNGEE_MODES \
16+
BUNGEE_MODES_RESAMPLE
17+
18+
#define X_BEGIN(Type, type) \
19+
enum Type##Mode \
20+
{
21+
#define X_ITEM(Type, type, mode, description) \
22+
type##Mode_##mode,
23+
#define X_END(Type, type) \
24+
};
25+
26+
BUNGEE_MODES
27+
28+
#undef X_BEGIN
29+
#undef X_ITEM
30+
#undef X_END

src/Grain.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ InputChunk Grain::specify(const Request &r, Grain &previous, SampleRates sampleR
3434
BUNGEE_ASSERT1(request.pitch > 0.);
3535

3636
const Assert::FloatingPointExceptions floatingPointExceptions(FE_INEXACT);
37-
const auto unitHop = (1 << log2SynthesisHop) * resampleOperations.setup(sampleRates, request.pitch);
37+
const auto unitHop = (1 << log2SynthesisHop) * resampleOperations.setup(sampleRates, request.pitch, request.resampleMode);
3838

3939
requestHop = request.position - previous.request.position;
4040

@@ -77,7 +77,7 @@ InputChunk Grain::specify(const Request &r, Grain &previous, SampleRates sampleR
7777
{
7878
int halfInputFrameCount = inputResampled.frameCount / 2;
7979
if (resampleOperations.input.ratio != 1.f)
80-
halfInputFrameCount = int(std::round(halfInputFrameCount / resampleOperations.input.ratio)) + 1;
80+
halfInputFrameCount = int(std::ceil(halfInputFrameCount / resampleOperations.input.ratio)) + 2;
8181

8282
inputChunk.begin = -halfInputFrameCount;
8383
inputChunk.end = +halfInputFrameCount;
@@ -88,6 +88,7 @@ InputChunk Grain::specify(const Request &r, Grain &previous, SampleRates sampleR
8888
const int offset = int(std::round(request.position - bufferStartPosition));
8989
inputChunk.begin += offset;
9090
inputChunk.end += offset;
91+
inputPosition = bufferStartPosition + offset;
9192
return inputChunk;
9293
}
9394
}
@@ -136,12 +137,15 @@ Eigen::Ref<Eigen::ArrayXXf> Grain::resampleInput(Eigen::Ref<Eigen::ArrayXXf> inp
136137
{
137138
if (resampleOperations.input.function)
138139
{
139-
float offset = float(inputChunk.begin - request.position);
140-
offset *= resampleOperations.input.ratio;
141-
offset += 1 << (log2WindowLength - 1);
142-
offset -= analysis.positionError;
140+
BUNGEE_ASSERT1(input.rows() % 2 == 0);
143141

144-
resampleOperations.input.function(inputResampled, offset, input, resampleOperations.input.ratio, resampleOperations.input.ratio, false, muteFrameCountHead, muteFrameCountTail);
142+
inputResampled.offset = inputPosition - request.position - input.rows() / 2;
143+
inputResampled.offset *= resampleOperations.input.ratio;
144+
inputResampled.offset += 1 << (log2WindowLength - 1);
145+
inputResampled.offset -= analysis.positionError;
146+
147+
Resample::External external(input, muteFrameCountHead, muteFrameCountTail);
148+
resampleOperations.input.function(inputResampled, external, resampleOperations.input.ratio, resampleOperations.input.ratio, false);
145149

146150
muteFrameCountHead = muteFrameCountTail = 0;
147151

src/Grain.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#pragma once
55

6+
#include "Assert.h"
67
#include "Fourier.h"
78
#include "Instrumentation.h"
89
#include "Output.h"
@@ -11,7 +12,6 @@
1112
#include "Stretch.h"
1213
#include "Window.h"
1314

14-
#include "bungee/../src/log2.h"
1515
#include "bungee/Bungee.h"
1616

1717
#include <Eigen/Core>
@@ -45,6 +45,7 @@ struct Grain
4545

4646
Resample::Operations resampleOperations{};
4747

48+
double inputPosition;
4849
InputChunk inputChunk{};
4950
Analysis analysis{};
5051

@@ -54,7 +55,7 @@ struct Grain
5455
Eigen::ArrayX<Phase::Type> rotation;
5556
Eigen::ArrayX<Phase::Type> delta;
5657
std::vector<Partials::Partial> partials;
57-
Resample::Padded inputResampled;
58+
Resample::Internal inputResampled;
5859
Eigen::ArrayXXf inputCopy;
5960

6061
Output::Segment segment;
@@ -97,6 +98,7 @@ struct Grain
9798
if (instrumentation.enabled || Bungee::Assert::level)
9899
overlapCheck(m, muteFrameCountHead, muteFrameCountTail, previous, instrumentation);
99100

101+
BUNGEE_ASSERT1(m.rows() % 2 == 0);
100102
return m;
101103
}
102104

src/Output.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Output::Segment::Segment(int log2FrameCount, int channelCount) :
5959

6060
void Output::Segment::lapPadding(Segment &current, Segment &next)
6161
{
62-
constexpr auto n = Resample::Padded::padding;
62+
constexpr auto n = Resample::Internal::padding;
6363

6464
if (current.needsResample)
6565
{
@@ -90,29 +90,34 @@ inline OutputChunk Output::Segment::outputChunk(Eigen::Ref<Eigen::ArrayXXf> ref,
9090
return outputChunk;
9191
}
9292

93-
OutputChunk Output::Segment::resample(float &resampleOffset, Resample::Operation resampleOperationBegin, Resample::Operation resampleOperationEnd, Eigen::Ref<Eigen::ArrayXXf> bufferResampled)
93+
OutputChunk Output::Segment::resample(double &resampleOffset, Resample::Operation resampleOperationBegin, Resample::Operation resampleOperationEnd, Eigen::Ref<Eigen::ArrayXXf> bufferResampled)
9494
{
95-
if (!resampleOperationBegin.function)
96-
resampleOperationBegin.ratio = 1.f;
97-
9895
if (!resampleOperationEnd.function)
9996
{
10097
resampleOperationEnd.ratio = 1.f;
10198
resampleOperationEnd.function = resampleOperationBegin.function;
10299
}
103100

101+
if (!resampleOperationBegin.function)
102+
resampleOperationBegin.ratio = resampleOperationEnd.ratio;
103+
104104
BUNGEE_ASSERT1(resampleOperationBegin.ratio != 0.f);
105105
BUNGEE_ASSERT1(resampleOperationEnd.ratio != 0.f);
106106

107107
if (resampleOperationEnd.function)
108108
{
109-
if (bufferLapped.allZeros)
110-
resampleOperationEnd.function = &Resample::resample<Resample::FixedToVariable, Resample::None>;
109+
bufferLapped.offset = resampleOffset;
110+
111+
const auto muteHead = bufferLapped.allZeros ? bufferResampled.rows() : 0;
112+
Resample::External external(bufferResampled, muteHead, 0);
113+
114+
const bool alignEnd = resampleOperationEnd.ratio == 1.;
115+
116+
resampleOperationEnd.function(bufferLapped, external, resampleOperationBegin.ratio, resampleOperationEnd.ratio, alignEnd);
111117

112-
const bool alignEnd = resampleOperationEnd.ratio == 1.f;
113-
const auto frameCount = resampleOperationEnd.function(bufferLapped, resampleOffset, bufferResampled, resampleOperationBegin.ratio, resampleOperationEnd.ratio, alignEnd, 0, 0);
118+
resampleOffset = bufferLapped.offset;
114119

115-
return outputChunk(bufferResampled.topRows(frameCount), bufferLapped.allZeros);
120+
return outputChunk(bufferResampled.topRows(external.activeFrameCount), bufferLapped.allZeros);
116121
}
117122
else
118123
{

src/Output.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct Output
2222
Eigen::ArrayXf synthesisWindow;
2323
Eigen::ArrayXXf inverseTransformed;
2424
Eigen::ArrayXXf bufferResampled;
25-
float resampleOffset = 0.f;
25+
double resampleOffset = 0.;
2626
Window::DispatchApply dispatchApply;
2727

2828
Output(Fourier::Transforms &transforms, int log2SynthesisHop, int channelCount, int maxOutputChunkSize, float windowGain, std::initializer_list<float> windowCoefficients);
@@ -31,13 +31,13 @@ struct Output
3131

3232
struct Segment
3333
{
34-
Resample::Padded bufferLapped;
34+
Resample::Internal bufferLapped;
3535
bool needsResample;
3636

3737
Segment(int log2FrameCount, int channelCount);
3838
static inline OutputChunk outputChunk(Eigen::Ref<Eigen::ArrayXXf> ref, bool allZeros);
3939
static void lapPadding(Segment &current, Segment &next);
40-
OutputChunk resample(float &resampleOffset, Resample::Operation resampleOperationBegin, Resample::Operation resampleOperationEnd, Eigen::Ref<Eigen::ArrayXXf> bufferResampled);
40+
OutputChunk resample(double &resampleOffset, Resample::Operation resampleOperationBegin, Resample::Operation resampleOperationEnd, Eigen::Ref<Eigen::ArrayXXf> bufferResampled);
4141
};
4242
};
4343

0 commit comments

Comments
 (0)