Good idea. Should be straightforward, very little code.
You’ve basically got two pointers to each frame in the buffer (actually 4 but…). Just average adjacent values, perhaps adding some follow-up gain with the depth parameter.
I haven’t done much with the SDK, so far just a very crude wavefolding mod effect.
I can have a look if you need a code example.
1 Like
That would be nice. Since I usually can adjust input volume on the monosynths themselves, I thought to use the NTS-1 knobs for pan L and pan R. This makes the averaging computation slightly more complicated but still quite simple.
This should work as your main.c
Sorry not sure how to format this correctly. 
//
#include “usermodfx.h”
static float gain;
void MODFX_INIT(uint32_t platform, uint32_t api)
{
gain = 1.0;
}
void MODFX_PROCESS(const float *main_xn, float *main_yn,
const float *sub_xn, float *sub_yn,
uint32_t frames)
{
for (uint32_t i = 0; i < frames; i++)
{
float inL = main_xn[i*2];
float inR = main_xn[i*2 + 1];
main_yn[i*2] = (inL+inR) * gain;
main_yn[i*2+1] = main_yn[i*2]
}
}
void MODFX_PARAM(uint8_t index, int32_t value)
{
const float valf = q31_to_f32(value);
switch (index)
{
case k_user_modfx_param_time:
break;
case k_user_modfx_param_depth:
gain = valf;
break;
default:
break;
}
}
2 Likes
Thanks! I think I can see how to use the knobs as I want. I’ll give it a try. (The code isn’t indentation-sensitive but I can format it properly anyway.)
1 Like
Yeah linear panning will be fairly easy and a good coding exercise.
Other pan laws for the advENTURous… 
https://www.cs.cmu.edu/~music/icm-online/readings/panlaws/
Sorry, just realised you didn’t want to sum to mono… but you get the gist.
1 Like
My wavefolder below …
Watch out for the depth (B) parameter (out level). (A) is pre-fold gain.
wavefolder.ntkdigunit (994 Bytes)
Okay, that wasn’t so bad, considering I haven’t written C in about eight years. Way more time futzing with the toolchain than actually programming/debugging. Here’s the dual mono panner in case anyone wants it. (It implements constant power panning.)
dmpan.ntkdigunit (3.8 KB)
4 Likes
And, for illustrative purposes, the code.
//
#include "usermodfx.h"
#include <math.h>
static float lpan,rpan;
void MODFX_INIT(uint32_t platform, uint32_t api)
{
lpan = 0.5;
rpan = 0.5;
}
void MODFX_PROCESS(const float *main_xn, float *main_yn,
const float *sub_xn, float *sub_yn,
uint32_t frames)
{
for (uint32_t i = 0; i < frames; i++)
{
float inL = main_xn[i*2];
float inR = main_xn[i*2 + 1];
main_yn[i*2] = (inL * sinf (lpan * PI / 2)) + (inR * sinf (rpan * PI / 2));
main_yn[i*2+1] = (inL * cosf (lpan * PI / 2)) + (inR * cosf (rpan * PI / 2));
}
}
void MODFX_PARAM(uint8_t index, int32_t value)
{
const float valf = q31_to_f32(value);
switch (index)
{
case k_user_modfx_param_time:
lpan = valf;
break;
case k_user_modfx_param_depth:
rpan = valf;
break;
default:
break;
}
}
5 Likes