NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev
Audio recording with waveform and spectrum visualization
Last release 17 days ago
21 Sep 2026
Ships on a steady schedule
a new release about every 2 months
Most releases are documented
notes for 4 of 5 stable releases
Nothing withdrawn
no release was ever pulled
4 months old
5 releases · first in 2026
One column per month.
`AudioPlayerEvent$SetSpeed` — скорость воспроизведения: событие ставит рейт и на уже играющий звук, и на все следующие запуски этого блока. Скорость в
AudioPlayerEvent$SetSpeed — скорость воспроизведения: событие ставит рейт и на уже играющий звук, и на все следующие запуски этого блока. Скорость видна в состоянии — новое поле speed у AudioPlayerState$Playing и AudioPlayerState$Paused (по умолчанию 1.0, существующие конструкторы и switch-ветки не ломаются), чтобы UI перерисовывал подпись сам. AudioPlayer кэширует рейт в своём поле, но не переприменяет его ни на play(), ни на setSource() — поэтому блок ставит его сам после каждого запуска источника, иначе скорость молча сбрасывалась бы на 1.0 при холодном старте и после Completed. Отказ платформы сменить рейт проглатывается: звук продолжает играть как играл. Верхнюю границу диапазона задаёт вызывающий — audioplayers даёт гарантию до 2× (iOS/macOS), Android требует API 23+`AudioRecordingBloc` — PCM с платформенного канала читался как chunk.buffer.asInt16List(): от начала буфера сообщения и на всю его длину, мимо offsetI
AudioRecordingBloc — PCM с платформенного канала читался как chunk.buffer.asInt16List(): от начала буфера сообщения и на всю его длину, мимо offsetInBytes и длины самого чанка. В визуализацию и спектр уходили служебные байты сообщения и соседние куски, а при нечётном смещении PCM16 разъезжался на байт — любой звук превращался в шум почти полной громкости, одинаковый и в тишине, и под речь. На записанный файл это не влияло: сервис пишет чанк сам. Теперь Int16List.sublistView(chunk), с копией при нечётном смещенииAudioUtils.wavToSamples — та же ошибка в wavBytes.buffer.asInt16List(44): для вью читались чужие байтыStringSnapshotPainter — размах «струны» не зависел от громкости: каждый кадр нормировался на собственный пик (scale = 1 / peak), поэтому и тихий шум, и громкая речь рисовались во всю высоту. Теперь размах задаётся уровнем сигнала (RMS) по логарифмической шкале floorDb…ceilingDb: тишина — прямая линия, шум комнаты — мелкая дрожь, речь — полный размах. Линейного усиления нет: между шумом и речью 30–40 дБ, одним множителем оба конца шкалы не показатьStringSnapshotPainter — форма волны нормируется отдельно от размаха, поэтому кривая больше не срезается по границе боксаStringSnapshotPainter — сырые сэмплы рисовались точка-в-сэмпл (2048 точек на ~360 px), на экране это полоса шума. Теперь усредняются до одной точки на pointSpacing логических пикселей; усреднение работает как ФНЧStringSnapshotPainter — окно снапшота съезжало по фазе на каждый чанк, волна ползла по горизонтали. Добавлен триггер по переходу через ноль снизу вверх (alignToZeroCrossing); сдвиг берётся из запаса точек, поэтому горизонтальный масштаб не меняется от кадра к кадруStringSnapshotPainter — контрольной точкой квадратичной кривой брался предыдущий сэмпл вместо текущего: кривая шла на шаг позади данных, а последний сегмент проходил прямой хордой мимо предпоследней точкиStringSnapshotPainter.shouldRepaint — сравнивал только samples и stringColor; смена остальных параметров не перерисовывала холстAudioRecordingBloc.waveformSamples — точки брались мгновенным отсчётом (каждый 441-й сэмпл): на самом громком слоге могла попасться фаза перехода через ноль и дать 0. Теперь это RMS-энергия по окнам ~10 мс — честная огибающая, неотрицательные значения. Окно RMS также было захардкожено под 44100 Гц (441 сэмпл); теперь считается от spectrumConfig.sampleRate, а остаток окна переносится через границу чанка вместо того, чтобы терятьсяRecordingLevelPainter — высота столбика бралась из мгновенного отсчёта линейно; после смены семантики waveformSamples (см. выше) индикатор всегда выглядел почти пустым на речи (−22 dBFS RMS = 8% высоты). Теперь высота — по той же логарифмической шкале floorDb/ceilingDb, что и у струныRecordingLevelPainter.shouldRepaint — сравнивал только samples и barColor; barSpacing, minBarHeightFraction и новые floorDb/ceilingDb не перерисовывали холстWaveformPainter (WaveformStyle.envelope) — каждый кадр нормировался на собственный пик (scale = 1 / peak), та же болезнь, что была у струны: тишина и речь рисовались во всю высоту. Теперь размах — по логарифмической шкале floorDb/ceilingDbWaveformDisplay (WaveformStyle.string) — рисовал дубль струны на 56 децимированных, после смены семантики waveformSamples уже неотрицательных (знака нет) отсчётах. Теперь при style: WaveformStyle.string рендерится StringSnapshotDisplay — он читает полные snapshotSamples и уже умеет форму, размах и триггер по нулюMessengerWaveformDisplay (живой виджет) — дефолт logarithmic: false (нормировка на глобальный пик) на скроллящемся окне прыгал на каждом кадре, а без окна один громкий всплеск пережимал всё, что было до него. Дефолт изменён на logarithmic: true. StaticMessengerWaveformDisplay (весь клип целиком) не тронут — пиковая нормировка там осознанный «телеграмный» видMessengerWaveformPainter.shouldRepaint — не сравнивал silenceThreshold, minDbThreshold, barSpacingStringSnapshotDisplay, StaticStringSnapshotDisplay, StringSnapshotPainter — новые параметры floorDb, ceilingDb, autoGain, maxGain, pointSpacing, alignToZeroCrossing. Сигнатуры совместимы, но вид по умолчанию изменился. Прежний возвращается комбинацией autoGain: true, pointSpacing: 0, alignToZeroCrossing: falseRecordingLevelDisplay, StaticLevelDisplay, RecordingLevelPainter — новые параметры floorDb (-55.0), ceilingDb (-15.0)WaveformDisplay, WaveformPainter — новые параметры floorDb (-55.0), ceilingDb (-15.0) для WaveformStyle.envelope; для WaveformStyle.string игнорируютсяMessengerWaveformDisplay — дефолт logarithmic изменён с false на true (см. Fixes)rmsOf, amplitudeForRms) вынесена в один файл lib/src/utils/level_scale.dart — раньше жила отдельно в StringSnapshotPainter и дублировалась бы в MessengerWaveformPainter. Не экспортируется из audio_waveform_kit.dart, публичный API не растётNothing published for this version
`AudioRecordingBloc` — убран повторный dispose() AudioRecordingService: при демонтаже AudioWaveformScope сервис закрывался дважды (в Bloc.close() и в
AudioRecordingBloc — убран повторный dispose() AudioRecordingService: при демонтаже AudioWaveformScope сервис закрывался дважды (в Bloc.close() и в RepositoryProvider.dispose), что могло бросать исключение на уже освобождённом AudioRecorder. Теперь жизненным циклом сервиса управляет только DI-scopeStringSnapshotPainter — исправлены NaN-координаты пути при снапшоте из одного сэмплаTimelineSpectrumPainter — исправлено деление на ноль при SpectrumConfig.frequencyBands == 1LiveSpectrumDisplay, SpectrumDisplay — чтение spectrumConfig в build() переведено с context.read() на context.select()AudioWaveformPlayer — убран небезопасный context.findRenderObject()! в пользу явной проверки на nullWaveformDisplay, RecordingLevelDisplay, StringSnapshotDisplay, MessengerWaveformDisplay, LiveSpectrumDisplay — сравнение списков сэмплов в buildWhen переведено с != (по ссылке) на ListEquality (по содержимому)AudioRecordingBloc — дефолт maxSnapshotSamples приведён к 2048, согласован с дефолтом AudioWaveformScopeWaveformStyle перенесён из widgets/waveform_display.dart в painters/waveform_painter.dart — устранена обратная зависимость painter → widget; публичный API не изменился (WaveformStyle по-прежнему доступен из audio_waveform_kit.dart)AudioRecordingState$Finished.toRecordingResult() — единая точка маппинга state в RecordingResult вместо ручной сборки в AudioRecordButtoncollection добавлен в прямые зависимости пакета (ранее использовался транзитивно)`AudioWaveformScope` — DI-scope виджет; оборачивает дерево и предоставляет AudioRecordingService, SpectrumAnalyzer и AudioRecordingBloc
AudioWaveformScope — DI-scope виджет; оборачивает дерево и предоставляет AudioRecordingService, SpectrumAnalyzer и AudioRecordingBlocAudioRecordButton — кнопка записи/остановки с фабричным конструктором defaultStyle()RecordingTimer — отображение прошедшего времени записиAudioWaveformPlayer — самодостаточный виджет воспроизведения с прогресс-баром; работает с файлом (native) и WAV-байтами (web)WaveformDisplay — огибающая волновой формы; стили WaveformStyle.envelope и WaveformStyle.stringMessengerWaveformDisplay — RMS-волновая форма в стиле мессенджераRecordingLevelDisplay — VU-метр реального времениLiveSpectrumDisplay — живой FFT-спектр, обновляется каждые 50 мсStringSnapshotDisplay — осциллограф из сырых PCM-семпловSpectrumDisplay — FFT-спектр; линейный и логарифмический режимыTimelineSpectrumDisplay — тепловая карта время–частотаStaticLevelDisplay — статический VU-метрStaticMessengerWaveformDisplay — статическая RMS-волновая формаStaticStringSnapshotDisplay — статическая осциллограммаAudioRecordingBloc — конечный автомат записи: $Idle → $Recording → $Finished / $Error; события $Start, $Stop, $ResetAudioPlayerBloc — конечный автомат воспроизведения: $Idle / $Playing / $Paused / $Completed / $Error; события $Play, $Pause, $Stop, $SeekSpectrumAnalyzer — чистый Dart FFT с оконной функцией Ханна, 50 % перекрытием, авто-нормализацией; методы analyze(), computeTimeline(), analyzeRaw()SpectrumConfig — настройки FFT: fftSize, frequencyBands, frequencyMin/Max, sampleRate, dynamicRangeDb, displayTypeRecordingResult — контейнер результата: filePath, wavBytes, duration, waveformSamples, rmsSamples, snapshotSamples, spectrumData, spectrumTimelineAudioUtils — encodeWav(), wavToSamples(), formatDuration()PlatformUtils — isWeb, hasMicrophonePermission()| Platform | Recording | Playback |
|---|---|---|
| Android | ✓ file | ✓ file |
| iOS | ✓ file | ✓ file |
| macOS | ✓ file | ✓ file |
| Windows | ✓ file | ✓ file |
| Linux | ✓ file | ✓ file |
| Web | ✓ WAV bytes | ✓ WAV bytes |
Your coding agent can read these notes before it upgrades. Set up the MCP server →