+ added libjack example program latent_client.pp (translated from latent_client.c)

This commit is contained in:
Nikolay Nikolov 2025-01-04 15:31:42 +02:00
parent 72daf3f556
commit e0cce52aaa
2 changed files with 222 additions and 0 deletions

View File

@ -0,0 +1,221 @@
{
This simple client demonstrates the most basic features of JACK
as they would be used by many applications.
}
program latent_client;
{$MODE objfpc}{$H+}
uses
Jack, CTypes, SysUtils;
var
input_port: Pjack_port_t;
output_port: Pjack_port_t;
client: Pjack_client_t;
delay_line: Pjack_default_audio_sample_t;
delay_index: jack_nframes_t;
latency: jack_nframes_t = 1024;
{
The process callback for this JACK application is called in a
special realtime thread once for each audio cycle.
This client does nothing more than copy data from its input
port to its output port. It will exit when stopped by
the user (e.g. using Ctrl-C on a unix-ish operating system)
}
function process (nframes: jack_nframes_t; arg: Pointer): cint; cdecl;
var
_in: Pjack_default_audio_sample_t;
_out: Pjack_default_audio_sample_t;
k: cint;
begin
_in := jack_port_get_buffer (input_port, nframes);
_out := jack_port_get_buffer (output_port, nframes);
for k := 0 to nframes - 1 do
begin
_out[k] := delay_line[delay_index];
delay_line[delay_index] := _in[k];
delay_index := (delay_index + 1) mod latency;
end;
Result := 0;
end;
procedure latency_cb (mode: jack_latency_callback_mode_t; arg: Pointer); cdecl;
var
range: jack_latency_range_t;
begin
if mode = JackCaptureLatency then
begin
jack_port_get_latency_range (input_port, mode, @range);
Inc(range.min, latency);
Inc(range.max, latency);
jack_port_set_latency_range (output_port, mode, @range);
end
else
begin
jack_port_get_latency_range (output_port, mode, @range);
Inc(range.min, latency);
Inc(range.max, latency);
jack_port_set_latency_range (input_port, mode, @range);
end;
end;
{
JACK calls this shutdown_callback if the server ever shuts down or
decides to disconnect the client.
}
procedure jack_shutdown (arg: Pointer); cdecl;
begin
Writeln(StdErr, 'JACK shut down, exiting ...');
Halt (1);
end;
var
ports: PPChar;
client_name: PChar = 'latent';
server_name: PChar = nil;
options: jack_options_t = JackNullOption;
status: jack_status_t;
begin
if ParamCount = 1 then
latency := StrToInt(ParamStr(1));
delay_line := GetMem( latency * SizeOf(jack_default_audio_sample_t));
if delay_line = nil then
begin
Writeln (StdErr, 'no memory');
Halt(1);
end;
FillChar (delay_line^, 0, latency * SizeOf(jack_default_audio_sample_t));
{ open a client connection to the JACK server }
client := jack_client_open (client_name, options, @status, server_name);
if client = nil then
begin
Writeln(StdErr, 'jack_client_open() failed, ',
'status = $', HexStr(Ord(status), 4));
if (Ord(status) and Ord(JackServerFailed)) <> 0 then
begin
Writeln(StdErr, 'Unable to connect to JACK server');
end;
Halt (1);
end;
if (Ord(status) and Ord(JackServerStarted)) <> 0 then
begin
Writeln (StdErr, 'JACK server started');
end;
if (Ord(status) and Ord(JackNameNotUnique)) <> 0 then
begin
client_name := jack_get_client_name(client);
Writeln (StdErr, 'unique name `', client_name, ''' assigned');
end;
{ tell the JACK server to call `process()' whenever
there is work to be done.
}
jack_set_process_callback (client, @process, nil);
{ tell the JACK server to call `latency()' whenever
the latency needs to be recalculated.
}
if Assigned(@jack_set_latency_callback) then
jack_set_latency_callback (client, @latency_cb, nil);
{ tell the JACK server to call `jack_shutdown()' if
it ever shuts down, either entirely, or if it
just decides to stop calling us.
}
jack_on_shutdown (client, @jack_shutdown, nil);
{ display the current sample rate.
}
Writeln ('engine sample rate: ',
jack_get_sample_rate (client));
{ create two ports }
input_port := jack_port_register (client, 'input',
JACK_DEFAULT_AUDIO_TYPE,
Ord(JackPortIsInput), 0);
output_port := jack_port_register (client, 'output',
JACK_DEFAULT_AUDIO_TYPE,
Ord(JackPortIsOutput), 0);
if (input_port = nil) or (output_port = nil) then
begin
Writeln(StdErr, 'no more JACK ports available');
Halt (1);
end;
{ Tell the JACK server that we are ready to roll. Our
process() callback will start running now. }
if jack_activate (client) <> 0 then
begin
Writeln (StdErr, 'cannot activate client');
Halt (1);
end;
{ Connect the ports. You can't do this before the client is
activated, because we can't make connections to clients
that aren't running. Note the confusing (but necessary)
orientation of the driver backend ports: playback ports are
"input" to the backend, and capture ports are "output" from
it.
}
ports := jack_get_ports (client, nil, nil,
Ord(JackPortIsPhysical) or Ord(JackPortIsOutput));
if ports = nil then
begin
Writeln(StdErr, 'no physical capture ports');
Halt (1);
end;
if jack_connect (client, ports[0], jack_port_name (input_port)) <> 0 then
begin
Writeln (StdErr, 'cannot connect input ports');
end;
jack_free (ports);
ports := jack_get_ports (client, nil, nil,
Ord(JackPortIsPhysical) or Ord(JackPortIsInput));
if ports = nil then
begin
Writeln(StdErr, 'no physical playback ports');
Halt (1);
end;
if jack_connect (client, jack_port_name (output_port), ports[0]) <> 0 then
begin
Writeln (StdErr, 'cannot connect output ports');
end;
jack_free (ports);
{ keep running until stopped by the user }
repeat
sleep (High(Cardinal));
until False;
{ this is never reached but if the program
had some other way to exit besides being killed,
they would be important to call.
}
jack_client_close (client);
Halt (0);
end.

View File

@ -39,6 +39,7 @@ begin
P.ExamplePath.Add('examples');
P.Targets.AddExampleProgram('simple_client.pp');
P.Targets.AddExampleProgram('latent_client.pp');
P.NamespaceMap:='namespaces.lst';