this repo has no description
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add real WAV tone generation to audio example

- Implement WAV file generation for sine wave tones
- Add base64 encoding function for binary data
- Update audio tool and prompt to generate real audio content
- Support duration and amplitude parameters

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

+187 -15
+187 -15
bin/audio_example.ml
··· 1 1 open Mcp 2 2 open Mcp_sdk 3 3 4 + (* WAV file format helper module *) 5 + module Wav = struct 6 + (* Simple WAV file generation for a sine wave *) 7 + let generate_sine_wave ~frequency ~duration ~sample_rate ~amplitude = 8 + (* WAV parameters *) 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 15 + 16 + (* Create buffer for the WAV data *) 17 + let buffer = Buffer.create (44 + data_size) in 18 + 19 + (* Write WAV header *) 20 + (* "RIFF" chunk *) 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"; 28 + 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); 39 + 40 + (* Sample rate *) 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)); 45 + 46 + (* Byte rate *) 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)); 51 + 52 + (* Block align *) 53 + Buffer.add_char buffer (char_of_int block_align); 54 + Buffer.add_char buffer (char_of_int 0); 55 + 56 + (* Bits per sample *) 57 + Buffer.add_char buffer (char_of_int bits_per_sample); 58 + Buffer.add_char buffer (char_of_int 0); 59 + 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)); 66 + 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)); 75 + done; 76 + 77 + Buffer.contents buffer 78 + 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 83 + 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 88 + 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)); 92 + 93 + if i * 3 + 1 < String.length bytes then 94 + Buffer.add_char buffer (String.get alphabet ((n lsr 6) land 63)) 95 + else 96 + Buffer.add_char buffer '='; 97 + 98 + if i * 3 + 2 < String.length bytes then 99 + Buffer.add_char buffer (String.get alphabet (n land 63)) 100 + else 101 + Buffer.add_char buffer '='; 102 + in 103 + 104 + for i = 0 to (String.length data + 2) / 3 - 1 do 105 + encode_block i data 106 + done; 107 + 108 + Buffer.contents buffer 109 + end 110 + 4 111 (* Helper for extracting string value from JSON *) 5 112 let get_string_param json name = 6 113 match json with ··· 49 156 ~schema_properties:[ 50 157 ("text", "string", "The text to describe with audio"); 51 158 ("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)"); 52 161 ] 53 162 ~schema_required:["text"] 54 163 (fun args -> 55 164 try 56 165 let text = get_string_param args "text" in 166 + 167 + (* Parse parameters with defaults *) 57 168 let frequency = 58 169 try 59 170 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 *) 63 - with _ -> 440 171 + | Some (`Int f) -> float_of_int f 172 + | Some (`Float f) -> f 173 + | _ -> 440.0 (* Default to A440 *) 174 + with _ -> 440.0 175 + in 176 + 177 + let duration = 178 + try 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 *) 183 + with _ -> 2.0 184 + in 185 + 186 + let amplitude = 187 + try 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 *) 192 + with _ -> 0.8 193 + in 194 + 195 + (* Generate WAV file for the tone *) 196 + let sample_rate = 44100 in (* CD quality *) 197 + let wav_data = Wav.generate_sine_wave 198 + ~frequency 199 + ~duration 200 + ~sample_rate 201 + ~amplitude 64 202 in 65 203 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 206 + 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)); 69 210 70 211 (* Create a response with both text and audio content *) 71 212 CallToolResult.yojson_of_t CallToolResult.{ 72 213 content = [ 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 } 214 + Text TextContent.{ 215 + text = Printf.sprintf "Description: %s (with %.1f Hz tone for %.1f seconds)" 216 + text frequency duration; 217 + annotations = None 218 + }; 219 + Audio AudioContent.{ 220 + data = base64_audio; 221 + mime_type = "audio/wav"; 222 + annotations = None 223 + } 75 224 ]; 76 225 is_error = false; 77 226 meta = None ··· 98 247 ~arguments:[ 99 248 ("description", Some "Text description to accompany the audio", true); 100 249 ("frequency", Some "Frequency in Hz for the audio tone", false); 250 + ("duration", Some "Duration in seconds for the audio tone", false); 101 251 ] 102 252 (fun args -> 103 253 let description = 104 254 try List.assoc "description" args 105 255 with Not_found -> "No description provided" 106 256 in 257 + 258 + (* Parse frequency with default *) 107 259 let frequency = 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 *) 110 262 in 111 263 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 *) 265 + let duration = 266 + try float_of_string (List.assoc "duration" args) 267 + with _ -> 3.0 (* Default to 3 seconds *) 268 + in 269 + 270 + (* Generate WAV data *) 271 + let sample_rate = 44100 in 272 + let wav_data = Wav.generate_sine_wave 273 + ~frequency 274 + ~duration 275 + ~sample_rate 276 + ~amplitude:0.8 277 + in 278 + 279 + (* Encode WAV data as base64 *) 280 + let base64_audio = Wav.base64_encode wav_data in 281 + 282 + Log.info (Printf.sprintf "Generated %.1f Hz tone for prompt (%.1f seconds, %.1f KB)" 283 + frequency duration 284 + (float_of_int (String.length wav_data) /. 1024.0)); 114 285 115 286 [ 116 287 Prompt.{ ··· 119 290 }; 120 291 Prompt.{ 121 292 role = `User; 122 - content = make_audio_content audio_data "audio/wav" 293 + content = make_audio_content base64_audio "audio/wav" 123 294 }; 124 295 Prompt.{ 125 296 role = `User; 126 - content = make_text_content description 297 + content = make_text_content (Printf.sprintf "%s (%.1f Hz tone for %.1f seconds)" 298 + description frequency duration) 127 299 }; 128 300 Prompt.{ 129 301 role = `Assistant;