hifiberry-dsp (1.5.6) stable; urgency=medium

  * Extra sigmatcpserver options can be set again without overriding the
    unit. sigmatcpserver.service reads an optional
    /etc/default/sigmatcpserver and appends $SIGMATCP_OPTS to its built-in
    options; no such file is installed. doc/debug.md still described the
    DEBUG_OPTIONS file that had been dropped, so enabling debug logging as
    documented did nothing; it now describes SIGMATCP_OPTS="--debug".
    Following a discussion with jspricke (#88, #97).
  * build.sh builds with sbuild instead of assembling the package by hand,
    the same way the package build host does. Contributed by
    LarsGrootkarzijn (#89).

 -- HiFiBerry <support@hifiberry.com>  Mon, 09 Nov 2026 10:58:43 +0000

hifiberry-dsp (1.5.5) stable; urgency=medium

  * sigmatcpserver.service is started by the SPI device instead of at boot.
    It is installed into dev-spidev0.0.device, which a new udev rule
    creates, and carries ConditionPathExists=/dev/spidev0.0, so on a system
    with SPI disabled it is skipped instead of failing to open
    /dev/spidev0.0 and being restarted every five seconds. Upgrades move an
    enabled unit over; a disabled one stays disabled.

 -- HiFiBerry <support@hifiberry.com>  Sat, 26 Sep 2026 08:26:50 +0000

hifiberry-dsp (1.5.4) stable; urgency=medium

  * Preset validation now rejects an optional channel field carrying an
    explicit null. Absent and null are not the same thing, but validate()
    skipped a null as if the key were missing, while the consumers did not:
    settings.get(field, default) returns the null rather than the default,
    because the key is present. level and delayMs reached float(None) and
    threw TypeError out of an apply as a bare 500 with no reason attached;
    enabled read as falsy and muted the channel outright, applying the
    preset with one driver silent and saying nothing about it. The write
    happens after the whole plan is built, so neither case ever reached the
    DSP half-applied.

 -- HiFiBerry <support@hifiberry.com>  Mon, 07 Sep 2026 11:20:00 +0000

hifiberry-dsp (1.5.3) stable; urgency=medium

  * Preset validation now rejects a filter that is not a JSON object, and a
    coefficient that is not a number, as PresetInvalid. A filter given as a
    bare value made the coefficient check raise TypeError, which
    list_presets() does not catch: one hand-edited file in the user preset
    directory took GET /presets down with a 500 and hid every bundled preset
    -- the opposite of the "skip the bad file, keep the rest" rule the
    listing is built around. Non-numeric coefficients passed validation
    entirely and failed later inside the apply, partway through a whole-bank
    write, leaving earlier banks applied behind a 500.

 -- HiFiBerry <support@hifiberry.com>  Mon, 07 Sep 2026 09:00:00 +0000

hifiberry-dsp (1.5.2) stable; urgency=medium

  * Add DELETE /presets/current: returns the four preset banks to genuinely
    empty (a transparent biquad in every slot, bypass cleared) and forgets
    the recorded selection, so the presets page stops showing the profile as
    "Applied" and the crossover/EQ pages stop treating the banks as
    preset-owned. Backs the WebUI's "Clear preset and edit freely" action.
    Per-channel registers -- role, level, delay, invert -- are left alone:
    clearing filters is not the same as re-routing the amplifier. Clearing
    when nothing is applied is a 200 with cleared: null, not an error; a
    missing profile checksum is a 503, same as apply; a selection that
    cannot be forgotten in the settings store is a 500, not a silent
    success.

 -- HiFiBerry <support@hifiberry.com>  Fri, 04 Sep 2026 14:16:53 +0000

hifiberry-dsp (1.5.1) stable; urgency=medium

  * Applying a speaker preset now clears the bypass state of the four
    filter banks it writes. The settings store deliberately preserves an
    existing bypass flag when a filter is stored, which is what the A/B
    compare on the crossover page needs but not what an apply needs: a bank
    left bypassed by a compare whose restore never landed kept that flag
    through the apply. The DSP was written correctly and the speaker sounded
    right, but at the next boot the restore path read the stale flag and put
    a unity biquad in every slot of that bank -- one channel flat and
    full-range into its driver while the other three stayed crossed over,
    appearing only after a reboot.
  * GET /presets/<id> now carries filterCounts, the same per-channel filter
    counts GET /presets carries, so a client does not have to fetch the
    listing to learn what the document it already holds contains.
  * GET /cache no longer counts _attributes as one of the profile's own
    metadata keys; like _system it is synthesised rather than declared by
    the profile.

 -- HiFiBerry <support@hifiberry.com>  Fri, 04 Sep 2026 13:23:59 +0000

hifiberry-dsp (1.5.0) stable; urgency=medium

  * Add speaker presets: GET /presets, GET /presets/<id> and
    POST /presets/<id>/apply. A preset describes a loudspeaker as four DSP
    channels and is applied as a unit -- four biquad banks and sixteen
    per-channel registers under one lock, recorded in the settings store so
    it survives a reboot and a profile reload. Presets are read from
    /usr/share/hifiberry/speaker-presets and
    /var/lib/hifiberry/speaker-presets, a local preset shadowing a shipped
    one of the same name.
  * Applying validates the loaded profile and every channel's role before
    writing anything: program id, version, sample rate, bank sizes, and
    whether the profile can express each channel's requested role. Any
    mismatch is a 409 that writes nothing. The role check used to run inside
    the write loop, so a role the profile could not express left the
    amplifier crossed over for two different speakers behind a response that
    claimed nothing had happened; it is now decided up front, for every
    channel, before the first write. The sample rate check matters because
    preset coefficients are computed for one rate and cannot be moved to
    another by writing them somewhere else.
  * A preset apply that reaches the DSP but fails to record which preset is
    now active reports a 500 instead of a silent 200.
  * GET /presets/<id> now tells a missing preset (404) apart from one that
    exists but fails validation (500), so a hand-written preset with a typo
    reports its own error instead of vanishing from the listing.
  * Preset files are validated when read: a non-numeric or negative
    level/delayMs, or a non-boolean invert/enabled, is rejected up front
    instead of failing later with an untyped error. Fields left out of a
    channel remain legal.
  * GET /metadata now reports an _attributes map, exposing XML attributes
    that were previously unreachable -- the role ordering on
    channelSelect*Register and the clamp on delay*Register.

 -- HiFiBerry <support@hifiberry.com>  Fri, 04 Sep 2026 12:37:30 +0000

hifiberry-dsp (1.4.1) stable; urgency=medium

  * restapi: filters are stored under the checksum the daemon restores
    them by, so they survive a reboot. get_current_program_checksum_sha1()
    documents itself as length-based and is what every REST write path keys
    the settings store with, but it called calculate_program_checksums()
    without a mode and so got the signature-based default. Startup looks
    the filters up by the length-based SHA-1. The two modes hash different
    byte ranges of the same program and so produce different digests, so
    every filter written through the API was filed where nothing would ever
    look for it, and was silently gone at the next restart. Observed on a
    Beocreate 4-Channel Amplifier: 64 filters in the store, and a boot log
    reading "No stored settings found for checksum E9C0...".
  * settings store: new migrate_checksum() moves a profile's settings from
    one checksum to another. It refuses when the destination already has an
    entry, empty one included: delete_filters() empties a profile but keeps
    its key, and migrating over that would bring deleted filters back at the
    next start. A write that could not be saved is reported as a failure
    rather than logged as a move, since save_store() returns False instead
    of raising and the caller would otherwise stop looking while the file
    still held the settings where they were.
  * sigmatcp: startup brings filters filed under an older checksum of the
    running program under the one the restore uses, so settings written by
    an affected version come back rather than staying stranded. Both older
    keys are tried, the signature-based SHA-1 and the signature-based MD5 an
    earlier version used; the first match wins and any other older entry is
    left where it is, since nothing can tell which of two holds the newer
    settings. A device whose store is already keyed correctly returns before
    computing anything, so the steady state costs one store read and logs
    nothing. Verified across a reboot: the same device that logged "No
    stored settings found" now logs the recovery and applies the filters,
    and a second start does not migrate again.
  * restapi: a filter written just after the DSP program changed is stored
    under the new program's checksum rather than the old one's. The helper
    noticed the change and cleared this module's cache, but then asked for a
    cached digest, and the cache inside Adau145x still held the previous
    program's. Only a change asks for a fresh digest, not a cache that has
    simply never been filled: an uncached read stops the core to read
    program memory, about a third of a second of silence, which would
    otherwise have been paid on the first filter change after every boot.
    Note that the check behind this compares program length, so it is a
    backstop rather than the main guard -- a replacement program of the same
    length leaves the cache valid, and it is prepare_update() clearing both
    caches that covers the upload paths.
  * sigmatcp: the legacy-key recovery returns before computing anything when
    the store holds nothing that could be migrated. Working that out costs
    one store read; computing a signature-mode checksum does not, since it
    is taken over program memory and stops the core to read it, and a device
    that has never stored a filter would have paid that on every start and
    after every profile install.
  * adau145x: reset_safeload_detection() takes the bus lock. The probe holds
    it while it reads and writes the recorded answer, across several
    transactions and the poll, so a reset landing mid-probe was undone by
    the answer for the program that had just been replaced. A stale
    "supported" is the dangerous way round: that path neither verifies the
    transfer nor restores the staging window, so it would have left filter
    coefficients in seven cells of the new program's live data.

 -- HiFiBerry <support@hifiberry.com>  Fri, 04 Sep 2026 11:05:00 +0000

hifiberry-dsp (1.4.0) stable; urgency=medium

  * adau145x: filter coefficients written through Adau145x.write_biquad()
    are applied using SigmaStudio software safeload. That covers the REST
    API and the daemon's own filter restore; the raw memory writes of the
    TCP protocol still go cell by cell, and dsptoolkit's set-filters with
    them. A biquad is five consecutive cells, and writing them one at a
    time while the core runs lets a frame execute against a slot holding
    part of the new filter and part of the old one -- a response that is
    neither, audible as a click on an EQ change and as a moment of wrong
    slope on a crossover. The five words now go into the staging window at
    the start of DM1 together with their target address and a word count,
    and the core copies them out between two frames. Posting the request is
    a single SPI burst of seven words, so it costs no more than the five
    individual writes it replaces.
  * adau145x: safeload is detected rather than assumed, because it is an
    option of the SigmaStudio project and not a property of the chip. The
    first filter write after a program is loaded doubles as the probe: the
    staging window is read, the request posted, and the count cell polled
    for up to 10ms. A program that does not answer gets its seven cells put
    back untouched and is never probed again until a different program is
    loaded, and its filters are written directly as before. The Beocreate
    Universal profile is verified to support it; the other shipped profiles
    have not been checked on hardware, which is what the probe is for.
  * adau145x: the probe proves the transfer instead of trusting the
    handshake. On a program without safeload the staging window is ordinary
    data belonging to that program, and the cell being polled can hold zero
    for reasons of its own; reading the count as an answer would then post
    every later filter into a window nobody reads, and report success each
    time. The words are now read back from the slot before safeload is
    believed, compared as they go on the wire rather than as Python values
    -- decimal_repr() can return 2**32 for a coefficient just below zero,
    which int_data() writes as four zero bytes. The window is restored on
    every path that does not end in a proven transfer, an error
    mid-handshake included, since coefficients left behind would be what
    the next probe takes for the program's own data and writes back over
    it.
  * adau145x: a probe that cannot prove anything is skipped rather than
    attempted. Re-applying a filter that is already in the slot is common
    -- restoring stored filters and bypassing to unity both do it -- and
    the readback would find the words there whether safeload ran or not.
    Concluding from that would enable safeload on a program without it,
    after which every filter write posts into a window nobody reads and
    stalls holding the bus.
  * adau145x: the core's state is read from the chip before probing and
    before a failure is recorded, rather than tracked in the process.
    Several operations stop the core to get a consistent read and start it
    again after, with the bus free in between, and a TCP client can stop it
    by writing the registers directly -- which a tracked flag would miss,
    sending a probe against a dead core and filing the timeout as the
    program's answer, so a capable profile would fall back to the
    non-atomic writes for the life of the process. Stopping and starting
    the core now also holds the bus for its whole register sequence, so no
    other thread meets the core half stopped.
  * adau145x: the safeload timeout is 10ms, measured on the monotonic
    clock rather than the wall clock -- these boards have no RTC, and an NTP
    step backwards during the poll would hold the bus for the size of the
    step. The core answers within one
    audio frame, about 21us at 48kHz, so the wait is ample margin either
    way -- and on a program without safeload it is also how long the
    probe's words sit in that program's live memory.
  * adau145x: safeload_write() refuses a word that does not fit a 32-bit
    cell instead of letting int_data() truncate it into four plausible
    bytes.
  * spi: SPI access is serialised with a reentrant lock on SpiHandler. The
    module previously assumed the underlying library was thread-safe and
    took no lock of its own, so the REST API workers, the ALSA volume sync
    and the TCP server could interleave. That is a correctness requirement
    for safeload rather than a refinement: the staging window is one shared
    resource, and a request posted before the core has consumed the
    previous one replaces it silently, so that filter is never applied.
    The lock is held across the write and the polling reads that follow.

 -- HiFiBerry <support@hifiberry.com>  Fri, 04 Sep 2026 08:51:21 +0000

hifiberry-dsp (1.3.14) stable; urgency=medium

  * settings store: writes are atomic and transactional again. save_store()
    wrote through a fixed shared '<store>.tmp' and took its flock on that
    temp file after open() had truncated it, so two Waitress worker threads
    shared one inode: the first renamed it over dspsettings.json, the second
    kept writing into what had become the live store and then failed its own
    rename with ENOENT. Applying a 16-band Room EQ correction hit this on
    nearly every write and left the left and right banks with different
    filters. Writes now go through a uniquely named temp file and
    os.replace(), under a lock held across the whole load-mutate-save cycle.
  * restapi: new POST /filters/bank writes a complete filter bank in one
    request instead of one round-trip per slot, and /biquad, /filters/bank
    and the bank-bypass loop now serialise their DSP writes so concurrent
    requests cannot interleave.
  * settings store: a coefficient that is not a finite number is refused
    rather than written to disk as NaN or Infinity, which are not JSON and
    would leave the store unreadable to a strict parser.
  * restapi: POST /filters/bank refuses a fractional numeric address instead
    of truncating it and writing the bank a few words below where the caller
    meant, while reporting success.
  * restapi: /filters/bank and /biquad answer an unresolvable bank name with
    400 rather than 404. The web UI reads a 404 from these routes as "this
    device predates the bulk endpoint" and falls back to per-slot writes, so a
    mistyped bank name was reported to the user as old firmware and then
    failed a second time on the fallback path.
  * restapi: /filters/bank refuses the write, with 503, when the active
    profile checksum cannot be determined. Without it the slots are never
    recorded in the settings store, and since the web UI does not persist on
    this path either, the bank reached the DSP and nowhere else and was lost
    at the next profile load -- behind a 200 reporting success.
  * restapi: /biquad refuses a write it cannot record, the same as
    /filters/bank. It had the identical hole, on the route a bank write falls
    back to when the device has no bulk endpoint -- so sixteen slots reached
    the DSP, none were recorded, and each reported success.

 -- HiFiBerry <support@hifiberry.com>  Tue, 01 Sep 2026 11:59:29 +0200

hifiberry-dsp (1.3.13) stable; urgency=medium

  * sigmatcpserver: --disable-tcp no longer leaves port 8086 listening. The
    SigmaTCP socket was created unconditionally in SigmaTCPServerMain.__init__
    while serve_forever() was only called when TCP was enabled, so with
    --disable-tcp (what the shipped unit uses) the port bound but never
    accepted: clients completed the TCP handshake, queued in the accept backlog
    and hung until they timed out, instead of getting a connection refused.

 -- HiFiBerry <support@hifiberry.com>  Fri, 07 Aug 2026 16:00:00 +0200

hifiberry-dsp (1.3.12) stable; urgency=medium

  * Ship an auth-gateway classification manifest (etc/hifiberry/auth.d/dsptoolkit.json).
    The DSP toolkit API had no manifest, so every call defaulted to "risky" and
    the DSP pages 401'd once the auth gateway is active. Reads (hardware detect,
    metadata, memory/register reads, cache/profile/checksum/program info, stored
    filters) and the pure-math frequency-response calc are "ok"; all DSP writes
    (memory/register write, biquad, deploy profile, store/delete filters, cache
    clear) stay risky.

 -- HiFiBerry <support@hifiberry.com>  Fri, 17 Jul 2026 16:00:00 +0000

hifiberry-dsp (1.3.11) stable; urgency=medium

  * systemd: launch sigmatcpserver with --store --restore so DSP data
    memory (including the volume register) is dumped on graceful stop
    and re-applied on next start. Audio level set via the DSPVolume
    ALSA control now survives reboots instead of resetting to the
    firmware's quiet default (~-39 dB on Beocreate).
  * systemd: KillSignal=SIGINT + TimeoutStopSec=15 so the --store path
    (which only runs from the KeyboardInterrupt handler) actually
    executes during `systemctl stop` / reboot.

 -- HiFiBerry <support@hifiberry.com>  Mon, 11 May 2026 14:00:00 +0000

hifiberry-dsp (1.3.10) stable; urgency=medium

  * sigmatcpserver: compare each XML checksum field against the matching
    algorithm — "checksum" (signature MD5) vs DSP signature-mode MD5,
    "checksum_sha1" (length SHA-1) vs DSP length-mode SHA-1. Either
    match is sufficient. Pairs with hifiberry-dspprofiles 1.1.1 which
    now ships both metadata entries. Profiles that still carry only the
    old "checksum" entry continue to work via the MD5 path.

 -- HiFiBerry <support@hifiberry.com>  Mon, 11 May 2026 13:00:00 +0000

hifiberry-dsp (1.3.9) stable; urgency=medium

  * sigmatcpserver: fix profile-vs-DSP checksum comparison. XML profiles
    store signature-based MD5/SHA-1 under <metadata type="checksum">, but
    the server was reading the DSP's length-based checksums and comparing
    against them. Two different algorithms over the same firmware never
    match, so every healthy boot was logging "checksums do not match,
    aborting" and refusing to wire alsasync to the volume control
    register. Result: the DSPVolume ALSA control had no effect on the
    actual DSP volume register, which boots at a near-mute default
    value, and speakers stayed silent. Fix is to compare signature ↔
    signature (with length as a fallback only). Length-mode is still
    used for filter-store indexing where the algorithm difference is
    intentional.

 -- HiFiBerry <support@hifiberry.com>  Mon, 11 May 2026 12:00:00 +0000

hifiberry-dsp (1.3.8) stable; urgency=medium

  * Fix critical bug: Biquad.from_parameters() replaced with correct Biquad() constructor
  * Fix duplicate _group_filters_by_bank() method in settings_store.py
  * Fix wrong import in test_filter_store.py (FilterStore → SettingsStore)
  * Fix @classmethod methods using self instead of cls in biquad.py
  * Change debug-level messages from logging.info to logging.debug in adau145x.py
  * Replace bare except clauses with specific exception types across 11 files

 -- HiFiBerry <support@hifiberry.com>  Mon, 02 Mar 2026 12:00:00 +0000

hifiberry-dsp (1.3.7) stable; urgency=medium

  * Add nginx include file for DSP Toolkit API proxy (/etc/nginx/hifiberry-api.d/)

 -- HiFiBerry <support@hifiberry.com>  Sat, 28 Feb 2026 12:00:00 +0000

hifiberry-dsp (1.3.6) stable; urgency=medium

  * Fixed ALSA volume sync to ignore ALSA changes when no DSP volume register
  * ALSA volume automatically resets to 100% when DSP volume register unavailable
  * Added --alsa option to sigmatcpserver systemd service
  * Enables automatic synchronization between DSP and ALSA volume controls
  * Simplified Debian packaging: removed postinst and prerm scripts
  * Streamlined debian/rules with simplified build configuration
  * Added postrm script for proper cleanup on package removal
  * Organized package documentation with .docs, .examples, and .manpages files
  * Simplified sigmatcpserver systemd service configuration
  * Code cleanup in api/filters.py, api/restapi.py, measurement/data.py, setup.py
  * Removed obsolete requirements.txt and dspparamreader documentation images
  
 -- HiFiBerry <info@hifiberry.com>  Fri, 06 Feb 2026 15:35:00 +0000

hifiberry-dsp (1.3.3) stable; urgency=medium

  * Added /version endpoint to REST API for toolkit version information

 -- HiFiBerry <info@hifiberry.com>  Tue, 02 Oct 2025 16:00:00 +0000

hifiberry-dsp (1.3.2) stable; urgency=medium

  * Enhanced checksum caching system with program length validation
  * Added START_PULSE_REGISTER constant for DSP sample rate detection
  * Refactored checksum functions into separate get_current_program_checksum() 
    and get_current_program_checksum_sha1()
  * Removed legacy cache fields for cleaner, more maintainable code
  * Improved cache invalidation based on DSP program length changes
  * Enhanced /cache endpoint with detailed checksum and validation status
  * Fixed checksum cache synchronization between REST API and SigmaTCP server
  * Added new dsp-install-profile command-line tool using REST API
  * Added new dsp-program-info command-line tool using REST API
  * Added new dsp-get-profile command-line tool using REST API
  * Added comprehensive man pages for all new command-line tools
  * Enhanced /dspprofile endpoint to accept raw XML content in addition to JSON
  * Improved XML profile validation logic for better reliability

 -- HiFiBerry <info@hifiberry.com>  Tue, 30 Sep 2025 16:00:00 +0000

hifiberry-dsp (1.3.1) stable; urgency=medium

  * Added XML profile support for checksum_sha1 attribute
  * Enhanced profile matching to prioritize SHA-1 over MD5 checksums
  * Updated SigmaTCP server to validate both MD5 and SHA-1 checksums
  * Added comprehensive /program-info API endpoint
  * Added deprecation notices to dsptoolkit command-line utility
  * Updated documentation to guide users to REST API
  * Fixed Debian packaging issues (aliased locations, removed unused binaries)
  * Added AI agent configuration to maintain code style consistency

 -- HiFiBerry <info@hifiberry.com>  Tue, 30 Sep 2025 15:00:00 +0000

hifiberry-dsp (1.3) stable; urgency=medium

  * Enhanced checksum API with dual MD5/SHA-1 algorithm support
  * Added intelligent checksum mode selection (signature-based vs length-based)
  * Implemented efficient checksum caching system to minimize DSP memory access
  * Added program memory subset functionality for optimized checksum calculation
  * Enhanced program length API with max parameter for maximum length retrieval
  * Enhanced program memory API with multiple end detection modes 
    (signature/full/len)
  * Added checksum priority system: SHA-1 (length-based) over MD5 
    (signature-based)
  * Added comprehensive checksum documentation (doc/checksum.md)
  * Improved performance with multi-level caching for memory and checksums
  * Enhanced REST API with detailed checksum response including mode and 
    length info

 -- HiFiBerry <info@hifiberry.com>  Mon, 29 Sep 2025 15:00:00 +0000

hifiberry-dsp (1.2.1) stable; urgency=medium

  * Fix logging bug
  * Added REST API endpoint to retrieve DSP program length (/program-length)
  * Added REST API endpoint to retrieve DSP program memory (/program-memory)
  * Enhanced program memory API with multiple output formats (hex, raw, base64)
  * Updated REST API documentation with new program memory endpoints

 -- HiFiBerry <info@hifiberry.com>  Sun, 29 Sep 2025 12:00:00 +0000

hifiberry-dsp (1.2) stable; urgency=medium

  * Major architecture refactoring: Filter Store to Settings Store
  * Renamed FilterStore class to SettingsStore for generic settings support
  * Renamed filters.json to dspsettings.json for consistent naming
  * Added dedicated memory settings storage with store parameter in memory API
  * Enhanced JSON structure to support both filters and memory settings
  * Added automatic migration from old filter-only format to new structure
  * Enhanced sigmatcp server with memory settings autoloading support
  * Fixed memory API address parsing bug (hex vs decimal inconsistency)
  * Added concurrent write protection with file locking for settings store
  * Added checksum normalization to prevent duplicate profile entries
  * Enhanced REST API consistency across memory and register endpoints
  * Added comprehensive backward compatibility for existing filter APIs
  * Improved error handling and validation throughout settings system
  * Enhanced debug logging with memory write tracing capabilities
  * Added comprehensive documentation for new settings store architecture

 -- HiFiBerry <info@hifiberry.com>  Thu, 08 Aug 2025 12:00:00 +0000

hifiberry-dsp (1.1) stable; urgency=medium

  * Added comprehensive Filter Store API with JSON persistence
  * Added filter storage organized by DSP profile checksum
  * Added automatic filter loading and persistence via REST API
  * Added sigmatcp server autoloading of stored filters on DSP updates
  * Added REST API endpoints for filter management (/filters)
  * Added DSP program checksum endpoint (/checksum)
  * Added cache management and status endpoint (/cache)
  * Added bulk profile metadata endpoint (/profiles/metadata)
  * Added checksum caching for improved REST API performance
  * Added debug mode with --debug flag for memory write logging
  * Added systemd configuration support via /etc/default/sigmatcpserver
  * Added Bypass/PassThrough filter type for signal pass-through
  * Added comprehensive debug logging for all memory writes
  * Enhanced REST API documentation with detailed examples
  * Fixed memory write debug logging bug in sigmatcp server
  * Enhanced filter store with offset-aware addressing
  * Enhanced biquad filter support with coefficient calculation
  * Moved filter storage to /var/lib/hifiberry/filters.json
  * Improved error handling and validation throughout system

 -- HiFiBerry <info@hifiberry.com>  Wed, 07 Aug 2025 14:00:00 +0000

hifiberry-dsp (1.0.3) stable; urgency=low

  * Changed distribution from unstable to stable
  * Improved package stability and compatibility

 -- HiFiBerry <info@hifiberry.com>  Mon, 30 Jun 2025 15:30:00 +0000

hifiberry-dsp (1.0.2) stable; urgency=low

  * Package renamed from python3-hifiberry-dsp to hifiberry-dsp

 -- HiFiBerry <info@hifiberry.com>  Mon, 30 Jun 2025 12:00:00 +0000

hifiberry-dsp (1.0.1) stable; urgency=low

  * Changed packaging to debian sbuild

 -- HiFiBerry <info@hifiberry.com>  Thu, 26 Jun 2025 12:00:00 +0000

hifiberry-dsp (1.0) stable; urgency=low

  * Initial Debian package release
  * Collection of tools to configure HiFiBerry DSP boards
  * Program DSP boards from SigmaStudio
  * Support for SigmaTCP server
  * ALSA volume control integration
  * REW filter support
  * DSP profile management
  * EEPROM programming capabilities
  * Zeroconf network discovery
  * Web interface support
  * Command line utilities for DSP configuration

 -- HiFiBerry <info@hifiberry.com>  Thu, 24 Jun 2025 12:00:00 +0000
