Something went wrong. Try again.
Plays short audio files such as a terminal bell through PipeWire on Linux and WASAPI on Windows.
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! MPEG audio, Layers I to III. Tags and anything else between frames are//! stepped over by the decoder. Where the stream has a LAME tag, the encoder's//! delay and padding are trimmed off, so that the Sound is as long as what was//! encoded.
const std = @import("std");const mp3 = @import("mp3");
const decode_mod = @import("../decode.zig");const Builder = decode_mod.Builder;const Error = decode_mod.Error;
pub fn decode(bytes: []const u8, out: *Builder) Error!void { var decoder: mp3.Decoder = .init(); var buffer: mp3.FrameBuffer = undefined; var rest = bytes; var first: ?mp3.Header = null; while (rest.len > 0) switch (decoder.next(rest, &buffer, .{ .end_of_input = true })) { .need_more => break, .skipped => |n| rest = rest[n..], .frame => |frame| { rest = rest[frame.consumed..]; // The format is the first frame's. A later frame that disagrees // cannot be represented in one Sound, and is left out. if (out.channels == 0) { try out.begin(frame.sample_rate, frame.channels); first = frame.header; } if (frame.sample_rate != out.rate or frame.channels != out.channels) continue; try out.appendInterleaved(buffer[0 .. frame.samples * frame.channels]); }, }; if (out.channels == 0) return error.NoAudio; if (decoder.info()) |info| trim(out, info, first.?);}
fn trim(out: *Builder, info: mp3.VbrInfo, header: mp3.Header) void { const lead = info.leadIn() orelse return; const playable = info.playableSamples(header) orelse return; const have = out.frames(); const start = @min(lead, have); const len = @min(std.math.cast(usize, playable) orelse have, have - start); const ch: usize = out.channels; const items = out.samples.items; std.mem.copyForwards(f32, items[0 .. len * ch], items[start * ch ..][0 .. len * ch]); out.samples.shrinkRetainingCapacity(len * ch);}
test "MP3 without a LAME tag keeps the encoder's delay and padding" { // A whole number of 1152-sample frames rather than 4410. try decode_mod.expectTone(@embedFile("../tests/data/tone-mono-44100.mp3"), 44100, 1, 5 * 1152);}
test "MP3 with a LAME tag is trimmed to what was encoded" { try decode_mod.expectTone(@embedFile("../tests/data/tone-mono-44100-lame.mp3"), 44100, 1, 4410);}