### Launch AAEQ Application Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md Command to start the AAEQ application using Cargo. This command assumes the user is in the project directory and is a convenient way to launch the application during development or initial setup. ```bash cargo run --release ``` -------------------------------- ### Build and Run AAEQ from Source Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md Instructions for cloning the AAEQ repository, building the project using Cargo, and running the compiled application in release mode. This is the primary method for installing AAEQ from source. ```bash cd /path/to/AAEQ # Build the project cargo build --release # Run the application ./target/release/aaeq ``` -------------------------------- ### Linux: Automate Audio Loopback Setup Source: https://github.com/jaschadub/aaeq/blob/main/AUDIO_CAPTURE_SETUP.md Executes a script to create a virtual sink ('AAEQ_Capture') and a loopback for system audio on Linux using PulseAudio or PipeWire. This simplifies the process of capturing system audio for AAEQ. ```bash ./setup-audio-loopback.sh ``` -------------------------------- ### Install ALSA Development Files on Linux Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Installs the ALSA development files required for audio configuration on Debian-based Linux systems. Includes commands to check audio devices and test audio output. ```bash # Install ALSA development files sudo apt-get install libasound2-dev # Check audio devices aplay -L # Test audio output speaker-test -t wav -c 2 ``` -------------------------------- ### macOS: Install BlackHole using Homebrew Source: https://github.com/jaschadub/aaeq/blob/main/AUDIO_CAPTURE_SETUP.md Installs the BlackHole 2ch audio driver on macOS using the Homebrew package manager. BlackHole is a virtual audio device used for routing audio between applications. ```bash brew install blackhole-2ch ``` -------------------------------- ### Start DLNA Server for Streaming with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Starts the DLNA server using the `test_dlna` example from the `stream-server` crate. This server provides an HTTP stream that can be accessed by network players. ```bash cargo run -p stream-server --example test_dlna ``` -------------------------------- ### Discover Audio Devices with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Executes the `discover_devices` example from the `stream-server` crate to list local audio output devices and AirPlay devices on the network. This is the first step in testing audio streaming capabilities. ```bash cd /home/jascha/Documents/repos/AAEQ cargo run -p stream-server --example discover_devices ``` -------------------------------- ### Manage AAEQ Mappings with SQLite (Bash) Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md This snippet demonstrates how to interact with the AAEQ SQLite database using the `sqlite3` command-line tool. It shows how to connect to the database, list all existing mappings, delete a specific mapping by its ID, and clear all mappings. This is useful for manual inspection and modification of the AAEQ's mapping configuration. ```bash sqlite3 ~/.config/aaeq/aaeq.db # List all mappings SELECT scope, key_normalized, preset_name FROM mapping; # Delete a specific mapping DELETE FROM mapping WHERE id = 1; # Clear all mappings DELETE FROM mapping; ``` -------------------------------- ### Test WiiM Device API Connection Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md A cURL command to test the connection to a WiiM device by fetching its player status. This is useful for troubleshooting connectivity issues and verifying that the device is reachable and its API is responsive. ```bash curl "http://192.168.1.100/httpapi.asp?command=getPlayerStatus" ``` -------------------------------- ### Linux: Manually Create Virtual Sink and Loopback Source: https://github.com/jaschadub/aaeq/blob/main/AUDIO_CAPTURE_SETUP.md Manually creates a virtual null sink named 'aaeq_capture' and a loopback module to capture audio from 'aaeq_capture.monitor' on Linux. This requires PulseAudio and is used for routing system audio. ```bash pactl load-module module-null-sink \ sink_name=aaeq_capture \ sink_properties=device.description="AAEQ_Capture" \ rate=48000 \ channels=2 pactl load-module module-loopback \ source=aaeq_capture.monitor \ latency_msec=1 ``` -------------------------------- ### Install cosign on Linux Source: https://github.com/jaschadub/aaeq/blob/main/VERIFY_SIGNATURES.md Installs the cosign command-line tool on Linux systems by downloading the binary from GitHub releases, making it executable, and moving it to the system's PATH. ```bash # Download and install from GitHub releases wget https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64 chmod +x cosign-linux-amd64 sudo mv cosign-linux-amd64 /usr/local/bin/cosign ``` -------------------------------- ### Install cosign on Windows Source: https://github.com/jaschadub/aaeq/blob/main/VERIFY_SIGNATURES.md Installs the cosign command-line tool on Windows systems using the winget package manager. ```powershell # Using winget winget install sigstore.cosign ``` -------------------------------- ### Run AAEQ with Debug Logging (Bash) Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md This command enables debug logging for the AAEQ application when run via cargo. It's useful for diagnosing runtime issues by increasing the verbosity of log messages. Use `RUST_LOG=debug` for standard debug output or `RUST_LOG=aaeq=trace` for extremely detailed logs specific to the AAEQ module. ```bash # Run with debug logging RUST_LOG=debug cargo run --release ``` ```bash RUST_LOG=aaeq=trace cargo run --release # Very detailed ``` -------------------------------- ### Test Local DAC Output with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Runs the `test_local_dac` example from the `stream-server` crate to verify direct audio output to the default sound card or USB DAC. It plays a 1kHz sine wave and reports progress and latency. ```bash cargo run -p stream-server --example test_local_dac ``` -------------------------------- ### Test Audio Format Conversions (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Provides examples of configuring an OutputConfig in Rust to test different audio sample formats, including F32 (native float), S24LE (24-bit PCM with dithering), and S16LE (16-bit PCM). ```rust // Test F32 (native float) let config = OutputConfig { format: SampleFormat::F32, ..Default::default() }; // Test S24LE (24-bit PCM with dithering) let config = OutputConfig { format: SampleFormat::S24LE, ..Default::default() }; // Test S16LE (16-bit PCM) let config = OutputConfig { format: SampleFormat::S16LE, ..Default::default() }; ``` -------------------------------- ### List Audio Devices on macOS Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Retrieves detailed information about audio hardware and devices on macOS using the system_profiler command. ```bash # List audio devices system_profiler SPAudioDataType ``` -------------------------------- ### Install cosign on macOS Source: https://github.com/jaschadub/aaeq/blob/main/VERIFY_SIGNATURES.md Installs the cosign command-line tool on macOS systems using the Homebrew package manager. ```bash brew install cosign ``` -------------------------------- ### Linux: Cleanup PulseAudio Loopback Modules Source: https://github.com/jaschadub/aaeq/blob/main/AUDIO_CAPTURE_SETUP.md Unloads the virtual null sink and loopback modules created by PulseAudio on Linux. This command is used to revert the audio routing changes made for AAEQ. ```bash pactl unload-module module-null-sink pactl unload-module module-loopback ``` -------------------------------- ### Install Linux Development Libraries for AAEQ Source: https://github.com/jaschadub/aaeq/blob/main/README.md Installs necessary development libraries for building AAEQ on Debian/Ubuntu and Arch Linux systems. These include GTK3, libxdo, libappindicator3, ALSA, and D-Bus, which are crucial for GUI, system tray, audio capture, and MPRIS integration. ```bash # Ubuntu/Debian sudo apt install libgtk-3-dev libxdo-dev libappindicator3-dev libasound2-dev dbus # Arch Linux/Manjaro sudo pacman -S gtk3 xdotool libappindicator-gtk3 alsa-lib dbus ``` -------------------------------- ### Run Development Server with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/development.md Command to build and run the AAEQ project in development mode using Cargo. Assumes Rust and its dependencies are installed. ```bash cargo run ``` -------------------------------- ### Build AAEQ from Source using Cargo Source: https://github.com/jaschadub/aaeq/blob/main/README.md Clones the AAEQ repository, builds the project in release mode using Cargo, and prepares it for execution. This process requires Rust 1.75+ and platform-specific dependencies to be installed beforehand. ```bash # Install Rust (if not already installed) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Clone and build git clone https://github.com/jaschadub/AAEQ.git cd AAEQ cargo build --release # Run ./target/release/aaeq ``` -------------------------------- ### macOS: Install nowplayingctl using Homebrew Source: https://github.com/jaschadub/aaeq/blob/main/crates/media-session/README.md This command installs the `nowplayingctl` utility on macOS using the Homebrew package manager. This utility is recommended for maximum compatibility with various media apps on macOS when using the AAEQ Media Session crate. ```bash brew install nowplayingctl ``` -------------------------------- ### Reset AAEQ Database (Bash) Source: https://github.com/jaschadub/aaeq/blob/main/QUICKSTART.md Commands to remove the AAEQ SQLite database file, effectively resetting all mappings and settings to their default state. This is useful for troubleshooting persistent configuration problems or starting with a clean slate. The command varies depending on the operating system. ```bash # Linux/macOS rm ~/.config/aaeq/aaeq.db ``` ```bash # Windows del %APPDATA%\AAEQ\aaeq.db ``` -------------------------------- ### Discover DLNA Devices with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Executes the `discover_dlna_devices` example from the `stream-server` crate to find DLNA MediaRenderer devices on the local network. This test verifies the server's ability to discover network audio players. ```bash cargo run -p stream-server --example discover_dlna_devices ``` -------------------------------- ### Test mDNS Discovery on Linux Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Tests Multicast DNS (mDNS) service discovery on Linux systems using the avahi-browse command. Helps diagnose AirPlay device discovery issues. ```bash # Test mDNS avahi-browse -a ``` -------------------------------- ### Stress Test - Long Duration Modification Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Illustrates how to modify existing examples to run for extended periods (e.g., 5 minutes) to perform stress testing. This helps monitor for memory leaks, buffer overflows, connection stability, and CPU usage. ```rust let duration_secs = 300.0; // 5 minutes ``` -------------------------------- ### Manage Listening Profiles in Rust Source: https://context7.com/jaschadub/aaeq/llms.txt This Rust code demonstrates how to create, list, update, and delete listening profiles. It utilizes the `aaeq_persistence` and `aaeq_core` crates for database interactions and profile data structures. The example covers initializing the database, creating multiple profiles, setting an active profile, retrieving it, updating an existing profile, and deleting a user-defined profile. ```rust use aaeq_persistence::{Database, ProfileRepository, AppSettingsRepository}; use aaeq_core::Profile; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize database let db = Database::new("~/.local/share/aaeq/aaeq.db").await?; let profile_repo = ProfileRepository::new(db.pool()); let settings_repo = AppSettingsRepository::new(db.pool()); // Create new profile let profile = Profile { id: None, name: "Headphones".to_string(), is_builtin: false, icon: "🎧".to_string(), color: "#4A90E2".to_string(), created_at: 0, updated_at: 0, }; let profile_id = profile_repo.create(&profile).await?; println!("Created profile '{}' with ID: {}", profile.name, profile_id); // Create another profile let car_profile = Profile { id: None, name: "Car Audio".to_string(), is_builtin: false, icon: "🚗".to_string(), color: "#FF6B6B".to_string(), created_at: 0, updated_at: 0, }; let car_id = profile_repo.create(&car_profile).await?; println!("Created profile '{}' with ID: {}", car_profile.name, car_id); // List all profiles let profiles = profile_repo.list_all().await?; println!("\nAll profiles:"); for p in &profiles { println!(" {} {} (ID: {:?})", p.icon, p.name, p.id); } // Set active profile settings_repo.set_active_profile_id(profile_id).await?; println!("\nActivated profile: {}", profile.name); // Get active profile if let Some(active_id) = settings_repo.get_active_profile_id().await? { if let Some(active_profile) = profile_repo.get_by_id(active_id).await? { println!("Current profile: {} {}", active_profile.icon, active_profile.name); } } // Update profile profile_repo.update( profile_id, "Studio Headphones", "🎙️", "#9B59B6" ).await?; // Delete user profile (builtin profiles cannot be deleted) profile_repo.delete(car_id).await?; println!("\nDeleted profile: {}", car_profile.name); Ok(()) } ``` -------------------------------- ### Run Cargo Tests Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Executes tests for Rust projects managed by Cargo. Supports running specific crates, integration tests, or all tests within the project. ```bash cargo test -p stream-server cargo test -p stream-server --test integration_test cargo test --all ``` -------------------------------- ### Enable Trace Logging with RUST_LOG Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Enables detailed trace logging for Rust applications by setting the RUST_LOG environment variable. Allows for verbose debugging output during execution. ```bash RUST_LOG=debug cargo run -p stream-server --example test_local_dac RUST_LOG=stream_server::sinks=debug cargo run ... RUST_LOG=axum=debug cargo run --example test_dlna RUST_LOG=stream_server::sinks::airplay=debug cargo run --example test_airplay "Device" ``` -------------------------------- ### Test mDNS Discovery on macOS Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Tests Multicast DNS (mDNS) service discovery on macOS using the dns-sd command. Useful for diagnosing AirPlay device discovery issues. ```bash # Test mDNS dns-sd -B _raop._tcp ``` -------------------------------- ### Add User to Audio Group on Linux Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Adds the current user to the 'audio' group on Linux systems to grant necessary audio permissions. Requires logging out and back in for changes to take effect. ```bash # Add user to audio group sudo usermod -a -G audio $USER # Log out and back in ``` -------------------------------- ### Test Multiple Sinks Simultaneously (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Demonstrates how to create a custom test program in Rust to manage and test multiple audio output sinks simultaneously, such as a local DAC and a DLNA sink. It utilizes the OutputManager to register, select, and potentially write audio to different sinks. ```rust use stream_server::* use anyhow::Result; #[tokio::main] async fn main() -> Result<()> { let mut manager = OutputManager::new(); // Register all sinks manager.register_sink(Box::new(LocalDacSink::new(None))); manager.register_sink(Box::new(DlnaSink::new( "Test".to_string(), "0.0.0.0:8090".parse()?, ))); // Select and test each let config = OutputConfig::default(); manager.select_sink_by_name("local_dac", config.clone()).await?; // ... write audio ... manager.select_sink_by_name("dlna", config).await?; // ... write audio ... Ok(()) } ``` -------------------------------- ### Test Different Sample Rates (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Shows how to configure an OutputConfig in Rust to test different audio sample rates, including CD quality (44100 Hz) and high resolution (96000 Hz). ```rust // CD quality let config = OutputConfig { sample_rate: 44100, ..Default::default() }; // High resolution let config = OutputConfig { sample_rate: 96000, ..Default::default() }; ``` -------------------------------- ### Monitor Network Traffic with iftop and tcpdump Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Monitors network traffic on a specified interface. iftop provides a real-time bandwidth usage overview, while tcpdump captures specific network packets based on interface and port. ```bash # Monitor network traffic sudo iftop -i wlan0 # Or use tcpdump sudo tcpdump -i wlan0 port 8090 # DLNA sudo tcpdump -i wlan0 port 6000 # AirPlay RTP ``` -------------------------------- ### Get Device Information Response Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Fetches comprehensive device information, including network details (SSID, MAC), firmware version, and unique identifiers (UUID). This is a JSON response with extensive fields. ```json { "ssid": "WiiM Mini-8FA2", "firmware": "Linkplay.4.6.425351", "uuid": "FF970016A6FE22C1660AB4D8", "DeviceName": "WiiM Mini-8FA2", "MAC": "08:E9:F6:8F:8F:A2", ... } ``` -------------------------------- ### Configure DSP Settings per Profile in Rust Source: https://context7.com/jaschadub/aaeq/llms.txt This Rust code illustrates how to configure and manage Digital Signal Processing (DSP) settings for specific profiles. It uses `aaeq_persistence` and `aaeq_core` to interact with a database and handle DSP settings data. The example covers creating, upserting (inserting or updating), retrieving, and listing DSP settings, including parameters like sample rate, buffer size, dithering, and resampling quality. ```rust use aaeq_persistence::{Database, DspSettingsRepository}; use aaeq_core::DspSettings; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize database let db = Database::new("~/.local/share/aaeq/aaeq.db").await?; let repo = DspSettingsRepository::new(db.pool()); // Create DSP settings for profile let settings = DspSettings { id: None, profile_id: 1, sample_rate: 96000, buffer_ms: 150, headroom_db: -3.0, auto_compensate: true, clip_detection: true, dither_enabled: true, dither_mode: "TPDF".to_string(), // "TPDF", "Rectangular", "Gaussian" noise_shaping: "None".to_string(), // "None", "F-Weighted", "Modified-E-Weighted", "Improved-E-Weighted" target_bits: 16, resample_enabled: true, resample_quality: "High".to_string(), // "Fast", "Balanced", "High", "Ultra" target_sample_rate: 96000, created_at: 0, updated_at: 0, }; // Save settings (upsert by profile_id) repo.upsert(&settings).await?; println!("Saved DSP settings for profile {}", settings.profile_id); // Retrieve settings if let Some(loaded) = repo.get_by_profile(1).await? { println!("\nDSP Configuration:"); println!(" Sample rate: {}Hz", loaded.sample_rate); println!(" Buffer: {}ms", loaded.buffer_ms); println!(" Headroom: {:.1}dB", loaded.headroom_db); println!(" Dithering: {} ({})", loaded.dither_mode, loaded.target_bits); println!(" Noise shaping: {}", loaded.noise_shaping); println!(" Resampling: {} quality to {}Hz", loaded.resample_quality, loaded.target_sample_rate); } // List all DSP settings let all_settings = repo.list_all().await?; println!("\nTotal DSP profiles: {}", all_settings.len()); Ok(()) } ``` -------------------------------- ### Run AAEQ with Debug Logs (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/docs/STREAMING_SERVICE_SUPPORT.md This command allows you to run the AAEQ application with debug logging enabled. This is crucial for diagnosing issues with streaming services. Ensure you have Rust installed and the AAEQ executable available in your PATH. ```shell RUST_LOG=debug aaeq ``` -------------------------------- ### Rust Media Session Abstraction Layer Source: https://github.com/jaschadub/aaeq/blob/main/docs/CROSS_PLATFORM_MEDIA_DETECTION.md Defines a cross-platform trait for media session detection and provides a function to create platform-specific implementations. It includes a struct for media metadata and a trait with methods to get current track information, check playback status, and list active players. Dependencies include 'anyhow' for error handling. ```rust use anyhow::Result; /// Metadata for currently playing media #[derive(Debug, Clone)] pub struct MediaMetadata { pub title: String, pub artist: String, pub album: String, pub album_art_url: Option, pub genre: Option, } /// Cross-platform trait for media session detection pub trait MediaSession { /// Get currently playing track metadata fn get_current_track(&self) -> Result>; /// Check if any media player is currently playing fn is_playing(&self) -> bool; /// Get a list of active media players (platform-specific) fn list_active_players(&self) -> Vec; } /// Create a platform-specific media session pub fn create_media_session() -> Box { #[cfg(target_os = "linux")] return Box::new(linux::MprisSession::new()); #[cfg(target_os = "windows")] return Box::new(windows::SmtcSession::new()); #[cfg(target_os = "macos")] return Box::new(macos::MacOsSession::new()); } ``` -------------------------------- ### Get MPRIS Player Metadata on Linux Source: https://github.com/jaschadub/aaeq/blob/main/docs/STREAMING_SERVICE_SUPPORT.md Retrieves the 'Metadata' property for the currently playing track from an MPRIS-compatible media player on Linux. This is useful for testing AAEQ's setup on Linux. ```bash # Start playing music, then: dbus-send --session --print-reply --dest=org.mpris.MediaPlayer2.* \ /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get \ string:org.mpris.MediaPlayer2.Player string:Metadata ``` -------------------------------- ### DLNA Push Mode Test (AVTransport Control) Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Tests the automatic DLNA push mode where AAEQ controls AVTransport to start playback on a discovered DLNA device. It verifies UPnP AVTransport SOAP control, SetAVTransportURI, Play/Stop actions, and DIDL-Lite metadata generation. ```bash cargo run -p stream-server --example test_dlna_push "Living Room" ``` -------------------------------- ### Control WiiM Devices with Rust HTTP API Source: https://context7.com/jaschadub/aaeq/llms.txt This Rust code snippet demonstrates how to interact with WiiM/LinkPlay devices using their HTTP API. It covers checking device online status, retrieving track metadata, listing EQ presets, applying presets, and controlling volume. Requires the `aaeq_device_wiim` and `aaeq_core` crates. ```rust use aaeq_device_wiim::WiimController; use aaeq_core::DeviceController; #[tokio::main] async fn main() -> anyhow::Result<()> { // Create controller with device IP let controller = WiimController::new("Living Room", "192.168.1.100"); // Check if device is online if !controller.is_online().await { eprintln!("Device is offline"); return Ok(()) } // Get currently playing track let track = controller.get_now_playing().await?; println!("Now playing: {} - {}", track.artist, track.title); println!("Album: {}", track.album); // List available EQ presets let presets = controller.list_presets().await?; println!("Available presets: {:?}", presets); // Apply a preset controller.apply_preset("Bass Booster").await?; println!("Applied Bass Booster preset"); // Control volume controller.set_volume(50).await?; // Check EQ status let eq_enabled = controller.is_eq_enabled().await?; println!("EQ enabled: {}", eq_enabled); Ok(()) } ``` -------------------------------- ### GitHub Actions CI/CD Build Workflow Source: https://github.com/jaschadub/aaeq/blob/main/docs/CROSS_PLATFORM_MEDIA_DETECTION.md A GitHub Actions workflow to compile the AAEQ project in release mode on multiple operating systems (Linux, Windows, macOS). This ensures cross-platform compatibility at build time. It checks out the code and runs `cargo build --release`. ```yaml jobs: build: strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 - name: Build run: cargo build --release ``` -------------------------------- ### Turn EQ On Command Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Enables the Equalizer (EQ) function on the device. The command is `EQOn`. A successful execution returns a JSON object with `{"status":"OK"}`. ```bash https://192.168.1.100/httpapi.asp?command=EQOn ``` -------------------------------- ### Get Device Information Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Retrieves comprehensive information about the WiiM device, including network details, firmware version, and unique identifiers. ```APIDOC ## GET /httpapi.asp?command=getStatusEx ### Description Fetches detailed information about the WiiM device. This includes network configuration (like SSID), firmware version, device UUID, MAC address, and device name. ### Method GET ### Endpoint `https://{device_ip}/httpapi.asp?command=getStatusEx` ### Parameters None ### Request Example `https://192.168.1.100/httpapi.asp?command=getStatusEx` ### Response #### Success Response (200) - **ssid** (string) - The Wi-Fi network name (SSID) the device is connected to. - **firmware** (string) - The current firmware version of the device. - **uuid** (string) - The unique identifier for the device. - **DeviceName** (string) - The name of the device as configured on the network. - **MAC** (string) - The MAC address of the device. - ... (other fields may be present) #### Response Example ```json { "ssid": "WiiM Mini-8FAFA2", "firmware": "Linkplay.4.6.425351", "uuid": "FF970016A6FE22C1660AB4D8", "DeviceName": "WiiM Mini-8FAFA2", "MAC": "08:E9:F6:8F:8F:A2", ... } ``` ``` -------------------------------- ### Get Player Status Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Retrieves the current playback status, including track information, volume, mute status, and playback mode. ```APIDOC ## GET /httpapi.asp?command=getPlayerStatus ### Description Retrieves the current playback status of the WiiM device, including song title, artist, album, volume level, mute status, and playback mode. ### Method GET ### Endpoint `https://{device_ip}/httpapi.asp?command=getPlayerStatus` ### Parameters None ### Request Example None ### Response #### Success Response (200) - **type** (string) - Unknown (observed values: "0") - **ch** (string) - Unknown (observed values: "2") - **mode** (string) - Playback mode (e.g., "10" for Wiimu playlist, "31" for Spotify Connect) - **loop** (string) - Unknown (observed values: "4") - **eq** (string) - Current EQ preset number (e.g., "0") - **status** (string) - Playback status (`"stop"`, `"play"`, `"loading"`, or `"pause"`) - **curpos** (string) - Current playback position in milliseconds - **offset_pts** (string) - Unknown (observed values: same as curpos) - **totlen** (string) - Total length of the track (may be "0") - **vol** (string) - Current volume level (0-100) - **mute** (string) - Mute status (`"0"` = unmuted, `"1"` = muted) - **title** (string) - Track title - **artist** (string) - Track artist - **album** (string) - Track album #### Response Example ```json { "type": "0", "ch": "2", "mode": "10", "loop": "4", "eq": "0", "status": "play", "curpos": "184919", "offset_pts": "184919", "totlen": "0", "vol": "39", "mute": "0", "title": "Time", "artist": "Pink Floyd", "album": "The Dark Side of the Moon" } ``` ``` -------------------------------- ### Format and Lint Code with Cargo Source: https://github.com/jaschadub/aaeq/blob/main/docs/development.md Commands to format the code according to project standards and run clippy for linting. These commands help maintain code quality and consistency. ```bash cargo fmt cargo clippy ``` -------------------------------- ### WiiM API Testing with Curl Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md This section provides `curl` commands to interact with the WiiM device's HTTP API for various functions like retrieving player status, listing EQ presets, applying presets, checking EQ status, and setting volume. ```bash # Get player status curl "http://192.168.1.100/httpapi.asp?command=getPlayerStatus" # List EQ presets curl "http://192.168.1.100/httpapi.asp?command=EQGetList" # Load Rock preset curl "http://192.168.1.100/httpapi.asp?command=EQLoad:Rock" # Check EQ status curl "http://192.168.1.100/httpapi.asp?command=EQGetStat" # Set volume to 50 curl "http://192.168.1.100/httpapi.asp?command=setPlayerCmd:vol:50" ``` -------------------------------- ### Test nowplayingctl on macOS Source: https://github.com/jaschadub/aaeq/blob/main/docs/STREAMING_SERVICE_SUPPORT.md Tests the `nowplayingctl` utility on macOS by retrieving the title, artist, and album of the currently playing track. This verifies the tool is installed and functional. ```bash # Check if nowplayingctl is installed which nowplayingctl # If not found, install it brew install nowplayingctl # Test it nowplayingctl get title artist album ``` -------------------------------- ### Turn EQ On Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Enables the Equalizer (EQ) functionality on the device. ```APIDOC ## GET /httpapi.asp?command=EQOn ### Description Enables the Equalizer (EQ) functionality for the currently active audio output. ### Method GET ### Endpoint `https://{device_ip}/httpapi.asp?command=EQOn` ### Parameters None ### Request Example `https://192.168.1.100/httpapi.asp?command=EQOn` ### Response #### Success Response (200) - **status** (string) - Indicates the result of the operation (`"OK"`). #### Response Example ```json {"status":"OK"} ``` ``` -------------------------------- ### Implement Windows SMTC Support (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/docs/CROSS_PLATFORM_MEDIA_DETECTION.md This task involves implementing the System Media Transport Controls (SMTC) support for Windows within the Rust media-session crate. It requires working with the Windows API, which is inherently asynchronous, necessitating the use of async/await patterns. Testing across various media players like Spotify, iTunes, and browsers is crucial. ```rust crates/media-session/src/windows.rs ``` -------------------------------- ### Check Port Usage with lsof Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Checks if a specific network port is currently in use by any process on the system. Useful for diagnosing 'Connection refused' errors. ```bash # Check port is not already in use: lsof -i :8090 ``` -------------------------------- ### Monitor CPU Usage with top Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Monitors the CPU usage of a specific process, identified by its PID. Useful for checking resource consumption of audio playback. ```bash # In another terminal top -p $(pgrep -f test_local_dac) ``` -------------------------------- ### Store EQ Preferences in SQLite with Rust Source: https://context7.com/jaschadub/aaeq/llms.txt This Rust code snippet illustrates how to persist EQ mappings for songs, albums, and genres using SQLite. It demonstrates creating, upserting, and listing mappings, as well as updating preset references. Requires the `aaeq_persistence` and `aaeq_core` crates and a SQLite database file. ```rust use aaeq_persistence::{Database, MappingRepository}; use aaeq_core::{Mapping, Scope}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize database let db = Database::new("~/.local/share/aaeq/aaeq.db").await?; let repo = MappingRepository::new(db.pool()); // Create song-level mapping let song_mapping = Mapping { id: None, scope: Scope::Song, key_normalized: Some("pink floyd - time".to_string()), preset_name: "Rock".to_string(), profile_id: 1, created_at: 0, updated_at: 0, }; // Upsert mapping (insert or update if exists) let mapping_id = repo.upsert(&song_mapping).await?; println!("Created mapping with ID: {}", mapping_id); // Create album-level mapping let album_mapping = Mapping { id: None, scope: Scope::Album, key_normalized: Some("pink floyd - dark side of the moon".to_string()), preset_name: "Progressive".to_string(), profile_id: 1, created_at: 0, updated_at: 0, }; repo.upsert(&album_mapping).await?; // List all mappings for a profile let mappings = repo.list_by_profile(1).await?; for mapping in mappings { println!("{:?} -> {}", mapping.scope, mapping.preset_name); } // Update preset references (e.g., when deleting a preset) let updated = repo.update_preset_references("Rock", "Flat").await?; println!("Updated {} mappings to Flat preset", updated); Ok(()) } ``` -------------------------------- ### Load and Apply FIR Impulse Responses for Room EQ (Rust) Source: https://github.com/jaschadub/aaeq/blob/main/ROADMAP.md Implements a convolution engine to load WAV impulse response files and apply them for room equalization. It pre-computes the FFT of the impulse response for efficient processing. Dependencies include 'hound' for WAV reading and 'rustfft'. Input is a WAV file path, and output is the processed audio block. ```rust use rustfft::{FftPlanner, num_complex::Complex}; use std::path::Path; use anyhow::{Result, anyhow}; pub struct ConvolutionEngine { impulse: Vec, fft_size: usize, overlap_buffer: Vec, fft_planner: FftPlanner, ir_fft: Vec>, } impl ConvolutionEngine { pub fn load_impulse_response(&mut self, path: &Path) -> Result<()> { // Load WAV file let mut reader = hound::WavReader::open(path)?; let samples: Vec = reader.samples::() .map(|s| s.unwrap() as f64 / i32::MAX as f64) .collect(); // Validate if samples.len() > 65536 { return Err(anyhow!("Impulse response too long (max 65536 samples)")); } // Pre-compute FFT of impulse response self.compute_ir_fft(&samples); self.impulse = samples; Ok(()) } pub fn process_block(&mut self, input: &[f64], output: &mut [f64]) { // Overlap-add convolution in frequency domain // ... FFT → multiply → IFFT → overlap-add } // Placeholder for the actual FFT computation fn compute_ir_fft(&mut self, samples: &[f64]) { // Implementation would go here using self.fft_planner and samples // For brevity, this is a stub. self.ir_fft = vec![Complex::new(0.0, 0.0); samples.len()]; } } ``` -------------------------------- ### TOML Dependencies for AAEQ Project Source: https://github.com/jaschadub/aaeq/blob/main/docs/CROSS_PLATFORM_MEDIA_DETECTION.md Configuration for project dependencies in Cargo.toml, separated by target operating systems. Includes common dependencies like 'anyhow' and 'tracing', and platform-specific ones for Windows ('windows' crate) and macOS ('objc', 'cocoa', 'core-foundation'). ```toml [dependencies] # Common dependencies anyhow = "1.0" tracing = "0.1" [target.'cfg(target_os = "linux")'.dependencies] # Keep existing dependencies for D-Bus [target.'cfg(target_os = "windows")'.dependencies] windows = { version = "0.58", features = [ "Media_Control", "Storage_Streams", "Foundation", "implement", ] [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2" cocoa = "0.25" core-foundation = "0.9" ``` -------------------------------- ### Load EQ Preset Command Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Loads a specified EQ preset by its name. The command format is `EQLoad:{preset_name}`. The response indicates success or failure with a JSON object containing a 'status' field. ```bash https://192.168.1.100/httpapi.asp?command=EQLoad:Rock ``` -------------------------------- ### List Available EQ Presets Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Fetches a list of all available Equalizer (EQ) presets supported by the device. The response is a JSON array containing the names of the presets. ```json ["Flat", "Acoustic", "Bass Booster", "Bass Reducer", "Classical", "Dance", "Deep", "Electronic", "Hip-Hop", "Jazz", "Latin", "Loudness", "Lounge", "Piano", "Pop", "R&B", "Rock", "Small Speakers", "Spoken Word", "Treble Booster", "Treble Reducer", "Vocal Booster"] ``` -------------------------------- ### Automated verification of AAEQ release artifact Source: https://github.com/jaschadub/aaeq/blob/main/VERIFY_SIGNATURES.md A bash script to automate the process of downloading an AAEQ release artifact and its signature files, verifying the signature using cosign, and then making the artifact executable and running it. It includes error handling to exit on failure. ```bash #!/bin/bash set -e ARTIFACT="aaeq-linux-x64.AppImage" CERT_IDENTITY="https://github.com/jaschadub/AAEQ/.github/workflows/build.yml@refs/heads/main" OIDC_ISSUER="https://token.actions.githubusercontent.com" # Download artifact and signature files wget "https://github.com/jaschadub/AAEQ/releases/latest/download/${ARTIFACT}" wget "https://github.com/jaschadub/AAEQ/releases/latest/download/${ARTIFACT}.sig" wget "https://github.com/jaschadub/AAEQ/releases/latest/download/${ARTIFACT}.pem" # Verify cosign verify-blob \ --certificate "${ARTIFACT}.pem" \ --signature "${ARTIFACT}.sig" \ --certificate-identity "${CERT_IDENTITY}" \ --certificate-oidc-issuer "${OIDC_ISSUER}" \ "${ARTIFACT}" echo "✓ Signature verified successfully!" chmod +x "${ARTIFACT}" ./"${ARTIFACT}" ``` -------------------------------- ### Stream WAV Audio with VLC Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Plays an HTTP WAV audio stream from a running DLNA server using VLC Media Player. This tests the DLNA pull mode functionality. ```bash vlc http://localhost:8090/stream.wav ``` -------------------------------- ### Stream WAV Audio with mpv Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Plays an HTTP WAV audio stream from a running DLNA server using the mpv media player. This is another method to test DLNA pull mode. ```bash mpv http://localhost:8090/stream.wav ``` -------------------------------- ### SQL: Create DSP Profile Settings Table Source: https://github.com/jaschadub/aaeq/blob/main/ROADMAP.md This SQL script defines the `dsp_profile_settings` table for storing Digital Signal Processing configurations associated with user profiles. It includes settings such as sample rate, buffer size, headroom, dither mode, and filter type, with foreign key constraints to the `profile` table. This is essential for the device-aware DSP templates feature. ```sql CREATE TABLE dsp_profile_settings ( id INTEGER PRIMARY KEY AUTOINCREMENT, profile_id INTEGER NOT NULL, sample_rate INTEGER NOT NULL DEFAULT 48000, buffer_ms INTEGER NOT NULL DEFAULT 150, headroom_db REAL NOT NULL DEFAULT -3.0, dither_mode TEXT NOT NULL DEFAULT 'none', filter_type TEXT NOT NULL DEFAULT 'linear', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, FOREIGN KEY (profile_id) REFERENCES profile(id) ON DELETE CASCADE ); ``` -------------------------------- ### Manage Custom EQ Presets with Database in Rust Source: https://context7.com/jaschadub/aaeq/llms.txt Manages custom equalizer presets by creating, storing, and retrieving them from a SQLite database. It uses the `aaeq-persistence` and `aaeq-core` crates to interact with the database and EQ preset data structures. Presets can be upserted (inserted or updated) by name and deleted. ```rust use aaeq_persistence::{Database, CustomEqPresetRepository}; use aaeq_core::{EqPreset, EqBand}; #[tokio::main] async fn main() -> anyhow::Result<()> { // Initialize database let db = Database::new("~/.local/share/aaeq/aaeq.db").await?; let repo = CustomEqPresetRepository::new(db.pool()); // Create custom preset let preset = EqPreset { name: "Vocal Boost".to_string(), bands: vec![ EqBand { frequency: 31, gain: -1.0 }, EqBand { frequency: 62, gain: -0.5 }, EqBand { frequency: 125, gain: 0.0 }, EqBand { frequency: 250, gain: 1.0 }, EqBand { frequency: 500, gain: 2.0 }, EqBand { frequency: 1000, gain: 3.0 }, EqBand { frequency: 2000, gain: 3.5 }, EqBand { frequency: 4000, gain: 2.0 }, EqBand { frequency: 8000, gain: 0.5 }, EqBand { frequency: 16000, gain: -1.0 }, ], curve_data: None, }; // Save preset (upsert by name) let preset_id = repo.upsert(&preset).await?; println!("Saved preset with ID: {}", preset_id); // List all preset names let names = repo.list_names().await?; println!("Available presets: {:?}", names); // Retrieve preset by name if let Some(loaded_preset) = repo.get_by_name("Vocal Boost").await? { println!("\nLoaded preset: {}", loaded_preset.name); println!("Bands:"); for band in &loaded_preset.bands { println!(" {}Hz: {:+.1}dB", band.frequency, band.gain); } } // Delete preset repo.delete("Vocal Boost").await?; println!("\nDeleted preset"); Ok(()) } ``` -------------------------------- ### Silent WAV Stream Test with curl Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Tests the availability and basic functionality of an HTTP WAV audio stream from a DLNA server by piping the output to /dev/null. This is a silent test to check for errors without playing audio. ```bash curl http://localhost:8090/stream.wav > /dev/null ``` -------------------------------- ### AirPlay Streaming Test (RAOP) Source: https://github.com/jaschadub/aaeq/blob/main/docs/TESTING_GUIDE.md Tests AirPlay (RAOP) streaming to compatible Apple devices or receivers. This involves mDNS device discovery, RTSP protocol implementation, RTP audio streaming, ALAC framing, UDP packet transmission, and RTCP feedback. ```bash cargo run -p stream-server --example discover_devices ``` ```bash cargo run -p stream-server --example test_airplay "Living Room" ``` -------------------------------- ### Get Current Track - macOS (MediaPlayer Framework) Source: https://github.com/jaschadub/aaeq/blob/main/docs/CROSS_PLATFORM_MEDIA_DETECTION.md Accesses 'Now Playing' information on macOS using the MediaPlayer Framework via an Objective-C bridge. This method is fast and native, working with any app that sets Now Playing info, and can access artwork. It requires knowledge of Objective-C FFI and uses the 'objc' and 'cocoa' crates. ```rust #[cfg(target_os = "macos")] pub fn get_current_track() -> Result> { use objc::{class, msg_send, sel, sel_impl}; use objc::runtime::Object; use cocoa::base::{id, nil}; unsafe { // Get the shared MPNowPlayingInfoCenter let center: id = msg_send![class!(MPNowPlayingInfoCenter), defaultCenter]; let now_playing_info: id = msg_send![center, nowPlayingInfo]; if now_playing_info == nil { return Ok(None); } // Extract metadata from NSDictionary let title: id = msg_send![now_playing_info, objectForKey: "kMPMediaItemPropertyTitle"]; let artist: id = msg_send![now_playing_info, objectForKey: "kMPMediaItemPropertyArtist"]; let album: id = msg_send![now_playing_info, objectForKey: "kMPMediaItemPropertyAlbumTitle"]; // Convert NSString to Rust String // Return MediaMetadata } } ``` -------------------------------- ### List EQ Presets Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Retrieves a list of all available Equalizer (EQ) preset names. ```APIDOC ## GET /httpapi.asp?command=EQGetList ### Description Retrieves a list of all available Equalizer (EQ) preset names that can be loaded on the device. ### Method GET ### Endpoint `https://{device_ip}/httpapi.asp?command=EQGetList` ### Parameters None ### Request Example None ### Response #### Success Response (200) - (array of strings) - A list of EQ preset names. #### Response Example ```json ["Flat", "Acoustic", "Bass Booster", "Bass Reducer", "Classical", "Dance", "Deep", "Electronic", "Hip-Hop", "Jazz", "Latin", "Loudness", "Lounge", "Piano", "Pop", "R&B", "Rock", "Small Speakers", "Spoken Word", "Treble Booster", "Treble Reducer", "Vocal Booster"] ``` ``` -------------------------------- ### Get Player Status JSON Response Source: https://github.com/jaschadub/aaeq/blob/main/WIIM_API_REFERENCE.md Retrieves the current playback status, track metadata, playback mode, EQ status, and volume level. The response is in JSON format. Key fields include 'status', 'title', 'artist', 'album', 'mode', 'eq', and 'vol'. Metadata availability can vary based on the playback source. ```json { "type": "0", "ch": "2", "mode": "10", "loop": "4", "eq": "0", "status": "play", "curpos": "184919", "offset_pts": "184919", "totlen": "0", "vol": "39", "mute": "0", "title": "Time", "artist": "Pink Floyd", "album": "The Dark Side of the Moon" } ```