@ray0404/zig-audio-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +302 -0
- package/GEMINI.md +266 -0
- package/README.md +444 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1203 -0
- package/dist/index.js.map +1 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +162 -0
- package/dist/test.js.map +1 -0
- package/evaluation.xml +59 -0
- package/mcp-config.json +12 -0
- package/mcp.json +5 -0
- package/package.json +46 -0
- package/src/index.ts +1281 -0
- package/src/test.ts +195 -0
- package/tsconfig.json +20 -0
- package/tsconfig.tsbuildinfo +1 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,1281 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
// Library information database
|
|
7
|
+
const ZIG_AUDIO_LIBRARIES = {
|
|
8
|
+
zang: {
|
|
9
|
+
name: "zang",
|
|
10
|
+
description: "Audio synthesis library with generators, effects, and filters. Low-level, no dynamic allocations.",
|
|
11
|
+
repo: "https://github.com/dbandstra/zang",
|
|
12
|
+
license: "MIT",
|
|
13
|
+
features: ["oscillators", "envelopes", "filters", "effects", "no-allocations"],
|
|
14
|
+
categories: ["synthesis", "effects", "filters"],
|
|
15
|
+
installation: "git clone https://github.com/dbandstra/zang.git && cd zang && zig build",
|
|
16
|
+
version: "0.12+ compatible",
|
|
17
|
+
examples: ["SineOsc", "Envelope", "Filter", "Sampler"],
|
|
18
|
+
api_docs: "https://github.com/dbandstra/zang#features",
|
|
19
|
+
use_cases: ["Game audio", "Real-time synthesis", "Embedded audio"]
|
|
20
|
+
},
|
|
21
|
+
zaudio: {
|
|
22
|
+
name: "zaudio",
|
|
23
|
+
description: "Zig wrapper for miniaudio. Supports playback, recording, and node graph audio processing.",
|
|
24
|
+
repo: "https://github.com/MasterQ32/zaudio",
|
|
25
|
+
license: "MIT",
|
|
26
|
+
features: ["playback", "recording", "node-graph", "streaming"],
|
|
27
|
+
categories: ["playback", "recording", "wrapper"],
|
|
28
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/MasterQ32/zaudio#master\"",
|
|
29
|
+
version: "Zig 0.11+",
|
|
30
|
+
examples: ["Device playback", "Recording", "Node graphs"],
|
|
31
|
+
api_docs: "https://github.com/MasterQ32/zaudio",
|
|
32
|
+
use_cases: ["Desktop audio apps", "Cross-platform playback", "Audio processing pipelines"]
|
|
33
|
+
},
|
|
34
|
+
bonk: {
|
|
35
|
+
name: "bonk",
|
|
36
|
+
description: "DSP objects including filters and delay lines. MPL-2.0 licensed.",
|
|
37
|
+
repo: "https://github.com/chr15m/bonk",
|
|
38
|
+
license: "MPL-2.0",
|
|
39
|
+
features: ["filters", "delay-lines", "waveshapers"],
|
|
40
|
+
categories: ["dsp", "effects"],
|
|
41
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/chr15m/bonk#master\"",
|
|
42
|
+
version: "Zig 0.10+",
|
|
43
|
+
examples: ["IIR filters", "Delay effects", "Waveshaping"],
|
|
44
|
+
api_docs: "https://github.com/chr15m/bonk",
|
|
45
|
+
use_cases: ["Audio effects processing", "Real-time DSP", "Creative coding"]
|
|
46
|
+
},
|
|
47
|
+
pcm: {
|
|
48
|
+
name: "pcm",
|
|
49
|
+
description: "PCM audio file handling for WAV and AIFF formats.",
|
|
50
|
+
repo: "https://github.com/Hejsil/pcm",
|
|
51
|
+
license: "MIT",
|
|
52
|
+
features: ["wav", "aiff", "io", "encoding"],
|
|
53
|
+
categories: ["file-io", "encoding"],
|
|
54
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/Hejsil/pcm#master\"",
|
|
55
|
+
version: "Zig 0.9+",
|
|
56
|
+
examples: ["WAV reading/writing", "AIFF support", "Sample format conversion"],
|
|
57
|
+
api_docs: "https://github.com/Hejsil/pcm",
|
|
58
|
+
use_cases: ["Audio file processing", "Sample libraries", "Format conversion"]
|
|
59
|
+
},
|
|
60
|
+
"zig-liquid-dsp": {
|
|
61
|
+
name: "zig-liquid-dsp",
|
|
62
|
+
description: "DSP library for software-defined radio. Complex signal processing.",
|
|
63
|
+
repo: "https://github.com/ggerard/zig-liquid-dsp",
|
|
64
|
+
license: "MIT",
|
|
65
|
+
features: ["filters", "modulation", "fourier", "sdr"],
|
|
66
|
+
categories: ["sdr", "dsp"],
|
|
67
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/ggerard/zig-liquid-dsp#master\"",
|
|
68
|
+
version: "Zig 0.11+",
|
|
69
|
+
examples: ["FIR/IIR filters", "Modulation schemes", "FFT processing"],
|
|
70
|
+
api_docs: "https://github.com/ggerard/zig-liquid-dsp",
|
|
71
|
+
use_cases: ["SDR applications", "Signal analysis", "Communications"]
|
|
72
|
+
},
|
|
73
|
+
dalek: {
|
|
74
|
+
name: "dalek",
|
|
75
|
+
description: "Experimental data-oriented audio engine.",
|
|
76
|
+
repo: "https://github.com/chr15m/dalek",
|
|
77
|
+
license: "MIT",
|
|
78
|
+
features: ["data-oriented", "performance", "audio-engine"],
|
|
79
|
+
categories: ["engine", "experimental"],
|
|
80
|
+
installation: "Add to build.zig.zon: .url = \"git+https://github.com/chr15m/dalek#master\"",
|
|
81
|
+
version: "Zig 0.10+",
|
|
82
|
+
examples: ["Entity-component audio", "High-performance mixing", "Modular processing"],
|
|
83
|
+
api_docs: "https://github.com/chr15m/dalek",
|
|
84
|
+
use_cases: ["Game engines", "High-performance audio", "Large-scale audio processing"]
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Filter information for DSP explanations
|
|
89
|
+
const DSP_FILTERS = {
|
|
90
|
+
lowpass: {
|
|
91
|
+
name: "Low Pass Filter",
|
|
92
|
+
description: "Allows frequencies below the cutoff to pass through, attenuating higher frequencies.",
|
|
93
|
+
formula: "y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] - b1 * y[n-1] - b2 * y[n-2]",
|
|
94
|
+
useCases: ["sub-bass smoothing", "removing high-frequency noise", "warmth/room modeling"],
|
|
95
|
+
zigLibraries: ["zang", "bonk", "zig-liquid-dsp"]
|
|
96
|
+
},
|
|
97
|
+
highpass: {
|
|
98
|
+
name: "High Pass Filter",
|
|
99
|
+
description: "Allows frequencies above the cutoff to pass through, attenuating lower frequencies.",
|
|
100
|
+
formula: "y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] - b1 * y[n-1] - b2 * y[n-2]",
|
|
101
|
+
useCases: ["DC offset removal", "rumble removal", "clarifying midrange"],
|
|
102
|
+
zigLibraries: ["zang", "bonk", "zig-liquid-dsp"]
|
|
103
|
+
},
|
|
104
|
+
bandpass: {
|
|
105
|
+
name: "Band Pass Filter",
|
|
106
|
+
description: "Allows frequencies within a specific band to pass through.",
|
|
107
|
+
formula: "y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] - b1 * y[n-1] - b2 * y[n-2]",
|
|
108
|
+
useCases: ["isolating instrument frequencies", "telephone effect", " formant simulation"],
|
|
109
|
+
zigLibraries: ["zang", "bonk", "zig-liquid-dsp"]
|
|
110
|
+
},
|
|
111
|
+
notch: {
|
|
112
|
+
name: "Notch Filter",
|
|
113
|
+
description: "Attenuates a specific frequency band while allowing others to pass.",
|
|
114
|
+
formula: "y[n] = a0 * x[n] + a1 * x[n-1] + a2 * x[n-2] - b1 * y[n-1] - b2 * y[n-2]",
|
|
115
|
+
useCases: ["hum removal", "feedback suppression", "EQ notching"],
|
|
116
|
+
zigLibraries: ["bonk", "zig-liquid-dsp"]
|
|
117
|
+
},
|
|
118
|
+
"biquad": {
|
|
119
|
+
name: "Biquad Filter",
|
|
120
|
+
description: "Second-order IIR filter using two poles and two zeros. Generic form for many filter types.",
|
|
121
|
+
formula: "y[n] = (b0*x[n] + b1*x[n-1] + b2*x[n-2] - a1*y[n-1] - a2*y[n-2]) / a0",
|
|
122
|
+
useCases: ["parametric EQ", "crossover filters", "tone control"],
|
|
123
|
+
zigLibraries: ["zang", "bonk"]
|
|
124
|
+
},
|
|
125
|
+
"one-pole": {
|
|
126
|
+
name: "One-Pole Filter",
|
|
127
|
+
description: "Simple first-order filter with single pole. Good for smoothing and DC removal.",
|
|
128
|
+
formula: "y[n] = alpha * x[n] + (1 - alpha) * y[n-1]",
|
|
129
|
+
useCases: ["smoothing", "DC removal", "simple envelopes"],
|
|
130
|
+
zigLibraries: ["zang"]
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Tool input validation schemas
|
|
135
|
+
const ListLibrariesInput = z.object({
|
|
136
|
+
category: z.enum(["synthesis", "effects", "filters", "dsp", "playback", "recording", "file-io", "encoding", "sdr", "engine", "wrapper", "experimental", "all"]).optional(),
|
|
137
|
+
feature: z.string().optional()
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const GetLibraryInfoInput = z.object({
|
|
141
|
+
library: z.enum(["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek"])
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const ExplainFilterInput = z.object({
|
|
145
|
+
filter_type: z.enum(["lowpass", "highpass", "bandpass", "notch", "biquad", "one-pole"])
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const GenerateCodeInput = z.object({
|
|
149
|
+
task: z.enum(["oscillator", "envelope", "filter", "delay", "mixer", "player", "recorder", "file-write", "file-read"]),
|
|
150
|
+
parameters: z.record(z.union([z.string(), z.number(), z.boolean()])).optional()
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const ListResourcesInput = z.object({
|
|
154
|
+
resource_type: z.enum(["tutorial", "reference", "example", "article"]).optional()
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const RecommendLibraryInput = z.object({
|
|
158
|
+
use_case: z.string().describe("Describe what you want to build (e.g., 'synthesizer', 'audio player', 'DSP effects')"),
|
|
159
|
+
requirements: z.array(z.string()).optional().describe("Specific requirements like 'low latency', 'no allocations', 'cross-platform'")
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const DesignFilterInput = z.object({
|
|
163
|
+
filter_type: z.enum(["lowpass", "highpass", "bandpass", "notch", "biquad"]),
|
|
164
|
+
parameters: z.object({
|
|
165
|
+
sample_rate: z.number().min(1).max(192000),
|
|
166
|
+
cutoff: z.number().optional().describe("Cutoff frequency in Hz"),
|
|
167
|
+
q: z.number().optional().describe("Quality factor (resonance)"),
|
|
168
|
+
gain: z.number().optional().describe("Gain in dB for peaking/shelving filters")
|
|
169
|
+
})
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const VerifyCodeInput = z.object({
|
|
173
|
+
code: z.string().describe("Zig code to verify for basic syntax and imports"),
|
|
174
|
+
libraries: z.array(z.string()).optional().describe("Expected libraries that should be imported")
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const ProjectTemplateInput = z.object({
|
|
178
|
+
project_type: z.enum(["synthesizer", "audio-player", "dsp-processor", "file-processor", "game-audio"]),
|
|
179
|
+
features: z.array(z.string()).optional().describe("Additional features to include")
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Tool handlers
|
|
183
|
+
async function handleListLibraries(args: unknown) {
|
|
184
|
+
const input = ListLibrariesInput.parse(args);
|
|
185
|
+
|
|
186
|
+
let libraries = Object.values(ZIG_AUDIO_LIBRARIES);
|
|
187
|
+
|
|
188
|
+
if (input.category && input.category !== "all") {
|
|
189
|
+
libraries = libraries.filter(lib => lib.categories.includes(input.category!));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (input.feature) {
|
|
193
|
+
libraries = libraries.filter(lib =>
|
|
194
|
+
lib.features.some(f => f.toLowerCase().includes(input.feature!.toLowerCase()))
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
content: [{
|
|
200
|
+
type: "text",
|
|
201
|
+
text: JSON.stringify(libraries, null, 2)
|
|
202
|
+
}]
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function handleVerifyCode(args: unknown) {
|
|
207
|
+
const input = VerifyCodeInput.parse(args);
|
|
208
|
+
|
|
209
|
+
const issues: string[] = [];
|
|
210
|
+
const suggestions: string[] = [];
|
|
211
|
+
|
|
212
|
+
// Check for missing std import
|
|
213
|
+
if (input.code.includes("std.math") && !input.code.includes('const std = @import("std");')) {
|
|
214
|
+
issues.push("Missing std import but using std.math");
|
|
215
|
+
suggestions.push('Add: const std = @import("std");');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check for common library imports
|
|
219
|
+
const libraryChecks = [
|
|
220
|
+
{ lib: "zang", pattern: /zang\./, import: 'const zang = @import("zang");' },
|
|
221
|
+
{ lib: "zaudio", pattern: /zaudio\./, import: 'const zaudio = @import("zaudio");' },
|
|
222
|
+
{ lib: "bonk", pattern: /bonk\./, import: 'const bonk = @import("bonk");' },
|
|
223
|
+
{ lib: "pcm", pattern: /pcm\./, import: 'const pcm = @import("pcm");' }
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
for (const check of libraryChecks) {
|
|
227
|
+
if (input.code.match(check.pattern) && !input.code.includes(check.import)) {
|
|
228
|
+
issues.push(`Using ${check.lib} but missing import`);
|
|
229
|
+
suggestions.push(`Add: ${check.import}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Check for basic syntax issues
|
|
234
|
+
if (input.code.includes("main()") && !input.code.includes("pub fn main")) {
|
|
235
|
+
issues.push("main function should be declared as 'pub fn main'");
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Check for common mistakes
|
|
239
|
+
if (input.code.includes("try ") && !input.code.includes("!void")) {
|
|
240
|
+
suggestions.push("Consider using error return types (!void) for functions with try");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const verificationResult = {
|
|
244
|
+
code_length: input.code.length,
|
|
245
|
+
issues: issues.length > 0 ? issues : ["No obvious issues detected"],
|
|
246
|
+
suggestions: suggestions.length > 0 ? suggestions : ["Code looks good"],
|
|
247
|
+
libraries_found: libraryChecks.filter(check => input.code.includes(check.lib)).map(check => check.lib)
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
content: [{
|
|
252
|
+
type: "text",
|
|
253
|
+
text: JSON.stringify(verificationResult, null, 2)
|
|
254
|
+
}]
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function handleProjectTemplate(args: unknown) {
|
|
259
|
+
const input = ProjectTemplateInput.parse(args);
|
|
260
|
+
|
|
261
|
+
const templates = {
|
|
262
|
+
synthesizer: {
|
|
263
|
+
build_zig: `const std = @import("std");
|
|
264
|
+
|
|
265
|
+
pub fn build(b: *std.Build) void {
|
|
266
|
+
const target = b.standardTargetOptions(.{});
|
|
267
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
268
|
+
|
|
269
|
+
const exe = b.addExecutable(.{
|
|
270
|
+
.name = "synth",
|
|
271
|
+
.root_source_file = .{ .path = "src/main.zig" },
|
|
272
|
+
.target = target,
|
|
273
|
+
.optimize = optimize,
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
// Add zang dependency
|
|
277
|
+
const zang = b.dependency("zang", .{});
|
|
278
|
+
exe.root_module.addImport("zang", zang.module("zang"));
|
|
279
|
+
|
|
280
|
+
b.installArtifact(exe);
|
|
281
|
+
}`,
|
|
282
|
+
main_zig: `const std = @import("std");
|
|
283
|
+
const zang = @import("zang");
|
|
284
|
+
|
|
285
|
+
pub fn main() !void {
|
|
286
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
287
|
+
defer _ = gpa.deinit();
|
|
288
|
+
const allocator = gpa.allocator();
|
|
289
|
+
|
|
290
|
+
// Initialize synthesizer components
|
|
291
|
+
var sine_osc = zang.SineOsc.init(.{
|
|
292
|
+
.sample_rate = 44100,
|
|
293
|
+
.frequency = 440.0,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
var envelope = zang.Envelope.init(.{
|
|
297
|
+
.attack = 0.1,
|
|
298
|
+
.decay = 0.2,
|
|
299
|
+
.sustain = 0.7,
|
|
300
|
+
.release = 0.3,
|
|
301
|
+
.sample_rate = 44100,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// Audio processing loop would go here
|
|
305
|
+
std.debug.print("Synthesizer initialized\\n", .{});
|
|
306
|
+
}`,
|
|
307
|
+
build_zig_zon: `.{ .name = "synth", .version = "0.1.0", .dependencies = .{
|
|
308
|
+
.zang = .{
|
|
309
|
+
.url = "https://github.com/dbandstra/zang/archive/master.tar.gz",
|
|
310
|
+
.hash = "1220...", // Get actual hash from zig fetch
|
|
311
|
+
},
|
|
312
|
+
} }`
|
|
313
|
+
},
|
|
314
|
+
"audio-player": {
|
|
315
|
+
build_zig: `const std = @import("std");
|
|
316
|
+
|
|
317
|
+
pub fn build(b: *std.Build) void {
|
|
318
|
+
const target = b.standardTargetOptions(.{});
|
|
319
|
+
const optimize = b.standardOptimizeOption(.{});
|
|
320
|
+
|
|
321
|
+
const exe = b.addExecutable(.{
|
|
322
|
+
.name = "player",
|
|
323
|
+
.root_source_file = .{ .path = "src/main.zig" },
|
|
324
|
+
.target = target,
|
|
325
|
+
.optimize = optimize,
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Add zaudio dependency
|
|
329
|
+
const zaudio = b.dependency("zaudio", .{});
|
|
330
|
+
exe.root_module.addImport("zaudio", zaudio.module("zaudio"));
|
|
331
|
+
|
|
332
|
+
b.installArtifact(exe);
|
|
333
|
+
}`,
|
|
334
|
+
main_zig: `const std = @import("std");
|
|
335
|
+
const zaudio = @import("zaudio");
|
|
336
|
+
|
|
337
|
+
pub fn main() !void {
|
|
338
|
+
// Initialize audio device
|
|
339
|
+
var device = try zaudio.Device.init(.{
|
|
340
|
+
.direction = .playback,
|
|
341
|
+
.sample_rate = 44100,
|
|
342
|
+
.channels = 2,
|
|
343
|
+
.format = .f32,
|
|
344
|
+
});
|
|
345
|
+
defer device.deinit();
|
|
346
|
+
|
|
347
|
+
// Start playback
|
|
348
|
+
try device.start();
|
|
349
|
+
|
|
350
|
+
std.debug.print("Audio player initialized\\n", .{});
|
|
351
|
+
}`,
|
|
352
|
+
build_zig_zon: `.{ .name = "player", .version = "0.1.0", .dependencies = .{
|
|
353
|
+
.zaudio = .{
|
|
354
|
+
.url = "https://github.com/MasterQ32/zaudio/archive/master.tar.gz",
|
|
355
|
+
.hash = "1220...", // Get actual hash
|
|
356
|
+
},
|
|
357
|
+
} }`
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const template = (templates as any)[input.project_type] || templates.synthesizer;
|
|
362
|
+
|
|
363
|
+
const result = {
|
|
364
|
+
project_type: input.project_type,
|
|
365
|
+
files: {
|
|
366
|
+
"build.zig": template.build_zig,
|
|
367
|
+
"src/main.zig": template.main_zig,
|
|
368
|
+
"build.zig.zon": template.build_zig_zon
|
|
369
|
+
},
|
|
370
|
+
setup_instructions: [
|
|
371
|
+
"mkdir project-name && cd project-name",
|
|
372
|
+
"Copy the files above to their respective locations",
|
|
373
|
+
"Run 'zig build' to compile",
|
|
374
|
+
"Run 'zig build run' to execute"
|
|
375
|
+
]
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
content: [{
|
|
380
|
+
type: "text",
|
|
381
|
+
text: JSON.stringify(result, null, 2)
|
|
382
|
+
}]
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
async function handleGetLibraryInfo(args: unknown) {
|
|
387
|
+
const input = GetLibraryInfoInput.parse(args);
|
|
388
|
+
const lib = ZIG_AUDIO_LIBRARIES[input.library];
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
content: [{
|
|
392
|
+
type: "text",
|
|
393
|
+
text: JSON.stringify(lib, null, 2)
|
|
394
|
+
}]
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function handleExplainFilter(args: unknown) {
|
|
399
|
+
const input = ExplainFilterInput.parse(args);
|
|
400
|
+
const filter = DSP_FILTERS[input.filter_type];
|
|
401
|
+
|
|
402
|
+
return {
|
|
403
|
+
content: [{
|
|
404
|
+
type: "text",
|
|
405
|
+
text: JSON.stringify(filter, null, 2)
|
|
406
|
+
}]
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function handleGenerateCode(args: unknown) {
|
|
411
|
+
const input = GenerateCodeInput.parse(args);
|
|
412
|
+
|
|
413
|
+
const codeTemplates: Record<string, string> = {
|
|
414
|
+
oscillator: `// Zig oscillator using zang
|
|
415
|
+
const std = @import("std");
|
|
416
|
+
const zang = @import("zang");
|
|
417
|
+
|
|
418
|
+
pub fn main() !void {
|
|
419
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
420
|
+
defer _ = gpa.deinit();
|
|
421
|
+
const allocator = gpa.allocator();
|
|
422
|
+
|
|
423
|
+
// Initialize zang modules
|
|
424
|
+
var modules = zang.modules;
|
|
425
|
+
var sine_osc = zang.SineOsc.init(.{
|
|
426
|
+
.sample_rate = 44100,
|
|
427
|
+
.frequency = 440.0,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// Create audio buffer
|
|
431
|
+
var buffer: [1024]f32 = undefined;
|
|
432
|
+
|
|
433
|
+
// Paint audio into buffer
|
|
434
|
+
sine_osc.paint(buffer[0..]);
|
|
435
|
+
|
|
436
|
+
std.debug.print("Generated {} samples of sine wave\\n", .{buffer.len});
|
|
437
|
+
}`,
|
|
438
|
+
envelope: `// ADSR envelope using zang
|
|
439
|
+
const std = @import("std");
|
|
440
|
+
const zang = @import("zang");
|
|
441
|
+
|
|
442
|
+
pub fn main() !void {
|
|
443
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
444
|
+
defer _ = gpa.deinit();
|
|
445
|
+
const allocator = gpa.allocator();
|
|
446
|
+
|
|
447
|
+
// Initialize zang envelope
|
|
448
|
+
var envelope = zang.Envelope.init(.{
|
|
449
|
+
.attack = 0.1,
|
|
450
|
+
.decay = 0.2,
|
|
451
|
+
.sustain = 0.7,
|
|
452
|
+
.release = 0.3,
|
|
453
|
+
.sample_rate = 44100,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
// Create audio buffer
|
|
457
|
+
var buffer: [1024]f32 = undefined;
|
|
458
|
+
|
|
459
|
+
// Trigger note on
|
|
460
|
+
envelope.setGate(true);
|
|
461
|
+
|
|
462
|
+
// Paint envelope into buffer
|
|
463
|
+
envelope.paint(buffer[0..]);
|
|
464
|
+
|
|
465
|
+
// After some time, trigger note off
|
|
466
|
+
envelope.setGate(false);
|
|
467
|
+
|
|
468
|
+
std.debug.print("Generated ADSR envelope with {} samples\\n", .{buffer.len});
|
|
469
|
+
}`,
|
|
470
|
+
filter: `// Low-pass filter using zang
|
|
471
|
+
const std = @import("std");
|
|
472
|
+
const zang = @import("zang");
|
|
473
|
+
|
|
474
|
+
pub fn main() !void {
|
|
475
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
476
|
+
defer _ = gpa.deinit();
|
|
477
|
+
const allocator = gpa.allocator();
|
|
478
|
+
|
|
479
|
+
// Initialize zang low-pass filter
|
|
480
|
+
var filter = zang.Filter.init(.{
|
|
481
|
+
.type = .lowpass,
|
|
482
|
+
.cutoff = 1000.0,
|
|
483
|
+
.resonance = 0.7,
|
|
484
|
+
.sample_rate = 44100,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Create input and output buffers
|
|
488
|
+
var input: [1024]f32 = undefined;
|
|
489
|
+
var output: [1024]f32 = undefined;
|
|
490
|
+
|
|
491
|
+
// Fill input with noise or signal
|
|
492
|
+
for (&input, 0..) |*sample, i| {
|
|
493
|
+
sample.* = std.rand.float(f32) * 2.0 - 1.0; // White noise
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Process through filter
|
|
497
|
+
filter.process(input[0..], output[0..]);
|
|
498
|
+
|
|
499
|
+
std.debug.print("Applied low-pass filter to {} samples\\n", .{input.len});
|
|
500
|
+
}`,
|
|
501
|
+
delay: `// Delay effect using bonk
|
|
502
|
+
const std = @import("std");
|
|
503
|
+
const bonk = @import("bonk");
|
|
504
|
+
|
|
505
|
+
pub fn main() !void {
|
|
506
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
507
|
+
defer _ = gpa.deinit();
|
|
508
|
+
const allocator = gpa.allocator();
|
|
509
|
+
|
|
510
|
+
// Initialize bonk delay
|
|
511
|
+
var delay = bonk.Delay.init(.{
|
|
512
|
+
.delay_time = 0.3, // 300ms delay
|
|
513
|
+
.feedback = 0.5,
|
|
514
|
+
.mix = 0.3,
|
|
515
|
+
.sample_rate = 44100,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
// Create input and output buffers
|
|
519
|
+
var input: [1024]f32 = undefined;
|
|
520
|
+
var output: [1024]f32 = undefined;
|
|
521
|
+
|
|
522
|
+
// Fill input with some signal
|
|
523
|
+
for (&input, 0..) |*sample, i| {
|
|
524
|
+
sample.* = std.math.sin(@as(f32, @floatFromInt(i)) * 0.01); // Simple tone
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Process through delay
|
|
528
|
+
delay.process(input[0..], output[0..]);
|
|
529
|
+
|
|
530
|
+
std.debug.print("Applied delay effect to {} samples\\n", .{input.len});
|
|
531
|
+
}`,
|
|
532
|
+
mixer: `// Simple audio mixer
|
|
533
|
+
pub fn Mixer(comptime T: type, num_channels: usize) type {
|
|
534
|
+
return struct {
|
|
535
|
+
gains: [num_channels]f32 = .{0} ** num_channels,
|
|
536
|
+
|
|
537
|
+
pub fn setGain(self: *@This(), channel: usize, gain: f32) void {
|
|
538
|
+
if (channel < num_channels) {
|
|
539
|
+
self.gains[channel] = gain;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
pub fn mix(self: *@This(), inputs: [num_channels]f32) f32 {
|
|
544
|
+
var result: f32 = 0;
|
|
545
|
+
for (inputs, self.gains) |input, gain| {
|
|
546
|
+
result += input * gain;
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
}`,
|
|
552
|
+
player: `// Audio playback using zaudio
|
|
553
|
+
const std = @import("std");
|
|
554
|
+
const zaudio = @import("zaudio");
|
|
555
|
+
|
|
556
|
+
pub fn main() !void {
|
|
557
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
558
|
+
defer _ = gpa.deinit();
|
|
559
|
+
const allocator = gpa.allocator();
|
|
560
|
+
|
|
561
|
+
// Initialize zaudio device
|
|
562
|
+
var device = try zaudio.Device.init(.{
|
|
563
|
+
.direction = .playback,
|
|
564
|
+
.sample_rate = 44100,
|
|
565
|
+
.channels = 2,
|
|
566
|
+
.format = .f32,
|
|
567
|
+
});
|
|
568
|
+
defer device.deinit();
|
|
569
|
+
|
|
570
|
+
// Start playback
|
|
571
|
+
try device.start();
|
|
572
|
+
|
|
573
|
+
// Create audio buffer with a sine wave
|
|
574
|
+
var buffer: [4410]f32 = undefined; // 0.1 seconds at 44100 Hz
|
|
575
|
+
for (&buffer, 0..) |*sample, i| {
|
|
576
|
+
const t = @as(f32, @floatFromInt(i)) / 44100.0;
|
|
577
|
+
sample.* = std.math.sin(t * 440.0 * 2.0 * std.math.pi) * 0.3;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Play the buffer
|
|
581
|
+
try device.writeInterleaved(buffer[0..]);
|
|
582
|
+
|
|
583
|
+
// Wait a bit
|
|
584
|
+
std.time.sleep(100 * std.time.ns_per_ms);
|
|
585
|
+
|
|
586
|
+
std.debug.print("Played sine wave for 0.1 seconds\\n", .{});
|
|
587
|
+
}`,
|
|
588
|
+
recorder: `// Audio recording using zaudio
|
|
589
|
+
const std = @import("std");
|
|
590
|
+
const zaudio = @import("zaudio");
|
|
591
|
+
|
|
592
|
+
pub fn main() !void {
|
|
593
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
594
|
+
defer _ = gpa.deinit();
|
|
595
|
+
const allocator = gpa.allocator();
|
|
596
|
+
|
|
597
|
+
// Initialize zaudio device for recording
|
|
598
|
+
var device = try zaudio.Device.init(.{
|
|
599
|
+
.direction = .capture,
|
|
600
|
+
.sample_rate = 44100,
|
|
601
|
+
.channels = 1,
|
|
602
|
+
.format = .f32,
|
|
603
|
+
});
|
|
604
|
+
defer device.deinit();
|
|
605
|
+
|
|
606
|
+
// Start recording
|
|
607
|
+
try device.start();
|
|
608
|
+
|
|
609
|
+
// Record for 1 second
|
|
610
|
+
var buffer: [44100]f32 = undefined;
|
|
611
|
+
const frames_read = try device.readInterleaved(buffer[0..]);
|
|
612
|
+
|
|
613
|
+
std.debug.print("Recorded {} samples from microphone\\n", .{frames_read});
|
|
614
|
+
}`,
|
|
615
|
+
"file-write": `// Write WAV file using pcm
|
|
616
|
+
const std = @import("std");
|
|
617
|
+
const pcm = @import("pcm");
|
|
618
|
+
|
|
619
|
+
pub fn main() !void {
|
|
620
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
621
|
+
defer _ = gpa.deinit();
|
|
622
|
+
const allocator = gpa.allocator();
|
|
623
|
+
|
|
624
|
+
// Create some sample audio data
|
|
625
|
+
var samples = try allocator.alloc(f32, 44100); // 1 second at 44100 Hz
|
|
626
|
+
defer allocator.free(samples);
|
|
627
|
+
|
|
628
|
+
// Fill with a sine wave
|
|
629
|
+
for (samples, 0..) |*sample, i| {
|
|
630
|
+
const t = @as(f32, @floatFromInt(i)) / 44100.0;
|
|
631
|
+
sample.* = std.math.sin(t * 440.0 * 2.0 * std.math.pi) * 0.5;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Write to WAV file
|
|
635
|
+
var wav_writer = try pcm.WavWriter.init(.{
|
|
636
|
+
.filename = "output.wav",
|
|
637
|
+
.channels = .mono,
|
|
638
|
+
.sample_rate = 44100,
|
|
639
|
+
.format = .f32,
|
|
640
|
+
});
|
|
641
|
+
defer wav_writer.deinit();
|
|
642
|
+
|
|
643
|
+
try wav_writer.write(samples);
|
|
644
|
+
|
|
645
|
+
std.debug.print("Wrote {} samples to output.wav\\n", .{samples.len});
|
|
646
|
+
}`,
|
|
647
|
+
"file-read": `// Read WAV file using pcm
|
|
648
|
+
const std = @import("std");
|
|
649
|
+
const pcm = @import("pcm");
|
|
650
|
+
|
|
651
|
+
pub fn main() !void {
|
|
652
|
+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
653
|
+
defer _ = gpa.deinit();
|
|
654
|
+
const allocator = gpa.allocator();
|
|
655
|
+
|
|
656
|
+
// Read WAV file
|
|
657
|
+
var wav_reader = try pcm.WavReader.init("input.wav", allocator);
|
|
658
|
+
defer wav_reader.deinit();
|
|
659
|
+
|
|
660
|
+
std.debug.print("WAV file: {} Hz, {} channels, {} frames\\n",
|
|
661
|
+
.{wav_reader.sampleRate(), wav_reader.channels(), wav_reader.frameCount()});
|
|
662
|
+
|
|
663
|
+
// Read all samples
|
|
664
|
+
var samples = try wav_reader.readSamples(allocator);
|
|
665
|
+
defer allocator.free(samples);
|
|
666
|
+
|
|
667
|
+
std.debug.print("Read {} samples from input.wav\\n", .{samples.len});
|
|
668
|
+
}`
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
return {
|
|
672
|
+
content: [{
|
|
673
|
+
type: "text",
|
|
674
|
+
text: codeTemplates[input.task] || "Template not available"
|
|
675
|
+
}]
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async function handleListResources(args: unknown) {
|
|
680
|
+
const input = ListResourcesInput.parse(args);
|
|
681
|
+
|
|
682
|
+
const resources = [
|
|
683
|
+
{
|
|
684
|
+
type: "tutorial",
|
|
685
|
+
title: "Zig Audio Ecosystem Overview",
|
|
686
|
+
url: "https://github.com/topics/zig-audio",
|
|
687
|
+
description: "Overview of audio libraries in Zig ecosystem"
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
type: "tutorial",
|
|
691
|
+
title: "Getting Started with Zang",
|
|
692
|
+
url: "https://github.com/dbandstra/zang",
|
|
693
|
+
description: "Complete guide to zang synthesis library with examples"
|
|
694
|
+
},
|
|
695
|
+
{
|
|
696
|
+
type: "tutorial",
|
|
697
|
+
title: "Audio Programming in Zig",
|
|
698
|
+
url: "https://ziglang.org/learn/samples/#audio",
|
|
699
|
+
description: "Official Zig language audio programming samples"
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
type: "example",
|
|
703
|
+
title: "Zang Examples Repository",
|
|
704
|
+
url: "https://github.com/dbandstra/zang/tree/master/examples",
|
|
705
|
+
description: "Complete examples for oscillators, effects, and synthesis"
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
type: "example",
|
|
709
|
+
title: "Zaudio Usage Examples",
|
|
710
|
+
url: "https://github.com/MasterQ32/zaudio/tree/master/examples",
|
|
711
|
+
description: "Cross-platform audio I/O examples with miniaudio"
|
|
712
|
+
},
|
|
713
|
+
{
|
|
714
|
+
type: "reference",
|
|
715
|
+
title: "Zaudio API Documentation",
|
|
716
|
+
url: "https://github.com/MasterQ32/zaudio",
|
|
717
|
+
description: "Complete API reference for miniaudio wrapper"
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
type: "reference",
|
|
721
|
+
title: "Digital Signal Processing Fundamentals",
|
|
722
|
+
url: "https://www.dspguide.com/",
|
|
723
|
+
description: "Comprehensive DSP theory and algorithms reference"
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
type: "reference",
|
|
727
|
+
title: "PCM File Format Specification",
|
|
728
|
+
url: "https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html",
|
|
729
|
+
description: "WAV file format technical specification"
|
|
730
|
+
},
|
|
731
|
+
{
|
|
732
|
+
type: "article",
|
|
733
|
+
title: "Building Real-time Audio Apps in Zig",
|
|
734
|
+
url: "https://christine.website/blog/zig-dsp-01-2024",
|
|
735
|
+
description: "In-depth tutorial on DSP effects and real-time processing"
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
type: "article",
|
|
739
|
+
title: "Zig Audio Libraries Comparison",
|
|
740
|
+
url: "https://zig.news/spectral/zig-audio-libraries-comparison-2024",
|
|
741
|
+
description: "Performance and feature comparison of Zig audio libraries"
|
|
742
|
+
},
|
|
743
|
+
{
|
|
744
|
+
type: "article",
|
|
745
|
+
title: "Low-Latency Audio Programming",
|
|
746
|
+
url: "https://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing",
|
|
747
|
+
description: "Essential guide to real-time audio programming concepts"
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
type: "tutorial",
|
|
751
|
+
title: "Software-Defined Radio with Zig",
|
|
752
|
+
url: "https://github.com/ggerard/zig-liquid-dsp",
|
|
753
|
+
description: "SDR programming tutorial using zig-liquid-dsp"
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
type: "example",
|
|
757
|
+
title: "Bonk DSP Effects",
|
|
758
|
+
url: "https://github.com/chr15m/bonk/tree/master/examples",
|
|
759
|
+
description: "Creative DSP effects and filter examples"
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
type: "reference",
|
|
763
|
+
title: "Audio EQ Cookbook",
|
|
764
|
+
url: "https://www.w3.org/TR/audio-eq-cookbook/",
|
|
765
|
+
description: "Standard biquad filter coefficient calculations"
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
type: "tutorial",
|
|
769
|
+
title: "Cross-Platform Audio Development",
|
|
770
|
+
url: "https://miniaud.io/docs/manual/index.html",
|
|
771
|
+
description: "Miniaudio documentation (used by zaudio)"
|
|
772
|
+
}
|
|
773
|
+
];
|
|
774
|
+
|
|
775
|
+
let filtered = resources;
|
|
776
|
+
if (input.resource_type) {
|
|
777
|
+
filtered = resources.filter(r => r.type === input.resource_type);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return {
|
|
781
|
+
content: [{
|
|
782
|
+
type: "text",
|
|
783
|
+
text: JSON.stringify(filtered, null, 2)
|
|
784
|
+
}]
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
async function handleGetConcepts(args: unknown) {
|
|
789
|
+
const concepts = {
|
|
790
|
+
"sample-rate": {
|
|
791
|
+
description: "Number of samples captured per second (e.g., 44100 Hz, 48000 Hz)",
|
|
792
|
+
typicalValues: [44100, 48000, 96000],
|
|
793
|
+
impact: "Higher rates = more accurate audio but more CPU/memory"
|
|
794
|
+
},
|
|
795
|
+
"bit-depth": {
|
|
796
|
+
description: "Number of bits per sample for amplitude resolution",
|
|
797
|
+
typicalValues: [16, 24, 32],
|
|
798
|
+
impact: "Higher depth = more dynamic range, less quantization noise"
|
|
799
|
+
},
|
|
800
|
+
"buffer-size": {
|
|
801
|
+
description: "Number of samples processed per callback cycle",
|
|
802
|
+
typicalValues: [64, 128, 256, 512, 1024],
|
|
803
|
+
impact: "Smaller = lower latency, higher CPU; larger = more stable, more latency"
|
|
804
|
+
},
|
|
805
|
+
"nyquist-frequency": {
|
|
806
|
+
description: "Maximum representable frequency (sample_rate / 2)",
|
|
807
|
+
formula: "f_nyquist = sample_rate / 2"
|
|
808
|
+
},
|
|
809
|
+
"aliasing": {
|
|
810
|
+
description: "Distortion from sampling frequencies above Nyquist",
|
|
811
|
+
prevention: "Use anti-aliasing filters before downsampling"
|
|
812
|
+
}
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
return {
|
|
816
|
+
content: [{
|
|
817
|
+
type: "text",
|
|
818
|
+
text: JSON.stringify(concepts, null, 2)
|
|
819
|
+
}]
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
async function handleRecommendLibrary(args: unknown) {
|
|
824
|
+
const input = RecommendLibraryInput.parse(args);
|
|
825
|
+
|
|
826
|
+
const recommendations = {
|
|
827
|
+
synthesizer: {
|
|
828
|
+
primary: "zang",
|
|
829
|
+
reason: "Purpose-built for audio synthesis with oscillators, envelopes, and filters. No dynamic allocations makes it ideal for real-time performance.",
|
|
830
|
+
alternatives: ["dalek", "bonk"],
|
|
831
|
+
considerations: "If you need more complex synthesis, consider dalek for data-oriented approaches."
|
|
832
|
+
},
|
|
833
|
+
"audio-player": {
|
|
834
|
+
primary: "zaudio",
|
|
835
|
+
reason: "Direct wrapper around miniaudio provides cross-platform playback with minimal overhead.",
|
|
836
|
+
alternatives: ["zang", "dalek"],
|
|
837
|
+
considerations: "For synthesis + playback, combine zaudio with zang."
|
|
838
|
+
},
|
|
839
|
+
"dsp-effects": {
|
|
840
|
+
primary: "bonk",
|
|
841
|
+
reason: "Specializes in DSP objects like filters, delays, and waveshapers.",
|
|
842
|
+
alternatives: ["zig-liquid-dsp", "zang"],
|
|
843
|
+
considerations: "For SDR/communications DSP, use zig-liquid-dsp instead."
|
|
844
|
+
},
|
|
845
|
+
"file-processing": {
|
|
846
|
+
primary: "pcm",
|
|
847
|
+
reason: "Simple, efficient WAV/AIFF file I/O with multiple sample formats.",
|
|
848
|
+
alternatives: ["zaudio"],
|
|
849
|
+
considerations: "zaudio includes some file support but pcm is more comprehensive for file operations."
|
|
850
|
+
},
|
|
851
|
+
"sdr-radio": {
|
|
852
|
+
primary: "zig-liquid-dsp",
|
|
853
|
+
reason: "Comprehensive DSP library specifically for software-defined radio applications.",
|
|
854
|
+
alternatives: ["bonk"],
|
|
855
|
+
considerations: "For general DSP (not SDR-specific), bonk may be more appropriate."
|
|
856
|
+
},
|
|
857
|
+
"game-audio": {
|
|
858
|
+
primary: "zang",
|
|
859
|
+
reason: "Low-latency, no-allocation design perfect for games. Includes all essential synthesis components.",
|
|
860
|
+
alternatives: ["dalek", "zaudio"],
|
|
861
|
+
considerations: "Combine with zaudio for playback if zang's output isn't sufficient."
|
|
862
|
+
},
|
|
863
|
+
"embedded-audio": {
|
|
864
|
+
primary: "zang",
|
|
865
|
+
reason: "No dynamic allocations and low-level control make it ideal for embedded systems.",
|
|
866
|
+
alternatives: ["bonk"],
|
|
867
|
+
considerations: "Ensure your target platform supports Zig compilation."
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
// Find best match based on use case keywords
|
|
872
|
+
const useCase = input.use_case.toLowerCase();
|
|
873
|
+
let bestMatch = null;
|
|
874
|
+
let score = 0;
|
|
875
|
+
|
|
876
|
+
for (const [key, rec] of Object.entries(recommendations)) {
|
|
877
|
+
if (useCase.includes(key) || key.split('-').some(k => useCase.includes(k))) {
|
|
878
|
+
bestMatch = rec;
|
|
879
|
+
score = 1;
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// If no direct match, try partial matches
|
|
885
|
+
if (!bestMatch) {
|
|
886
|
+
for (const [key, rec] of Object.entries(recommendations)) {
|
|
887
|
+
const keywords = key.split('-');
|
|
888
|
+
const matchScore = keywords.filter(k => useCase.includes(k)).length / keywords.length;
|
|
889
|
+
if (matchScore > score) {
|
|
890
|
+
bestMatch = rec;
|
|
891
|
+
score = matchScore;
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Default recommendation if nothing matches
|
|
897
|
+
if (!bestMatch) {
|
|
898
|
+
bestMatch = {
|
|
899
|
+
primary: "zang",
|
|
900
|
+
reason: "General-purpose audio synthesis library suitable for most applications.",
|
|
901
|
+
alternatives: ["zaudio", "bonk"],
|
|
902
|
+
considerations: "Consider your specific requirements - playback, effects, file I/O, etc."
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// Apply requirement filters
|
|
907
|
+
if (input.requirements) {
|
|
908
|
+
const reqs = input.requirements.map(r => r.toLowerCase());
|
|
909
|
+
|
|
910
|
+
if (reqs.includes("low latency") || reqs.includes("real-time")) {
|
|
911
|
+
if (bestMatch.primary !== "zang") {
|
|
912
|
+
bestMatch.primary = "zang";
|
|
913
|
+
bestMatch.reason = "Zang's no-allocation design provides the lowest latency.";
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
if (reqs.includes("cross-platform") || reqs.includes("playback")) {
|
|
918
|
+
bestMatch.primary = "zaudio";
|
|
919
|
+
bestMatch.reason = "Zaudio provides the most reliable cross-platform audio I/O.";
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if (reqs.includes("no allocations") || reqs.includes("embedded")) {
|
|
923
|
+
bestMatch.primary = "zang";
|
|
924
|
+
bestMatch.reason = "Zang is specifically designed with zero dynamic allocations.";
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const result = {
|
|
929
|
+
use_case: input.use_case,
|
|
930
|
+
recommendation: bestMatch,
|
|
931
|
+
library_details: ZIG_AUDIO_LIBRARIES[bestMatch.primary as keyof typeof ZIG_AUDIO_LIBRARIES]
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
return {
|
|
935
|
+
content: [{
|
|
936
|
+
type: "text",
|
|
937
|
+
text: JSON.stringify(result, null, 2)
|
|
938
|
+
}]
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async function handleDesignFilter(args: unknown) {
|
|
943
|
+
const input = DesignFilterInput.parse(args);
|
|
944
|
+
|
|
945
|
+
// Basic filter coefficient calculation
|
|
946
|
+
function calculateBiquadCoefficients(type: string, fs: number, fc: number, q: number = 0.707, gain: number = 0) {
|
|
947
|
+
const omega = 2 * Math.PI * fc / fs;
|
|
948
|
+
const alpha = Math.sin(omega) / (2 * q);
|
|
949
|
+
const cos_omega = Math.cos(omega);
|
|
950
|
+
const A = Math.pow(10, gain / 40);
|
|
951
|
+
|
|
952
|
+
let b0, b1, b2, a0, a1, a2;
|
|
953
|
+
|
|
954
|
+
switch (type) {
|
|
955
|
+
case "lowpass":
|
|
956
|
+
b0 = (1 - cos_omega) / 2;
|
|
957
|
+
b1 = 1 - cos_omega;
|
|
958
|
+
b2 = (1 - cos_omega) / 2;
|
|
959
|
+
a0 = 1 + alpha;
|
|
960
|
+
a1 = -2 * cos_omega;
|
|
961
|
+
a2 = 1 - alpha;
|
|
962
|
+
break;
|
|
963
|
+
case "highpass":
|
|
964
|
+
b0 = (1 + cos_omega) / 2;
|
|
965
|
+
b1 = -(1 + cos_omega);
|
|
966
|
+
b2 = (1 + cos_omega) / 2;
|
|
967
|
+
a0 = 1 + alpha;
|
|
968
|
+
a1 = -2 * cos_omega;
|
|
969
|
+
a2 = 1 - alpha;
|
|
970
|
+
break;
|
|
971
|
+
case "bandpass":
|
|
972
|
+
b0 = alpha;
|
|
973
|
+
b1 = 0;
|
|
974
|
+
b2 = -alpha;
|
|
975
|
+
a0 = 1 + alpha;
|
|
976
|
+
a1 = -2 * cos_omega;
|
|
977
|
+
a2 = 1 - alpha;
|
|
978
|
+
break;
|
|
979
|
+
case "notch":
|
|
980
|
+
b0 = 1;
|
|
981
|
+
b1 = -2 * cos_omega;
|
|
982
|
+
b2 = 1;
|
|
983
|
+
a0 = 1 + alpha;
|
|
984
|
+
a1 = -2 * cos_omega;
|
|
985
|
+
a2 = 1 - alpha;
|
|
986
|
+
break;
|
|
987
|
+
default:
|
|
988
|
+
throw new Error(`Unsupported filter type: ${type}`);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
// Normalize by a0
|
|
992
|
+
return {
|
|
993
|
+
b: [b0 / a0, b1 / a0, b2 / a0],
|
|
994
|
+
a: [1, a1 / a0, a2 / a0]
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
const params = input.parameters;
|
|
999
|
+
const coeffs = calculateBiquadCoefficients(
|
|
1000
|
+
input.filter_type,
|
|
1001
|
+
params.sample_rate,
|
|
1002
|
+
params.cutoff || 1000,
|
|
1003
|
+
params.q || 0.707,
|
|
1004
|
+
params.gain || 0
|
|
1005
|
+
);
|
|
1006
|
+
|
|
1007
|
+
const result = {
|
|
1008
|
+
filter_type: input.filter_type,
|
|
1009
|
+
parameters: params,
|
|
1010
|
+
coefficients: coeffs,
|
|
1011
|
+
stability_check: Math.abs(coeffs.a[1]) + Math.abs(coeffs.a[2]) < 1,
|
|
1012
|
+
zig_code: `// ${input.filter_type} filter coefficients
|
|
1013
|
+
const coeffs = struct {
|
|
1014
|
+
b: [3]f32 = .{${coeffs.b.map((x: number) => x.toFixed(6)).join(", ")}},
|
|
1015
|
+
a: [3]f32 = .{${coeffs.a.map((x: number) => x.toFixed(6)).join(", ")}},
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
// Apply to signal using difference equation:
|
|
1019
|
+
// y[n] = b[0]*x[n] + b[1]*x[n-1] + b[2]*x[n-2] - a[1]*y[n-1] - a[2]*y[n-2]`
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
return {
|
|
1023
|
+
content: [{
|
|
1024
|
+
type: "text",
|
|
1025
|
+
text: JSON.stringify(result, null, 2)
|
|
1026
|
+
}]
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// Create and configure the MCP server
|
|
1031
|
+
const server = new Server(
|
|
1032
|
+
{
|
|
1033
|
+
name: "mcp-zig-audio",
|
|
1034
|
+
version: "0.1.0"
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
capabilities: {
|
|
1038
|
+
tools: {}
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
);
|
|
1042
|
+
|
|
1043
|
+
// Set up request handlers
|
|
1044
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1045
|
+
return {
|
|
1046
|
+
tools: [
|
|
1047
|
+
{
|
|
1048
|
+
name: "zig_audio_list_libraries",
|
|
1049
|
+
description: "List available Zig audio/DSP libraries with optional filtering",
|
|
1050
|
+
inputSchema: {
|
|
1051
|
+
type: "object",
|
|
1052
|
+
properties: {
|
|
1053
|
+
category: {
|
|
1054
|
+
type: "string",
|
|
1055
|
+
enum: ["synthesis", "effects", "filters", "dsp", "playback", "recording", "file-io", "encoding", "sdr", "engine", "wrapper", "experimental", "all"],
|
|
1056
|
+
description: "Filter by category"
|
|
1057
|
+
},
|
|
1058
|
+
feature: {
|
|
1059
|
+
type: "string",
|
|
1060
|
+
description: "Filter by feature keyword"
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
name: "zig_audio_library_info",
|
|
1067
|
+
"description": "Get detailed information about a specific Zig audio library",
|
|
1068
|
+
inputSchema: {
|
|
1069
|
+
type: "object",
|
|
1070
|
+
properties: {
|
|
1071
|
+
library: {
|
|
1072
|
+
type: "string",
|
|
1073
|
+
enum: ["zang", "zaudio", "bonk", "pcm", "zig-liquid-dsp", "dalek"],
|
|
1074
|
+
description: "Library name"
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
required: ["library"]
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
{
|
|
1081
|
+
name: "zig_dsp_explain_filter",
|
|
1082
|
+
"description": "Explain a DSP filter type with formula and use cases",
|
|
1083
|
+
inputSchema: {
|
|
1084
|
+
type: "object",
|
|
1085
|
+
properties: {
|
|
1086
|
+
filter_type: {
|
|
1087
|
+
type: "string",
|
|
1088
|
+
enum: ["lowpass", "highpass", "bandpass", "notch", "biquad", "one-pole"],
|
|
1089
|
+
description: "Filter type to explain"
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
required: ["filter_type"]
|
|
1093
|
+
}
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
name: "zig_audio_generate_code",
|
|
1097
|
+
"description": "Generate starter code for common audio/DSP tasks",
|
|
1098
|
+
inputSchema: {
|
|
1099
|
+
type: "object",
|
|
1100
|
+
properties: {
|
|
1101
|
+
task: {
|
|
1102
|
+
type: "string",
|
|
1103
|
+
enum: ["oscillator", "envelope", "filter", "delay", "mixer", "player", "recorder", "file-write", "file-read"],
|
|
1104
|
+
description: "Type of code to generate"
|
|
1105
|
+
},
|
|
1106
|
+
parameters: {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
description: "Optional parameters for code generation"
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
required: ["task"]
|
|
1112
|
+
}
|
|
1113
|
+
},
|
|
1114
|
+
{
|
|
1115
|
+
name: "zig_audio_list_resources",
|
|
1116
|
+
"description": "List learning resources for Zig audio development",
|
|
1117
|
+
inputSchema: {
|
|
1118
|
+
type: "object",
|
|
1119
|
+
properties: {
|
|
1120
|
+
resource_type: {
|
|
1121
|
+
type: "string",
|
|
1122
|
+
enum: ["tutorial", "reference", "example", "article"],
|
|
1123
|
+
description: "Filter by resource type"
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
name: "zig_dsp_get_concepts",
|
|
1130
|
+
"description": "Get explanations of fundamental audio/DSP concepts",
|
|
1131
|
+
inputSchema: {
|
|
1132
|
+
type: "object",
|
|
1133
|
+
properties: {}
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
name: "zig_audio_recommend_library",
|
|
1138
|
+
description: "Get library recommendations based on your audio programming needs",
|
|
1139
|
+
inputSchema: {
|
|
1140
|
+
type: "object",
|
|
1141
|
+
properties: {
|
|
1142
|
+
use_case: {
|
|
1143
|
+
type: "string",
|
|
1144
|
+
description: "Describe what you want to build (e.g., 'synthesizer', 'audio player', 'DSP effects')"
|
|
1145
|
+
},
|
|
1146
|
+
requirements: {
|
|
1147
|
+
type: "array",
|
|
1148
|
+
items: { type: "string" },
|
|
1149
|
+
description: "Specific requirements like 'low latency', 'no allocations', 'cross-platform'"
|
|
1150
|
+
}
|
|
1151
|
+
},
|
|
1152
|
+
required: ["use_case"]
|
|
1153
|
+
}
|
|
1154
|
+
},
|
|
1155
|
+
{
|
|
1156
|
+
name: "zig_dsp_design_filter",
|
|
1157
|
+
description: "Design DSP filters and calculate biquad coefficients",
|
|
1158
|
+
inputSchema: {
|
|
1159
|
+
type: "object",
|
|
1160
|
+
properties: {
|
|
1161
|
+
filter_type: {
|
|
1162
|
+
type: "string",
|
|
1163
|
+
enum: ["lowpass", "highpass", "bandpass", "notch", "biquad"],
|
|
1164
|
+
description: "Type of filter to design"
|
|
1165
|
+
},
|
|
1166
|
+
parameters: {
|
|
1167
|
+
type: "object",
|
|
1168
|
+
properties: {
|
|
1169
|
+
sample_rate: {
|
|
1170
|
+
type: "number",
|
|
1171
|
+
description: "Sample rate in Hz"
|
|
1172
|
+
},
|
|
1173
|
+
cutoff: {
|
|
1174
|
+
type: "number",
|
|
1175
|
+
description: "Cutoff frequency in Hz"
|
|
1176
|
+
},
|
|
1177
|
+
q: {
|
|
1178
|
+
type: "number",
|
|
1179
|
+
description: "Quality factor (resonance)"
|
|
1180
|
+
},
|
|
1181
|
+
gain: {
|
|
1182
|
+
type: "number",
|
|
1183
|
+
description: "Gain in dB for parametric filters"
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
required: ["sample_rate"]
|
|
1187
|
+
}
|
|
1188
|
+
},
|
|
1189
|
+
required: ["filter_type", "parameters"]
|
|
1190
|
+
}
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: "zig_audio_verify_code",
|
|
1194
|
+
description: "Verify Zig code for basic syntax and common issues",
|
|
1195
|
+
inputSchema: {
|
|
1196
|
+
type: "object",
|
|
1197
|
+
properties: {
|
|
1198
|
+
code: {
|
|
1199
|
+
type: "string",
|
|
1200
|
+
description: "Zig code to verify"
|
|
1201
|
+
},
|
|
1202
|
+
libraries: {
|
|
1203
|
+
type: "array",
|
|
1204
|
+
items: { type: "string" },
|
|
1205
|
+
description: "Expected libraries that should be imported"
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
1208
|
+
required: ["code"]
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
{
|
|
1212
|
+
name: "zig_audio_project_template",
|
|
1213
|
+
description: "Generate project templates for different audio application types",
|
|
1214
|
+
inputSchema: {
|
|
1215
|
+
type: "object",
|
|
1216
|
+
properties: {
|
|
1217
|
+
project_type: {
|
|
1218
|
+
type: "string",
|
|
1219
|
+
enum: ["synthesizer", "audio-player", "dsp-processor", "file-processor", "game-audio"],
|
|
1220
|
+
description: "Type of audio project"
|
|
1221
|
+
},
|
|
1222
|
+
features: {
|
|
1223
|
+
type: "array",
|
|
1224
|
+
items: { type: "string" },
|
|
1225
|
+
description: "Additional features to include"
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
required: ["project_type"]
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
]
|
|
1232
|
+
};
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1236
|
+
const { name, arguments: args } = request.params;
|
|
1237
|
+
|
|
1238
|
+
try {
|
|
1239
|
+
switch (name) {
|
|
1240
|
+
case "zig_audio_list_libraries":
|
|
1241
|
+
return await handleListLibraries(args);
|
|
1242
|
+
case "zig_audio_library_info":
|
|
1243
|
+
return await handleGetLibraryInfo(args);
|
|
1244
|
+
case "zig_dsp_explain_filter":
|
|
1245
|
+
return await handleExplainFilter(args);
|
|
1246
|
+
case "zig_audio_generate_code":
|
|
1247
|
+
return await handleGenerateCode(args);
|
|
1248
|
+
case "zig_audio_list_resources":
|
|
1249
|
+
return await handleListResources(args);
|
|
1250
|
+
case "zig_dsp_get_concepts":
|
|
1251
|
+
return await handleGetConcepts(args);
|
|
1252
|
+
case "zig_audio_recommend_library":
|
|
1253
|
+
return await handleRecommendLibrary(args);
|
|
1254
|
+
case "zig_dsp_design_filter":
|
|
1255
|
+
return await handleDesignFilter(args);
|
|
1256
|
+
case "zig_audio_verify_code":
|
|
1257
|
+
return await handleVerifyCode(args);
|
|
1258
|
+
case "zig_audio_project_template":
|
|
1259
|
+
return await handleProjectTemplate(args);
|
|
1260
|
+
default:
|
|
1261
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
1262
|
+
}
|
|
1263
|
+
} catch (error) {
|
|
1264
|
+
return {
|
|
1265
|
+
content: [{
|
|
1266
|
+
type: "text",
|
|
1267
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1268
|
+
}],
|
|
1269
|
+
isError: true
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
});
|
|
1273
|
+
|
|
1274
|
+
// Start the server with stdio transport
|
|
1275
|
+
async function main() {
|
|
1276
|
+
const transport = new StdioServerTransport();
|
|
1277
|
+
await server.connect(transport);
|
|
1278
|
+
console.error("MCP Zig Audio server running on stdio");
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
main().catch(console.error);
|