No, è molto più semplice la cosa: in un editor di testo incolli il codice C e salvi il testo ad esempio nella cartella /tmp con il nome jk.c .
In tal caso nel codice Gambas dovrai solo cambiare la riga:
Shell "gcc -o /tmp/libjk.so " & Application.Path &/ "jk.c -shared -fPIC -ljack"
con questa nuova:
Shell "gcc -o /tmp/libjk.so /tmp/jk.c -shared -fPIC -ljack"
Sarà questa riga, appunto, a creare la libreria esterna dinamica condivisa .so ad hoc .
Perfetto! Funziona benissimo :D
Pero' nel wiki la sezione relativa al codice C e' rimasta un po' confusa ..... magari sarebbe da fare riferimento al fatto che il codice c e' incluso nella procedura CreaLibjk dell'applicazione gambas.
Ho fatto qualche modifica "duplicando" le porte di in e out:
Private input_port_right As Pointer 'AGGIUNTA PER LO STEREO
Private output_port_right As Pointer 'AGGIUNTA PER LO STEREO
nelle dichiarazioni ...
e
input_port_right = jack_port_register(client, "input_2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0) 'AGGIUNTA PER LO STEREO
output_port_right = jack_port_register(client, "output_2", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0) 'AGGIUNTA PER LO STEREO
If (input_port_right = 0) Or (output_port_right = 0) Then Print "Nessuna porta di Jack disponibile !" 'AGGIUNTA PER LO STEREO
ed effettivamente jack riconoscie le porte di in e out ....
Ma suppongo che dovro' lavorare su CreaLibjk per avere il feedback audio come succede sul canale left ......
File.Save("/tmp/jk.c", "#include <string.h>\n" &
"#include <jack/jack.h>" &
"\n\n" &
"jack_port_t *input_port_left;\n" &
"jack_port_t *input_port_right;\n" & 'AGGIUNTA PER LO STEREO
"jack_port_t *output_port_left;\n" &
"jack_port_t *output_port_right;\n" & 'AGGIUNTA PER LO STEREO
"jack_client_t *client;" &
"\n\n" &
"int process (jack_nframes_t, void *);" &
"\n\n" &
"void principale (jack_client_t *cl, jack_port_t *it, jack_port_t *ot) {\n" &
" client = cl;\n" &
" input_port_left = it;\n" &
" input_port_right = it;\n" & 'AGGIUNTA PER LO STEREO
" output_port_left = ot;\n" &
" output_port_right = ot;\n\n" & 'AGGIUNTA PER LO STEREO
" jack_set_process_callback (client, process, 0);\n}" &
"\n\n" &
"int process (jack_nframes_t nframes, void *arg) {\n"
" jack_default_audio_sample_t *in, *out;\n" &
" in = jack_port_get_buffer (input_port_left, nframes);\n" &
" out = jack_port_get_buffer (output_port_left, nframes);\n\n" &
" memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes);\n\n" &
" return 0;\n}\n")
manca qualcosa in in e out .....
potrebbe essere un larsen?
cioe' manda in out quel che entra e lo rimanda su se stesso?
per ora ho praticamente riciclato il tuo wiki nello scheletro di un programmino che "fara'" da "orecchio".
di suo (ora) il programma fa anche le connessioni di jack, dall'ingresso all'usicita, ma pensavo di eliminarle e demandare il compito all'utente in fase di setup delle connessioni su jack (si puo' richiamare un patch panel pre programmato).
Ora sto studiando il codice in allegato per vedere come estrarre da jack informazioni sull'energia del segnale audio:
/* Pick the jack process method */
if (meter_type == MET_VU) {
init_buffers_rms();
jack_set_process_callback(client, process_rms, 0);
} else if (meter_type == MET_JF || meter_type == MET_SCO) {
jack_set_process_callback(client, process_ring, 0);
} else {
jack_set_process_callback(client, process_peak, 0);
}
// This is used to maually save the session, but the auto stuff
// does a better job anyway. Just here for testing
//jack_set_store_callback(client, store, NULL);
if (jack_activate(client)) {
fprintf (stderr, "cannot activate client");
exit(1);
}
che dovrebbe fare riferimento a questo:
http://jackaudio.org/files/docs/html/group__ClientCallbacks.html#gafb5ec9fb4b736606d676c135fb97888b (http://jackaudio.org/files/docs/html/group__ClientCallbacks.html#gafb5ec9fb4b736606d676c135fb97888b)
suppongo che per esempio "process_rms" sai una costante, un puntatore che dice al call back di ritornare (con una funzione interna delle api di jack) il livello RMS del segnale audio ... ma in internet non sono riuscito a trovare alcun riferimento ....
mi sono incastrato alla grande ....
/tmp/jk.c:30:48: error: unknown type name ‘JACK_METER_TYPE’
}void Invoca_Callback_meter (jack_client_t *cl,JACK_METER_TYPE,0) {
^
/tmp/jk.c:30:64: error: expected declaration specifiers or ‘...’ before numeric constant
}void Invoca_Callback_meter (jack_client_t *cl,JACK_METER_TYPE,0) {
^
ma penso che questo mio codice C "in linea" sia zeppo di errori (partendo da quello funzionante di VUOTT) ...
File.Save("/tmp/jk.c", "#include <string.h>\n" &
"#include <jack/jack.h>" &
"\n\n" &
"jack_port_t **input_port;\n" &
"jack_port_t **output_port;\n" &
"jack_client_t *client;" &
"\n\n" &
"int process (jack_nframes_t, void *);" &
"\n\n" &
"int process (jack_nframes_t nframes, void *arg) {\n"
" int i;\n" &
" jack_default_audio_sample_t *in, *out;\n" &
" for (i = 0; i < 2; i++) {\n" &
" in = jack_port_get_buffer (input_port[i], nframes);\n" &
" out = jack_port_get_buffer (output_port[i], nframes);\n\n" &
" memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes);\n}\n\n" &
" return 0;\n}\n" &
"\n\n" &
"void Invoca_Callback (jack_client_t *cl, jack_port_t **it, jack_port_t **ot) {\n" &
" client = cl;\n" &
" input_port = it;\n" &
" output_port = ot;\n\n" &
" jack_set_process_callback (client, process, 0);\n}" &
"void Invoca_Callback_meter (jack_client_t *cl,JACK_METER_TYPE,0) {\n" &
" client = cl;\n" &
" valore = process_rms;\n\n" &
" jack_set_process_callback (client, process, 0);\n}")
Shell "gcc -o /tmp/libjk.so /tmp/jk.c -shared -fPIC -ljack" Wait
" Ma 'sto JACK_METER_TYPE che è ? Quanto vale ?"
........insomma, è come se in Gambas tu avessi una situazione del genere:
Public Sub Main()
Dim i As Integer
i = 10 + JACK_METER_TYPE
Print i
End
Provalo.... e vedi pure Gambas che ti dice. (http://forum.ubuntu-it.org/images/smilies/whistle.gif)
Puoi porre quella costante con la direttiva #define.
Esempio in C:
#include <stdio.h>
#define JACK_METER_TYPE 99 /* definiamo una costante avente valore di tipo intero = 99 */
int main() {
printf("%d\n", JACK_METER_TYPE);
return (0);
}
nella main.c del sorgente che ho trovato che implementa il vumeter per jack, c'e' una linea di questo tipo:
/* Pick the jack process method */
if (meter_type == MET_VU) {
init_buffers_rms();
jack_set_process_callback(client, process_rms, 0);
} else if (meter_type == MET_JF || meter_type == MET_SCO) {
jack_set_process_callback(client, process_ring, 0);
} else {
jack_set_process_callback(client, process_peak, 0);
}
il problema e' che ci sono anche un po' di include in testa:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_thread.h>
#include <jack/jack.h>
#include <getopt.h>
#include "config.h"
#include "main.h"
#include "envelopes.h"
#include "find_image.h"
#include "vu_meters.h"
#include "ppm_meters.h"
#include "dpm_meters.h"
#include "jf_meters.h"
#include "scope.h"
#define MET_VU 1 // The meter display type
#define MET_PPM 2
#define MET_DPM 3
#define MET_JF 4
#define MET_SCO 5
spulciarseli a mano per capire dove sta' la costante che definisce il tipo di vumeter e' tosta ...
in realta' e' un po' piu' complicato ....
process_rms, process_ring e process_peak penso siano le uscite di queste funzioni (che estraggono i dati dai buffer di jack):
#include <math.h>
#include <jack/jack.h>
#include "main.h"
#include "envelopes.h"
float rms_buffers[MAX_METERS][RMS_BUF_SIZE];
float rms_sums[MAX_METERS];
float ring_buf[MAX_METERS][RING_BUF_SIZE];
unsigned int rms_pos = 0;
float env_read(int port)
{
float tmp = env[port];
env[port] = 0.0f;
return tmp;
}
int process_peak(jack_nframes_t nframes, void *arg)
{
unsigned int i, port;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
jack_default_audio_sample_t *in, *out;
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
for (i = 0; i < nframes; i++) {
const float s = fabs(in[i]);
out[i] = in[i];
if (s > env[port]) {
env[port] = s;
}
}
}
return 0;
}
void init_buffers_rms()
{
unsigned int i, j;
for (i=0; i < MAX_METERS; i++) {
rms_sums[i] = 0.0f;
for (j=0; j < RMS_BUF_SIZE; j++) {
rms_buffers[i][j] = 0.0f;
}
}
}
int process_rms(jack_nframes_t nframes, void *arg)
{
unsigned int i, port;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
jack_default_audio_sample_t *in, *out;
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
/* Update running total of samples in RMS buffer */
for (i = 0; i < nframes; i++) {
const float s = in[i] * in[i];
out[i] = in[i];
rms_sums[port] -= rms_buffers[port][rms_pos];
rms_sums[port] += s;
rms_buffers[port][rms_pos] = s;
rms_pos = (rms_pos + 1) & (RMS_BUF_SIZE - 1);
} if (rms_sums[port] < 0.0f) {
/* This should never happnen, but can occasionally due
* the the difference between the rms history and the
* float sum. We clobber it just to make sure. */
rms_sums[port] = 0.0f;
}
env[port] = sqrtf(rms_sums[port] / (float)RMS_BUF_SIZE); }
return 0;
}
int process_ring(jack_nframes_t nframes, void *arg)
{
static unsigned int pos = 0;
unsigned int i, port;
jack_default_audio_sample_t *in, *out;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
for (i=0; i<nframes; i++) {
out[i] = in[i];
ring_buf[port][(pos + i) & (RING_BUF_SIZE - 1)] = in[i];
}
}
pos = (pos + nframes) & (RING_BUF_SIZE - 1);
return 0;
}
che e' il codice di envelopers.c
............. la vedo dura ....
jack_meter_type e' una variabile che ho creato io, in realta' nel programma C originale, nella sezione main.c e' definito cosi':
/* Pick the jack process method */
if (meter_type == MET_VU) {
init_buffers_rms();
jack_set_process_callback(client, process_rms, 0);
} else if (meter_type == MET_JF || meter_type == MET_SCO) {
jack_set_process_callback(client, process_ring, 0);
} else {
jack_set_process_callback(client, process_peak, 0);
}
quindi il valore che potrebbe prendere e':
MET_VU o MET_SCO o MET_JF
in base a quale tipo di meter si voglia fare accesso, il callback di jack ha come opzioni:
client (puntatore a quale client di jack fa' la richiesta?)
process_rms / process_ring / process_peak (puntatore alla funzione che fa il calcolo dei valori? valore di ritorno della funzione di calcolo opportuna?)
0 (che cosa rappresenta?)
Sicuramente in envelopers.c ci sono queste funzioni:
#include <math.h>
#include <jack/jack.h>
#include "main.h"
#include "envelopes.h"
float rms_buffers[MAX_METERS][RMS_BUF_SIZE];
float rms_sums[MAX_METERS];
float ring_buf[MAX_METERS][RING_BUF_SIZE];
unsigned int rms_pos = 0;
float env_read(int port)
{
float tmp = env[port];
env[port] = 0.0f;
return tmp;
}
int process_peak(jack_nframes_t nframes, void *arg)
{
unsigned int i, port;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
jack_default_audio_sample_t *in, *out;
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
for (i = 0; i < nframes; i++) {
const float s = fabs(in[i]);
out[i] = in[i];
if (s > env[port]) {
env[port] = s;
}
}
}
return 0;
}
void init_buffers_rms()
{
unsigned int i, j;
for (i=0; i < MAX_METERS; i++) {
rms_sums[i] = 0.0f;
for (j=0; j < RMS_BUF_SIZE; j++) {
rms_buffers[i][j] = 0.0f;
}
}
}
int process_rms(jack_nframes_t nframes, void *arg)
{
unsigned int i, port;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
jack_default_audio_sample_t *in, *out;
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
/* Update running total of samples in RMS buffer */
for (i = 0; i < nframes; i++) {
const float s = in[i] * in[i];
out[i] = in[i];
rms_sums[port] -= rms_buffers[port][rms_pos];
rms_sums[port] += s;
rms_buffers[port][rms_pos] = s;
rms_pos = (rms_pos + 1) & (RMS_BUF_SIZE - 1);
} if (rms_sums[port] < 0.0f) {
/* This should never happnen, but can occasionally due
* the the difference between the rms history and the
* float sum. We clobber it just to make sure. */
rms_sums[port] = 0.0f;
}
env[port] = sqrtf(rms_sums[port] / (float)RMS_BUF_SIZE); }
return 0;
}
int process_ring(jack_nframes_t nframes, void *arg)
{
static unsigned int pos = 0;
unsigned int i, port;
jack_default_audio_sample_t *in, *out;
if (meter_freeze) {
return 0;
}
for (port = 0; port < num_meters; port++) {
/* just incase the port isn't registered yet */
if (input_ports[port] == 0 || output_ports[port] == 0) {
break;
}
in = (jack_default_audio_sample_t *) jack_port_get_buffer(input_ports[port], nframes);
out = (jack_default_audio_sample_t *) jack_port_get_buffer(output_ports[port], nframes);
for (i=0; i<nframes; i++) {
out[i] = in[i];
ring_buf[port][(pos + i) & (RING_BUF_SIZE - 1)] = in[i];
}
}
pos = (pos + nframes) & (RING_BUF_SIZE - 1);
return 0;
}
che a loro volta vogliono (giustamente) i dati dei buffer di jack ..... la vedo complicata.
Io speravo che fosse una "feature" interna di jack.