Skip to content

Commit

Permalink
Tentative allowing variable ticks per buffer
Browse files Browse the repository at this point in the history
This is a tentative implementation of variable ticks per buffer.  It's
currently for evaluation only and may or may not be folded back into the
main branch.
  • Loading branch information
nettoyeurny committed Oct 9, 2011
1 parent 1b13c02 commit 311aef1
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 72 deletions.
24 changes: 14 additions & 10 deletions java/org/puredata/core/PdBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,10 @@ public synchronized static void release() {
* @param inputChannels
* @param outputChannels
* @param sampleRate
* @param ticksPerBuffer
* number of pd ticks per process call
* @return error code, 0 on success
*/
public synchronized native static int openAudio(int inputChannels,
int outputChannels, int sampleRate, int ticksPerBuffer);
public synchronized native static int openAudio(
int inputChannels, int outputChannels, int sampleRate);

/**
* raw process callback, processes one pd tick, writes raw data to buffers
Expand All @@ -147,43 +145,49 @@ public synchronized native static int processRaw(float[] inBuffer,
* main process callback, reads samples from inBuffer and writes samples to
* outBuffer, using arrays of type short
*
* @param ticks
* the number of Pd ticks (i.e., blocks of 64 frames) to compute
* @param inBuffer
* must be an array of the right size, never null; use inBuffer =
* new short[0] if no input is desired
* @param outBuffer
* must be an array of size outBufferSize from openAudio call
* @return error code, 0 on success
*/
public synchronized native static int process(short[] inBuffer,
short[] outBuffer);
public synchronized native static int process(int ticks,
short[] inBuffer, short[] outBuffer);

/**
* main process callback, reads samples from inBuffer and writes samples to
* outBuffer, using arrays of type float
*
* @param ticks
* the number of Pd ticks (i.e., blocks of 64 frames) to compute
* @param inBuffer
* must be an array of the right size, never null; use inBuffer =
* new short[0] if no input is desired
* @param outBuffer
* must be an array of size outBufferSize from openAudio call
* @return error code, 0 on success
*/
public synchronized native static int process(float[] inBuffer,
float[] outBuffer);
public synchronized native static int process(int ticks,
float[] inBuffer, float[] outBuffer);

/**
* main process callback, reads samples from inBuffer and writes samples to
* outBuffer, using arrays of type double
*
* @param ticks
* the number of Pd ticks (i.e., blocks of 64 frames) to compute
* @param inBuffer
* must be an array of the right size, never null; use inBuffer =
* new short[0] if no input is desired
* @param outBuffer
* must be an array of size outBufferSize from openAudio call
* @return error code, 0 on success
*/
public synchronized native static int process(double[] inBuffer,
double[] outBuffer);
public synchronized native static int process(int ticks,
double[] inBuffer, double[] outBuffer);

/**
* @param name of the array in Pd
Expand Down
23 changes: 22 additions & 1 deletion javatests/org/puredata/core/PdBaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
/**
*
* Some tests for messaging and MIDI support in libpd. Basically, this class tests everything except
* the parts of libpd that matter. If you have any idea how to write tests for the signal processing
* the parts of libpd that matter. If you have any idea how to write real tests for the signal processing
* part, please let me know.
*
* @author Peter Brinkmann ([email protected])
Expand All @@ -39,8 +39,10 @@ public class PdBaseTest {

@BeforeClass
public static void loadPatch() throws IOException {
PdBase.openAudio(2, 3, 44100);
PdBase.subscribe("eggs");
patch = PdBase.openPatch("javatests/org/puredata/core/test_java.pd");
PdBase.computeAudio(true);
}

@AfterClass
Expand All @@ -60,6 +62,25 @@ public void setUp() {
public void tearDown() {
// Do nothing; just here for symmetry.
}

@Test
public void testAudio() {
float in[] = new float[256];
float out[] = new float[768];
for (int i = 0; i < 256; i++) {
in[i] = i;
}
int err = PdBase.process(2, in, out);
assertEquals(0, err);
for (int i = 0; i < 128; i++) {
assertEquals(2 * i, out[3 * i], 0.0001);
assertEquals(-6 * i, out[3 * i + 1], 0.0001);
assertEquals(Math.cos(2 * Math.PI * 440 / 44100 * i), out[3 * i + 2], 0.0001);
}
for (int i = 384; i < 768; i++) {
assertEquals(0, out[i], 0);
}
}

@Test
public void testBlockSize() {
Expand Down
12 changes: 10 additions & 2 deletions javatests/org/puredata/core/test_java.pd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#N canvas 170 303 379 331 10;
#N canvas 128 522 379 331 10;
#X obj 18 16 r spam;
#X obj 18 37 s eggs;
#X obj 19 72 notein;
Expand All @@ -16,10 +16,14 @@
#X obj 91 18 r foo;
#X obj 91 39 print;
#X obj 160 57 - 8192;
#N canvas 0 0 450 300 (subpatch) 0;
#N canvas 0 22 450 300 (subpatch) 0;
#X array array1 128 float 2;
#X coords 0 1 127 -1 200 140 1;
#X restore 16 153 graph;
#X obj 245 172 adc~;
#X obj 260 201 *~ -3;
#X obj 247 231 dac~ 1 2 3;
#X obj 304 170 osc~ 440;
#X connect 0 0 1 0;
#X connect 2 0 3 0;
#X connect 2 1 3 1;
Expand All @@ -38,3 +42,7 @@
#X connect 12 2 13 2;
#X connect 14 0 15 0;
#X connect 16 0 9 0;
#X connect 18 0 19 0;
#X connect 18 0 20 0;
#X connect 19 0 20 1;
#X connect 21 0 20 2;
27 changes: 14 additions & 13 deletions libpd_wrapper/z_jni.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,11 +243,9 @@ JNIEXPORT void JNICALL Java_org_puredata_core_PdBase_addToSearchPath
}

JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_openAudio
(JNIEnv *env, jclass cls, jint inChans, jint outChans,
jint srate, jint ticksPerBuffer) {
(JNIEnv *env, jclass cls, jint inChans, jint outChans, jint srate) {
CACHE_ENV
return libpd_init_audio((int) inChans, (int) outChans,
(int) srate, (int) ticksPerBuffer);
return libpd_init_audio((int) inChans, (int) outChans, (int) srate);
}

JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_processRaw
Expand All @@ -264,43 +262,46 @@ JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_processRaw
return err;
}

JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process___3S_3S
(JNIEnv *env, jclass cls, jshortArray inBuffer, jshortArray outBuffer) {
JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process__I_3S_3S
(JNIEnv *env, jclass cls, jint ticks,
jshortArray inBuffer, jshortArray outBuffer) {
if (inBuffer == NULL || outBuffer == NULL) {
return -10;
}
CACHE_ENV
short *pIn = (*env)->GetShortArrayElements(env, inBuffer, NULL);
short *pOut = (*env)->GetShortArrayElements(env, outBuffer, NULL);
jint err = libpd_process_short(pIn, pOut);
jint err = libpd_process_short((int) ticks, pIn, pOut);
(*env)->ReleaseShortArrayElements(env, inBuffer, pIn, 0);
(*env)->ReleaseShortArrayElements(env, outBuffer, pOut, 0);
return err;
}

JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process___3F_3F
(JNIEnv *env, jclass cls, jfloatArray inBuffer, jfloatArray outBuffer) {
JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process__I_3F_3F
(JNIEnv *env, jclass cls, jint ticks,
jfloatArray inBuffer, jfloatArray outBuffer) {
if (inBuffer == NULL || outBuffer == NULL) {
return -10;
}
CACHE_ENV
float *pIn = (*env)->GetFloatArrayElements(env, inBuffer, NULL);
float *pOut = (*env)->GetFloatArrayElements(env, outBuffer, NULL);
jint err = libpd_process_float(pIn, pOut);
jint err = libpd_process_float((int) ticks, pIn, pOut);
(*env)->ReleaseFloatArrayElements(env, inBuffer, pIn, 0);
(*env)->ReleaseFloatArrayElements(env, outBuffer, pOut, 0);
return err;
}

JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process___3D_3D
(JNIEnv *env, jclass cls, jdoubleArray inBuffer, jdoubleArray outBuffer) {
JNIEXPORT jint JNICALL Java_org_puredata_core_PdBase_process__I_3D_3D
(JNIEnv *env, jclass cls, jint ticks,
jdoubleArray inBuffer, jdoubleArray outBuffer) {
if (inBuffer == NULL || outBuffer == NULL) {
return -10;
}
CACHE_ENV
double *pIn = (*env)->GetDoubleArrayElements(env, inBuffer, NULL);
double *pOut = (*env)->GetDoubleArrayElements(env, outBuffer, NULL);
jint err = libpd_process_double(pIn, pOut);
jint err = libpd_process_double((int) ticks, pIn, pOut);
(*env)->ReleaseDoubleArrayElements(env, inBuffer, pIn, 0);
(*env)->ReleaseDoubleArrayElements(env, outBuffer, pOut, 0);
return err;
Expand Down
22 changes: 11 additions & 11 deletions libpd_wrapper/z_jni.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 5 additions & 8 deletions libpd_wrapper/z_libpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ t_libpd_aftertouchhook libpd_aftertouchhook = NULL;
t_libpd_polyaftertouchhook libpd_polyaftertouchhook = NULL;
t_libpd_midibytehook libpd_midibytehook = NULL;

static int ticks_per_buffer;

static t_atom *argv = NULL, *curr;
static int argm = 0, argc;

Expand Down Expand Up @@ -83,10 +81,9 @@ void libpd_add_to_search_path(const char *s) {
sys_searchpath = namelist_append(sys_searchpath, s, 0);
}

int libpd_init_audio(int inChans, int outChans, int sampleRate, int tpb) {
int libpd_init_audio(int inChans, int outChans, int sampleRate) {
int indev[MAXAUDIOINDEV], inch[MAXAUDIOINDEV],
outdev[MAXAUDIOOUTDEV], outch[MAXAUDIOOUTDEV];
ticks_per_buffer = tpb;
indev[0] = outdev[0] = DEFAULTAUDIODEV;
inch[0] = inChans;
outch[0] = outChans;
Expand Down Expand Up @@ -119,7 +116,7 @@ static const t_sample sample_to_short = SHRT_MAX,
#define PROCESS(_x, _y) \
int i, j, k; \
t_sample *p0, *p1; \
for (i = 0; i < ticks_per_buffer; i++) { \
for (i = 0; i < ticks; i++) { \
for (j = 0, p0 = sys_soundin; j < DEFDACBLKSIZE; j++, p0++) { \
for (k = 0, p1 = p0; k < sys_inchannels; k++, p1 += DEFDACBLKSIZE) { \
*p1 = *inBuffer++ _x; \
Expand All @@ -135,15 +132,15 @@ static const t_sample sample_to_short = SHRT_MAX,
} \
return 0;

int libpd_process_short(short *inBuffer, short *outBuffer) {
int libpd_process_short(int ticks, short *inBuffer, short *outBuffer) {
PROCESS(* short_to_sample, * sample_to_short)
}

int libpd_process_float(float *inBuffer, float *outBuffer) {
int libpd_process_float(int ticks, float *inBuffer, float *outBuffer) {
PROCESS(,)
}

int libpd_process_double(double *inBuffer, double *outBuffer) {
int libpd_process_double(int ticks, double *inBuffer, double *outBuffer) {
PROCESS(,)
}

Expand Down
8 changes: 4 additions & 4 deletions libpd_wrapper/z_libpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ void libpd_closefile(void *p);
int libpd_getdollarzero(void *p);

int libpd_blocksize(void);
int libpd_init_audio(int inChans, int outChans, int sampleRate, int tpb);
int libpd_init_audio(int inChans, int outChans, int sampleRate);
int libpd_process_raw(float *inBuffer, float *outBuffer);
int libpd_process_short(short *inBuffer, short *outBuffer);
int libpd_process_float(float *inBuffer, float *outBuffer);
int libpd_process_double(double *inBuffer, double *outBuffer);
int libpd_process_short(int ticks, short *inBuffer, short *outBuffer);
int libpd_process_float(int ticks, float *inBuffer, float *outBuffer);
int libpd_process_double(int ticks, double *inBuffer, double *outBuffer);

int libpd_arraysize(const char *name);
// The parameters of the next two functions are inspired by memcpy.
Expand Down
2 changes: 2 additions & 0 deletions objc/PdAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

@interface PdAudio : NSObject {
AudioUnit audioUnit;
int ticksPerBuffer;
int numInputChannels;
int numOutputChannels;
Float64 sampleRate;
Expand All @@ -49,6 +50,7 @@
}

@property (nonatomic, readonly) AudioUnit audioUnit;
@property (nonatomic, readonly) int ticksPerBuffer;
@property (nonatomic, readonly) int numInputChannels;
@property (nonatomic, readonly) int numOutputChannels;
@property (nonatomic, readonly) Float64 sampleRate;
Expand Down
6 changes: 4 additions & 2 deletions objc/PdAudio.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ @implementation PdAudio

@synthesize audioUnit;
@synthesize sampleRate;
@synthesize ticksPerBuffer;
@synthesize numInputChannels;
@synthesize numOutputChannels;
@synthesize microphoneVolume;
Expand Down Expand Up @@ -106,7 +107,7 @@ OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlag
for (int i = 0; i < floatBufferLength; i++) {
floatBuffer[i] = ((float) shortBuffer[i]) * a;
}
[PdBase processFloatWithInputBuffer:floatBuffer andOutputBuffer:floatBuffer];
[PdBase processFloatWithInputBuffer:floatBuffer andOutputBuffer:floatBuffer andTicks:controller.ticksPerBuffer];
for (int i = 0; i < floatBufferLength; i++) {
float f = floatBuffer[i];
if (f < -1.0f) shortBuffer[i] = -32767;
Expand Down Expand Up @@ -169,6 +170,7 @@ - (id)initWithSampleRate:(float)newSampleRate andTicksPerBuffer:(int)ticks
self = [super init];
if (self != nil) {
audioUnit = NULL;
ticksPerBuffer = ticks;
numInputChannels = inputChannels;
numOutputChannels = outputChannels;
sampleRate = (Float64) newSampleRate;
Expand All @@ -184,7 +186,7 @@ - (id)initWithSampleRate:(float)newSampleRate andTicksPerBuffer:(int)ticks
FAIL_ON_ERROR(@"init audio unit failed with status %ld", [self initializeAudioUnit]);
FAIL_ON_ERROR(@"PdBase openAudio failed wit status %ld",
[PdBase openAudioWithSampleRate:sampleRate andInputChannels:numInputChannels
andOutputChannels:numOutputChannels andTicksPerBuffer:ticks]);
andOutputChannels:numOutputChannels]);

[PdBase computeAudio:YES];
}
Expand Down
8 changes: 4 additions & 4 deletions objc/PdBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@
+ (int)getBlockSize;
+ (BOOL)exists:(NSString *)symbol;
+ (int)openAudioWithSampleRate:(int)samplerate andInputChannels:(int)inputChannels
andOutputChannels:(int)outputchannels andTicksPerBuffer:(int)ticksPerBuffer;
+ (int)processFloatWithInputBuffer:(float *)inputBuffer andOutputBuffer:(float *)outputBuffer;
+ (int)processDoubleWithInputBuffer:(double *)inputBuffer andOutputBuffer:(double *)outputBuffer;
+ (int)processShortWithInputBuffer:(short *)inputBuffer andOutputBuffer:(short *)outputBuffer;
andOutputChannels:(int)outputchannels;
+ (int)processFloatWithInputBuffer:(float *)inputBuffer andOutputBuffer:(float *)outputBuffer andTicks:(int)ticks;
+ (int)processDoubleWithInputBuffer:(double *)inputBuffer andOutputBuffer:(double *)outputBuffer andTicks:(int)ticks;
+ (int)processShortWithInputBuffer:(short *)inputBuffer andOutputBuffer:(short *)outputBuffer andTicks:(int)ticks;
+ (void)computeAudio:(BOOL)enable;
+ (void *)openFile:(NSString *)baseName path:(NSString *)pathName;
+ (void)closeFile:(void *)x;
Expand Down
Loading

0 comments on commit 311aef1

Please sign in to comment.