talk-rs 0.7.1

Voice dictation for Linux -- record, transcribe, and paste
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
//! Audio container writers for producing valid OGG/Opus and WAV output.
//!
//! Provides the [`AudioWriter`] trait and two implementations:
//! - [`OggOpusWriter`]: encodes PCM to Opus and wraps in OGG container
//! - [`WavWriter`]: wraps raw PCM in a WAV container

use byteorder::{LittleEndian, WriteBytesExt};
use ogg::{PacketWriteEndInfo, PacketWriter};

use crate::config::AudioConfig;
use crate::error::TalkError;

/// Trait for audio container writers.
///
/// Takes raw PCM i16 samples as input and produces containerized audio bytes.
/// Each implementation handles its own encoding (if any) and container format.
pub trait AudioWriter: Send {
    /// Return the header/preamble bytes for this container format.
    /// Must be called once before any `write_pcm()` calls.
    fn header(&mut self) -> Result<Vec<u8>, TalkError>;

    /// Write PCM samples and return containerized output bytes.
    /// May return empty Vec if buffering internally (e.g., collecting a full Opus frame).
    fn write_pcm(&mut self, pcm: &[i16]) -> Result<Vec<u8>, TalkError>;

    /// Finalize the container. Returns any trailing bytes (flush, EOS page, etc.).
    /// For WAV: returns updated header with correct data size.
    fn finalize(&mut self) -> Result<Vec<u8>, TalkError>;

    /// MIME type for HTTP uploads.
    fn mime_type(&self) -> &str;

    /// File extension for this format.
    fn extension(&self) -> &str;
}

/// Generate a pseudo-random serial number for OGG streams.
fn rand_serial() -> u32 {
    use std::time::SystemTime;
    SystemTime::now()
        .duration_since(SystemTime::UNIX_EPOCH)
        .map(|d| d.as_nanos() as u32)
        .unwrap_or(42)
}

/// OGG/Opus container writer.
///
/// Encodes PCM input with Opus and wraps the output in OGG pages
/// conforming to RFC 7845 (OpusHead + OpusTags headers).
pub struct OggOpusWriter {
    /// Ogg page writer, wrapping a `Vec<u8>` buffer.
    packet_writer: PacketWriter<'static, Vec<u8>>,
    /// Opus encoder.
    encoder: opus::Encoder,
    /// PCM sample buffer (accumulates until a full Opus frame).
    pcm_buffer: Vec<i16>,
    /// Frame size in samples (per channel) for 20ms at the configured sample rate.
    frame_size: usize,
    /// Number of channels.
    channels: u8,
    /// Stream serial number.
    serial: u32,
    /// Total granule position (in 48kHz samples, per RFC 7845).
    granule_position: u64,
    /// Sample rate ratio for granule calculation (48000 / sample_rate).
    granule_rate_ratio: f64,
    /// Whether header has been written.
    header_written: bool,
    /// Test-only instrumentation: running total of `i16` samples
    /// relocated by front-drains of `pcm_buffer` inside `write_pcm`.
    ///
    /// This is the exact quantity that made the old implementation
    /// quadratic, so it is the quantity the regression test asserts on.
    /// Compiled out of every non-test build (including the release
    /// binary), so it costs nothing at runtime.
    #[cfg(test)]
    tail_samples_relocated: u64,
}

// OggOpusWriter is Send because opus::Encoder is Send and PacketWriter<Vec<u8>> is Send
unsafe impl Send for OggOpusWriter {}

impl OggOpusWriter {
    /// Create a new OGG/Opus writer with the given audio configuration.
    ///
    /// Uses Opus `Application::Voip`, optimised for speech intelligibility.
    /// This is the right choice for the transcription / dictation paths
    /// (16 kHz mono).  For human-facing recordings that may contain
    /// music or ambient sound, prefer [`OggOpusWriter::new_for_recording`].
    pub fn new(config: AudioConfig) -> Result<Self, TalkError> {
        Self::new_with_application(config, opus::Application::Voip)
    }

    /// Create a new OGG/Opus writer tuned for human-facing recordings.
    ///
    /// Uses Opus `Application::Audio`, which preserves more fidelity
    /// across the full spectrum (music, ambient, non-speech) than the
    /// speech-optimised `Voip` mode.  Used by the `record` command.
    pub fn new_for_recording(config: AudioConfig) -> Result<Self, TalkError> {
        Self::new_with_application(config, opus::Application::Audio)
    }

    /// Create a new OGG/Opus writer with an explicit Opus application
    /// mode.  See [`OggOpusWriter::new`] (Voip) and
    /// [`OggOpusWriter::new_for_recording`] (Audio) for the two
    /// intended call sites.
    pub fn new_with_application(
        config: AudioConfig,
        application: opus::Application,
    ) -> Result<Self, TalkError> {
        let opus_channels = match config.channels {
            1 => opus::Channels::Mono,
            2 => opus::Channels::Stereo,
            _ => {
                return Err(TalkError::Audio(format!(
                    "Unsupported channel count: {}",
                    config.channels
                )))
            }
        };

        let mut encoder = opus::Encoder::new(config.sample_rate, opus_channels, application)
            .map_err(|e| TalkError::Audio(format!("Failed to create Opus encoder: {e}")))?;

        encoder
            .set_bitrate(opus::Bitrate::Bits(config.bitrate as i32))
            .map_err(|e| TalkError::Audio(format!("Failed to set Opus bitrate: {e}")))?;

        let frame_size = (config.sample_rate as usize * 20) / 1000; // 20ms frames
        let serial = rand_serial();

        Ok(Self {
            packet_writer: PacketWriter::new(Vec::new()),
            encoder,
            pcm_buffer: Vec::new(),
            frame_size,
            channels: config.channels,
            serial,
            granule_position: 0,
            granule_rate_ratio: 48000.0 / config.sample_rate as f64,
            header_written: false,
            #[cfg(test)]
            tail_samples_relocated: 0,
        })
    }
}

impl AudioWriter for OggOpusWriter {
    fn header(&mut self) -> Result<Vec<u8>, TalkError> {
        // Build OpusHead packet (19 bytes for mono/stereo, mapping family 0)
        let mut head = Vec::with_capacity(19);
        head.extend_from_slice(b"OpusHead");
        head.push(1); // version
        head.push(self.channels); // channel count
                                  // Pre-skip: standard value for Opus
        head.extend_from_slice(&3840u16.to_le_bytes()); // pre-skip
        head.extend_from_slice(&48000u32.to_le_bytes()); // input sample rate (always 48kHz for Opus)
        head.extend_from_slice(&0i16.to_le_bytes()); // output gain
        head.push(0); // mapping family 0

        self.packet_writer
            .write_packet(head, self.serial, PacketWriteEndInfo::EndPage, 0)
            .map_err(|e| TalkError::Audio(format!("failed to write OpusHead: {e}")))?;

        // Build OpusTags packet
        let mut tags = Vec::new();
        tags.extend_from_slice(b"OpusTags");
        let vendor = b"talk-rs";
        tags.extend_from_slice(&(vendor.len() as u32).to_le_bytes());
        tags.extend_from_slice(vendor);
        tags.extend_from_slice(&0u32.to_le_bytes()); // 0 user comments

        self.packet_writer
            .write_packet(tags, self.serial, PacketWriteEndInfo::EndPage, 0)
            .map_err(|e| TalkError::Audio(format!("failed to write OpusTags: {e}")))?;

        self.header_written = true;

        // Extract the bytes written so far
        let bytes = self.packet_writer.inner_mut().clone();
        self.packet_writer.inner_mut().clear();
        Ok(bytes)
    }

    fn write_pcm(&mut self, pcm: &[i16]) -> Result<Vec<u8>, TalkError> {
        self.pcm_buffer.extend_from_slice(pcm);

        let samples_per_frame = self.frame_size * self.channels as usize;
        let mut output = Vec::new();

        // Consume `pcm_buffer` with a read cursor and drain ONCE at the
        // end of the call.
        //
        // Do NOT reintroduce `self.pcm_buffer.drain(..samples_per_frame)`
        // inside this loop: `Vec::drain` from the front memmoves the whole
        // remaining tail down on *every* iteration, making the function
        // O(n^2) in the number of buffered samples.  Harmless for the
        // streaming call sites (`dictate`, `record`) which feed small
        // chunks, but catastrophic for `encode_16k_mono_ogg`
        // (`src/transcription/mod.rs`), which hands a whole decoded file
        // over in a single call.
        //
        // Measured, release build, one call: 20 minutes of 16 kHz mono
        // audio took 455.6 s with the front-drain versus 15.6 s with this
        // cursor form (29x; the remainder is the Opus encode itself,
        // which is linear and unchanged).  Extrapolated to the 2h22m
        // (8524.8 s) voice memo that exposed this, the front-drain costs
        // ~132 minutes of pure memmove — 426240 frames, ~58 TB moved —
        // before a single byte reaches the network.
        let mut pos = 0usize;
        while self.pcm_buffer.len() - pos >= samples_per_frame {
            // Split the borrows: `frame` borrows `pcm_buffer` immutably
            // while `encoder` / `packet_writer` are borrowed mutably.
            let frame = &self.pcm_buffer[pos..pos + samples_per_frame];

            // Encode with Opus
            let mut opus_output = vec![0u8; 4000];
            let len = self
                .encoder
                .encode(frame, &mut opus_output)
                .map_err(|e| TalkError::Audio(format!("Opus encoding failed: {e}")))?;
            opus_output.truncate(len);

            pos += samples_per_frame;

            // Update granule position (in 48kHz samples)
            self.granule_position += (self.frame_size as f64 * self.granule_rate_ratio) as u64;

            // Write as OGG packet (each Opus frame gets its own page for streaming)
            self.packet_writer
                .write_packet(
                    opus_output,
                    self.serial,
                    PacketWriteEndInfo::EndPage,
                    self.granule_position,
                )
                .map_err(|e| TalkError::Audio(format!("failed to write OGG page: {e}")))?;

            // Extract bytes
            output.extend_from_slice(self.packet_writer.inner_mut());
            self.packet_writer.inner_mut().clear();
        }

        // Single memmove for the whole call.  The leftover tail is exactly
        // the samples after the last full frame, which is what
        // `finalize()` pads and flushes.
        if pos > 0 {
            #[cfg(test)]
            {
                self.tail_samples_relocated += (self.pcm_buffer.len() - pos) as u64;
            }
            self.pcm_buffer.drain(..pos);
        }

        Ok(output)
    }

    fn finalize(&mut self) -> Result<Vec<u8>, TalkError> {
        let mut output = Vec::new();

        // Pad remaining buffer and encode final frame
        if !self.pcm_buffer.is_empty() {
            let samples_per_frame = self.frame_size * self.channels as usize;
            while self.pcm_buffer.len() < samples_per_frame {
                self.pcm_buffer.push(0i16);
            }

            let frame = self.pcm_buffer.drain(..).collect::<Vec<i16>>();
            let mut opus_output = vec![0u8; 4000];
            let len = self
                .encoder
                .encode(&frame, &mut opus_output)
                .map_err(|e| TalkError::Audio(format!("Opus flush failed: {e}")))?;
            opus_output.truncate(len);

            self.granule_position += (self.frame_size as f64 * self.granule_rate_ratio) as u64;

            // Write as final OGG packet with EndStream flag
            self.packet_writer
                .write_packet(
                    opus_output,
                    self.serial,
                    PacketWriteEndInfo::EndStream,
                    self.granule_position,
                )
                .map_err(|e| TalkError::Audio(format!("failed to write final OGG page: {e}")))?;

            output.extend_from_slice(self.packet_writer.inner_mut());
            self.packet_writer.inner_mut().clear();
        }

        Ok(output)
    }

    fn mime_type(&self) -> &str {
        "audio/ogg"
    }

    fn extension(&self) -> &str {
        "ogg"
    }
}

/// WAV container writer.
///
/// Wraps raw PCM i16 samples in a standard 44-byte WAV header.
/// Uses a placeholder size on initial header; `finalize()` returns a
/// corrected header with actual data size.
pub struct WavWriter {
    sample_rate: u32,
    channels: u16,
    bits_per_sample: u16,
    data_size: u32,
    header_written: bool,
}

impl WavWriter {
    /// Create a new WAV writer with the given audio configuration.
    pub fn new(config: AudioConfig) -> Self {
        Self {
            sample_rate: config.sample_rate,
            channels: config.channels as u16,
            bits_per_sample: 16,
            data_size: 0,
            header_written: false,
        }
    }

    /// Build a 44-byte WAV header with the given data size.
    fn build_header(&self, data_size: u32) -> Result<Vec<u8>, TalkError> {
        let byte_rate = self.sample_rate * self.channels as u32 * (self.bits_per_sample as u32 / 8);
        let block_align = self.channels * (self.bits_per_sample / 8);

        let mut hdr = Vec::with_capacity(44);
        hdr.extend_from_slice(b"RIFF");
        hdr.write_u32::<LittleEndian>(if data_size == 0xFFFF_FFFF {
            0xFFFF_FFFF
        } else {
            36 + data_size
        })?;
        hdr.extend_from_slice(b"WAVE");
        hdr.extend_from_slice(b"fmt ");
        hdr.write_u32::<LittleEndian>(16)?; // fmt chunk size
        hdr.write_u16::<LittleEndian>(1)?; // PCM format
        hdr.write_u16::<LittleEndian>(self.channels)?;
        hdr.write_u32::<LittleEndian>(self.sample_rate)?;
        hdr.write_u32::<LittleEndian>(byte_rate)?;
        hdr.write_u16::<LittleEndian>(block_align)?;
        hdr.write_u16::<LittleEndian>(self.bits_per_sample)?;
        hdr.extend_from_slice(b"data");
        hdr.write_u32::<LittleEndian>(data_size)?;

        Ok(hdr)
    }
}

impl AudioWriter for WavWriter {
    fn header(&mut self) -> Result<Vec<u8>, TalkError> {
        self.header_written = true;
        // Use placeholder sizes for streaming
        self.build_header(0xFFFF_FFFF)
    }

    fn write_pcm(&mut self, pcm: &[i16]) -> Result<Vec<u8>, TalkError> {
        let mut bytes = Vec::with_capacity(pcm.len() * 2);
        for &sample in pcm {
            bytes.extend_from_slice(&sample.to_le_bytes());
        }
        self.data_size += bytes.len() as u32;
        Ok(bytes)
    }

    fn finalize(&mut self) -> Result<Vec<u8>, TalkError> {
        // Return a corrected 44-byte header with the actual sizes.
        // The caller is responsible for seeking to offset 0 and writing this.
        self.build_header(self.data_size)
    }

    fn mime_type(&self) -> &str {
        "audio/wav"
    }

    fn extension(&self) -> &str {
        "wav"
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::config::AudioConfig;

    fn test_config() -> AudioConfig {
        AudioConfig::new()
    }

    /// Fixed OGG stream serial, so two independently constructed
    /// writers produce byte-comparable output (`rand_serial()` is
    /// time-derived and would otherwise differ).
    const FIXED_SERIAL: u32 = 0x1234_5678;

    fn writer_with_fixed_serial() -> OggOpusWriter {
        let mut w = OggOpusWriter::new(test_config()).unwrap();
        w.serial = FIXED_SERIAL;
        w
    }

    /// Reference copy of the ORIGINAL `OggOpusWriter::write_pcm`, kept
    /// in the test module only.
    ///
    /// It drains `samples_per_frame` from the *front* of `pcm_buffer` on
    /// every iteration — the O(n²) form that the production
    /// implementation deliberately no longer uses (see the comment on
    /// the cursor loop in `write_pcm`).  It exists so the fixed
    /// implementation can be asserted **byte-identical** to the
    /// behaviour it replaced, and so the quadratic blow-up can be
    /// measured side by side.
    fn legacy_write_pcm(w: &mut OggOpusWriter, pcm: &[i16]) -> Result<Vec<u8>, TalkError> {
        w.pcm_buffer.extend_from_slice(pcm);

        let samples_per_frame = w.frame_size * w.channels as usize;
        let mut output = Vec::new();

        while w.pcm_buffer.len() >= samples_per_frame {
            w.tail_samples_relocated += (w.pcm_buffer.len() - samples_per_frame) as u64;
            let frame: Vec<i16> = w.pcm_buffer.drain(..samples_per_frame).collect();

            let mut opus_output = vec![0u8; 4000];
            let len = w
                .encoder
                .encode(&frame, &mut opus_output)
                .map_err(|e| TalkError::Audio(format!("Opus encoding failed: {e}")))?;
            opus_output.truncate(len);

            w.granule_position += (w.frame_size as f64 * w.granule_rate_ratio) as u64;

            w.packet_writer
                .write_packet(
                    opus_output,
                    w.serial,
                    PacketWriteEndInfo::EndPage,
                    w.granule_position,
                )
                .map_err(|e| TalkError::Audio(format!("failed to write OGG page: {e}")))?;

            output.extend_from_slice(w.packet_writer.inner_mut());
            w.packet_writer.inner_mut().clear();
        }

        Ok(output)
    }

    /// Deterministic test signal: a sine sweep, so successive frames
    /// differ and the Opus encoder cannot collapse them into identical
    /// packets.
    fn sine_pcm(n: usize) -> Vec<i16> {
        (0..n)
            .map(|i| {
                let t = i as f32 / 16000.0;
                let freq = 220.0 + 80.0 * (t * 0.7).sin();
                ((t * freq * std::f32::consts::TAU).sin() * 12000.0) as i16
            })
            .collect()
    }

    #[test]
    fn test_ogg_opus_writer_produces_valid_ogg() {
        let mut writer = OggOpusWriter::new(test_config()).unwrap();
        let header = writer.header().unwrap();

        // Must start with OggS magic
        assert_eq!(&header[0..4], b"OggS", "output must start with OggS");
        // Must contain OpusHead somewhere in first pages
        assert!(
            header.windows(8).any(|w| w == b"OpusHead"),
            "output must contain OpusHead"
        );
        assert!(
            header.windows(8).any(|w| w == b"OpusTags"),
            "output must contain OpusTags"
        );

        // Write some PCM data
        let pcm: Vec<i16> = (0..320)
            .map(|i| ((i as f32 * 0.1).sin() * 10000.0) as i16)
            .collect();
        let audio_bytes = writer.write_pcm(&pcm).unwrap();

        // Finalize
        let final_bytes = writer.finalize().unwrap();

        // Combined output should be parseable
        let all_bytes = [header, audio_bytes, final_bytes].concat();
        assert!(
            all_bytes.len() > 100,
            "output should have substantial content"
        );
        assert_eq!(&all_bytes[0..4], b"OggS");
    }

    #[test]
    fn test_wav_writer_produces_valid_wav() {
        let mut writer = WavWriter::new(test_config());
        let header = writer.header().unwrap();

        assert_eq!(&header[0..4], b"RIFF", "must start with RIFF");
        assert_eq!(&header[8..12], b"WAVE", "must contain WAVE");
        assert_eq!(&header[12..16], b"fmt ", "must contain fmt chunk");
        assert_eq!(header.len(), 44, "WAV header must be 44 bytes");

        // Write some PCM
        let pcm = vec![100i16, -200, 300];
        let audio_bytes = writer.write_pcm(&pcm).unwrap();
        assert_eq!(audio_bytes.len(), 6); // 3 samples * 2 bytes

        // Finalize returns corrected header
        let final_header = writer.finalize().unwrap();
        assert_eq!(final_header.len(), 44);
        assert_eq!(&final_header[0..4], b"RIFF");
    }

    #[test]
    fn test_ogg_opus_writer_mime_type() {
        let writer = OggOpusWriter::new(test_config()).unwrap();
        assert_eq!(writer.mime_type(), "audio/ogg");
        assert_eq!(writer.extension(), "ogg");
    }

    #[test]
    fn test_wav_writer_mime_type() {
        let writer = WavWriter::new(test_config());
        assert_eq!(writer.mime_type(), "audio/wav");
        assert_eq!(writer.extension(), "wav");
    }

    /// The cursor-based `write_pcm` must be byte-for-byte equivalent to
    /// the drain-in-loop implementation it replaced, when the whole
    /// signal arrives in ONE call — the `encode_16k_mono_ogg` shape.
    #[test]
    fn test_write_pcm_bulk_call_is_byte_identical_to_legacy() {
        // 5000 frames (100 s of 16 kHz mono audio) plus a deliberate
        // partial tail of 137 samples, so `finalize()` padding is
        // exercised too.
        let pcm = sine_pcm(5000 * 320 + 137);

        let mut fixed = writer_with_fixed_serial();
        let fixed_bytes = [
            fixed.header().unwrap(),
            fixed.write_pcm(&pcm).unwrap(),
            fixed.finalize().unwrap(),
        ]
        .concat();

        let mut legacy = writer_with_fixed_serial();
        let legacy_bytes = [
            legacy.header().unwrap(),
            legacy_write_pcm(&mut legacy, &pcm).unwrap(),
            legacy.finalize().unwrap(),
        ]
        .concat();

        assert_eq!(
            fixed.pcm_buffer.len(),
            legacy.pcm_buffer.len(),
            "leftover tail length must match the legacy implementation"
        );
        assert_eq!(
            fixed_bytes.len(),
            legacy_bytes.len(),
            "output length must match the legacy implementation"
        );
        assert!(
            fixed_bytes == legacy_bytes,
            "cursor-based write_pcm must be byte-identical to the legacy draining impl"
        );
    }

    /// Same equivalence, but fed in irregular chunks — the streaming
    /// (`dictate` / `record`) shape, where each call carries only a
    /// fraction of a frame and the tail must survive across calls.
    #[test]
    fn test_write_pcm_chunked_calls_are_byte_identical_to_legacy() {
        let pcm = sine_pcm(200 * 320 + 45);
        // Chunk sizes that are deliberately not multiples of 320.
        let chunk_sizes = [1usize, 7, 319, 320, 321, 1000, 4097];

        let mut fixed = writer_with_fixed_serial();
        let mut legacy = writer_with_fixed_serial();
        let mut fixed_bytes = fixed.header().unwrap();
        let mut legacy_bytes = legacy.header().unwrap();

        let mut offset = 0usize;
        let mut which = 0usize;
        while offset < pcm.len() {
            let take = chunk_sizes[which % chunk_sizes.len()].min(pcm.len() - offset);
            let chunk = &pcm[offset..offset + take];
            fixed_bytes.extend_from_slice(&fixed.write_pcm(chunk).unwrap());
            legacy_bytes.extend_from_slice(&legacy_write_pcm(&mut legacy, chunk).unwrap());
            offset += take;
            which += 1;
        }

        fixed_bytes.extend_from_slice(&fixed.finalize().unwrap());
        legacy_bytes.extend_from_slice(&legacy.finalize().unwrap());

        assert!(
            fixed_bytes == legacy_bytes,
            "chunked cursor-based write_pcm must be byte-identical to the legacy draining impl"
        );
    }

    /// Regression guard against reintroducing `drain(..frame)` inside
    /// the encode loop.
    ///
    /// This asserts on the *cause*, not on wall-clock time: the
    /// `tail_samples_relocated` counter (test-only, `#[cfg(test)]`)
    /// records how many `i16` samples each front-drain has to memmove.
    /// That is precisely the quantity that grows quadratically.
    ///
    /// A wall-clock assertion was tried first and rejected: at
    /// test-affordable input sizes the (linear) Opus encode dominates,
    /// so the measured ratio between the two implementations was only
    /// ~1.2x — too close to noise to be a reliable discriminator, while
    /// a size large enough to separate them would make the test take
    /// minutes.  The counter separates them by three orders of
    /// magnitude, deterministically, in a fraction of a second.
    #[test]
    fn test_write_pcm_bulk_call_does_not_memmove_quadratically() {
        // 4000 frames = 80 s of 16 kHz mono audio, delivered in ONE call
        // (the `encode_16k_mono_ogg` shape).
        const FRAMES: usize = 4000;
        const SAMPLES_PER_FRAME: usize = 320;
        let pcm = sine_pcm(FRAMES * SAMPLES_PER_FRAME);

        let mut fixed = writer_with_fixed_serial();
        let _ = fixed.header().unwrap();
        let fixed_out = fixed.write_pcm(&pcm).unwrap();

        let mut legacy = writer_with_fixed_serial();
        let _ = legacy.header().unwrap();
        let legacy_out = legacy_write_pcm(&mut legacy, &pcm).unwrap();

        // Same input, same bytes: the only difference is the memmove cost.
        assert!(
            fixed_out == legacy_out,
            "both implementations must produce the same bytes"
        );

        // Legacy: sum over i in 0..FRAMES of (FRAMES - 1 - i) * 320
        // = 320 * FRAMES * (FRAMES - 1) / 2 — quadratic in FRAMES.
        let expected_legacy = SAMPLES_PER_FRAME as u64 * FRAMES as u64 * (FRAMES as u64 - 1) / 2;
        assert_eq!(
            legacy.tail_samples_relocated, expected_legacy,
            "the legacy reference must exhibit the quadratic memmove it is here to model"
        );

        // Fixed: one drain at the end of the call, and there is no tail
        // left over (the input is an exact multiple of the frame size),
        // so nothing is relocated at all.
        eprintln!(
            "write_pcm {FRAMES} frames in one call: fixed relocated {} samples, \
             legacy(drain-in-loop) relocated {} samples",
            fixed.tail_samples_relocated, legacy.tail_samples_relocated
        );
        assert!(
            fixed.tail_samples_relocated <= SAMPLES_PER_FRAME as u64,
            "write_pcm is memmoving its buffer tail per frame again ({} samples relocated \
             for {FRAMES} frames) — did drain(..frame) come back inside the loop?",
            fixed.tail_samples_relocated
        );
    }
}