belangeo / pyo

Python DSP module
GNU Lesser General Public License v3.0
1.28k stars 130 forks source link

Custom RAW input [On the way to a smart guitar pedal] #201

Closed felipeinfantino closed 3 years ago

felipeinfantino commented 3 years ago

Hi, first of all, great library. Thanks for developing it.

My goal is to build an embedded custom standalone guitar pedal. I want to embed a raspberry pi, and read the raw guitar input with an ADC then route it to pyo (without going through the Linux Jack Server) process it in pyo, and then send it back to a DAC. This would be basically an embedded soundcard + programmable guitar effect pedals in the size of your palm.

My concrete implementation is this:

https://www.instructables.com/Raspberry-Pi-Zero-Guitar-Pedal/

The C code to read the guitar input (this is just a clean guitar sound, no DSP):

https://www.electrosmash.com/forum/pedal-pi/214-clean-transparent-guitar-pedal?lang=en

The magical lines are this:

//read 12 bits ADC bcm2835_spi_transfernb(mosi, miso, 3); input_signal = miso[2] + ((miso[1] & 0x0F) << 8);

There the signal is being read with the bcm2835 library (http://www.airspayce.com/mikem/bcm2835/) The input_signal is a uint32_t Then we do the DSP to the input_signal Then we output it to the PWM pins on the pi to output the actual sound like this: //generate output PWM signal 6 bits bcm2835_pwm_set_data(1,input_signal & 0x3F); bcm2835_pwm_set_data(0,input_signal >> 6);

My concrete goal: I want to route this input_signal to pyo , there use all the pyo functionallity for DSP then get the signal back for writing it to the pwm pins.

I've seen the emmbeded options for Bela, Juce, etc. But as I want something directly from a custom hardware I found it a little confusing what to do exactly.

My approaches

Why all of this ?

My motivation is to build a high-performance stand alone open source smart pedal. That is easy to program for everyone (That's the main reason why I want to use pyo) and that is also "smart", meaning it has Bluetooth and WIFI, so you could actually share code with your friends or in a forum and upload it wireless and effortless to your pedal, maybe develop an app so you could easily manage through a smartphone, add drums, synths and use all the power of pyo so, at the end of the day, musicians can pack this pedal in a backpack plug in and play. Without needing a sound card (because is already built-in) or any other audio software to sound. Once we achieve this, a new world of possibilities opens, because, besides the guitar audio jack embedded into the pi, the pi has other GPIO pins, that means you can put any type of buttons/sensors so anyone could easily make a theremin or an electronic drum kit, etc. Or different kinds of outputs, like moving a servo motor with the sound of your guitar, etc, like I said a whole world to experiment with.

How can you help me?

Please guide me on how to make this happen, should it be possible by only creating a similar version from this https://github.com/belangeo/pyo/blob/master/embedded/openframeworks/PyoClass.cpp

What else should I consider? Which is the easiest approach to make this happen?

Any guidance would be appreciated. If this sounds also interesting for you, and you want to help me doing this let me know.

belangeo commented 3 years ago

Hi, The basic setup would be to start an embedded python interpreter at the beginning of your program, something like:

#include <Python.h>
#include <m_pyo.h>

int id;                 /* pyo server id */ 
float *inbuf;           /* pyo input buffer */ 
float *outbuf;          /* pyo output buffer */ 
char *msg;              /* preallocated string to construct message for pyo */ 
void (*callback)(int);  /* pointer to pyo embedded server callback */ 
PyThreadState *interp;  /* Python thread state linked to this sub interpreter */

msg = (char *)malloc(262144 * sizeof(char *));

interp = pyo_new_interpreter(<sampling rate>, <buffer size>, <chnls>);

inbuf = (float *)pyo_get_input_buffer_address(interp);
outbuf = (float *)pyo_get_output_buffer_address(interp);
callback = (void *)pyo_get_embedded_callback_address(interp);
id = pyo_get_server_id(interp);

if (pyo_is_server_started(interp)) { 
    err = pyo_exec_file(interp, <path/to/your/python/file.py>, msg, 0);
    if (err != 0)
        // handle error...
}

Then, in your process loop:

// use a counter to accum <buffer size> samples in inbuf

// when inbuf is full, process it
(*callback)(id);

// retrieve <buffer size> processed samples from outbuf

Finally, when you're done:

// at the very, don't forget to release resources and close the python interpreter
free(msg);
pyo_end_interpreter(interp);

The embedded/puredata is probably a more useful example in your case than a c++ example. All the above is extracted, and slightly modified, from embedded/puredata/pyo~.c.

belangeo commented 3 years ago

Closing as this is more a question than an issue.