ofelia lua table and a few questions
second try. i commented out the lines where i am not sure if they are a methods (i am quite new to programming except for pure data and a little bit of python). there are only "public:" methods in the list.
class Canvas
// Canvas(t_symbol *s)
// Canvas(t_symbol *s, t_floatarg f)
t_symbol *realizeDollar(t_symbol *s)
t_symbol *getName()
int getIndex()
void getArgs(int *argcp, t_atom **argvp, t_canvas **canvasp)
void setArgs(int argc, t_atom *argv)
void getPosition(int **posp)
void setPosition(int xpos, int ypos)
t_symbol *getDir()
void remove()
class Send
// Send(t_symbol *s)
void sendBang()sendFloat(t_floatarg f)
void sendSymbol(t_symbol *s)
void sendPointer(t_gpointer *p)
void sendList(int argc, t_atom *argv)
void sendAnything(int argc, t_atom *argv)
class Inlet
// Inlet(t_symbol *s)
void setFloatInlet(t_floatarg f)
void setFloatInlets(int n, t_floatarg *f)
void setSignalInlet(t_floatarg f)
class Outlet
// Outlet(t_symbol *s)
void outletBang(int index)
void outletFloat(int index, t_floatarg f)
void outletSymbol(int index, t_symbol *s)
void outletPointer(int index, t_gpointer *p)
void outletList(int index, int argc, t_atom *argv)
void outletAnything(int index, int argc, t_atom *argv)
class Value
// Value(t_symbol *s)
virtual ~Value()
t_float get()
void set(t_floatarg f)
class Array
// Array(t_symbol *s)
bool exists(t_garray **a)
float getAt(int n)
float getAt(int n)
void getTable(t_word **vecp, int *sizep)
void setTable(int n, t_floatarg *f)
int getSize()
void setSize(long n)
class Clock
// Clock(t_symbol *s)
// Clock(t_symbol *s, t_symbol *s2)
virtual ~Clock()
void delay(double delayTime)
void unset()
class Sys
double getRealTime()
void lock()
void unlock()
int tryLock()
void gui(t_symbol *s)
class Signal
int getBlockSize()
t_float getSampleRate()
int getInChannels()
int getOutChannels()
bool getDspState()
class PD
int getMaxString()
int getFloatSize()
t_float getMinFloat()
t_float getMaxFloat()
bool isBadFloat(t_floatarg f)
bool isBigOrSmall(t_floatarg f)
tuple<int, int, int> getVersion()
// int maxString;
// int floatSize;
// t_float minFloat;
// t_float maxFloat;
// tuple<int, int, int> version;
class Log
void post(const char *s)
void post(const char *s, int level)
void startPost(const char *s)
void postString(const char *s)
void postFloat(t_floatarg f)
void postAtom(int argc, t_atom *argv)
void endPost()
void error(const char *s)
ofelia lua table and a few questions
@cuinjune i took a look into ofeliaBindings.h and i tried to make a list of the classes and methods that call internal pd methods.
i am not sure if it is correct / complete but at least it is an orientation for me.
class Canvas
int getIndex()
void getArgs(int *argcp, t_atom **argvp, t_canvas **canvasp)
void setArgs(int argc, t_atom *argv)
void getPosition(int **posp)
void setPosition(int xpos, int ypos)
void remove()
class Send
void sendBang()sendFloat(t_floatarg f)
void sendSymbol(t_symbol *s)
void sendPointer(t_gpointer *p)
void sendList(int argc, t_atom *argv)
void sendAnything(int argc, t_atom *argv)
class Inlet
void setFloatInlet(t_floatarg f)
void setFloatInlets(int n, t_floatarg *f)
void setSignalInlet(t_floatarg f)
class Outlet
void outletBang(int index)
void outletFloat(int index, t_floatarg f)
void outletSymbol(int index, t_symbol *s)
void outletPointer(int index, t_gpointer *p)
void outletList(int index, int argc, t_atom *argv)
void outletAnything(int index, int argc, t_atom *argv)
class Value
void set(t_floatarg f)
class Array
float getAt(int n)
float getAt(int n)
void getTable(t_word **vecp, int *sizep)
void setTable(int n, t_floatarg *f)
int getSize()
void setSize(long n)
class Clock
void delay(double delayTime)
void unset()
class Sys
double getRealTime()
void lock()
void unlock()
int tryLock()
void gui(t_symbol *s)
class PD
int getMaxString()
int getFloatSize()
t_float getMinFloat()
t_float getMaxFloat()
bool isBadFloat(t_floatarg f)
bool isBigOrSmall(t_floatarg f)
tuple<int, int, int> getVersion()
TimbreID On Raspberry Pi
Compiling only TimbreId against pd (not compiling pd source).
To load timbreIDLib, list the path to the timbreIDLib library file in Pd's startup dialog (e.g., /home/yourname/pd_libs/timbreID/timbreIDLib).
timbreID version 0.7 requires the FFTW library, available at http://www.fftw.org.
FFTW is included pre-compiled with timbreID's Windows binary package available through deken. It's fine to simply leave libfftw3f-3.dll in the timbreID directory for use as a shared library. For Linux and Macintosh, FFTW is statically linked with the timbreIDLib library file, so there is no need for compiling or obtaining FFTW.
If you are compliling FFTW yourself, it must be compiled in single precision. To do so in Linux, configure FFTW like this:
./configure CFLAGS="-fPIC" --enable-float
and like this on a Macintosh:
./configure CFLAGS="-arch i386 -arch x86_64" --enable-float
Then run:
make
sudo make install
The FFTW library for Windows is available precompiled at:
http://www.fftw.org/install/windows.html
You will need the 32-bit version, and the single precision version specifically. The provided zip file contains several compiled versions of FFTW, but only libfftw3f-3.dll is required for timbreID version 0.7.
On Linux and Macintosh, the FFTW library files should be installed to /usr/local/lib by default. Once FFTW is properly built and installed, you can make timbreID using the included Makefile by running:
make
You must specify the location of your Pure Data source code directory in the Makefile beforehand. Compilation from source on Windows can be done with the same Makefile if you use MinGW: http://www.mingw.org
On Linux and Macintosh, timbreIDLib will statically link the FFTW library. On Windows, you will either have to set up an environment variable to point to the location of libfftw3f-3.dll, or simply put libfftw3f-3.dll directly in the timbreID directory.
Cheers~
Matrices and reallocating memory
I tried to find all my errors, corrected them and now it seemes to work.
(The method init_array is still missing a check if allocations failed.)
Do I really have to free the colums before reallocating the rows? Isn't it the other way around?
#include "m_pd.h"
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
static t_class *average_tilde_class;
typedef struct _average_tilde {
t_object x_obj;
t_int len_avg;
t_int block_size;
t_int pos;
t_sample **sampel_arr;
t_sample *avg;
t_inlet* x_in;
t_outlet* x_out;
} t_average_tilde;
void average_tilde_free (t_average_tilde *x)
{
// Deallocate rows
for (int i = 0; i < x->len_avg; ++i)
free(x->sampel_arr[i]);
// Deallocate columns
free(x->sampel_arr);
// Deallocate avg
free(x->avg);
}
t_int *average_tilde_perform(t_int *w)
{
t_average_tilde *x = (t_average_tilde *)(w[1]);
t_sample *in = (t_sample *)(w[2]);
t_sample *out = (t_sample *)(w[3]);
//int block = (int)(w[4]);
t_sample val;
for (int n = 0; n < x->block_size; n++) {
x->avg[n] -= x->sampel_arr[x->pos][n];
val = in[n] / x->block_size;
x->avg[n] += val;
x->sampel_arr[x->pos][n] = val;
*out++ = x->avg[n];
}
x->pos++;
if (x->pos == x->len_avg) x->pos = 0;
return (w + 5);
}
void resize_avg_array(t_average_tilde *x, int len_avg_new)
{
int i,j;
int success = 1;
t_sample **temp = NULL;
t_sample *temp2 = NULL;
do {
success = 1;
// Allocate the columns
temp = realloc(temp, len_avg_new * sizeof(t_sample*));
if (temp == NULL) {
len_avg_new--;
free(temp);
success = 0;
}
else {
// The new column's pointer must be initialised to NULL
for (i = 0; i < len_avg_new; i++)
temp[i] = NULL;
// Allocate the rows
for (i = 0; i < len_avg_new; i++) {
temp[i] = realloc(temp[i], x->block_size * sizeof(t_sample));
if (temp[i] == NULL) {
len_avg_new--;
success = 0;
break;
}
else {
// Initialize the element(s)
for (j = 0; j < x->block_size; j++)
temp[i][j] = 0.0;
}
}
if (success == 1) {
// Initialize avg-array
temp2 = realloc(temp2, x->block_size * sizeof(t_sample));
if (temp2 == NULL) {
len_avg_new--;
success = 0;
free(temp2);
}
else {
// Initialize the element(s)
for (i = 0; i < x->block_size; i++)
temp2[i] = 0.0;
}
if (success == 1) {
// Deallocate rows
for (i = 0; i < x->len_avg; ++i)
free(x->sampel_arr[i]);
// Deallocate columns
free(x->sampel_arr);
// Copy temps to arrays
x->len_avg = len_avg_new;
x->sampel_arr = temp;
free(x->avg);
x->avg = temp2;
x->pos = 0;
}
}
}
} while (success = 0 && len_avg_new > 0);
if (success = 0) {
post("length of avg-vector stayed at %i samples", x->len_avg);
}
}
void average_tilde_dsp(t_average_tilde *x, t_signal **sp)
{
x->block_size = sp[0]->s_n;
float arr_size = sizeof(x->sampel_arr) / sizeof(x->sampel_arr[0][0]);
if (x->block_size * x->len_avg != arr_size)
resize_avg_array(x, 10);
dsp_add(average_tilde_perform, 4,
x,
sp[0]->s_vec,
sp[1]->s_vec,
sp[0]->s_n);
}
void set_len_avg(t_average_tilde *x, t_floatarg f)
{
if ((int)f > 0)
resize_avg_array(x, f);
}
void init_array(t_average_tilde *x)
{
int i = 0, j = 0;
// Allocate the columns
x->sampel_arr = (t_sample**)calloc(x->len_avg, sizeof(t_sample*));
// Allocate the rows
for (i = 0; i < x->len_avg; i++)
x->sampel_arr[i] = (t_sample*)calloc(x->block_size, sizeof(t_sample));
// Initialize the element(s)
for (i = 0; i < x->len_avg; i++)
for (j = 0; j < x->block_size; j++)
x->sampel_arr[i][j] = 0.0;
// Initialize avg-array
x->avg = realloc(x->avg, x->block_size * sizeof(t_sample));
for (j = 0; j < x->block_size; j++)
x->avg[j] = 0.0;
}
void *average_tilde_new(t_floatarg f)
{
t_average_tilde *x = (t_average_tilde *)pd_new(average_tilde_class);
// initialize values with defaults
x->len_avg = ((int)f > 0) ? (int)f : 10;
x->block_size = 64;
x->pos = 0;
init_array(x);
x->x_out = outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
void init_average(void) {
average_tilde_class = class_new(gensym("r_avg~"),
(t_newmethod)average_tilde_new,
(t_method)average_tilde_free,
sizeof(t_average_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(average_tilde_class,
(t_method)average_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(average_tilde_class, t_average_tilde, len_avg);
class_addfloat(average_tilde_class, set_len_avg);
}
void helloworld_setup(void) {
init_average();
}
EDIT: Corrected some logical errors in the code.
The variable success has to be set to 1 in each do while loop
The arrays x->sampel_arr and x->avg have to be freed inside the do while loop
Matrices and reallocating memory
EDIT:
The original thread title was: Interrupt/disable perform method while reallocating memory
I changed it, as it was based on searching for a solution caused by faulty code.
After discarding my code the topic of the thread got a new direction.
Hi,
I try to write an external, which calculates the averages of samples at their positions over time.
For example:
With average time = 3:
3 signals given over 3 blocks: [1, 3, 5 ...], [2, 4, 6...],[1, 3, 5...] should result in [1.33, 3,33, 5,33, ...]
The example code below works, as long as I don't try to resize the average time to a lower value while dsp is running.
On changing the average time while dsp is on, my code reallocates memory for the average vector. The result is an exception in the perform method in line 53 x->avg[n] -= x->sampel_arr[x->pos][n];
The commented lines using x->busy were an attempt to use a switch. But the code of the switch is never used, so it doesn't work.
Does anybody have an idea how to bypass the perform method while memory is reallocated?
#include "m_pd.h"
#include <string.h>
#include <stdlib.h>
static t_class *average_tilde_class;
typedef struct _average_tilde {
t_object x_obj;
t_int len_delay;
t_int block_size;
t_int pos;
t_sample **sampel_arr;
t_float *avg;
t_int busy;
t_inlet* x_in;
t_outlet* x_out;
} t_average_tilde;
void average_tilde_free(t_average_tilde *x)
{
// Deallocate rows
for (int i = 0; i < x->len_delay; ++i)
free(x->sampel_arr[i]);
// Deallocate columns
free(x->sampel_arr);
// Deallocate avg
free(x->avg);
}
t_int *average_tilde_perform(t_int *w)
{
t_average_tilde *x = (t_average_tilde *)(w[1]);
t_sample *in = (t_sample *)(w[2]);
t_sample *out = (t_sample *)(w[3]);
int block = (int)(w[4]);
/*if (x->busy == 1) {
while (block--) {
*out++ = *in++;
}
return (w + 5);
};*/
float val;
for (int n = 0; n < x->block_size; n++) {
x->avg[n] -= x->sampel_arr[x->pos][n];
val = in[n] / x->block_size;
x->avg[n] += val;
x->sampel_arr[x->pos][n] = val;
*out++ = x->avg[n];
}
x->pos++;
if (x->pos == x->len_delay) x->pos = 0;
return (w + 5);
}
void resize_avg_array(t_average_tilde *x)
{
int i,j;
/*post("resize");
x->busy = 1;*/
// Allocate the columns
x->sampel_arr = (int**)realloc(x->sampel_arr, x->len_delay * sizeof(int*));
/*if (x->sampel_arr == NULL)
post("error");*/
// The new column's pointer must be initialised to NULL
for (i = 0; i < x->len_delay; i++)
x->sampel_arr[i] = NULL;
// Allocate the rows
for (i = 0; i < x->len_delay; i++) {
x->sampel_arr[i] = (float*)realloc(x->sampel_arr[i], x->block_size * sizeof(float));
/*if (x->sampel_arr == NULL)
post("error");*/
}
// Initialize the element(s)
for (i = 0; i < x->len_delay; i++)
for (j = 0; j < x->block_size; j++)
x->sampel_arr[i][j] = 0.0;
// Allocate avg-array
x->avg = realloc(x->avg, x->block_size * sizeof(float));
/*if (x->avg == NULL)
post("error");*/
// Initialize avg-array
for (i = 0; i < x->block_size; i++)
x->avg[i] = 0.0;
/*x->busy = 0;
post("resize2");
post(" ");*/
}
void average_tilde_dsp(t_average_tilde *x, t_signal **sp)
{
x->block_size = sp[0]->s_n;
float arr_size = sizeof(x->sampel_arr) / sizeof(x->sampel_arr[0][0]);
if (x->block_size * x->len_delay != arr_size)
resize_avg_array(x);
dsp_add(average_tilde_perform, 4,
x,
sp[0]->s_vec,
sp[1]->s_vec,
sp[0]->s_n);
}
void set_len_delay(t_average_tilde *x, t_floatarg f)
{
if ((int)f > 0) x->len_delay = (int)f;
resize_avg_array(x);
}
void init_array(t_average_tilde *x)
{
int i = 0, j = 0;
// Allocate the columns
x->sampel_arr = (int**)calloc(x->len_delay, sizeof(int*));
// Allocate the rows
for (i = 0; i < x->len_delay; i++)
x->sampel_arr[i] = (float*)calloc(x->block_size, sizeof(float));
// Initialize the element(s)
for (i = 0; i < x->len_delay; i++)
for (j = 0; j < x->block_size; j++)
x->sampel_arr[i][j] = 0.0;
// Initialize avg-array
x->avg = realloc(x->avg, x->block_size * sizeof(float));
for (j = 0; j < x->block_size; j++)
x->avg[j] = 0.0;
}
void *average_tilde_new(t_floatarg f)
{
t_average_tilde *x = (t_average_tilde *)pd_new(average_tilde_class);
// initialize values with defaults
x->len_delay = ((int)f > 0) ? (int)f : 10;
x->block_size = 64;
x->pos = 0;
init_array(x);
x->busy = 0;
x->x_out = outlet_new(&x->x_obj, &s_signal);
return (void *)x;
}
void init_average(void) {
average_tilde_class = class_new(gensym("r_avg~"),
(t_newmethod)average_tilde_new,
(t_method)average_tilde_free,
sizeof(t_average_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(average_tilde_class,
(t_method)average_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(average_tilde_class, t_average_tilde, len_delay);
class_addfloat(average_tilde_class, set_len_delay);
}
void helloworld_setup(void) {
init_average();
}```
// The code for reallocation is taken from here:
// http://hesham-rafi.blogspot.de/2009/02/resize-2d-matrix-using-realloc.html
send list to outlet in external
Hi,
I try to write an external, which sends a list to an outlet.
The following code is an example of calculating the maximum of a signal and send the maximum and it's index to the outlet.
But line 30 outlet_float(x->f_out, (max[0], max[1]) ); does only send one value to the outlet.
How can I achieve to send lists?
Thanks for help in advance,
Xaver
#include "m_pd.h"
static t_class *maximum_tilde_class;
typedef struct _maximum_tilde {
t_object x_obj;
t_sample d;
t_inlet*x_in1;
t_outlet*f_out;
} t_maximum_tilde;
t_int *maximum_tilde_perform(t_int *w)
{
t_maximum_tilde *x = (t_maximum_tilde *)(w[1]);
t_sample *in1 = (t_sample *)(w[2]);
int n = (int)(w[3]);
float max[] = { 0,-1 };
float abs_val = 0;
for (int i = 0; i < n; i++) {
abs_val = fabs(in1[i]);
if (abs_val > max[0]) {
max[0] = abs_val;
max[1] = i;
}
}
outlet_float(x->f_out, (max[0], max[1]) );
return (w + 4);
}
void maximum_tilde_dsp(t_maximum_tilde *x, t_signal **sp)
{
dsp_add(maximum_tilde_perform, 3,
x,
sp[0]->s_vec, data forum
sp[0]->s_n);
}
void maximum_tilde_free(t_maximum_tilde *x)
{
inlet_free(x->x_in1);
}
void *maximum_tilde_new(t_floatarg f)
{
t_maximum_tilde *x = (t_maximum_tilde *)pd_new(maximum_tilde_class);
x->f_out = outlet_new(&x->x_obj, &s_list);
return (void *)x;
}
void init_maximum(void) {
maximum_tilde_class = class_new(gensym("max~"),
(t_newmethod)maximum_tilde_new,
0,//(t_method)maximum_tilde_free,
sizeof(t_maximum_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(maximum_tilde_class,
(t_method)maximum_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(maximum_tilde_class, t_maximum_tilde, d);
}
void helloworld_setup(void) {
init_maximum();
}
use of threads for i²c I/O external : looking for a good strategy
@nau Hi, same boat (I don't know much about Pd internal functions & pthread), but maybe you can try to see if this external (really similar to my template, but this time to fetch real data for my HiCu project).
Look for m_clock / m_interval and clock_delay.
// ==============================================================================
// gac.c
//
// pd-Interface to [ 11h11 | gac ]
// Adapted by: Patrick Sebastien Coulombe
// Website: http://www.workinprogress.ca/guitare-a-crayon
//
// Original Author: Michael Egger
// Copyright: 2007 [ a n y m a ]
// Website: http://gnusb.sourceforge.net/
//
// License: GNU GPL 2.0 www.gnu.org
// Version: 2009-04-11
// ==============================================================================
// ==============================================================================
#include "m_pd.h"
#include <usb.h> //http://libusb-win32.sourceforge.net
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pthread.h"
#include "../common/gac_cmds.h"
// ==============================================================================
// Constants
// ------------------------------------------------------------------------------
#define USBDEV_SHARED_VENDOR 0x16c0 /* VOTI */
#define USBDEV_SHARED_PRODUCT 0x05dc /* Obdev's free shared PID */
#define DEFAULT_CLOCK_INTERVAL 34 /* ms */
#define OUTLETS 11
#define USBREPLYBUFFER 14
unsigned char buffer[USBREPLYBUFFER]; //accessible everywhere
// ==============================================================================
// Our External's Memory structure
// ------------------------------------------------------------------------------
typedef struct _gac // defines our object's internal variables for each instance in a patch
{
t_object p_ob; // object header - ALL pd external MUST begin with this...
usb_dev_handle *dev_handle; // handle to the gac usb device
void *m_clock; // handle to our clock
double m_interval; // clock interval for polling edubeat
double m_interval_bak; // backup clock interval for polling edubeat
int is_running; // is our clock ticking?
void *outlets[OUTLETS]; // handle to the objects outlets
int x_verbose;
pthread_attr_t gac_thread_attr;
pthread_t x_threadid;
} t_gac;
void *gac_class; // global pointer to the object class - so pd can reference the object
// ==============================================================================
// Function Prototypes
// ------------------------------------------------------------------------------
void *gac_new(t_symbol *s);
void gac_assist(t_gac *x, void *b, long m, long a, char *s);
void gac_bang(t_gac *x);
void gac_bootloader(t_gac *x);
static int usbGetStringAscii(usb_dev_handle *dev, int ndex, int langid, char *buf, int buflen);
void find_device(t_gac *x);
// =============================================================================
// Threading
// ------------------------------------------------------------------------------
static void *usb_thread_read(void *w)
{
t_gac *x = (t_gac*) w;
int nBytes;
while(1) {
pthread_testcancel();
if (!(x->dev_handle)) find_device(x);
else {
nBytes = usb_control_msg(x->dev_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
EDUBEAT_CMD_POLL, 0, 0, (char *)buffer, sizeof(buffer), DEFAULT_CLOCK_INTERVAL);
if(x->x_verbose)post("thread read %i bytes", nBytes);
//post("%i b", nBytes);
}
}
return 0;
}
static void usb_thread_start(t_gac *x) {
// create the worker thread
if(pthread_attr_init(&x->gac_thread_attr) < 0)
{
error("gac: could not launch receive thread");
return;
}
if(pthread_attr_setdetachstate(&x->gac_thread_attr, PTHREAD_CREATE_DETACHED) < 0)
{
error("gac: could not launch receive thread");
return;
}
if(pthread_create(&x->x_threadid, &x->gac_thread_attr, usb_thread_read, x) < 0)
{
error("gac: could not launch receive thread");
return;
}
else
{
if(x->x_verbose)post("gac: thread %d launched", (int)x->x_threadid );
}
}
//--------------------------------------------------------------------------
// - Message: bootloader
//--------------------------------------------------------------------------
void gac_bootloader(t_gac *x)
{
int cmd;
int nBytes;
unsigned char bootloaderbuffer[8];
cmd = 0;
cmd = EDUBEAT_CMD_START_BOOTLOADER;
if (!(x->dev_handle)) find_device(x);
else {
nBytes = usb_control_msg(x->dev_handle, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN,
cmd, 0, 0, (char *)bootloaderbuffer, sizeof(bootloaderbuffer), DEFAULT_CLOCK_INTERVAL);
}
}
//--------------------------------------------------------------------------
// - Message: bang -> poll gac
//--------------------------------------------------------------------------
void gac_bang(t_gac *x) {
int i,n;
int replymask,replyshift,replybyte;
int temp;
for (i = 0; i < OUTLETS; i++) {
temp = buffer[i];
switch(i) {
case 0:
replybyte = buffer[8];
replyshift = ((0 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 1:
replybyte = buffer[8];
replyshift = ((1 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 2:
replybyte = buffer[8];
replyshift = ((2 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 3:
replybyte = buffer[8];
replyshift = ((3 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 4:
replybyte = buffer[9];
replyshift = ((0 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 5:
replybyte = buffer[9];
replyshift = ((1 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 6:
replybyte = buffer[9];
replyshift = ((2 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 8:
temp = buffer[10];
replybyte = buffer[13];
replyshift = ((0 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 9:
temp = buffer[11];
replybyte = buffer[13];
replyshift = ((1 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
case 10:
temp = buffer[12];
replybyte = buffer[13];
replyshift = ((2 % 4) * 2);
replymask = (3 << replyshift);
temp = temp * 4 + ((replybyte & replymask) >> replyshift);
break;
}
outlet_float(x->outlets[i], temp);
}
}
//--------------------------------------------------------------------------
// - The clock is ticking, tic, tac...
//--------------------------------------------------------------------------
void gac_tick(t_gac *x) {
clock_delay(x->m_clock, x->m_interval); // schedule another tick
gac_bang(x); // poll the edubeat
}
//--------------------------------------------------------------------------
// - Object creation and setup
//--------------------------------------------------------------------------
int gac_setup(void)
{
gac_class = class_new ( gensym("gac"),(t_newmethod)gac_new, 0, sizeof(t_gac), CLASS_DEFAULT,0);
// Add message handlers
class_addbang(gac_class, (t_method)gac_bang);
class_addmethod(gac_class, (t_method)gac_bootloader, gensym("bootloader"), A_DEFSYM,0);
post("bald-approved gac version 0.1",0);
return 1;
}
//--------------------------------------------------------------------------
void *gac_new(t_symbol *s) // s = optional argument typed into object box (A_SYM) -- defaults to 0 if no args are typed
{
t_gac *x; // local variable (pointer to a t_gac data structure)
x = (t_gac *)pd_new(gac_class); // create a new instance of this object
x->m_clock = clock_new(x,(t_method)gac_tick);
x->x_verbose = 0;
x->m_interval = DEFAULT_CLOCK_INTERVAL;
x->m_interval_bak = DEFAULT_CLOCK_INTERVAL;
x->dev_handle = NULL;
int i;
// create outlets and assign it to our outlet variable in the instance's data structure
for (i=0; i < OUTLETS; i++) {
x->outlets[i] = outlet_new(&x->p_ob, &s_float);
}
usb_thread_start(x); //start polling the device
clock_delay(x->m_clock,0.); //start reading the buffer
return x; // return a reference to the object instance
}
//--------------------------------------------------------------------------
// - Object destruction
//--------------------------------------------------------------------------
void gac_free(t_gac *x)
{
if (x->dev_handle) usb_close(x->dev_handle);
freebytes((t_object *)x->m_clock, sizeof(x->m_clock));
while(pthread_cancel(x->x_threadid) < 0)
if(x->x_verbose)post("gac: killing thread\n");
if(x->x_verbose)post("gac: thread canceled\n");
}
//--------------------------------------------------------------------------
// - USB Utility Functions
//--------------------------------------------------------------------------
static int usbGetStringAscii(usb_dev_handle *dev, int ndex, int langid, char *buf, int buflen)
{
char asciibuffer[256];
int rval, i;
if((rval = usb_control_msg(dev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + ndex, langid, asciibuffer, sizeof(asciibuffer), 1000)) < 0)
return rval;
if(asciibuffer[1] != USB_DT_STRING)
return 0;
if((unsigned char)asciibuffer[0] < rval)
rval = (unsigned char)asciibuffer[0];
rval /= 2;
/* lossy conversion to ISO Latin1 */
for(i=1;i<rval;i++){
if(i > buflen) /* destination buffer overflow */
break;
buf[i-1] = asciibuffer[2 * i];
if(asciibuffer[2 * i + 1] != 0) /* outside of ISO Latin1 range */
buf[i-1] = '?';
}
buf[i-1] = 0;
return i-1;
}
//--------------------------------------------------------------------------
void find_device(t_gac *x) {
usb_dev_handle *handle = NULL;
struct usb_bus *bus;
struct usb_device *dev;
usb_init();
usb_find_busses();
usb_find_devices();
for(bus=usb_busses; bus; bus=bus->next){
for(dev=bus->devices; dev; dev=dev->next){
if(dev->descriptor.idVendor == USBDEV_SHARED_VENDOR && dev->descriptor.idProduct == USBDEV_SHARED_PRODUCT){
char string[256];
int len;
handle = usb_open(dev); /* we need to open the device in order to query strings */
if(!handle){
error ("Warning: cannot open USB device: %s", usb_strerror());
continue;
}
/* now find out whether the device actually is gac */
len = usbGetStringAscii(handle, dev->descriptor.iManufacturer, 0x0409, string, sizeof(string));
if(len < 0){
post("gac: warning: cannot query manufacturer for device: %s", usb_strerror());
goto skipDevice;
}
post("::::::%s", string);
if(strcmp(string, "11h11") != 0)
goto skipDevice;
len = usbGetStringAscii(handle, dev->descriptor.iProduct, 0x0409, string, sizeof(string));
if(len < 0){
post("gac: warning: cannot query product for device: %s", usb_strerror());
goto skipDevice;
}
if(strcmp(string, "Gac") == 0)
break;
skipDevice:
usb_close(handle);
handle = NULL;
}
}
if(handle)
break;
}
if(!handle){
post("Could not find USB device 11h11/gac");
x->dev_handle = NULL;
} else {
x->dev_handle = handle;
post("Found USB device 11h11/gac");
}
}
Cheers
FFT Normalization with different block sizes
FFT normalization factor depends on FFT frame size (which is equal to blocksize for fft~ & Co.), overlap, and window type.
In my experience you can normalize where ever you want: in time domain before FFT, in frequency domain, or in time domain after FFT. It is also possible to normalize in two steps, each of sqrt(normalization-factor), for example when you want the bin content normalized for certain processing or analysis jobs. Then you do one step before processing and the second after processing.
In frequency domain, you can either normalize amplitude of polar coefficients, or real and imaginary part of rectangular coefficients. This has no consequence for normalization factor.
If you would do normalization outside the reblocked subpatch, that is also possible, before or after FFT patch. But you must still consider blocksize and overlap.
You can verify all these possibilities by tweaking mentioned patch. The fact that normalization factor does not depend on the point in the process where you apply it, is convenient. However, for analysis or processing, that point of normalization does matter sometimes.
Consider for example a single-sample click: it's energy is spread over all frequency bins. With no normalization before spectral analysis, magnitude would be 1 for each bin. Next, consider a perfectly white noise, or a complex linear chirp over the full spectrum. With sqrt(normalization-factor) before analysis, the magnitude is 1 for each bin. Next, consider a pure sinusoid. With full normalization before analysis, the magnitude is 1 for a single bin, or the leaked equivalent of that.
You see, it depends on signal type how spectral magnitude can be shown independent of FFT size. Probably, the signals you want to process resemble a white noise rather than single pulses or full-scale pure sinusoids. So my guess is, that you would opt for the two-step normalization if you want to mix spectral data of different FFT sizes.
Good luck,
Katja
Vd~
1. i've never seen vd~ with a 2nd creation argument before. The first creation argument is of course the buffer name, but i think this second creation argument is just gibberish. It's not really an 'error' because it won't hurt your patch, but it does nothing and means nothing. As far as i know, vd~ only takes one creation argument, everything after that will just be ignored.
2. not sure which loadbang you're talking about
3. vd~ stands for "variable delay", so that's all it does. it plays back the buffer specified in [delwrite~] after a period of ~n ms. This delay time is set by the signal coming into [vd~]. So, for example If you just send a normal phasor~ directly into [vd~] then really not much will happen, because you'd just be changing the delay between 0 and 1 ms. This is equivalent to playing back the buffer with the tiniest of tiny vibrato's.
When it gets interesting, is when you use vd~ to change the delay time by much more than 1ms while the patch runs. The end result of this is much like slowing down or speeding up a tape recorder - you get variations in pitch, basically.
What the Karplus-Strong algorithm does, then, is to send FEEDBACK into this process. That's what 'makes the loop go'. If you look at that patch again, follow the signal chain and you will see what is happening. The [noise~] goes into the buffer. Then the buffer is played back (at a variable rate) by vd~. After that, there is some averaging process (not sure about that bit, but it's basically a lowpass filter, i guess) and then the signal is multiplied by about 0.99 and then sent back into the original [delwrite~] object, where it gets read by the vd~ again, etc...that's your feedback loop, and that's what makes the loop go.
incidentally, i think there's another error in that patch as well. The [dac~] should come BEFORE the 'damping factor' calculation, otherwise, with low damping, you also get low volume.
Oh, and just in case the terminology is confusing: "damping" and "feedback amount" are two sides of the one coin in this case. I prefer to use the term 'feedback' because it more clearly expresses what is happening. 90% feedback = 10% damping, etc.
Invert spectra
toxonic:
did you ever connect that FFT shift subpatch that is hidden in your patch? i just tried that and it works really well.
i can see a really neat school of fx coming out of this:
as far as i know, the following can all be pretty easily applied:
inverse
shift
all sort of EQ effects
feedback
then, some that i just brainstormed, but haven't tried yet:
FFT "bitcrush"
reduce the resolution of each value output by the FFT
LFO'd FFT
use lfo's (or envelopes, or whatever) to modulate FFT outputs. i already tried this a little bit with one of my DIY objects, and it works well.
Randomization:
rather than just an inverse, maybe try some random scrambling on a selected range of the FFT to see what happens.
again, might be even more interesting if the range gets modulated with an LFO or something.
however, the bug 'AHA' moment i just had was that it will be a million times more computationally efficient to include ALL of these things within one FFT analysis, and have switches to turn them on and off as desired.
you know, it's going in and out of the FFT domain that uses most of the cpu resources, so while you're in there, you may as well get as much done as possible.
i'll have a go with this in the coming week and see where i can get. any help or more ideas would be totally excellently appreciated!