Creare un semplice Client di Jack con le risorse della libreria libjack

Da Gambas-it.org - Wikipedia.

JACK Audio Connection Kit è un server audio a bassa latenza sia per dati audio che per dati Midi. Jack consente di connettere svariate applicazioni client ad un dispositivo audio, ed anche con altri client. I Client di Jack possono funzionare sia come applicazioni con processi separati rispetto al server audio, sia come plugin all'interno dello stesso Jack.


Creare un semplice Client di Jack con Gambas

Per creare in Gambas un semplice Client di Jack, e connettere le sue porte con il server Jack, bisognerà servirsi delle risorse offerte dalla libreria condivisa: "libjack.so.0.1.0 ".

Ogni porta audio in Jack è un canale audio mono in formato Single, quindi a 32-bit.
V'è da precisare che Jack in tal caso prevede una chiamata ad una routine callback, ma Gambas - almeno allo stato attuale - non è in grado di funzionare in un altro thread, causando così il crash dell'applicativo. Pertanto, bisognerà far funzionare la procedura di callback all'interno di una libreria C esterna da noi appositamente creata, che chiameremo ad esempio jk.c . Il codice sorgente di tale nostra apposita libreria dinamica condivisa potrà essere inserito nel codice Gambas; lì sarà prevista anche la riga di comando con Shell per compilare la libreria.


Creazione di un Client e connessione di tipo mono al server jack

Viene creato un Client di Jack, e vengono generate connessioni audio di tipo mono.

Private Const JackNullOption As Byte = 0
' Private Const JACK_DEFAULT_MIDI_TYPE As String = "8 bit raw midi"
Private Const JACK_DEFAULT_AUDIO_TYPE As String = "32 bit float mono audio"
Private Const JackPortIsInput As Byte = 1
Private Const JackPortIsOutput As Byte = 2
Private Const JackPortIsPhysical As Byte = 4

Private err As Integer
Private client As Pointer
Private input_port As Pointer
Private output_port As Pointer


Library "libjack:0.1.0"

' jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status,...)
' Open an external client session with a JACK server.
Private Extern jack_client_open(client_name As String, options As Integer, status As Pointer, server_name As String) As Pointer

' char * jack_get_client_name (jack_client_t *client)
' Returns a pointer to actual client name.
Private Extern jack_get_client_name(jack_client As Pointer) As String

' jack_nframes_t jack_get_sample_rate (jack_client_t *)
' Returns the sample rate of the jack system, as set by the user when jackd was started.
Private Extern jack_get_sample_rate(jack_client As Pointer) As Integer

' jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size)
' Create a new port for the client.
Private Extern jack_port_register(jack_client As Pointer, port_name As String, port_type As String, flags As Integer, buffer_size As Integer) As Pointer

' int jack_activate (jack_client_t *client)
' Tell the Jack server that the program is ready to start processing audio.
Private Extern jack_activate(jack_client As Pointer) As Integer

' const char ** jack_get_ports (jack_client_t *, const char *port_name_pattern, const char *type_name_pattern, unsigned long flags)
' Returns a NULL-terminated array of ports that match the specified arguments.
Private Extern jack_get_ports(jack_client As Pointer, port_name_pattern As String, type_name_pattern As String, flags As Integer) As Pointer

' const char * jack_port_name (const jack_port_t *port)
' Returns the full name of the jack_port_t (including the "client_name:" prefix).
Private Extern jack_port_name(jack_port As Pointer) As String

' int jack_connect (jack_client_t *, const char *source_port, const char *destination_port)
' Establish a connection between two ports.
Private Extern jack_connect(jack_client As Pointer, src_port As String, dest_port As String) As Integer

' int jack_client_close (jack_client_t *client)
' Disconnects an external client from a JACK server.
Private Extern jack_client_close(jack_client As Pointer) As Integer


' void Invoca_Callback (jack_client_t *cl, jack_port_t *it, jack_port_t *ot)
' Invoca la funzione principale della libreria ad hoc per la funzione "callback"
Private Extern Invoca_Callback(Pclient As Pointer, it As Pointer, ot As Pointer) As Integer In "/tmp/libjk"


Public Sub Form_Open()

' Invoca la sotto-procedura per salvare in apposto file .c il codice sorgente della nostra libreria "ad hoc" e per crearla:
 CreaLibjk()

End


Public Sub Button1_Click()

 Dim nomeClient As String = "Client_1"
 Dim ports As Pointer
 Dim sample As Integer
 Dim porta, nome_porta As String
 Dim status As Single


  client = jack_client_open(nomeClient, JackNullOption, VarPtr(status), Null)
  If IsNull(client) Then Print "Impossibile aprire una sessione del Client con  il server Jack !"

  nomeClient = jack_get_client_name(client)
  Print "\nNome del Client = "; nomeClient

  sample = jack_get_sample_rate(client)
  Print "Campionamento = hz "; sample
   
  Print "\n=========================================\n"


  input_port = jack_port_register(client, "input_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)
   
  output_port = jack_port_register(client, "output_1", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)
  If (input_port = 0) Or (output_port = 0) Then Print "Nessuna porta di Jack disponibile !"
   
  Invoca_Callback(client, input_port, output_port)
   
  err = jack_activate(client)
  If err <> 0 Then Print "Impossibile attivare il Client !"

 
' Connette le porte:
  ports = jack_get_ports(client, "system:capture_1", Null, JackPortIsPhysical Or JackPortIsOutput)
   
  If ports = 0 Then
    Print "Impossibile agganciare alcuna porta fisica !"
    Free(Object.Address(Button1))
    Quit
  Endif
   
  porta = String@(Pointer@(ports))
   
  nome_porta = jack_port_name(input_port)
  Print porta, nome_porta
  
  err = jack_connect(client, porta, nome_porta)

  If err <> 0 Then Print "Impossibile connettere la porta d'Entrata !"


' Connette le porte:
  ports = jack_get_ports(client, Null, Null, JackPortIsPhysical Or JackPortIsInput)
  If ports = 0 Then Print "Impossibile agganciare alcuna porta fisica !"
   
  porta = String@(Pointer@(ports))
   
  nome_porta = jack_port_name(output_port)
  Print nome_porta, porta
   
  err = jack_connect(client, nome_porta, porta)
  If err <> 0 Then Print "Impossibile connettere la porta d'Uscita !", err
  
  Print

End


Private Procedure CreaLibjk()   ' Salva il codice sorgente della nostra libreria dinamica .so, e poi la crea
  
  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" &
         "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}" &
           "\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, nframes);\n" &
           "   out = jack_port_get_buffer (output_port, nframes);\n\n" &
           "   memcpy (out, in, sizeof (jack_default_audio_sample_t) * nframes);\n\n" &
           "   return 0;\n}\n")
 
  Shell "gcc -o  /tmp/libjk.so /tmp/jk.c -shared -fPIC -ljack" Wait
 
End


Public Sub Button2_Click()

  jack_client_close(client)

End


Creazione di un Client e connessione di tipo stereo al server jack

Vediamo ora un possibile codice per creare un Client ed effettuare una connessione di tipo stereo al server jack:

Private Const JackNullOption As Byte = 0
' Private Const JACK_DEFAULT_MIDI_TYPE As String = "8 bit raw midi"
Private Const JACK_DEFAULT_AUDIO_TYPE As String = "32 bit float mono audio"
Private Const JackPortIsInput As Byte = 1
Private Const JackPortIsOutput As Byte = 2
Private Const JackPortIsPhysical As Byte = 4

Private err As Integer
Private client As Pointer
Private porte_input As New Pointer[2]
Private porte_output As New Pointer[2]


Library "libjack:0.1.0"

' jack_client_t * jack_client_open (const char *client_name, jack_options_t options, jack_status_t *status,...)
' Open an external client session with a JACK server.
Private Extern jack_client_open(client_name As String, options As Integer, status As Pointer, server_name As String) As Pointer

' char * jack_get_client_name (jack_client_t *client)
' Returns a pointer to actual client name.
Private Extern jack_get_client_name(jack_client As Pointer) As String

' jack_nframes_t jack_get_sample_rate (jack_client_t *)
' Returns the sample rate of the jack system, as set by the user when jackd was started.
Private Extern jack_get_sample_rate(jack_client As Pointer) As Integer

' jack_port_t * jack_port_register (jack_client_t *client, const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size)
' Create a new port for the client.
Private Extern jack_port_register(jack_client As Pointer, port_name As String, port_type As String, flags As Integer, buffer_size As Integer) As Pointer

' int jack_activate (jack_client_t *client)
' Tell the Jack server that the program is ready to start processing audio.
Private Extern jack_activate(jack_client As Pointer) As Integer

' const char ** jack_get_ports (jack_client_t *, const char *port_name_pattern, const char *type_name_pattern, unsigned long flags)
' Returns a NULL-terminated array of ports that match the specified arguments.
Private Extern jack_get_ports(jack_client As Pointer, port_name_pattern As String, type_name_pattern As String, flags As Integer) As Pointer

' const char * jack_port_name (const jack_port_t *port)
' Returns the full name of the jack_port_t (including the "client_name:" prefix).
Private Extern jack_port_name(jack_port As Pointer) As String

' int jack_connect (jack_client_t *, const char *source_port, const char *destination_port)
' Establish a connection between two ports.
Private Extern jack_connect(jack_client As Pointer, src_port As String, dest_port As String) As Integer

' int jack_client_close (jack_client_t *client)
' Disconnects an external client from a JACK server.
Private Extern jack_client_close(jack_client As Pointer) As Integer


' void Invoca_Callback (jack_client_t *cl, jack_port_t *it, jack_port_t *ot)
' Invoca la funzione principale della libreria ad hoc per la funzione "callback"
Private Extern Invoca_Callback(Pclient As Pointer, it As Pointer[], ot As Pointer[]) As Integer In "/tmp/libjk"


Public Sub Form_Open()

' Invoca la sotto-procedura per salvare in apposto file .c il codice sorgente della nostra libreria "ad hoc" e per crearla:
 CreaLibjk()

End


Public Sub Button1_Click()

 Dim nomeClient, nome_porta As String
 Dim porte As Pointer
 Dim b As Byte
 Dim sample As Integer
 Dim status As Single
 
  nomeClient = "Client_1"
 
  client = jack_client_open(nomeClient, JackNullOption, VarPtr(status), Null)
  If IsNull(client) Then Print "Impossibile aprire una sessione del Client con  il server Jack !"

  nomeClient = jack_get_client_name(client)
  Print "\nNome del Client = "; nomeClient

  sample = jack_get_sample_rate(client)
  Print "Campionamento = hz "; sample
   
  Print "\n=========================================\n"


  For b = 0 To 1
 
    nome_porta = "input_" & CStr(b + 1)
    porte_input[b] = jack_port_register(client, nome_porta, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)
    
    nome_porta = "output_" & CStr(b + 1)
    porte_output[b] = jack_port_register(client, nome_porta, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)
    
    If (porte_input[b] = 0) Or (porte_output[b] = 0) Then Print "Nessuna porta di Jack disponibile !"
    
  Next
  
  Invoca_Callback(client, porte_input, porte_output)
  
  err = jack_activate(client)
  If err <> 0 Then Print "Impossibile attivare il Client !"
  
  
' '''''''''''''''''''''''''''''
  Print "Connessione delle porte\n"
' Connette le porte:
  porte = jack_get_ports(client, Null, Null, JackPortIsPhysical Or JackPortIsOutput)
  nome_porta = String@(Pointer@(porte))
  Print nome_porta, "--->", jack_port_name(porte_input[0])
  Print String@(Pointer@(porte + SizeOf(gb.Pointer))), "--->", jack_port_name(porte_input[1])
  Print
  If porte = 0 Then
    Print "Impossibile ottenere i nomi delle porte !"
    Free(Object.Address(Button1))
    Quit
  Endif
  For b = 0 To 1
    err = jack_connect(client, String@(Pointer@(porte + (CInt(b) * SizeOf(gb.Pointer)))), jack_port_name(porte_input[b]))
    If err <> 0 Then Print "Impossibile connettere le porte !"
  Next
  
  
  porte = jack_get_ports(client, Null, Null, JackPortIsPhysical Or JackPortIsInput)
  nome_porta = String@(Pointer@(porte))
  Print jack_port_name(porte_output[0]), "--->", nome_porta, nome_porta
  Print jack_port_name(porte_output[1]), "--->", nome_porta, String@(Pointer@(porte + SizeOf(gb.Pointer)))
  If porte = 0 Then
    Print "Impossibile ottenere i nomi delle porte !"
    Free(Object.Address(Button1))
    Quit
  Endif
  For b = 0 To 1
    err = jack_connect(client, jack_port_name(porte_output[b]), String@(Pointer@(porte + (CInt(b) * SizeOf(gb.Pointer)))))
    If err <> 0 Then Print "Impossibile connettere le porte !"
  Next
  
End


Private Procedure CreaLibjk()   ' Salva il codice sorgente della nostra libreria dinamica .so, e poi la crea
  
 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}")

 Shell "gcc -o  /tmp/libjk.so /tmp/jk.c -shared -fPIC -ljack" Wait
 
End


Public Sub Button2_Click()

  jack_client_close(client)

End


Riferimenti