From dcb9e1fac4990275e03d8263c459f953a68d3e14 Mon Sep 17 00:00:00 2001 From: Hanan Date: Thu, 20 Nov 2025 23:31:54 +0200 Subject: [PATCH 1/3] feat: configurable sample rates --- src/index.ts | 54 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index 8f4e660..a48b255 100644 --- a/src/index.ts +++ b/src/index.ts @@ -33,6 +33,12 @@ export interface AgentConfig { pre_speech_pad_frames?: number; frame_samples?: number; }; + client: { + sample_rate: number; + }; + response: { + sample_rate: number; + }; } interface AuthorizeSessionRequestParams { @@ -49,6 +55,8 @@ type AuthorizeSessionRequest = (params: AuthorizeSessionRequestParams) => Promis const NOOP = () => {}; const DEFAULT_WS_URL = 'wss://api.layercode.com/v1/agents/web/websocket'; +const DEFAULT_CLIENT_SAMPLE_RATE = 8000; +const DEFAULT_RESPONSE_SAMPLE_RATE = 16000; // SDK version - updated when publishing const SDK_VERSION = '2.7.0'; @@ -349,6 +357,8 @@ class LayercodeClient implements ILayercodeClient { private options: NormalizedLayercodeClientOptions; private wavRecorder: WavRecorder; private wavPlayer: WavStreamPlayer; + private clientSampleRate: number; + private responseSampleRate: number; private vad: MicVAD | null; private ws: WebSocket | null; private audioInput: boolean; @@ -421,11 +431,12 @@ class LayercodeClient implements ILayercodeClient { this.AMPLITUDE_MONITORING_SAMPLE_RATE = 2; this._websocketUrl = DEFAULT_WS_URL; - - this.wavRecorder = new WavRecorder({ sampleRate: 8000 }); // TODO should be set my fetched agent config + this.clientSampleRate = DEFAULT_CLIENT_SAMPLE_RATE; + this.responseSampleRate = DEFAULT_RESPONSE_SAMPLE_RATE; + this.wavRecorder = new WavRecorder({ sampleRate: DEFAULT_CLIENT_SAMPLE_RATE }); this.wavPlayer = new WavStreamPlayer({ finishedPlayingCallback: this._clientResponseAudioReplayFinished.bind(this), - sampleRate: 16000, // TODO should be set my fetched agent config + sampleRate: DEFAULT_RESPONSE_SAMPLE_RATE, }); this.vad = null; this.ws = null; @@ -885,6 +896,37 @@ class LayercodeClient implements ILayercodeClient { } } + private async applyAgentSampleRates(config: AgentConfig): Promise { + const desiredRecorderSampleRate = config?.client?.sample_rate ?? DEFAULT_CLIENT_SAMPLE_RATE; + const desiredPlayerSampleRate = config?.response?.sample_rate ?? DEFAULT_RESPONSE_SAMPLE_RATE; + + if (desiredRecorderSampleRate !== this.clientSampleRate) { + await this._recreateWavRecorder(desiredRecorderSampleRate); + } + + if (desiredPlayerSampleRate !== this.responseSampleRate) { + this._recreateWavPlayer(desiredPlayerSampleRate); + } + } + + private async _recreateWavRecorder(sampleRate: number): Promise { + await this.wavRecorder.quit(); + this.wavRecorder = new WavRecorder({ sampleRate }); + this.clientSampleRate = sampleRate; + this.stopRecorderAmplitude = undefined; + } + + private _recreateWavPlayer(sampleRate: number): void { + this.wavPlayer.stop?.(); + this.wavPlayer.disconnect(); + this.wavPlayer = new WavStreamPlayer({ + finishedPlayingCallback: this._clientResponseAudioReplayFinished.bind(this), + sampleRate, + }); + this.responseSampleRate = sampleRate; + this.stopPlayerAmplitude = undefined; + } + async audioInputConnect(): Promise { // Turn mic ON await this.wavRecorder.requestPermission(); @@ -990,6 +1032,10 @@ class LayercodeClient implements ILayercodeClient { // Get conversation key from server const authorizeSessionResponseBody = await this.authorizeSession(); + const config: AgentConfig = authorizeSessionResponseBody.config; + console.log('AgentConfig', config); + + await this.applyAgentSampleRates(config); await this.connectToAudioInput(); @@ -1000,8 +1046,6 @@ class LayercodeClient implements ILayercodeClient { })}` ); this.ws = ws; - const config: AgentConfig = authorizeSessionResponseBody.config; - console.log('AgentConfig', config); // Store VAD configuration this.setupVadConfig(config); From fa7ad94ab1d92428e5886102ff56544d788d8c01 Mon Sep 17 00:00:00 2001 From: Hanan Date: Fri, 21 Nov 2025 01:06:29 +0200 Subject: [PATCH 2/3] update SDK_VERSION --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index a48b255..0cc3612 100644 --- a/src/index.ts +++ b/src/index.ts @@ -59,7 +59,7 @@ const DEFAULT_CLIENT_SAMPLE_RATE = 8000; const DEFAULT_RESPONSE_SAMPLE_RATE = 16000; // SDK version - updated when publishing -const SDK_VERSION = '2.7.0'; +const SDK_VERSION = '2.9.0'; export type LayercodeAudioInputDevice = (MediaDeviceInfo & { default: boolean }) & { label: string; From ed2d41426587488b57fd5a9a55084269ea3ec6ef Mon Sep 17 00:00:00 2001 From: Hanan Date: Fri, 21 Nov 2025 01:07:16 +0200 Subject: [PATCH 3/3] remove unneeded log --- src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 0cc3612..b6f7cd5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1033,7 +1033,6 @@ class LayercodeClient implements ILayercodeClient { // Get conversation key from server const authorizeSessionResponseBody = await this.authorizeSession(); const config: AgentConfig = authorizeSessionResponseBody.config; - console.log('AgentConfig', config); await this.applyAgentSampleRates(config);