Hello, dial

import dev.telequick.sdk.TeleQuickClient;

public class Hello {{
    public static void main(String[] args) throws Exception {{
        TeleQuickClient client = new TeleQuickClient(
            "quic://engine.telequick.dev:9090",
            "/etc/telequick/service-account.json"
        );

        client.dial(
            "sip:+15551234567@example.sip.livekit.cloud",  // to
            "default"                                       // trunkId
        );

        Thread.sleep(30_000);
    }}
}}
The two-arg dial(...) overload uses sensible defaults for everything else. For full control:
client.dial(
    /* to */                  "sip:+15551234567@example.sip.livekit.cloud",
    /* trunkId */             "default",
    /* callFrom */            "+18005550100",
    /* maxDurationMs */       60_000,
    /* defaultApp */          1,                // PARK
    /* defaultAppArgs */      "",
    /* aiWs */                "",
    /* aiQuic */              "",
    /* autoBargeIn */         false,
    /* bargeInPatienceMs */   250,
    /* clientId override */   null
);

Receiving audio and events

client.onAudioFrame = (payload) -> {{
    // payload is the AudioFrame envelope. Decode it via JNA helpers.
    var frame = TeleQuickClient.CoreFFI.INSTANCE.telequick_deserialize_audio_frame(
        com.sun.jna.Native.toByteArrayPointer(payload), payload.length
    );
    // frame.payload is the raw codec bytes.
    System.out.println("audio bytes: " + payload.length);
}};

client.onCallEvent = (payload) -> {{
    var ev = TeleQuickClient.CoreFFI.INSTANCE.telequick_deserialize_call_event(
        com.sun.jna.Native.toByteArrayPointer(payload), payload.length
    );
    System.out.printf("event %s -> %s%n", ev.call_sid, ev.status);
}};
Set callbacks before the first RPC.

Pushing audio

byte[] pcm = Files.readAllBytes(Paths.get("./prompt.alaw"));
final int frame = 160;
long seq = 0;
for (int i = 0; i < pcm.length; i += frame) {{
    int end = Math.min(i + frame, pcm.length);
    byte[] slice = Arrays.copyOfRange(pcm, i, end);
    client.pushAudio(callSid, slice, "PCMU", seq++, false);
    Thread.sleep(20);
}}

Hanging up

client.terminate(callSid);

Threading model

  • dial, terminate, barge, … are blocking: they marshal arguments through JNA, write to Netty’s QUIC channel, and return.
  • onAudioFrame and onCallEvent run on Netty’s I/O event loop. Don’t block them — hand work off to your own executor.
ExecutorService work = Executors.newFixedThreadPool(4);

client.onAudioFrame = (p) -> work.submit(() -> processAudio(p));