Skip to content

Commit 521330b

Browse files
author
Hannes Barfuss
committed
Added panic button to delay. Made short delay times possible (down to 1 sample). Added omnijack ins for time and feedback, but they are not working yet.
1 parent 7ff6238 commit 521330b

File tree

8 files changed

+1131
-82
lines changed

8 files changed

+1131
-82
lines changed

Assets/Resources/Prefabs/Delay.prefab

+1,014-31
Large diffs are not rendered by default.

Assets/Scripts/Delay/delayDeviceInterface.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@
1818

1919
public class delayDeviceInterface : deviceInterface
2020
{
21-
public omniJack input, output;
21+
public omniJack input, cTimeInput, cFeedbackInput, output;
2222
public dial timeDial, feedbackDial, wetDial, dryDial;
23+
public button panicButton;
2324
delaySignalGenerator signal;
2425
private dial[] dials;
2526

@@ -33,11 +34,15 @@ public override void Awake()
3334
void Update()
3435
{
3536
if (signal.input != input.signal) signal.input = input.signal;
37+
if (signal.cFeedbackInput != cFeedbackInput.signal) signal.cFeedbackInput = cFeedbackInput.signal;
38+
if (signal.cTimeInput != cTimeInput.signal) signal.cTimeInput = cTimeInput.signal;
3639

37-
for(int i = 0; i < dials.Length; i++)
40+
for (int i = 0; i < dials.Length; i++)
3841
{
3942
signal.SetParam(dials[i].percent, i);
4043
}
44+
if (panicButton.isHit)
45+
signal.SetParam(1, (int)delaySignalGenerator.Param.P_CLEAR);
4146
}
4247

4348
public override InstrumentData GetData()

Assets/Scripts/Delay/delaySignalGenerator.cs

+45-6
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,29 @@ public enum Param : int
1414
P_FEEDBACK,
1515
P_WET,
1616
P_DRY,
17+
P_CLEAR,
1718
P_N
1819
};
1920

20-
public const float MIN_TIME = 0.050f; // * 1000ms
21-
public const float MAX_TIME = 5.0f; // * 1000 ms
21+
public const float MIN_TIME = 0.001f; // * 1000ms
22+
public const float MAX_TIME = 0.05f; // * 1000 ms
2223
public const float MIN_FEEDBACK = 0;
2324
public const float MAX_FEEDBACK = 1f;
2425
public const float MIN_WET = -96; //dB
2526
public const float MAX_WET = 0; //dB
2627
public const float MIN_DRY = -96; //dB
2728
public const float MAX_DRY = 0; //dB
2829

29-
public signalGenerator input;
30+
public signalGenerator input, cTimeInput, cFeedbackInput;
3031

3132
private IntPtr x;
3233
private float[] p = new float[(int)Param.P_N];
33-
private bool processing = false;
34+
private bool shouldClear = false;
35+
36+
private float[] cTimeBuffer = new float[2] { 0, 0 };
37+
private float[] cFeedbackBuffer = new float[2] { 0, 0 };
38+
private float cTime;
39+
private float cFeedback;
3440

3541
public override void Awake()
3642
{
@@ -59,11 +65,15 @@ public void SetParam(float value, int param)
5965
case (int)Param.P_DRY:
6066
p[param] = Utils.dbToLin( Utils.map(value, 0, 1, MIN_DRY, MAX_DRY, 0.8f) );
6167
break;
68+
case (int)Param.P_CLEAR:
69+
if (value != 0)
70+
shouldClear = true;
71+
break;
6272
}
6373
}
6474

6575
[DllImport("SoundStageNative")]
66-
private static extern void Delay_Process(float[] buffer, int length, int channels, IntPtr x);
76+
private static extern void Delay_Process(float[] buffer, int length, int channels, float cTime, float cFeedback, IntPtr x);
6777

6878
[DllImport("SoundStageNative")]
6979
private static extern IntPtr Delay_New(int maxDelayTimeSamples);
@@ -74,8 +84,23 @@ public void SetParam(float value, int param)
7484
[DllImport("SoundStageNative")]
7585
private static extern void Delay_SetParam(float value, int param, IntPtr x);
7686

87+
[DllImport("SoundStageNative")]
88+
private static extern void Delay_Clear(IntPtr x);
89+
7790
public override void processBuffer(float[] buffer, double dspTime, int channels)
7891
{
92+
if (cTimeBuffer.Length != buffer.Length)
93+
System.Array.Resize(ref cTimeBuffer, buffer.Length);
94+
95+
if (cFeedbackBuffer.Length != buffer.Length)
96+
System.Array.Resize(ref cFeedbackBuffer, buffer.Length);
97+
98+
if (shouldClear)
99+
{
100+
Delay_Clear(x);
101+
shouldClear = false;
102+
}
103+
79104
Delay_SetParam(p[(int)Param.P_TIME], (int)Param.P_TIME, x);
80105
Delay_SetParam(p[(int)Param.P_FEEDBACK], (int)Param.P_FEEDBACK, x);
81106
Delay_SetParam(p[(int)Param.P_WET], (int)Param.P_WET, x);
@@ -86,11 +111,25 @@ public override void processBuffer(float[] buffer, double dspTime, int channels)
86111
input.processBuffer(buffer, dspTime, channels);
87112
}
88113

114+
cTime = cFeedback = 0;
115+
116+
if (cTimeInput != null)
117+
{
118+
cTimeInput.processBuffer(cTimeBuffer, dspTime, channels);
119+
cTime = cTimeBuffer[0];
120+
}
121+
122+
if (cFeedbackInput != null)
123+
{
124+
cFeedbackInput.processBuffer(cFeedbackBuffer, dspTime, channels);
125+
cFeedback = cFeedbackBuffer[0];
126+
}
127+
89128
//We need to process the delay even if there is currently no input,
90129
//bc there could be unconsumed samples from a previous input left in the delay line.
91130
//To optimize, we could store the timestamp when the last input connection was removed.
92131
//Then we only have to process if P_TIME is larger then the elapsed time since the connection was removed.
93-
Delay_Process(buffer, buffer.Length, channels, x);
132+
Delay_Process(buffer, buffer.Length, channels, cTime, cFeedback, x);
94133
}
95134
}
96135

Binary file not shown.

SoundStageNative/Delay.cpp

+36-26
Original file line numberDiff line numberDiff line change
@@ -19,35 +19,45 @@ enum DelayParams
1919
P_N
2020
};
2121

22-
/* TODO: Right now, if x->time < n, the delay will produce strange glitches. */
23-
void Delay_ProcessPitchShift(float buffer[], int n, int channels, DelayData* x)
22+
void Delay_ProcessPitchShift(float buffer[], int n, int channels, float cTime, float cFeedback, DelayData* x)
2423
{
25-
assert(x->time > 0);
26-
float stride = (float)x->maxTime / (float)x->time;
24+
/* Prepare */
25+
int time = cTime != 0 ? (x->maxTime - x->time) * cTime : x->time;
26+
float feedback = cFeedback != 0 ? (1.0f - x->feedback) * cFeedback : x->feedback;
2727

28-
if(x->time < n)
29-
{
30-
/* if the delay time is shorter than the buffer, then we should avoid writing samples we will not use anyway.
31-
This is especially important since short delay times resut in more oversampling and thus more CPU load...
32-
*/
33-
}
28+
assert(time > 0);
29+
30+
float stride = (float)x->maxTime / (float)time;
3431

3532
int nPerChannel = n/channels;
3633
if(channels > 1)
3734
_fDeinterleave(buffer, buffer, n, channels);
3835

39-
RingBuffer_ReadPadded(x->temp, nPerChannel, -(x->maxTime), stride, x->tap);
40-
_fCopy(x->temp, x->temp2, nPerChannel);
41-
42-
_fScale(x->temp, x->temp, x->feedback, nPerChannel);
43-
_fAdd(buffer, x->temp, x->temp, nPerChannel);
44-
RingBuffer_WritePadded(x->temp, nPerChannel, stride, x->tap);
36+
/* Process delay */
37+
int m;
38+
int r = nPerChannel;
39+
float* bufOffset = buffer;
40+
while(r)
41+
{
42+
m = time < r ? time : r;
43+
44+
RingBuffer_ReadPadded(x->temp, m, -(x->maxTime), stride, x->tap);
45+
_fCopy(x->temp, x->temp2, m);
46+
47+
_fScale(x->temp, x->temp, feedback, m);
48+
_fAdd(bufOffset, x->temp, x->temp, m);
49+
RingBuffer_WritePadded(x->temp, m, stride, x->tap);
4550

46-
_fScale(buffer, buffer, x->dry, nPerChannel);
47-
_fScale(x->temp2, x->temp2, x->wet, nPerChannel);
51+
_fScale(bufOffset, bufOffset, x->dry, m);
52+
_fScale(x->temp2, x->temp2, x->wet, m);
53+
54+
_fAdd(bufOffset, x->temp2, bufOffset, m);
4855

49-
_fAdd(buffer, x->temp2, buffer, nPerChannel);
56+
bufOffset += m;
57+
r -= m;
58+
}
5059

60+
/* Finalize */
5161
if(channels > 1)
5262
{
5363
for(int i = 1; i < channels; i++)
@@ -84,12 +94,17 @@ void Delay_ProcessSimple(float buffer[], int n, int channels, DelayData* x) {
8494
}
8595

8696

87-
SOUNDSTAGE_API void Delay_Process(float buffer[], int n, int channels, DelayData* x)
97+
SOUNDSTAGE_API void Delay_Process(float buffer[], int n, int channels, float cTime, float cFeedback, DelayData* x)
8898
{
89-
Delay_ProcessPitchShift(buffer, n, channels, x);
99+
Delay_ProcessPitchShift(buffer, n, channels, cTime, cFeedback, x);
90100
//Delay_ProcessSimple(buffer, n, channels, x);
91101
}
92102

103+
SOUNDSTAGE_API void Delay_Clear(DelayData* x)
104+
{
105+
_fZero(x->tap->buf, x->tap->n);
106+
}
107+
93108
SOUNDSTAGE_API void Delay_SetParam(float value, int param, struct DelayData *x)
94109
{
95110
assert(param < P_N);
@@ -99,11 +114,6 @@ SOUNDSTAGE_API void Delay_SetParam(float value, int param, struct DelayData *x)
99114
switch (param) {
100115
case P_TIME:
101116
assert(value <= x->tap->n);
102-
if(intval % 2 != 0)
103-
{
104-
//This is a quick hack to ensure the ringbuffer will not randomly switch left and right channels when used with interleaved audio data.
105-
value--;
106-
}
107117
x->time = intval;
108118
//printv("Set delay time to %d\n", intval);
109119
break;

SoundStageNative/Delay.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ SOUNDSTAGE_API void Delay_Free(struct DelayData *x);
2929
///Sets the parameter to the specified value.
3030
SOUNDSTAGE_API void Delay_SetParam(float value, int param, struct DelayData *x);
3131
///Processes 1 block of interleaved audio data.
32-
SOUNDSTAGE_API void Delay_Process(float buffer[], int n, int channels, DelayData* x);
32+
SOUNDSTAGE_API void Delay_Process(float buffer[], int n, int channels, float cTime, float cFeedback, DelayData* x);
33+
//Clears the delay buffer.
34+
SOUNDSTAGE_API void Delay_Clear(DelayData* x);
3335
#ifdef __cplusplus
3436
}
3537
#endif

SoundStageNative/RingBuffer.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ SOUNDSTAGE_API int RingBuffer_WritePadded(float *src, int n, float stride, struc
4848
//Wrap fPtr
4949
while(fPtr >= x->n) // >= bc max valid index is n-1
5050
fPtr -= x->n;
51-
while(m >= x->n) // > bc can write n samples max
51+
while(m > x->n) // > bc can write n samples max
5252
m -= x->n;
5353

5454
//printv("fPtr == %f, m == %d, x->ptr == %d\n", fPtr, m, x->ptr);

SoundStageNative/util.c

+25-15
Original file line numberDiff line numberDiff line change
@@ -143,15 +143,20 @@ extern "C" {
143143
/* Surprisingly, this version is a little faster than using vmulq_n_f32.
144144
But it uses one register more (float32x4_t scale).
145145
*/
146-
assert(n % 4 == 0);
147-
float32x4_t scale = vdupq_n_f32(factor);
148-
float32x4_t temp;
149-
for(int i = 0; i < n; i+= 4)
146+
if(n % 4 == 0)
150147
{
151-
temp = vld1q_f32(src + i);
152-
temp = vmulq_f32(temp, scale);
153-
vst1q_f32(src + i, temp);
148+
float32x4_t scale = vdupq_n_f32(factor);
149+
float32x4_t temp;
150+
for(int i = 0; i < n; i+= 4)
151+
{
152+
temp = vld1q_f32(src + i);
153+
temp = vmulq_f32(temp, scale);
154+
vst1q_f32(src + i, temp);
155+
}
154156
}
157+
else
158+
for(int i = 0; i < n; i++)
159+
dest[i] = factor * src[i];
155160
#else
156161
for(int i = 0; i < n; i++)
157162
{
@@ -196,16 +201,21 @@ extern "C" {
196201
/* 1.5x as fast as naive implementation (Onone) */
197202
/* TODO: naive implementation is twice as fast (O3) */
198203
/* A bit slower than naive implementation (Ofast). Interestingly, the Neon version gets a speedup from O3 to Ofast, while the naive implementation does not change. */
199-
assert(n % 4 == 0);
200-
float32x4_t temp1;
201-
float32x4_t temp2;
202-
for(int i = 0; i < n; i+=4)
204+
if(n % 4 == 0)
203205
{
204-
temp1 = vld1q_f32(src1 + i);
205-
temp2 = vld1q_f32(src2 + i);
206-
temp2 = vaddq_f32(temp1, temp2);
207-
vst1q_f32(dest + i, temp2);
206+
float32x4_t temp1;
207+
float32x4_t temp2;
208+
for(int i = 0; i < n; i+=4)
209+
{
210+
temp1 = vld1q_f32(src1 + i);
211+
temp2 = vld1q_f32(src2 + i);
212+
temp2 = vaddq_f32(temp1, temp2);
213+
vst1q_f32(dest + i, temp2);
214+
}
208215
}
216+
else
217+
for(int i = 0; i < n; i++)
218+
dest[i] = src1[i] + src2[i];
209219
#else
210220
for(int i = 0; i < n; i++)
211221
{

0 commit comments

Comments
 (0)