···
4
+
(* WAV file format helper module *)
6
+
(* Simple WAV file generation for a sine wave *)
7
+
let generate_sine_wave ~frequency ~duration ~sample_rate ~amplitude =
9
+
let num_channels = 1 in (* Mono *)
10
+
let bits_per_sample = 16 in
11
+
let byte_rate = sample_rate * num_channels * bits_per_sample / 8 in
12
+
let block_align = num_channels * bits_per_sample / 8 in
13
+
let num_samples = int_of_float (float_of_int sample_rate *. duration) in
14
+
let data_size = num_samples * block_align in
16
+
(* Create buffer for the WAV data *)
17
+
let buffer = Buffer.create (44 + data_size) in
19
+
(* Write WAV header *)
21
+
Buffer.add_string buffer "RIFF";
22
+
let file_size = 36 + data_size in
23
+
Buffer.add_char buffer (char_of_int (file_size land 0xff));
24
+
Buffer.add_char buffer (char_of_int ((file_size lsr 8) land 0xff));
25
+
Buffer.add_char buffer (char_of_int ((file_size lsr 16) land 0xff));
26
+
Buffer.add_char buffer (char_of_int ((file_size lsr 24) land 0xff));
27
+
Buffer.add_string buffer "WAVE";
29
+
(* "fmt " sub-chunk *)
30
+
Buffer.add_string buffer "fmt ";
31
+
Buffer.add_char buffer (char_of_int 16); (* Sub-chunk size (16 for PCM) *)
32
+
Buffer.add_char buffer (char_of_int 0);
33
+
Buffer.add_char buffer (char_of_int 0);
34
+
Buffer.add_char buffer (char_of_int 0);
35
+
Buffer.add_char buffer (char_of_int 1); (* Audio format (1 for PCM) *)
36
+
Buffer.add_char buffer (char_of_int 0);
37
+
Buffer.add_char buffer (char_of_int num_channels); (* Number of channels *)
38
+
Buffer.add_char buffer (char_of_int 0);
41
+
Buffer.add_char buffer (char_of_int (sample_rate land 0xff));
42
+
Buffer.add_char buffer (char_of_int ((sample_rate lsr 8) land 0xff));
43
+
Buffer.add_char buffer (char_of_int ((sample_rate lsr 16) land 0xff));
44
+
Buffer.add_char buffer (char_of_int ((sample_rate lsr 24) land 0xff));
47
+
Buffer.add_char buffer (char_of_int (byte_rate land 0xff));
48
+
Buffer.add_char buffer (char_of_int ((byte_rate lsr 8) land 0xff));
49
+
Buffer.add_char buffer (char_of_int ((byte_rate lsr 16) land 0xff));
50
+
Buffer.add_char buffer (char_of_int ((byte_rate lsr 24) land 0xff));
53
+
Buffer.add_char buffer (char_of_int block_align);
54
+
Buffer.add_char buffer (char_of_int 0);
56
+
(* Bits per sample *)
57
+
Buffer.add_char buffer (char_of_int bits_per_sample);
58
+
Buffer.add_char buffer (char_of_int 0);
60
+
(* "data" sub-chunk *)
61
+
Buffer.add_string buffer "data";
62
+
Buffer.add_char buffer (char_of_int (data_size land 0xff));
63
+
Buffer.add_char buffer (char_of_int ((data_size lsr 8) land 0xff));
64
+
Buffer.add_char buffer (char_of_int ((data_size lsr 16) land 0xff));
65
+
Buffer.add_char buffer (char_of_int ((data_size lsr 24) land 0xff));
67
+
(* Generate sine wave data *)
68
+
let max_amplitude = float_of_int (1 lsl (bits_per_sample - 1)) -. 1.0 in
69
+
for i = 0 to num_samples - 1 do
70
+
let t = float_of_int i /. float_of_int sample_rate in
71
+
let value = int_of_float (amplitude *. max_amplitude *. sin (2.0 *. Float.pi *. frequency *. t)) in
72
+
(* Write 16-bit sample (little-endian) *)
73
+
Buffer.add_char buffer (char_of_int (value land 0xff));
74
+
Buffer.add_char buffer (char_of_int ((value lsr 8) land 0xff));
77
+
Buffer.contents buffer
79
+
(* Encode binary data as base64 *)
80
+
let base64_encode data =
81
+
let buffer = Buffer.create (4 * (String.length data + 2) / 3) in
82
+
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" in
84
+
let encode_block i bytes =
85
+
let b1 = Char.code (String.get bytes (i * 3)) in
86
+
let b2 = if i * 3 + 1 < String.length bytes then Char.code (String.get bytes (i * 3 + 1)) else 0 in
87
+
let b3 = if i * 3 + 2 < String.length bytes then Char.code (String.get bytes (i * 3 + 2)) else 0 in
89
+
let n = (b1 lsl 16) lor (b2 lsl 8) lor b3 in
90
+
Buffer.add_char buffer (String.get alphabet ((n lsr 18) land 63));
91
+
Buffer.add_char buffer (String.get alphabet ((n lsr 12) land 63));
93
+
if i * 3 + 1 < String.length bytes then
94
+
Buffer.add_char buffer (String.get alphabet ((n lsr 6) land 63))
96
+
Buffer.add_char buffer '=';
98
+
if i * 3 + 2 < String.length bytes then
99
+
Buffer.add_char buffer (String.get alphabet (n land 63))
101
+
Buffer.add_char buffer '=';
104
+
for i = 0 to (String.length data + 2) / 3 - 1 do
105
+
encode_block i data
108
+
Buffer.contents buffer
(* Helper for extracting string value from JSON *)
let get_string_param json name =
···
("text", "string", "The text to describe with audio");
("frequency", "number", "The frequency in Hz for the tone (optional)");
159
+
("duration", "number", "The duration in seconds for the tone (optional)");
160
+
("amplitude", "number", "The amplitude (0.0-1.0) for the tone (optional)");
~schema_required:["text"]
let text = get_string_param args "text" in
167
+
(* Parse parameters with defaults *)
match List.assoc_opt "frequency" (match args with `Assoc l -> l | _ -> []) with
60
-
| Some (`Int f) -> f
61
-
| Some (`Float f) -> int_of_float f
62
-
| _ -> 440 (* Default to A440 *)
171
+
| Some (`Int f) -> float_of_int f
172
+
| Some (`Float f) -> f
173
+
| _ -> 440.0 (* Default to A440 *)
179
+
match List.assoc_opt "duration" (match args with `Assoc l -> l | _ -> []) with
180
+
| Some (`Int d) -> float_of_int d
181
+
| Some (`Float d) -> d
182
+
| _ -> 2.0 (* Default to 2 seconds *)
188
+
match List.assoc_opt "amplitude" (match args with `Assoc l -> l | _ -> []) with
189
+
| Some (`Int a) -> float_of_int a
190
+
| Some (`Float a) -> a
191
+
| _ -> 0.8 (* Default to 80% amplitude *)
195
+
(* Generate WAV file for the tone *)
196
+
let sample_rate = 44100 in (* CD quality *)
197
+
let wav_data = Wav.generate_sine_wave
66
-
(* This is just a placeholder for actual audio data *)
67
-
(* In a real implementation, you would generate a WAV or MP3 file and base64 encode it *)
68
-
let audio_data = Printf.sprintf "BASE64_ENCODED_AUDIO_DATA_FOR_%d_HZ_TONE" frequency in
204
+
(* Encode WAV data as base64 *)
205
+
let base64_audio = Wav.base64_encode wav_data in
207
+
Log.info (Printf.sprintf "Generated %d Hz tone for %.1f seconds (%.1f KB)"
208
+
(int_of_float frequency) duration
209
+
(float_of_int (String.length wav_data) /. 1024.0));
(* Create a response with both text and audio content *)
CallToolResult.yojson_of_t CallToolResult.{
73
-
Text TextContent.{ text = Printf.sprintf "Description: %s (with %d Hz tone)" text frequency; annotations = None };
74
-
Audio AudioContent.{ data = audio_data; mime_type = "audio/wav"; annotations = None }
215
+
text = Printf.sprintf "Description: %s (with %.1f Hz tone for %.1f seconds)"
216
+
text frequency duration;
219
+
Audio AudioContent.{
220
+
data = base64_audio;
221
+
mime_type = "audio/wav";
···
("description", Some "Text description to accompany the audio", true);
("frequency", Some "Frequency in Hz for the audio tone", false);
250
+
("duration", Some "Duration in seconds for the audio tone", false);
try List.assoc "description" args
with Not_found -> "No description provided"
258
+
(* Parse frequency with default *)
108
-
try int_of_string (List.assoc "frequency" args)
109
-
with _ -> 440 (* Default to A440 *)
260
+
try float_of_string (List.assoc "frequency" args)
261
+
with _ -> 440.0 (* Default to A440 *)
112
-
(* Placeholder for audio data *)
113
-
let audio_data = Printf.sprintf "BASE64_ENCODED_AUDIO_DATA_FOR_%d_HZ_TONE" frequency in
264
+
(* Parse duration with default *)
266
+
try float_of_string (List.assoc "duration" args)
267
+
with _ -> 3.0 (* Default to 3 seconds *)
270
+
(* Generate WAV data *)
271
+
let sample_rate = 44100 in
272
+
let wav_data = Wav.generate_sine_wave
279
+
(* Encode WAV data as base64 *)
280
+
let base64_audio = Wav.base64_encode wav_data in
282
+
Log.info (Printf.sprintf "Generated %.1f Hz tone for prompt (%.1f seconds, %.1f KB)"
284
+
(float_of_int (String.length wav_data) /. 1024.0));
···
122
-
content = make_audio_content audio_data "audio/wav"
293
+
content = make_audio_content base64_audio "audio/wav"
126
-
content = make_text_content description
297
+
content = make_text_content (Printf.sprintf "%s (%.1f Hz tone for %.1f seconds)"
298
+
description frequency duration)