Merge remote-tracking branch 'upstream/master' into temperture

This commit is contained in:
LordGrey 2023-02-20 08:46:18 +01:00
commit 2a91e2af93
182 changed files with 6996 additions and 1787 deletions

4
.github/config/codeql.yml vendored Normal file
View File

@ -0,0 +1,4 @@
name: "CodeQL config"
paths-ignore:
- 'dependencies/external/'
- 'assets/webconfig/js/lib'

View File

@ -108,7 +108,7 @@ jobs:
reprepro -Vb apt export
- name: Download artifacts
uses: actions/download-artifact@v3.0.1
uses: actions/download-artifact@v3.0.2
- name: Include artifacts into the package source
run: |
@ -121,7 +121,7 @@ jobs:
done
- name: Upload packages to APT server (DRAFT)
uses: SamKirkland/FTP-Deploy-Action@4.3.2
uses: SamKirkland/FTP-Deploy-Action@4.3.3
with:
server: apt.hyperion-project.org
username: ${{ secrets.APT_USER }}

View File

@ -2,64 +2,64 @@
{
"distribution": "Bionic",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
"package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg, libcec4",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
"package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 18.04 (Bionic Beaver) (amd64)"
},
{
"distribution": "Focal",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg, libcec4",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 20.04 (Focal Fossa) (amd64)"
},
{
"distribution": "Jammy",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 22.04 (Jammy Jellyfish) (amd64)"
},
{
"distribution": "Kinetic",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)"
},
{
"distribution": "Stretch",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
"package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg0, libcec4",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
"package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg0, libcec4",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 9.x (Stretch) (amd64)"
},
{
"distribution": "Buster",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec4",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 10.x (Buster) (amd64)"
},
{
"distribution": "Bullseye",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 11.x (Bullseye) (amd64)"
},
{
"distribution": "Bookworm",
"architecture": "amd64",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6",
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6",
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 12.x (Bookworm) (amd64)"
}

76
.github/workflows/codeql.yml vendored Normal file
View File

@ -0,0 +1,76 @@
name: "CodeQL"
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
schedule:
- cron: "36 18 * * 4"
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ python, javascript, cpp ]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: recursive
- name: Install Packages (cpp)
if: ${{ matrix.language == 'cpp' }}
run: |
sudo apt-get update
sudo apt-get install --yes git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
queries: +security-and-quality
config-file: ./.github/config/codeql.yml
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
with:
category: "/language:${{ matrix.language }}"
upload: False
output: sarif-results
- name: Filter SARIF
uses: advanced-security/filter-sarif@v1
with:
patterns: |
-**/dependencies/**
-**/moc_*.cpp
-**/libsrc/flatbufserver/hyperion_request_generated.h
-**/libsrc/protoserver/message.pb.cc
-**/libsrc/protoserver/message.pb.h
input: sarif-results/${{ matrix.language }}.sarif
output: sarif-results/${{ matrix.language }}.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: sarif-results/${{ matrix.language }}.sarif
- name: Upload loc as a Build Artifact
uses: actions/upload-artifact@v2.2.0
with:
name: sarif-results
path: sarif-results
retention-days: 1

View File

@ -158,7 +158,7 @@ jobs:
reprepro -Vb nightly export
- name: Download artifacts
uses: actions/download-artifact@v3.0.1
uses: actions/download-artifact@v3.0.2
- name: Include artifacts into the package source
run: |
@ -171,7 +171,7 @@ jobs:
done
- name: Upload packages to nightly server
uses: SamKirkland/FTP-Deploy-Action@4.3.2
uses: SamKirkland/FTP-Deploy-Action@4.3.3
with:
server: nightly.apt.hyperion-project.org
username: ${{ secrets.NIGHTLY_USER }}

View File

@ -171,7 +171,7 @@ jobs:
# Download artifacts from previous build process
- name: Download artifacts
uses: actions/download-artifact@v3.0.1
uses: actions/download-artifact@v3.0.2
with:
path: artifacts

View File

@ -10,7 +10,7 @@ jobs:
steps:
# Dispatch event to build new HyperBian image
- name: Dispatch HyperBian build
uses: peter-evans/repository-dispatch@v2.1.0
uses: peter-evans/repository-dispatch@v2.1.1
if: ${{ github.repository_owner == 'hyperion-project'}}
with:
repository: hyperion-project/HyperBian

1
.gitignore vendored
View File

@ -30,6 +30,7 @@ libsrc/flatbufserver/hyperion_request_generated.h
# Ignore
.vs/*
CMakeSettings.json
/out
# Allow
!.vs/launch.vs.json

3
.gitmodules vendored
View File

@ -12,3 +12,6 @@
[submodule "dependencies/external/qmdnsengine"]
path = dependencies/external/qmdnsengine
url = https://github.com/nitroshare/qmdnsengine.git
[submodule "dependencies/external/mbedtls"]
path = dependencies/external/mbedtls
url = ../../Mbed-TLS/mbedtls.git

View File

@ -1 +1 @@
2.0.15-beta.1
2.0.16-beta.1

View File

@ -4,12 +4,26 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://github.com/hyperion-project/hyperion.ng/compare/2.0.14...HEAD)
## [Unreleased](https://github.com/hyperion-project/hyperion.ng/compare/2.0.15...HEAD)
### Breaking
### Added
### Changed
### Fixed
## Removed
## [2.0.15](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.15) - 2023-02
### Added
- Audio Grabber to add audio visualization support for both Windows and Linux.
- Support streaming to individual WLED segments (requires WLED 0.13.3+).
To allow segment streaming, enable "Realtime - Use main segment only" in WLED's Sync Interfaces setup screen
- Allow to keep WLED powered on after streaming and restoring state
- Allow to Disable / Enable all instances (#970) by
- Suspend/Resume support for Linux and Windows (#1493,#1282, #978).
Suspend/Resume/Restart is supported via API, UI, Systray and hyperion-remote
@ -17,13 +31,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
In Idle, all instances, components will be disabled besides the output processing (LED-Devices, smoothing).
The current priorities will be cleared and the background effect per instance will be executed, if enabled.
- Commands toogleSuspend and toggleIdle allow to flip between modes, e.g. might be used to trigger modes by a remote
- Reduced pixel processing to reduce resources on big assignment areas
- Support for squared mean color processing
- Support for dominant color processing on assigned LED areas (#1382). A simple and advanced way is provided. Advanced and high accuracy might be combined with reduced pixel processing to lower CPU usage.
- Add instance# in API response (#1504)
### Changed
- REST API - Increased default timeout to address "Operation cancelled" errors
- LED Devices: Allow to differentiate between recoverable/unrecoverable errors
- Renamed LED area assignment naming to provide clarity on the processing algorithms
### Fixed
- Effects/Smoothing: Effects with dedicated smoothing settings will now run with those settings (even if general smoothing is off)
- No interim color update after streaming and turning off WLED
- LED-Matrix Layout: Add Cabling direction selection element again (#1566)
- Restart correctly, if running as service (#1368)
- Hue-Wizard: In case auto discovery failed, port 80 was not used as default (#1544)
- Send only one reply per Start Instance request (#1551)
- Add instance# in JSON-API replies (aligning to Add instance in websocket response to a subscription #1504 behaviour)
- hyperion-remote: Extracting reply for a configGet request correctly (#1555)
- Grabber fps setting was not applied correctly
- Smoothing: No empty updates
### Technical
- Add CodeQL for GitHub code scanning
- Update to Protocol Buffers 3.21.12
- Update to Mbed TLS 3.3.0
- Qt6 alignments
- cmake support of libcec without version in lib-name
- Refactor for Python 3.11 deprecated functions
## Removed

View File

@ -67,6 +67,7 @@ SET ( DEFAULT_MF OFF )
SET ( DEFAULT_OSX OFF )
SET ( DEFAULT_QT ON )
SET ( DEFAULT_V4L2 OFF )
SET ( DEFAULT_AUDIO ON )
SET ( DEFAULT_X11 OFF )
SET ( DEFAULT_XCB OFF )
@ -172,8 +173,10 @@ if ( "${PLATFORM}" MATCHES "osx" )
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${SUBDIRPY})
include_directories("/opt/X11/include/")
SET ( DEFAULT_OSX ON )
SET ( DEFAULT_DEV_USB_HID ON )
SET ( DEFAULT_OSX ON )
SET ( DEFAULT_AUDIO OFF )
SET ( DEFAULT_DEV_USB_HID ON )
elseif ( "${PLATFORM}" MATCHES "rpi" )
SET ( DEFAULT_DISPMANX ON )
SET ( DEFAULT_DEV_WS281XPWM ON )
@ -222,6 +225,7 @@ if (HYPERION_LIGHT)
SET ( DEFAULT_OSX OFF )
SET ( DEFAULT_QT OFF )
SET ( DEFAULT_V4L2 OFF )
SET ( DEFAULT_AUDIO OFF )
SET ( DEFAULT_X11 OFF )
SET ( DEFAULT_XCB OFF )
@ -273,6 +277,11 @@ message(STATUS "ENABLE_V4L2 = ${ENABLE_V4L2}")
option(ENABLE_X11 "Enable the X11 grabber" ${DEFAULT_X11})
message(STATUS "ENABLE_X11 = ${ENABLE_X11}")
option(ENABLE_AUDIO "Enable the AUDIO grabber" ${DEFAULT_AUDIO})
message(STATUS "ENABLE_AUDIO = ${ENABLE_AUDIO}")
option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_WS281XPWM} )
message(STATUS "ENABLE_WS281XPWM = ${ENABLE_WS281XPWM}")
option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB})
message(STATUS "ENABLE_XCB = ${ENABLE_XCB}")

View File

@ -9,6 +9,10 @@
// Define to enable the DirectX grabber
#cmakedefine ENABLE_DX
// Define to enable the framebuffer grabber
// Define to enable the Audio grabber
#cmakedefine ENABLE_AUDIO
// Define to enable the Framebuffer grabber
#cmakedefine ENABLE_FB

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2014-2022 Hyperion Project
Copyright (c) 2014-2023 Hyperion Project
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -6,7 +6,7 @@
[![Latest-Release](https://img.shields.io/github/v/release/hyperion-project/hyperion.ng?include_prereleases&label=Latest%20Release&logo=github&logoColor=white&color=0f83e7)](https://github.com/hyperion-project/hyperion.ng/releases)
[![GitHub Actions](https://github.com/hyperion-project/hyperion.ng/workflows/Hyperion%20CI%20Build/badge.svg?branch=master)](https://github.com/hyperion-project/hyperion.ng/actions)
[![LGTM](https://img.shields.io/lgtm/grade/cpp/github/hyperion-project/hyperion.ng?label=Code%20Quality&logo=lgtm&logoColor=white&color=4bc51d)](https://lgtm.com/projects/g/hyperion-project/hyperion.ng/context:cpp)
[![CodeQL Analysis](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml/badge.svg)](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml)
[![Forum](https://img.shields.io/website/https/hyperion-project.org.svg?label=Forum&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=homeadvisor&logoColor=white)](https://www.hyperion-project.org)
[![Documentation](https://img.shields.io/website/https/docs.hyperion-project.org.svg?label=Documentation&down_color=red&down_message=offline&up_color=4bc51d&up_message=online&logo=read-the-docs)](https://docs.hyperion-project.org)
[![Discord](https://img.shields.io/discord/785578322167463937?label=Discord&logo=discord&logoColor=white&color=4bc51d)](https://discord.gg/khkR8Vx3ff)

View File

@ -0,0 +1,655 @@
#include <NeoPixelBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////// CONFIG SECTION STARTS /////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
#define THIS_IS_RGBW // RGBW SK6812, otherwise comment it
#define COLD_WHITE // for RGBW (THIS_IS_RGBW enabled) select COLD version, comment it if NEUTRAL
const bool skipFirstLed = false; // if set the first led in the strip will be set to black (for level shifters using sacrifice LED)
const int serialSpeed = 2000000; // serial port speed
#define DATA_PIN 2 // PIN: data output for LED strip
const bool reportStats = false; // Send back processing statistics
const int reportStatInterval_s = 10; // Send back processing every interval in seconds
/* Statistics breakdown:
FPS: Updates to the LEDs per second
F-FPS: Frames identified per second
S: Shown (Done) updates to the LEDs per given interval
F: Frames identified per interval (garbled grames cannot be counted)
G: Good frames identified per interval
B: Total bad frames of all types identified per interval
BF: Bad frames identified per interval
BS: Skipped incomplete frames
BC: Frames failing CRC check per interval
BFL Frames failing Fletcher content validation per interval
*/
//Developer configs
#define ENABLE_STRIP
#define ENABLE_CHECK_FLETCHER
const int SERIAL_SIZE_RX = 4096;
#ifndef ENABLE_STRIP
const int serial2Speed = 460800;
const bool reportInput = false;
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////// CONFIG SECTION ENDS /////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
const String version = "8.0";
#ifdef THIS_IS_RGBW
float whiteLimit = 1.0f;
#ifdef COLD_WHITE
uint8_t rCorrection = 0xA0; // adjust red -> white in 0-0xFF range
uint8_t gCorrection = 0xA0; // adjust green -> white in 0-0xFF range
uint8_t bCorrection = 0xA0; // adjust blue -> white in 0-0xFF range
#else
uint8_t rCorrection = 0xB0; // adjust red -> white in 0-0xFF range
uint8_t gCorrection = 0xB0; // adjust green -> white in 0-0xFF range
uint8_t bCorrection = 0x70; // adjust blue -> white in 0-0xFF range
#endif
#endif
int ledCount = 0; // This is dynamic, don't change it
int pixelCount = 0; // This is dynamic, don't change it
#ifdef THIS_IS_RGBW
#define LED_TYPE NeoGrbwFeature
#if defined(ARDUINO_LOLIN_S2_MINI)
#define LED_METHOD NeoEsp32I2s0Sk6812Method
#else
#define LED_METHOD NeoEsp32I2s1Sk6812Method
#endif
#else
#define LED_TYPE NeoGrbFeature
#if defined(ARDUINO_LOLIN_S2_MINI)
#define LED_METHOD NeoEsp32I2s0Ws2812xMethod
#else
#define LED_METHOD NeoEsp32I2s1Ws2812xMethod
#endif
#endif
#define LED_DRIVER NeoPixelBus<LED_TYPE, LED_METHOD>
uint8_t* ledBuffer;
int ledBufferSize;
#ifdef ENABLE_STRIP
LED_DRIVER* strip = NULL;
#endif
enum class AwaProtocol
{
HEADER_A,
HEADER_w,
HEADER_a,
HEADER_HI,
HEADER_LO,
HEADER_CRC,
CHANNELCALIB_GAIN,
CHANNELCALIB_RED,
CHANNELCALIB_GREEN,
CHANNELCALIB_BLUE,
PIXEL,
FLETCHER1,
FLETCHER2,
FLETCHER_EXT
};
AwaProtocol state = AwaProtocol::HEADER_A;
const int headerSize = 6;
const int trailerSize = 3;
const int calibInfoSize = 4;
int bytesRead = 0;
bool isVersion2 = false;
bool isChannelCalib = false;
uint8_t CRC = 0;
int count = 0;
int currentPixel = 0;
uint16_t fletcher1 = 0;
uint16_t fletcher2 = 0;
uint16_t fletcherExt = 0;
#ifdef THIS_IS_RGBW
RgbwColor inputColor;
uint8_t wChannel[256];
uint8_t rChannel[256];
uint8_t gChannel[256];
uint8_t bChannel[256];
#else
RgbColor inputColor;
#endif
bool ledsComplete = false;
// statistics
const int reportStatInterval_ms = reportStatInterval_s * 1000;
unsigned long curTime;
unsigned long stat_start = 0;
uint16_t stat_shown = 0;
uint16_t stat_frames = 0;
uint16_t stat_good = 0;
uint16_t stat_bad = 0;
uint16_t stat_bad_frame = 0;
uint16_t stat_bad_skip = 0;
uint16_t stat_bad_crc = 0;
uint16_t stat_bad_fletcher = 0;
uint16_t stat_final_shown = 0;
uint16_t stat_final_frames = 0;
uint16_t stat_final_good = 0;
uint16_t stat_final_bad = 0;
uint16_t stat_final_bad_frame = 0;
uint16_t stat_final_bad_skip = 0;
uint16_t stat_final_bad_crc = 0;
uint16_t stat_final_bad_fletcher = 0;
//Debugging
String inputString;
String inputErrorString;
String debugString;
void printStringHex(String string)
{
#ifndef ENABLE_STRIP
Serial2.println(string.length());
for (int i = 0; i < string.length(); ++i)
{
if (i % 36 == 0)
{
Serial2.println();
Serial2.print("[");
Serial2.print(i);
Serial2.print("] ");
}
if (string[i] < 16)
Serial2.print("0");
Serial2.print(string[i], HEX);
Serial2.print(":");
}
#endif
}
inline void showMe()
{
#ifdef ENABLE_STRIP
if (strip != NULL && strip->CanShow())
{
stat_shown++;
strip->Show();
}
#endif
}
// statistics
inline void showStats()
{
if (reportStats)
{
if (stat_frames > 0)
{
stat_final_shown = stat_shown;
stat_final_frames = stat_frames;
stat_final_good = stat_good;
stat_final_bad = stat_bad;
stat_final_bad_frame = stat_bad_frame;
stat_final_bad_skip = stat_bad_skip;
stat_final_bad_crc = stat_bad_crc;
stat_final_bad_fletcher = stat_bad_fletcher;
}
stat_start = curTime;
stat_shown = 0;
stat_frames = 0;
stat_good = 0;
stat_bad = 0;
stat_bad_frame = 0;
stat_bad_skip = 0;
stat_bad_crc = 0;
stat_bad_fletcher = 0;
String summary = String("FPS: ") + (stat_final_shown / reportStatInterval_s) +
" F-FPS: " + (stat_final_frames / reportStatInterval_s) +
" S: " + stat_final_shown +
" F: " + stat_final_frames +
" G: " + stat_final_good +
" B: " + stat_final_bad +
" (BF: " + stat_final_bad_frame +
" BS: " + stat_final_bad_skip +
" BC: " + stat_final_bad_crc +
" BFL: " + stat_final_bad_fletcher +
")";
#ifdef ENABLE_STRIP
Serial.println(summary);
#else
Serial2.println(summary);
#endif
}
}
void InitLeds(uint16_t ledCount, int pixelCount, bool channelCalibration = false)
{
if (ledBuffer != NULL)
delete ledBuffer;
ledBufferSize = pixelCount + (channelCalibration ? calibInfoSize : 0);
ledBuffer = new uint8_t[ledBufferSize];
#ifdef ENABLE_STRIP
if (strip != NULL)
delete strip;
strip = new LED_DRIVER(ledCount, DATA_PIN);
strip->Begin();
#endif
}
inline void processSerialData()
{
while (Serial.available()) {
char input = Serial.read();
++bytesRead;
#ifndef ENABLE_STRIP
if (reportInput)
inputString += input;
#endif
switch (state)
{
case AwaProtocol::HEADER_A:
if (input == 'A')
{
state = AwaProtocol::HEADER_w;
}
break;
case AwaProtocol::HEADER_w:
if (input == 'w')
state = AwaProtocol::HEADER_a;
else
{
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::HEADER_a:
if (input == 'a')
{
isVersion2 = false;
state = AwaProtocol::HEADER_HI;
}
else if (input == 'A')
{
state = AwaProtocol::HEADER_HI;
isVersion2 = true;
}
else
{
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::HEADER_HI:
stat_frames++;
count = input << 8;
CRC = input;
fletcher1 = 0;
fletcher2 = 0;
fletcherExt = 0;
state = AwaProtocol::HEADER_LO;
break;
case AwaProtocol::HEADER_LO:
count += input + 1;
if (ledCount != count || isChannelCalib != isVersion2)
{
ledCount = count;
isChannelCalib = isVersion2;
pixelCount = ledCount * 3;
if (isChannelCalib)
prepareCalibration();
InitLeds(ledCount, pixelCount, isChannelCalib);
}
CRC = CRC ^ input ^ 0x55;
state = AwaProtocol::HEADER_CRC;
break;
case AwaProtocol::HEADER_CRC:
// Check, if incomplete package information was skipped, set bytesread to headersize and skip wrong input
if (bytesRead != headerSize)
{
stat_bad_skip++;
bytesRead = headerSize;
}
currentPixel = 0;
if (CRC == input)
{
state = AwaProtocol::PIXEL;
}
else
{
// CRC failure
stat_bad++;
stat_bad_crc++;
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::PIXEL:
ledBuffer[currentPixel++] = input;
if (currentPixel == pixelCount)
{
if (isChannelCalib)
state = AwaProtocol::CHANNELCALIB_GAIN;
else
state = AwaProtocol::FLETCHER1;
}
break;
case AwaProtocol::CHANNELCALIB_GAIN:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_RED;
break;
case AwaProtocol::CHANNELCALIB_RED:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_GREEN;
break;
case AwaProtocol::CHANNELCALIB_GREEN:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_BLUE;
break;
case AwaProtocol::CHANNELCALIB_BLUE:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::FLETCHER1;
break;
case AwaProtocol::FLETCHER1:
fletcher1 = input;
state = AwaProtocol::FLETCHER2;
break;
case AwaProtocol::FLETCHER2:
fletcher2 = input;
state = AwaProtocol::FLETCHER_EXT;
break;
case AwaProtocol::FLETCHER_EXT:
fletcherExt = input;
ledsComplete = true;
state = AwaProtocol::HEADER_A;
break;
}
}
}
void setup()
{
// Init serial port
int bufSize = Serial.setRxBufferSize(SERIAL_SIZE_RX);
Serial.begin(serialSpeed);
Serial.setTimeout(50);
#ifndef ENABLE_STRIP
Serial2.begin(serial2Speed);
Serial2.println();
Serial2.println("Welcome!");
Serial2.println("Hyperion Awa driver " + version);
Serial2.println("!!! Debug Output !!!");
#endif
// Display config
Serial.println();
Serial.println("Welcome!");
Serial.println("Hyperion Awa driver " + version);
Serial.print("(Build: ");
Serial.print(__DATE__);
Serial.print(" ");
Serial.print(__TIME__);
Serial.println(")");
// first LED info
if (skipFirstLed)
Serial.println("First LED: disabled");
else
Serial.println("First LED: enabled");
// RGBW claibration info
#ifdef THIS_IS_RGBW
#ifdef COLD_WHITE
Serial.println("Default color mode: RGBW cold");
#else
Serial.println("Default color mode: RGBW neutral");
#endif
prepareCalibration();
#else
Serial.println("Color mode: RGB");
#endif
InitLeds(ledCount, pixelCount);
}
void prepareCalibration()
{
#ifdef THIS_IS_RGBW
// prepare LUT calibration table, cold white is much better than "neutral" white
for (uint32_t i = 0; i < 256; i++)
{
// color calibration
float red = rCorrection * i; // adjust red
float green = gCorrection * i; // adjust green
float blue = bCorrection * i; // adjust blue
wChannel[i] = (uint8_t)round(min(whiteLimit * i, 255.0f));
rChannel[i] = (uint8_t)round(min(red / 0xFF, 255.0f));
gChannel[i] = (uint8_t)round(min(green / 0xFF, 255.0f));
bChannel[i] = (uint8_t)round(min(blue / 0xFF, 255.0f));
}
Serial.write("RGBW calibration. White limit(%): ");
Serial.print(whiteLimit * 100.0f);
Serial.write(" %, red: ");
Serial.print(rCorrection);
Serial.write(" , green: ");
Serial.print(gCorrection);
Serial.write(" , blue: ");
Serial.print(bCorrection);
Serial.println();
#endif
}
void loop()
{
curTime = millis();
#ifdef __AVR__
// nothing , USART Interrupt is implemented
ESPserialEvent();
#else
// ESP8266 polling
ESPserialEvent();
#endif
if (ledsComplete)
{
#ifndef ENABLE_STRIP
if (reportInput)
{
Serial2.println();
Serial2.print("<input> L: ");
printStringHex(inputString);
Serial2.println("<\input>");
inputString = "";
Serial2.print("bytesRead: ");
Serial2.print(bytesRead);
Serial2.print(" , currentPixel: ");
Serial2.print(currentPixel);
Serial2.print(" ,pixelCount: ");
Serial2.print(pixelCount);
Serial2.println();
}
#endif
int frameSize = headerSize + ledBufferSize + trailerSize;
if (bytesRead > frameSize)
{
//Add number of frames ignored on top of frame
int frames = bytesRead / frameSize;
stat_frames += frames;
//Count frame plus frames ignored as bad frames
int badFrames = frames + 1;
stat_bad += badFrames;
stat_bad_frame += badFrames;
}
else
{
#ifdef ENABLE_CHECK_FLETCHER
//Test if content is valid
uint16_t item = 0;
uint16_t fletch1 = 0;
uint16_t fletch2 = 0;
uint16_t fletchExt = 0;
while (item < ledBufferSize)
{
fletch1 = (fletch1 + (uint16_t)ledBuffer[item]) % 255;
fletch2 = (fletch2 + fletch1) % 255;
fletcherExt = (fletcherExt + ((uint16_t)ledBuffer[item] ^ (item))) % 255;
++item;
}
if ((fletch1 == fletcher1) && (fletch2 == fletcher2) && (ledBuffer[item-1] == (fletcherExt != 0x41) ? fletcherExt : 0xaa))
{
#endif
stat_good++;
uint16_t startLed = 0;
if (skipFirstLed)
{
#ifdef ENABLE_STRIP
#ifdef THIS_IS_RGBW
strip->SetPixelColor(startLed, RgbwColor(0, 0, 0, 0));
#else
strip->SetPixelColor(startLed, RgbColor(0, 0, 0));
#endif
#endif
startLed = 1;
}
for (uint16_t led = startLed; led < ledCount; ++led)
{
inputColor.R = ledBuffer[led * 3];
inputColor.G = ledBuffer[led * 3 + 1];
inputColor.B = ledBuffer[led * 3 + 2];
#ifdef THIS_IS_RGBW
inputColor.W = min(rChannel[inputColor.R],
min(gChannel[inputColor.G],
bChannel[inputColor.B]));
inputColor.R -= rChannel[inputColor.W];
inputColor.G -= gChannel[inputColor.W];
inputColor.B -= bChannel[inputColor.W];
inputColor.W = wChannel[inputColor.W];
#endif
#ifdef ENABLE_STRIP
strip->SetPixelColor(led, inputColor);
#endif
}
showMe();
yield();
#ifdef THIS_IS_RGBW
if (isChannelCalib)
{
uint8_t incoming_gain = ledBuffer[pixelCount];
uint8_t incoming_red = ledBuffer[pixelCount + 1];
uint8_t incoming_green = ledBuffer[pixelCount + 2];
uint8_t incoming_blue = ledBuffer[pixelCount + 3];
float final_limit = (incoming_gain != 255) ? incoming_gain / 255.0f : 1.0f;
if (rCorrection != incoming_red || gCorrection != incoming_green || bCorrection != incoming_blue || whiteLimit != final_limit)
{
rCorrection = incoming_red;
gCorrection = incoming_green;
bCorrection = incoming_blue;
whiteLimit = final_limit;
prepareCalibration();
}
}
#endif
#ifdef ENABLE_CHECK_FLETCHER
}
else
{
stat_bad++;
stat_bad_fletcher++;
}
#endif
}
bytesRead = 0;
state = AwaProtocol::HEADER_A;
ledsComplete = false;
}
if ((curTime - stat_start > reportStatInterval_ms))
{
if (stat_frames > 0)
{
showStats();
}
}
}
#ifdef __AVR__
void serialEvent()
{
processSerialData();
}
#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
void ESPserialEvent()
{
processSerialData();
}
#endif

View File

@ -0,0 +1,646 @@
#include <NeoPixelBus.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////// CONFIG SECTION STARTS /////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
#define THIS_IS_RGBW // RGBW SK6812, otherwise comment it
#define COLD_WHITE // for RGBW (THIS_IS_RGBW enabled) select COLD version, comment it if NEUTRAL
const bool skipFirstLed = false; // if set the first led in the strip will be set to black (for level shifters using sacrifice LED)
const int serialSpeed = 2000000; // serial port speed
const bool reportStats = false; // Send back processing statistics
const int reportStatInterval_s = 10; // Send back processing every interval in seconds
/* Statistics breakdown:
FPS: Updates to the LEDs per second
F-FPS: Frames identified per second
S: Shown (Done) updates to the LEDs per given interval
F: Frames identified per interval (garbled grames cannot be counted)
G: Good frames identified per interval
B: Total bad frames of all types identified per interval
BF: Bad frames identified per interval
BS: Skipped incomplete frames
BC: Frames failing CRC check per interval
BFL Frames failing Fletcher content validation per interval
*/
//Developer configs
#define ENABLE_STRIP
#define ENABLE_CHECK_FLETCHER
const int SERIAL_SIZE_RX = 4096;
#ifndef ENABLE_STRIP
const int serial2Speed = 460800;
const bool reportInput = false;
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////// CONFIG SECTION ENDS /////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
const String version = "8.0";
#ifdef THIS_IS_RGBW
float whiteLimit = 1.0f;
#ifdef COLD_WHITE
uint8_t rCorrection = 0xA0; // adjust red -> white in 0-0xFF range
uint8_t gCorrection = 0xA0; // adjust green -> white in 0-0xFF range
uint8_t bCorrection = 0xA0; // adjust blue -> white in 0-0xFF range
#else
uint8_t rCorrection = 0xB0; // adjust red -> white in 0-0xFF range
uint8_t gCorrection = 0xB0; // adjust green -> white in 0-0xFF range
uint8_t bCorrection = 0x70; // adjust blue -> white in 0-0xFF range
#endif
#endif
int ledCount = 0; // This is dynamic, don't change it
int pixelCount = 0; // This is dynamic, don't change it
#ifdef THIS_IS_RGBW
#define LED_TYPE NeoGrbwFeature
#define LED_METHOD NeoEsp8266Uart1Sk6812Method
#else
#define LED_TYPE NeoGrbFeature
#define LED_METHOD NeoEsp8266Uart1Ws2812xMethod
#endif
#define LED_DRIVER NeoPixelBus<LED_TYPE, LED_METHOD>
uint8_t* ledBuffer;
int ledBufferSize;
#ifdef ENABLE_STRIP
LED_DRIVER* strip = NULL;
#endif
enum class AwaProtocol
{
HEADER_A,
HEADER_w,
HEADER_a,
HEADER_HI,
HEADER_LO,
HEADER_CRC,
CHANNELCALIB_GAIN,
CHANNELCALIB_RED,
CHANNELCALIB_GREEN,
CHANNELCALIB_BLUE,
PIXEL,
FLETCHER1,
FLETCHER2,
FLETCHER_EXT
};
AwaProtocol state = AwaProtocol::HEADER_A;
const int headerSize = 6;
const int trailerSize = 3;
const int calibInfoSize = 4;
int bytesRead = 0;
bool isVersion2 = false;
bool isChannelCalib = false;
uint8_t CRC = 0;
int count = 0;
int currentPixel = 0;
uint16_t fletcher1 = 0;
uint16_t fletcher2 = 0;
uint16_t fletcherExt = 0;
#ifdef THIS_IS_RGBW
RgbwColor inputColor;
uint8_t wChannel[256];
uint8_t rChannel[256];
uint8_t gChannel[256];
uint8_t bChannel[256];
#else
RgbColor inputColor;
#endif
bool ledsComplete = false;
// statistics
const int reportStatInterval_ms = reportStatInterval_s * 1000;
unsigned long curTime;
unsigned long stat_start = 0;
uint16_t stat_shown = 0;
uint16_t stat_frames = 0;
uint16_t stat_good = 0;
uint16_t stat_bad = 0;
uint16_t stat_bad_frame = 0;
uint16_t stat_bad_skip = 0;
uint16_t stat_bad_crc = 0;
uint16_t stat_bad_fletcher = 0;
uint16_t stat_final_shown = 0;
uint16_t stat_final_frames = 0;
uint16_t stat_final_good = 0;
uint16_t stat_final_bad = 0;
uint16_t stat_final_bad_frame = 0;
uint16_t stat_final_bad_skip = 0;
uint16_t stat_final_bad_crc = 0;
uint16_t stat_final_bad_fletcher = 0;
//Debugging
String inputString;
String inputErrorString;
String debugString;
void printStringHex(String string)
{
#ifndef ENABLE_STRIP
Serial2.println(string.length());
for (int i = 0; i < string.length(); ++i)
{
if (i % 36 == 0)
{
Serial2.println();
Serial2.print("[");
Serial2.print(i);
Serial2.print("] ");
}
if (string[i] < 16)
Serial2.print("0");
Serial2.print(string[i], HEX);
Serial2.print(":");
}
#endif
}
inline void showMe()
{
#ifdef ENABLE_STRIP
if (strip != NULL && strip->CanShow())
{
stat_shown++;
strip->Show();
}
#endif
}
// statistics
inline void showStats()
{
if (reportStats)
{
if (stat_frames > 0)
{
stat_final_shown = stat_shown;
stat_final_frames = stat_frames;
stat_final_good = stat_good;
stat_final_bad = stat_bad;
stat_final_bad_frame = stat_bad_frame;
stat_final_bad_skip = stat_bad_skip;
stat_final_bad_crc = stat_bad_crc;
stat_final_bad_fletcher = stat_bad_fletcher;
}
stat_start = curTime;
stat_shown = 0;
stat_frames = 0;
stat_good = 0;
stat_bad = 0;
stat_bad_frame = 0;
stat_bad_skip = 0;
stat_bad_crc = 0;
stat_bad_fletcher = 0;
String summary = String("FPS: ") + (stat_final_shown / reportStatInterval_s) +
" F-FPS: " + (stat_final_frames / reportStatInterval_s) +
" S: " + stat_final_shown +
" F: " + stat_final_frames +
" G: " + stat_final_good +
" B: " + stat_final_bad +
" (BF: " + stat_final_bad_frame +
" BS: " + stat_final_bad_skip +
" BC: " + stat_final_bad_crc +
" BFL: " + stat_final_bad_fletcher +
")";
#ifdef ENABLE_STRIP
Serial.println(summary);
#else
Serial2.println(summary);
#endif
}
}
void InitLeds(uint16_t ledCount, int pixelCount, bool channelCalibration = false)
{
if (ledBuffer != NULL)
delete ledBuffer;
ledBufferSize = pixelCount + (channelCalibration ? calibInfoSize : 0);
ledBuffer = new uint8_t[ledBufferSize];
#ifdef ENABLE_STRIP
if (strip != NULL)
delete strip;
strip = new LED_DRIVER(ledCount);
strip->Begin();
#endif
}
inline void processSerialData()
{
while (Serial.available()) {
char input = Serial.read();
++bytesRead;
#ifndef ENABLE_STRIP
if (reportInput)
inputString += input;
#endif
switch (state)
{
case AwaProtocol::HEADER_A:
if (input == 'A')
{
state = AwaProtocol::HEADER_w;
}
break;
case AwaProtocol::HEADER_w:
if (input == 'w')
state = AwaProtocol::HEADER_a;
else
{
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::HEADER_a:
if (input == 'a')
{
isVersion2 = false;
state = AwaProtocol::HEADER_HI;
}
else if (input == 'A')
{
state = AwaProtocol::HEADER_HI;
isVersion2 = true;
}
else
{
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::HEADER_HI:
stat_frames++;
count = input << 8;
CRC = input;
fletcher1 = 0;
fletcher2 = 0;
fletcherExt = 0;
state = AwaProtocol::HEADER_LO;
break;
case AwaProtocol::HEADER_LO:
count += input + 1;
if (ledCount != count || isChannelCalib != isVersion2)
{
ledCount = count;
isChannelCalib = isVersion2;
pixelCount = ledCount * 3;
if (isChannelCalib)
prepareCalibration();
InitLeds(ledCount, pixelCount, isChannelCalib);
}
CRC = CRC ^ input ^ 0x55;
state = AwaProtocol::HEADER_CRC;
break;
case AwaProtocol::HEADER_CRC:
// Check, if incomplete package information was skipped, set bytesread to headersize and skip wrong input
if (bytesRead != headerSize)
{
stat_bad_skip++;
bytesRead = headerSize;
}
currentPixel = 0;
if (CRC == input)
{
state = AwaProtocol::PIXEL;
}
else
{
// CRC failure
stat_bad++;
stat_bad_crc++;
state = AwaProtocol::HEADER_A;
}
break;
case AwaProtocol::PIXEL:
ledBuffer[currentPixel++] = input;
if (currentPixel == pixelCount)
{
if (isChannelCalib)
state = AwaProtocol::CHANNELCALIB_GAIN;
else
state = AwaProtocol::FLETCHER1;
}
break;
case AwaProtocol::CHANNELCALIB_GAIN:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_RED;
break;
case AwaProtocol::CHANNELCALIB_RED:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_GREEN;
break;
case AwaProtocol::CHANNELCALIB_GREEN:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::CHANNELCALIB_BLUE;
break;
case AwaProtocol::CHANNELCALIB_BLUE:
ledBuffer[currentPixel++] = input;
state = AwaProtocol::FLETCHER1;
break;
case AwaProtocol::FLETCHER1:
fletcher1 = input;
state = AwaProtocol::FLETCHER2;
break;
case AwaProtocol::FLETCHER2:
fletcher2 = input;
state = AwaProtocol::FLETCHER_EXT;
break;
case AwaProtocol::FLETCHER_EXT:
fletcherExt = input;
ledsComplete = true;
state = AwaProtocol::HEADER_A;
break;
}
}
}
void setup()
{
// Init serial port
int bufSize = Serial.setRxBufferSize(SERIAL_SIZE_RX);
Serial.begin(serialSpeed);
Serial.setTimeout(50);
#ifndef ENABLE_STRIP
Serial2.begin(serial2Speed);
Serial2.println();
Serial2.println("Welcome!");
Serial2.println("Hyperion Awa driver " + version);
Serial2.println("!!! Debug Output !!!");
#endif
// Display config
Serial.println();
Serial.println("Welcome!");
Serial.println("Hyperion Awa driver " + version);
Serial.print("(Build: ");
Serial.print(__DATE__);
Serial.print(" ");
Serial.print(__TIME__);
Serial.println(")");
// first LED info
if (skipFirstLed)
Serial.println("First LED: disabled");
else
Serial.println("First LED: enabled");
// RGBW claibration info
#ifdef THIS_IS_RGBW
#ifdef COLD_WHITE
Serial.println("Default color mode: RGBW cold");
#else
Serial.println("Default color mode: RGBW neutral");
#endif
prepareCalibration();
#else
Serial.println("Color mode: RGB");
#endif
InitLeds(ledCount, pixelCount);
}
void prepareCalibration()
{
#ifdef THIS_IS_RGBW
// prepare LUT calibration table, cold white is much better than "neutral" white
for (uint32_t i = 0; i < 256; i++)
{
// color calibration
float red = rCorrection * i; // adjust red
float green = gCorrection * i; // adjust green
float blue = bCorrection * i; // adjust blue
wChannel[i] = (uint8_t)round(min(whiteLimit * i, 255.0f));
rChannel[i] = (uint8_t)round(min(red / 0xFF, 255.0f));
gChannel[i] = (uint8_t)round(min(green / 0xFF, 255.0f));
bChannel[i] = (uint8_t)round(min(blue / 0xFF, 255.0f));
}
Serial.write("RGBW calibration. White limit(%): ");
Serial.print(whiteLimit * 100.0f);
Serial.write(" %, red: ");
Serial.print(rCorrection);
Serial.write(" , green: ");
Serial.print(gCorrection);
Serial.write(" , blue: ");
Serial.print(bCorrection);
Serial.println();
#endif
}
void loop()
{
curTime = millis();
#ifdef __AVR__
// nothing , USART Interrupt is implemented
ESPserialEvent();
#else
// ESP8266 polling
ESPserialEvent();
#endif
if (ledsComplete)
{
#ifndef ENABLE_STRIP
if (reportInput)
{
Serial2.println();
Serial2.print("<input> L: ");
printStringHex(inputString);
Serial2.println("<\input>");
inputString = "";
Serial2.print("bytesRead: ");
Serial2.print(bytesRead);
Serial2.print(" , currentPixel: ");
Serial2.print(currentPixel);
Serial2.print(" ,pixelCount: ");
Serial2.print(pixelCount);
Serial2.println();
}
#endif
int frameSize = headerSize + ledBufferSize + trailerSize;
if (bytesRead > frameSize)
{
//Add number of frames ignored on top of frame
int frames = bytesRead / frameSize;
stat_frames += frames;
//Count frame plus frames ignored as bad frames
int badFrames = frames + 1;
stat_bad += badFrames;
stat_bad_frame += badFrames;
}
else
{
#ifdef ENABLE_CHECK_FLETCHER
//Test if content is valid
uint16_t item = 0;
uint16_t fletch1 = 0;
uint16_t fletch2 = 0;
uint16_t fletchExt = 0;
while (item < ledBufferSize)
{
fletch1 = (fletch1 + (uint16_t)ledBuffer[item]) % 255;
fletch2 = (fletch2 + fletch1) % 255;
fletcherExt = (fletcherExt + ((uint16_t)ledBuffer[item] ^ (item))) % 255;
++item;
}
if ((fletch1 == fletcher1) && (fletch2 == fletcher2) && (ledBuffer[item-1] == (fletcherExt != 0x41) ? fletcherExt : 0xaa))
{
#endif
stat_good++;
uint16_t startLed = 0;
if (skipFirstLed)
{
#ifdef ENABLE_STRIP
#ifdef THIS_IS_RGBW
strip->SetPixelColor(startLed, RgbwColor(0, 0, 0, 0));
#else
strip->SetPixelColor(startLed, RgbColor(0, 0, 0));
#endif
#endif
startLed = 1;
}
for (uint16_t led = startLed; led < ledCount; ++led)
{
inputColor.R = ledBuffer[led * 3];
inputColor.G = ledBuffer[led * 3 + 1];
inputColor.B = ledBuffer[led * 3 + 2];
#ifdef THIS_IS_RGBW
inputColor.W = min(rChannel[inputColor.R],
min(gChannel[inputColor.G],
bChannel[inputColor.B]));
inputColor.R -= rChannel[inputColor.W];
inputColor.G -= gChannel[inputColor.W];
inputColor.B -= bChannel[inputColor.W];
inputColor.W = wChannel[inputColor.W];
#endif
#ifdef ENABLE_STRIP
strip->SetPixelColor(led, inputColor);
#endif
}
showMe();
yield();
#ifdef THIS_IS_RGBW
if (isChannelCalib)
{
uint8_t incoming_gain = ledBuffer[pixelCount];
uint8_t incoming_red = ledBuffer[pixelCount + 1];
uint8_t incoming_green = ledBuffer[pixelCount + 2];
uint8_t incoming_blue = ledBuffer[pixelCount + 3];
float final_limit = (incoming_gain != 255) ? incoming_gain / 255.0f : 1.0f;
if (rCorrection != incoming_red || gCorrection != incoming_green || bCorrection != incoming_blue || whiteLimit != final_limit)
{
rCorrection = incoming_red;
gCorrection = incoming_green;
bCorrection = incoming_blue;
whiteLimit = final_limit;
prepareCalibration();
}
}
#endif
#ifdef ENABLE_CHECK_FLETCHER
}
else
{
stat_bad++;
stat_bad_fletcher++;
}
#endif
}
bytesRead = 0;
state = AwaProtocol::HEADER_A;
ledsComplete = false;
}
if ((curTime - stat_start > reportStatInterval_ms))
{
if (stat_frames > 0)
{
showStats();
}
}
}
#ifdef __AVR__
void serialEvent()
{
processSerialData();
}
#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32)
void ESPserialEvent()
{
processSerialData();
}
#endif

View File

@ -153,10 +153,9 @@ to this service over the network.
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(('0.0.0.0', args.localport)) # lgtm [py/bind-socket-all-network-interfaces]
srv.bind(('0.0.0.0', args.localport))
try:
intentional_exit = False
while True:
try:
while True:
@ -180,7 +179,7 @@ to this service over the network.
# probably got disconnected
break
except KeyboardInterrupt:
intentional_exit = True
# intentional_exit
raise
except socket.error as msg:
if args.develop:
@ -190,6 +189,7 @@ to this service over the network.
ser_to_net.socket = None
sys.stderr.write('Disconnected\n')
except KeyboardInterrupt:
# do not handle exceptions
pass
sys.stderr.write('\n--- exit ---\n')

View File

@ -322,6 +322,17 @@
</select>
</td>
</tr>
<tr>
<td class="ltd">
<label class="ltdlabel" for="ip_ma_direction" data-i18n="conf_leds_layout_ma_direction">Cabling</label>
</td>
<td class="itd">
<select class="form-control ledMAconstr" id="ip_ma_direction">
<option value="horizontal" data-i18n="conf_leds_layout_ma_opthoriz">Horizontal</option>
<option value="vertical" data-i18n="conf_leds_layout_ma_optvert">Vertical</option>
</select>
</td>
</tr>
<tr>
<td class="ltd">
<label class="ltdlabel" for="ip_ma_start" data-i18n="conf_leds_layout_ma_position">Input</label>

View File

@ -42,6 +42,14 @@
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_videograbber')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr>
<tr id="dash_audio_grabber_row">
<td></td>
<td data-i18n="edt_conf_audio_heading_title">Audio-Grabber</td>
<td style="text-align: right; padding-right: 0">
<span id="dash_audio_grabber">disabled</span>
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_audiograbber')" style="text-decoration: none; cursor: pointer"></a>
</td>
</tr>
</tbody>
</table>
<table id="dash_ports" class="table borderless">
@ -135,6 +143,7 @@
</div>
<!-- /.row -->
</div>
</div>
<!-- /.container-fluid -->
<script src="/js/content_dashboard.js"></script>

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "Die Webkonfiguration wird automatisch wieder freigegeben, sobald das Problem behoben wurde!",
"InfoDialog_nowrite_text": "Hyperion hat keinen Schreibzugriff auf die aktuell geladene Konfiguration. Bitte korrigiere die Dateizugriffsrechte, um fortzufahren.",
"InfoDialog_nowrite_title": "Fehler beim Schreibzugriff!",
"InfoDialog_systemRestart_title": "Neustart",
"InfoDialog_systemResume_title": "Aktivieren",
"InfoDialog_systemSuspend_title": "Ruhezustand",
"about_3rd_party_licenses": "Drittanbieter Lizenzen",
"about_3rd_party_licenses_error": "Wir hatten Probleme beim Laden der Drittanbieter Lizenzen aus dem Internet. <br />Klicke hier, um die Datei auf GitHub aufzurufen.",
"about_build": "Build",
@ -41,6 +44,7 @@
"conf_general_inst_title": "LED-Hardware Instanzverwaltung",
"conf_general_intro": "Grundsätzliche Einstellungen zu Hyperion oder WebUI, die in keine andere Kategorie passen.",
"conf_general_label_title": "Allgemeine Einstellungen",
"conf_grabber_audio_intro": "Bei der Audioerfassung wird ein Audioeingabegerät als Quelle für die Visualisierung verwendet.",
"conf_grabber_fg_intro": "Bildschirm Aufnahme ist das lokale System auf dem Hyperion installiert wurde, welches als Bildquelle dient.",
"conf_grabber_inst_grabber_config_info": "Konfiguriere deine Aufnahmegeräte bevor du sie in einer LED-Instanz benutzt.",
"conf_grabber_v4l_intro": "USB-Aufnahme ist ein Gerät, welches via USB angeschlossen ist und als Bildquelle dient.",
@ -55,7 +59,9 @@
"conf_leds_error_get_properties_title": "Geräteeigenschaften",
"conf_leds_error_hwled_gt_layout": "Die Zahl der gegebenen Hardware LEDs ($1) ist größer, als die Anzahl der im LED-Layout definierten ($2), $3 {{plural:$3|LED wird|LEDs werden}} schwarz bleiben.",
"conf_leds_error_hwled_gt_maxled": "Die Zahl der gegebenen Hardware LEDs ($1) ist größer, als die Anzahl der durch den LED-Steuerungstyp unterstützen ($2). <br>Die Anzahl Hardware LEDs wird auf ($3) gesetzt.",
"conf_leds_error_hwled_gt_maxled_protocol": "Die Anzahl der Hardware-LEDs ($1) ist größer als die maximale Anzahl der vom Streaming-Protokoll unterstützten LEDs ($2).<br> Das Streaming-Protokoll wird auf ($3) geändert.",
"conf_leds_error_hwled_lt_layout": "Die Zahl der gegebenen Hardware LEDs ($1) ist kleiner, als die Anzahl der im LED-Layout definierten ($2). <br> Im LED-Layout dürfen nicht mehr LEDs konfiguriert sein, als vorhanden.",
"conf_leds_error_wled_segment_missing": "Das aktuell konfigurierte Segment ($1) ist in WLED-Gerät nicht konfiguriert.<br> Überprüfe die WLED-Konfiguration!<br>Die Konfigurationsseite zeigt die aktuelle WLED-Einstellung.",
"conf_leds_info_ws281x": "Hyperion muss mit 'root' Rechten für diesen LED-Steuerungstyp laufen!",
"conf_leds_layout_advanced": "Erweiterte Optionen",
"conf_leds_layout_blacklist_num_title": "LED-Anzahl",
@ -229,6 +235,27 @@
"edt_append_pixel": "Pixel",
"edt_append_s": "s",
"edt_append_sdegree": "s/grad",
"edt_conf_audio_device_expl": "Ausgewähltes Audio-Eingabegerät",
"edt_conf_audio_device_title": "Audio-Eingabegerät",
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
"edt_conf_audio_effect_hotcolor_expl": "Farbe die einen hohen Pegel anzeigt.",
"edt_conf_audio_effect_hotcolor_title": "Farbe hoher Pegel",
"edt_conf_audio_effect_multiplier_expl": "Multiplikator zur Verstärkung des Audiosignals.",
"edt_conf_audio_effect_multiplier_title": "Multiplikator",
"edt_conf_audio_effect_safecolor_expl": "Farbe die einen niedrigen Pegel anzeigt.",
"edt_conf_audio_effect_safecolor_title": "Farbe niedriger Pegel",
"edt_conf_audio_effect_safevalue_expl": "Schwellwert bis zu dem ein niedriger Pegel gegeben ist.",
"edt_conf_audio_effect_safevalue_title": "Schwellwert niedriger Pegel",
"edt_conf_audio_effect_set_defaults": "Zurücksetzen auf Standardwerte",
"edt_conf_audio_effect_tolerance_expl": "Toleranz für die automatische Berechnung eines Signalmultiplikators von 0-100",
"edt_conf_audio_effect_tolerance_title": "Toleranz",
"edt_conf_audio_effect_warncolor_expl": "Farbe die einen mittleren Pegel anzeigt.",
"edt_conf_audio_effect_warncolor_title": "Farbe mittlerer Pegel",
"edt_conf_audio_effect_warnvalue_expl": "Schwellwert bis zu dem ein mittlerer Pegel gegeben ist.",
"edt_conf_audio_effect_warnvalue_title": "Schwellwert mittlerer Pegel",
"edt_conf_audio_effects_expl": "Wähle einen Effekt für die Umwandlung des Audiosignals.",
"edt_conf_audio_effects_title": "Audio-Effekte",
"edt_conf_audio_heading_title": "Audio Aufnahme",
"edt_conf_bb_blurRemoveCnt_expl": "Anzahl an Pixeln, die zusätzlich vom Rand abgeschnitten werden.",
"edt_conf_bb_blurRemoveCnt_title": "Unscharfe Pixel",
"edt_conf_bb_borderFrameCnt_expl": "Anzahl an Bildern bis ein neuer Rand festgelegt wird.",
@ -244,6 +271,8 @@
"edt_conf_bb_unknownFrameCnt_title": "Unbekannte Bilder",
"edt_conf_bge_heading_title": "Hintergrund Effekt/Farbe",
"edt_conf_bobls_heading_title": "Boblight Server",
"edt_conf_color_accuracyLevel_expl": "Stufe, wie genau dominante Farben ausgewertet werden. Eine höhere Stufe erzeugt genauere Ergebnisse, erfordert aber auch mehr Rechenleistung. Sollte mit reduzierter Pixelverarbeitung kombiniert werden.",
"edt_conf_color_accuracyLevel_title": "Genauigkeitsstufe",
"edt_conf_color_backlightColored_expl": "Die Hintergrundbeleuchtung kann mit oder ohne Farbanteile genutzt werden.",
"edt_conf_color_backlightColored_title": "Farbige Hintergrundbeleuchtung",
"edt_conf_color_backlightThreshold_expl": "Eine Beleuchtung die dauerhaft aktiv ist. (Automatisch deaktiviert bei Effekten, Farben oder im Zustand \"Aus\")",
@ -274,7 +303,7 @@
"edt_conf_color_heading_title": "Farbkalibrierung",
"edt_conf_color_id_expl": "Eine vom Benutzer frei angegebene ID.",
"edt_conf_color_id_title": "ID",
"edt_conf_color_imageToLedMappingType_expl": "Sofern nicht \"Mehrfarbig\", wird dein LED-Layout mit einer anderen Bildzuweisung überschrieben",
"edt_conf_color_imageToLedMappingType_expl": "Sofern nicht \"Durchschnittsfarbe einfach \", wird dein LED-Layout mit einer anderen Bildzuweisung überschrieben",
"edt_conf_color_imageToLedMappingType_title": "LED-Bereich Zuordnungstyp",
"edt_conf_color_leds_expl": "Zugewiesen zu allen (*) LEDs oder nur zu bestimmten LED Nummern (0-17).",
"edt_conf_color_leds_title": "LED-Iindex",
@ -282,6 +311,8 @@
"edt_conf_color_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Kalibrierter Rotwert.",
"edt_conf_color_red_title": "Rot",
"edt_conf_color_reducedPixelSetFactorFactor_expl": "Es wird nur eine reduzierte Menge von Pixeln pro definiertem LED-Bereich ausgewertet, Niedrig ~25%, Mittel ~10%, Hoch ~6%",
"edt_conf_color_reducedPixelSetFactorFactor_title": "Reduzierte Pixelverarbeitung",
"edt_conf_color_saturationGain_expl": "Anpassung der Farbsättigung. 1,0 bedeutet keine Änderung, Werte größer 1,0 erhöhen die Sättigung, kleiner 1,0 verringern diese.",
"edt_conf_color_saturationGain_title": "Sättigungsverstärkung",
"edt_conf_color_white_expl": "Kalibrierter Weißwert.",
@ -313,6 +344,8 @@
"edt_conf_enum_color": "Farbe",
"edt_conf_enum_custom": "Benutzerdefiniert",
"edt_conf_enum_decay": "Dämpfung",
"edt_conf_enum_delay": "Nur Verzögerung",
"edt_conf_enum_disabled": "Deaktiviert",
"edt_conf_enum_dl_error": "nur Fehler",
"edt_conf_enum_dl_informational": "informativ",
"edt_conf_enum_dl_nodebug": "keine Debugausgabe",
@ -321,9 +354,12 @@
"edt_conf_enum_dl_verbose1": "Stufe 1",
"edt_conf_enum_dl_verbose2": "Stufe 2",
"edt_conf_enum_dl_verbose3": "Stufe 3",
"edt_conf_enum_dominant_color": "Dominante Farbe - pro LED",
"edt_conf_enum_dominant_color_advanced": "Dominante Farbe fortgeschritten - pro LED",
"edt_conf_enum_effect": "Effekt",
"edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB",
"edt_conf_enum_high": "Hoch",
"edt_conf_enum_hsv": "HSV",
"edt_conf_enum_left_right": "von links nach rechts",
"edt_conf_enum_linear": "Linear",
@ -331,7 +367,10 @@
"edt_conf_enum_logsilent": "Stille",
"edt_conf_enum_logverbose": "Ausführlich",
"edt_conf_enum_logwarn": "Warnung",
"edt_conf_enum_multicolor_mean": "Mehrfarbig",
"edt_conf_enum_low": "Niedrig",
"edt_conf_enum_medium": "Mittel",
"edt_conf_enum_multicolor_mean": "Durchschnittsfarbe einfach - pro LED",
"edt_conf_enum_multicolor_mean_squared": "Durchschnittsfarbe zum Quadrat - pro LED",
"edt_conf_enum_please_select": "Bitte auswählen",
"edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB",
@ -341,7 +380,7 @@
"edt_conf_enum_transeffect_sudden": "Sofort",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Einfarbig",
"edt_conf_enum_unicolor_mean": "Durchschnittsfarbe Gesamtbild - auf alle LED angewandt",
"edt_conf_fbs_heading_title": "Flatbuffers Server",
"edt_conf_fbs_timeout_expl": "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
"edt_conf_fbs_timeout_title": "Zeitüberschreitung",
@ -400,11 +439,13 @@
"edt_conf_grabber_discovered_title": "Gefundenes Aufnahmegerät",
"edt_conf_grabber_discovered_title_info": "Wähle dein gefundenes Aufnahmegerät aus.",
"edt_conf_grabber_discovery_inprogress": "Suche Aufnahmegeräte",
"edt_conf_instC_audioEnable_expl": "Aktiviert die Audioaufnahme für diese LED Hardware Instanz.",
"edt_conf_instC_audioEnable_title": "Aktivieren der Audioaufnahme",
"edt_conf_instC_screen_grabber_device_expl": "Das verwendet Bildschirmaufnahmegerät",
"edt_conf_instC_screen_grabber_device_title": "Bildschirmaufnahmegerät",
"edt_conf_instC_systemEnable_expl": "Aktiviert die Bildschirm Aufnahme für diese LED Hardware Instanz.",
"edt_conf_instC_systemEnable_title": "Aktiviere Bildschirm Aufnahme",
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED -Hardware Instanz.",
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED Hardware Instanz.",
"edt_conf_instC_v4lEnable_title": "Aktiviere USB-Aufnahme",
"edt_conf_instC_video_grabber_device_expl": "Das verwendete Videoaufnahmegerät.",
"edt_conf_instC_video_grabber_device_title": "Videoaufnahmegerät",
@ -439,8 +480,6 @@
"edt_conf_smooth_heading_title": "Glättung",
"edt_conf_smooth_interpolationRate_expl": "Frequenz in der Zwischenschritte zur Glättung berechnet werden.",
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrequenz",
"edt_conf_smooth_outputRate_expl": "Die Ausgangfrequenz zum LED-Gerät",
"edt_conf_smooth_outputRate_title": "Ausgabefrequenz",
"edt_conf_smooth_time_ms_expl": "Wie lange soll die Glättung Bilder sammeln?",
"edt_conf_smooth_time_ms_title": "Zeit",
"edt_conf_smooth_type_expl": "Algorithmus der Glättung.",
@ -614,10 +653,17 @@
"edt_dev_spec_rgbw_calibration_green": "Grün/Weiß-Kanal Aspekt",
"edt_dev_spec_rgbw_calibration_limit": "Grenzwert für Weißkanal",
"edt_dev_spec_rgbw_calibration_red": "Rot/Weiß-Kanal Aspekt",
"edt_dev_spec_segmentId_title": "Segment-ID",
"edt_dev_spec_segmentsOverlapValidation_error": "Korrigiere die WLED-Konfiguration! Das Segment darf sich nicht mit {{Plural:$1|Segment|Segmenten}} überschneiden: \"$2\".",
"edt_dev_spec_segmentsSwitchOffOthers_title": "Abschalten anderer Segmente",
"edt_dev_spec_segments_disabled_title": "Segment-Streaming ist in WLED deaktiviert.",
"edt_dev_spec_segments_title": "Stream zum Segment",
"edt_dev_spec_serial_title": "Seriennummer",
"edt_dev_spec_spipath_title": "SPI Pfad",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer Handshake maximum Timeout",
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer Handshake minimum Timeout",
"edt_dev_spec_stayOnAfterStreaming_title": "LED bleiben nach dem Stream an",
"edt_dev_spec_stayOnAfterStreaming_title_info": "Die LEDs bleiben nach dem Streaming oder der Wiederherstellung des Status eingeschaltet.",
"edt_dev_spec_stream_protocol_title": "Streaming-Protokoll",
"edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Aus bei Minimum",
@ -841,6 +887,7 @@
"general_col_blue": "blau",
"general_col_green": "grün",
"general_col_red": "rot",
"general_comp_AUDIO": "Audioaufnahme",
"general_comp_BLACKBORDER": "Schwarze Balken Erkennung",
"general_comp_BOBLIGHTSERVER": "Boblight Server",
"general_comp_FLATBUFSERVER": "Flatbuffers Server",
@ -964,8 +1011,11 @@
"remote_losthint": "Notiz: Alle Änderungen gehen nach einem Neustart verloren.",
"remote_maptype_intro": "Für gewöhnlich entscheidet dein LED-Layout welcher Bildbereich welche LED zugewiesen bekommt, dies kann hier geändert werden. $1",
"remote_maptype_label": "LED-Bereich Zuordnung",
"remote_maptype_label_multicolor_mean": "Mehrfarbig",
"remote_maptype_label_unicolor_mean": "Einfarbig",
"remote_maptype_label_dominant_color": "Dominante Farbe",
"remote_maptype_label_dominant_color_advanced": "Dominante Farbe fortgeschritten",
"remote_maptype_label_multicolor_mean": "Durchschnittsfarbe einfach",
"remote_maptype_label_multicolor_mean_squared": "Durchschnittsfarbe zum Quadrat",
"remote_maptype_label_unicolor_mean": "Durchschnittsfarbe Gesamtbild",
"remote_optgroup_syseffets": "Mitgelieferte Effekte",
"remote_optgroup_templates_custom": "Nutzer Vorlage",
"remote_optgroup_templates_system": "System Vorlage",

View File

@ -51,6 +51,7 @@
"conf_grabber_fg_intro": "Screen capture is your local system capture as input source, Hyperion is installed on.",
"conf_grabber_inst_grabber_config_info": "Configure your capturing hardware devices to be used by the instance in advance",
"conf_grabber_v4l_intro": "USB capture is a (capture) device connected via USB which is used to input source pictures for processing.",
"conf_grabber_audio_intro": "Audio capture utilizes an audio input device as the source for visualization.",
"conf_helptable_expl": "Explanation",
"conf_helptable_option": "Option",
"conf_leds_config_error": "Error in LED/LED layout configuration",
@ -58,11 +59,13 @@
"conf_leds_contr_label_contrtype": "Controller type:",
"conf_leds_device_info_log": "In case your LEDs do not work, check here for errors:",
"conf_leds_device_intro": "Hyperion supports a lot of controllers to transmit data to your target device. Select a LED controller out of the sorted list and configure it. We have chosen the best default settings for each device.",
"conf_leds_error_get_properties_text" : "Failed to get the device's properties. Please check the configuration items.",
"conf_leds_error_get_properties_title" : "Device properties",
"conf_leds_error_get_properties_text": "Failed to get the device's properties. Please check the configuration items.",
"conf_leds_error_get_properties_title": "Device properties",
"conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),<br>$3 {{plural:$3|LED|LEDs}} will stay black if you continue.",
"conf_leds_error_hwled_lt_layout": "The hardware LED count ($1) is less than LEDs configured via layout ($2). <br> The number of LEDs configured in the layout must not exceed the available LEDs",
"conf_leds_error_hwled_gt_maxled": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the device ($2). <br> The hardware LED count is set to ($3).",
"conf_leds_error_hwled_gt_maxled_protocol": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the streaming protocol ($2). <br> The streaming protocol will be changed to ($3).",
"conf_leds_error_wled_segment_missing": "The currently configured segment ($1) is not configured at your WLED device.<br>You might need to check the WLED configuration!<br>The configuration page represents the current WLED setup.",
"conf_leds_info_ws281x": "Hyperion must run with 'root' privileges for this controller type!",
"conf_leds_layout_advanced": "Advanced Settings",
"conf_leds_layout_blacklist_num_title": "Number of LEDs",
@ -252,6 +255,8 @@
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
"edt_conf_bge_heading_title": "Background Effect/Color",
"edt_conf_bobls_heading_title": "Boblight Server",
"edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.",
"edt_conf_color_accuracyLevel_title": "Accuracy level",
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
"edt_conf_color_backlightColored_title": "Colored backlight",
"edt_conf_color_backlightThreshold_expl": "The minimum amount of brightness (backlight). Disabled during effects, colors and in status \"Off\"",
@ -282,7 +287,7 @@
"edt_conf_color_heading_title": "Color Calibration",
"edt_conf_color_id_expl": "User given name",
"edt_conf_color_id_title": "ID",
"edt_conf_color_imageToLedMappingType_expl": "Overwrites the LED area assignment of your LED layout if it's not \"multicolor\"",
"edt_conf_color_imageToLedMappingType_expl": "Overwrites the LED area assignment of your LED layout if it's not \"Mean Color Simple\"",
"edt_conf_color_imageToLedMappingType_title": "LED area assignment",
"edt_conf_color_leds_expl": "Assign this adjustment to all LEDs (*) or just some (0-24).",
"edt_conf_color_leds_title": "LED index",
@ -294,6 +299,8 @@
"edt_conf_color_temperature_title": "Temperature",
"edt_conf_color_saturationGain_expl": "Adjusts the saturation of colors. 1.0 means no change, over 1.0 increases saturation, under 1.0 desaturates.",
"edt_conf_color_saturationGain_title": "Saturation gain",
"edt_conf_color_reducedPixelSetFactorFactor_expl": "Evaluate only a set of pixels per LED area defined, Low ~25%, Medium ~10%, High ~6%",
"edt_conf_color_reducedPixelSetFactorFactor_title": "Reduced pixel processing",
"edt_conf_color_white_expl": "The calibrated white value.",
"edt_conf_color_white_title": "White",
"edt_conf_color_yellow_expl": "The calibrated yellow value.",
@ -323,6 +330,8 @@
"edt_conf_enum_color": "Color",
"edt_conf_enum_custom": "Custom",
"edt_conf_enum_decay": "Decay",
"edt_conf_enum_delay": "Delay only",
"edt_conf_enum_disabled": "Disabled",
"edt_conf_enum_dl_error": "Error",
"edt_conf_enum_dl_informational": "Informational",
"edt_conf_enum_dl_nodebug": "No Debug output",
@ -331,9 +340,12 @@
"edt_conf_enum_dl_verbose1": "Verbosity level 1",
"edt_conf_enum_dl_verbose2": "Verbosity level 2",
"edt_conf_enum_dl_verbose3": "Verbosity level 3",
"edt_conf_enum_dominant_color": "Dominant Color - per LED",
"edt_conf_enum_dominant_color_advanced": "Dominant Color Advanced - per LED",
"edt_conf_enum_effect": "Effect",
"edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB",
"edt_conf_enum_high": "High",
"edt_conf_enum_hsv": "HSV",
"edt_conf_enum_left_right": "Left to right",
"edt_conf_enum_linear": "Linear",
@ -341,7 +353,10 @@
"edt_conf_enum_logsilent": "Silent",
"edt_conf_enum_logverbose": "Verbose",
"edt_conf_enum_logwarn": "Warning",
"edt_conf_enum_multicolor_mean": "Multicolor",
"edt_conf_enum_low": "Low",
"edt_conf_enum_medium": "Medium",
"edt_conf_enum_multicolor_mean": "Mean Color Simple - per LED",
"edt_conf_enum_multicolor_mean_squared": "Mean Color Squared - per LED",
"edt_conf_enum_please_select": "Please Select",
"edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB",
@ -351,7 +366,7 @@
"edt_conf_enum_transeffect_sudden": "Sudden",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Unicolor",
"edt_conf_enum_unicolor_mean": "Mean Color Image - applied to all LEDs",
"edt_conf_fbs_heading_title": "Flatbuffers Server",
"edt_conf_fbs_timeout_expl": "If no data is received for the given period, the component will be (soft) disabled.",
"edt_conf_fbs_timeout_title": "Timeout",
@ -416,6 +431,8 @@
"edt_conf_instC_systemEnable_title": "Enable screen capture",
"edt_conf_instC_v4lEnable_expl": "Enables the USB capture for this LED hardware instance",
"edt_conf_instC_v4lEnable_title": "Enable USB capture",
"edt_conf_instC_audioEnable_expl": "Enables the Audio capture for this LED hardware instance",
"edt_conf_instC_audioEnable_title": "Enable Audio capture",
"edt_conf_instC_video_grabber_device_expl": "The video capture device used",
"edt_conf_instC_video_grabber_device_title": "Video capture device",
"edt_conf_instCapture_heading_title": "Capture Devices",
@ -518,6 +535,27 @@
"edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation",
"edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold",
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered",
"edt_conf_audio_device_expl": "Selected audio input device",
"edt_conf_audio_device_title": "Audio Device",
"edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to",
"edt_conf_audio_effects_title": "Audio Effects",
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
"edt_conf_audio_effect_hotcolor_expl": "Hot Color",
"edt_conf_audio_effect_hotcolor_title": "Hot Color",
"edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier",
"edt_conf_audio_effect_multiplier_title": "Multiplier",
"edt_conf_audio_effect_safecolor_expl": "Safe Color",
"edt_conf_audio_effect_safecolor_title": "Safe Color",
"edt_conf_audio_effect_safevalue_expl": "Safe Threshold",
"edt_conf_audio_effect_safevalue_title": "Safe Threshold",
"edt_conf_audio_effect_set_defaults": "Reset to default values",
"edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100",
"edt_conf_audio_effect_tolerance_title": "Tolerance",
"edt_conf_audio_effect_warncolor_expl": "Warning Color",
"edt_conf_audio_effect_warncolor_title": "Warning Color",
"edt_conf_audio_effect_warnvalue_expl": "Warning Threshold",
"edt_conf_audio_effect_warnvalue_title": "Warning Threshold",
"edt_conf_audio_heading_title": "Audio Capture",
"edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)",
"edt_conf_webc_crtPath_title": "Certificate path",
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
@ -563,7 +601,7 @@
"edt_dev_spec_brightnessOverwrite_title": "Overwrite brightness",
"edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum",
"edt_dev_spec_brightness_title": "Brightness",
"edt_dev_spec_candyGamma_title" : "'Candy' mode (double gamma correction)",
"edt_dev_spec_candyGamma_title": "'Candy' mode (double gamma correction)",
"edt_dev_spec_chanperfixture_title": "Channels per Fixture",
"edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Clientkey",
@ -620,15 +658,22 @@
"edt_dev_spec_razer_device_title": "Razer Chroma Device",
"edt_dev_spec_restoreOriginalState_title": "Restore lights' state",
"edt_dev_spec_restoreOriginalState_title_info": "Restore the device's original state when device is disabled",
"edt_dev_spec_rgbw_calibration_enable" : "White channel calibration (RGBW only)",
"edt_dev_spec_rgbw_calibration_limit" : "White channel limit",
"edt_dev_spec_rgbw_calibration_red" : "Red/White channel aspect",
"edt_dev_spec_rgbw_calibration_green" : "Green/White channel aspect",
"edt_dev_spec_rgbw_calibration_blue" : "Blue/White channel aspect",
"edt_dev_spec_rgbw_calibration_enable": "White channel calibration (RGBW only)",
"edt_dev_spec_rgbw_calibration_limit": "White channel limit",
"edt_dev_spec_rgbw_calibration_red": "Red/White channel aspect",
"edt_dev_spec_rgbw_calibration_green": "Green/White channel aspect",
"edt_dev_spec_rgbw_calibration_blue": "Blue/White channel aspect",
"edt_dev_spec_segments_disabled_title": "Segment streaming disabled at WLED.",
"edt_dev_spec_segments_title": "Stream to segment",
"edt_dev_spec_segmentId_title": "Segment-ID",
"edt_dev_spec_segmentsSwitchOffOthers_title": "Switch-off other segments",
"edt_dev_spec_segmentsOverlapValidation_error": "Correct the WLED setup! The segment must not overlap with {{plural:$1| segment|segments}}: \"$2\".",
"edt_dev_spec_serial_title": "Serial number",
"edt_dev_spec_spipath_title": "SPI Device",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum",
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer handshake timeout minimum",
"edt_dev_spec_stayOnAfterStreaming_title": "Stay on after streaming",
"edt_dev_spec_stayOnAfterStreaming_title_info": "The device will stay on after streaming or restoring state.",
"edt_dev_spec_switchOffOnBlack_title": "Switch off on black",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum",
"edt_dev_spec_syncOverwrite_title": "Disable synchronisation",
@ -860,6 +905,7 @@
"general_comp_PROTOSERVER": "Protocol Buffers Server",
"general_comp_SMOOTHING": "Smoothing",
"general_comp_V4L": "Capture USB-Input",
"general_comp_AUDIO": "Audio Capture",
"general_country_cn": "China",
"general_country_de": "Germany",
"general_country_es": "Spain",
@ -871,11 +917,11 @@
"general_country_us": "United States",
"general_disabled": "disabled",
"general_enabled": "enabled",
"general_speech_ca": "Catalan",
"general_speech_ca": "Catalan",
"general_speech_cs": "Czech",
"general_speech_da": "Danish",
"general_speech_de": "German",
"general_speech_el": "Greek",
"general_speech_el": "Greek",
"general_speech_en": "English",
"general_speech_es": "Spanish",
"general_speech_fr": "French",
@ -970,8 +1016,11 @@
"remote_losthint": "Note: All changes will be lost after a restart.",
"remote_maptype_intro": "Usually the LED layout defines which LED covers a specific picture area. You can change it here: $1.",
"remote_maptype_label": "Mapping type",
"remote_maptype_label_multicolor_mean": "Multicolor",
"remote_maptype_label_unicolor_mean": "Unicolor",
"remote_maptype_label_dominant_color": "Dominant Color",
"remote_maptype_label_dominant_color_advanced": "Dominant Color Advanced",
"remote_maptype_label_multicolor_mean": "Mean Color Simple",
"remote_maptype_label_multicolor_mean_squared": "Mean Color Squared",
"remote_maptype_label_unicolor_mean": "Mean Color Image",
"remote_optgroup_syseffets": "System Effects",
"remote_optgroup_templates_custom": "User Templates",
"remote_optgroup_templates_system": "System Templates",

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "¡El WebUI se desbloqueará automáticamente después de haber resuelto el problema!",
"InfoDialog_nowrite_text": "Hyperion no puede escribir en el archivo de configuración cargado actual. Repara los permisos de archivo para continuar.",
"InfoDialog_nowrite_title": "¡Error de permiso de escritura!",
"InfoDialog_systemRestart_title": "Reiniciar",
"InfoDialog_systemResume_title": "Retomar",
"InfoDialog_systemSuspend_title": "Suspender",
"about_3rd_party_licenses": "Licencias de terceros",
"about_3rd_party_licenses_error": "Tuvimos problemas para recopilar información de licencias de terceros a través de la Web. <br />Por favor, sigue este enlace para acceder al Recurso GitHub.",
"about_build": "Build",
@ -55,7 +58,9 @@
"conf_leds_error_get_properties_title": "Propiedades del dispositivo",
"conf_leds_error_hwled_gt_layout": "El recuento de LEDs por hardware ($1) es mayor que los LEDs configurados a través de la disposición ($2),<br>$3 {{plural:$1|LED|LEDs}} se quedará en negro si continúas.",
"conf_leds_error_hwled_gt_maxled": "El recuento de LEDs por hardware ($1) es mayor que el número máximo de LEDs soportado por el dispositivo ($2). <br> El recuento de LEDs de hardware se establece en ($3).",
"conf_leds_error_hwled_gt_maxled_protocol": "El recuento de LEDs por hardware ($1) es mayor que el número máximo de LEDs soportado por el protocolo de streaming ($2). <br> El protocolo de streaming cambiará a ($3).",
"conf_leds_error_hwled_lt_layout": "El recuento de LEDs por hardware ($1) es menor que los LEDs configurados a través del diseño ($2). <br> El número de LEDs configurados en el esquema no debe superar los LEDs disponibles.",
"conf_leds_error_wled_segment_missing": "El segmento configurado actualmente ($1) no está configurado en tu dispositivo WLED.<br>¡Es posible que tengas que comprobar la configuración del WLED!<br>La página de configuración representa la configuración actual del WLED.",
"conf_leds_info_ws281x": "Hyperion debe ejecutarse con privilegios 'root' para este tipo de controlador.",
"conf_leds_layout_advanced": "Ajustes Avanzados",
"conf_leds_layout_blacklist_num_title": "Número de LEDs",
@ -439,8 +444,6 @@
"edt_conf_smooth_heading_title": "Suavizado",
"edt_conf_smooth_interpolationRate_expl": "Velocidad de cálculo de los fotogramas intermedios suaves.",
"edt_conf_smooth_interpolationRate_title": "Tasa de interpolación",
"edt_conf_smooth_outputRate_expl": "La velocidad de salida a tu controlador de leds.",
"edt_conf_smooth_outputRate_title": "Tasa de salida",
"edt_conf_smooth_time_ms_expl": "¿Cuánto tiempo debe recoger las imágenes el suavizado?",
"edt_conf_smooth_time_ms_title": "Tiempo",
"edt_conf_smooth_type_expl": "Tipo de suavizado",
@ -614,10 +617,17 @@
"edt_dev_spec_rgbw_calibration_green": "Aspecto del canal Verde/Blanco",
"edt_dev_spec_rgbw_calibration_limit": "Límite del canal blanco",
"edt_dev_spec_rgbw_calibration_red": "Aspecto del canal Rojo/Blanco",
"edt_dev_spec_segmentId_title": "Segmento-ID",
"edt_dev_spec_segmentsOverlapValidation_error": "¡Corrige la configuración de WLED! El segmento no debe solaparse con {{plural:$1| segmento|segmentos}}: \"$2\".",
"edt_dev_spec_segmentsSwitchOffOthers_title": "Desconectar otros segmentos",
"edt_dev_spec_segments_disabled_title": "Transmisión de segmentos desactivada en WLED.",
"edt_dev_spec_segments_title": "Transmisión a segmento",
"edt_dev_spec_serial_title": "Número de serie",
"edt_dev_spec_spipath_title": "Dispositivo SPI",
"edt_dev_spec_sslHSTimeoutMax_title": "Máximo tiempo de espera para el contacto con el Streamer",
"edt_dev_spec_sslHSTimeoutMin_title": "Tiempo mínimo de espera para el contacto con el Streamer",
"edt_dev_spec_stayOnAfterStreaming_title": "Permanecer encendido después del streaming",
"edt_dev_spec_stayOnAfterStreaming_title_info": "El dispositivo permanecerá encendido después de transmitir o restaurar el estado.",
"edt_dev_spec_stream_protocol_title": "Protocolo de streaming",
"edt_dev_spec_switchOffOnBlack_title": "Apagar en negro",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Apagado, por debajo del mínimo",
@ -861,9 +871,11 @@
"general_country_us": "Estados Unidos",
"general_disabled": "Deshabilitado",
"general_enabled": "Habilitado",
"general_speech_ca": "Catalán",
"general_speech_cs": "Czech",
"general_speech_da": "Danés",
"general_speech_de": "Alemán",
"general_speech_el": "Griego",
"general_speech_en": "Inglés",
"general_speech_es": "Español",
"general_speech_fr": "Francés",

View File

@ -398,9 +398,11 @@
"edt_conf_v4l2_greenSignalThreshold_expl": "Assombrie les valeurs vertes faibles (reconnues comme noires)",
"edt_conf_v4l2_greenSignalThreshold_title": "Seuil de signal vert",
"edt_conf_v4l2_hardware_brightness_expl": "Définie la luminosité matérielle",
"edt_conf_v4l2_hardware_brightness_title": "Contrôle matériel de la luminosité",
"edt_conf_v4l2_hardware_contrast_expl": "Définie le contraste matériel",
"edt_conf_v4l2_hardware_contrast_title": "Contrôle le contraste matériel",
"edt_conf_v4l2_hardware_hue_expl": "Définie le hue matériel",
"edt_conf_v4l2_hardware_hue_title": "Contrôle matériel de la teinte",
"edt_conf_v4l2_hardware_saturation_expl": "Définie la saturation matérielle",
"edt_conf_v4l2_hardware_saturation_title": "Contrôle la saturation matériel",
"edt_conf_v4l2_heading_title": "Capture USB",

View File

@ -5,6 +5,8 @@
"InfoDialog_changePassword_title": "Cambia Password",
"InfoDialog_iswitch_text": "Se esegui più di un Hyperion nella tua rete locale puoi passare tra configurazioni web. Seleziona l'istanza di Hyperion qui sotto e cambia!",
"InfoDialog_iswitch_title": "Cambia Hyperion",
"InfoDialog_nostorage_text": "Il tuo browser non supporta localStorage. Non è possibile salvare un'impostazione di lingua specifica (fallback su \"rilevamento automatico\") e livello di accesso (fallback su \"predefinito\"). Alcuni maghi potrebbero essere nascosti. Puoi ancora utilizzare l'interfaccia web senza ulteriori problemi",
"InfoDialog_nostorage_title": "Impossibile memorizzare le impostazioni",
"InfoDialog_nowrite_foottext": "WebUI sarà sbloccata automaticamente appena avrai risolto il problema!",
"InfoDialog_nowrite_text": "Hyperion non può scrivere sull'attuale file di configurazione caricato. Riparare i permessi del file per procedere",
"InfoDialog_nowrite_title": "errore dei permessi di scrittura!",
@ -40,14 +42,29 @@
"conf_general_intro": "Impostazioni base di Hyperion e WebUI che non rientrano in altre categorie.",
"conf_general_label_title": "Impostazioni generali",
"conf_grabber_fg_intro": "Cattura di sistema usa la cattura locale del sistema su cui è installato Hyperion come sorgente di input.",
"conf_grabber_inst_grabber_config_info": "Configura in anticipo i dispositivi hardware di acquisizione in modo che vengano utilizzati dall'istanza",
"conf_grabber_v4l_intro": "Cattura USB è un dispositivo (di cattura) connesso via USB usato come sorgente di immagini per l'elaborazione.",
"conf_helptable_expl": "Spiegazione",
"conf_helptable_option": "Opzioni",
"conf_leds_config_error": "Errore nella configurazione del layout LED/LED",
"conf_leds_config_warning": "Controlla la configurazione del layout LED/LED",
"conf_leds_contr_label_contrtype": "Tipo di Controller",
"conf_leds_device_info_log": "Nel caso in cui i tuoi LED non funzionino, controlla qui per gli errori:",
"conf_leds_device_intro": "Hyperion supporta molti controller per trasmettere dati al tuo dispositivo. Seleziona un controller LED dalla lista e configuralo. Abbiamo scelto le impostazioni di default migliori per ogni dispositivo.",
"conf_leds_error_get_properties_text": "Impossibile ottenere le proprietà del dispositivo. Si prega di controllare gli elementi di configurazione.",
"conf_leds_error_get_properties_title": "Proprietà del dispositivo",
"conf_leds_error_hwled_gt_layout": "Il conteggio dei LED hardware ($1) è maggiore dei LED configurati tramite layout ($2),<br>$3 {{plural:$3|LED|LED}} rimarranno neri se continui.",
"conf_leds_error_hwled_gt_maxled": "Il numero di LED hardware ($ 1) è superiore al numero massimo di LED supportati dal dispositivo ($ 2). <br> Il conteggio dei LED hardware è impostato su ($ 3)",
"conf_leds_error_hwled_lt_layout": "Il numero di LED hardware ($ 1) è inferiore ai LED configurati tramite layout ($ 2). <br> Il numero di LED configurati nel layout non deve superare i LED disponibili",
"conf_leds_info_ws281x": "Hyperion deve essere eseguito con privilegi di 'root' per questo tipo di controller!",
"conf_leds_layout_advanced": "Impostazioni Avanzate",
"conf_leds_layout_blacklist_num_title": "Numero di LEDs",
"conf_leds_layout_blacklist_rule_title": "Regole della lista nera",
"conf_leds_layout_blacklist_rules_title": "Regole delle liste nere",
"conf_leds_layout_blacklist_start_title": "LED Iniziale",
"conf_leds_layout_blacklistleds_title": "I LED della lista nera",
"conf_leds_layout_btn_checklist": "Mostra lista",
"conf_leds_layout_btn_keystone": "Correzione trapezoidale",
"conf_leds_layout_button_savelay": "Salva Layout",
"conf_leds_layout_button_updsim": "Aggiorna Anteprima",
"conf_leds_layout_checkp1": "Il led nero è il tuo primo led, il primo led è il punto in cui c'è l'input del segnale dati",
@ -67,6 +84,16 @@
"conf_leds_layout_cl_leftbottom": "Sinistra 0% - 100% Sotto",
"conf_leds_layout_cl_leftmiddle": "Sinistra 0% - 75% Centro",
"conf_leds_layout_cl_lefttop": "Sinistra 0% - 50% Sopra",
"conf_leds_layout_cl_lightPosBottomLeft11": "In basso: 75 - 100% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeft112": "In basso: 0 - 50% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeft12": "In basso: 25 - 50% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeft121": "In basso: 50 - 100% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeft14": "In basso: 0 - 25% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeft34": "In basso: 50 - 75% da sinistra",
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "In basso: 25 - 75% da sinistra",
"conf_leds_layout_cl_lightPosTopLeft112": "In alto: 0 - 50% da sinistra",
"conf_leds_layout_cl_lightPosTopLeft121": "In alto: 50 - 100% da sinistra",
"conf_leds_layout_cl_lightPosTopLeftNewMid": "In alto: 25 - 75% da sinistra",
"conf_leds_layout_cl_overlap": "Overlap",
"conf_leds_layout_cl_reversdir": "Inverti direzione",
"conf_leds_layout_cl_right": "Destra",
@ -81,6 +108,7 @@
"conf_leds_layout_generatedconf": "Configurazione LED Generata/Attuale",
"conf_leds_layout_intro": "è necessario anche un layout dei led che rifletta la disposizione dei tuoi led. Il layout classico è il comune bordo della tv, ma supportiamo anche la creazione di matrici led (parete led). La visuale sul layout è SEMPRE DI FRONTE alla TV.",
"conf_leds_layout_ma_cabling": "Cablaggio",
"conf_leds_layout_ma_direction": "Direzione",
"conf_leds_layout_ma_horiz": "Orizzontale",
"conf_leds_layout_ma_optbottomleft": "Basso a sinistra",
"conf_leds_layout_ma_optbottomright": "Basso a destra",
@ -112,17 +140,21 @@
"conf_leds_layout_textf1": "Questo campo testo mostra di default il layout attualmente caricato e viene sovrascritto se ne generi uno nuovo con le opzioni sopra. Se vuoi puoi fare ulteriori modifiche.",
"conf_leds_nav_label_ledcontroller": "Controller LED",
"conf_leds_nav_label_ledlayout": "Layout LED",
"conf_leds_note_layout_overwrite": "Nota: Overwrite crea un layout predefinito per {{plural:$1| un LED| tutti i LED $1}} dati dal conteggio dei LED hardware",
"conf_leds_optgroup_RPiGPIO": "RPi GPIO",
"conf_leds_optgroup_RPiPWM": "RPi PWM",
"conf_leds_optgroup_RPiSPI": "RPi SPI",
"conf_leds_optgroup_debug": "Debug",
"conf_leds_optgroup_network": "Rete",
"conf_leds_optgroup_other": "Altro",
"conf_leds_optgroup_usb": "USB/Seriale",
"conf_logging_btn_autoscroll": "Auto scrolling",
"conf_logging_btn_clipboard": "Copia nel registro Appunti",
"conf_logging_btn_pbupload": "Invia segnalazione per richieste di supporto",
"conf_logging_contpolicy": "Policy per la Privacy delle segnalazioni",
"conf_logging_label_intro": "Area per controllare i messaggi di log, a seconda dell'impostazione di loglevel vedrai più o meno informazioni.",
"conf_logging_lastreports": "Segnalazioni precedenti",
"conf_logging_logoutput": "Log output",
"conf_logging_nomessage": "Nessun messaggio di log disponibile.",
"conf_logging_report": "Segnala",
"conf_logging_uplfailed": "Upload fallito! Controlla la tua connessione a internet!",
@ -163,7 +195,12 @@
"dashboard_infobox_label_instance": "Istanza:",
"dashboard_infobox_label_latesthyp": "L'ultima versione di Hyperion:",
"dashboard_infobox_label_platform": "Piattaforma:",
"dashboard_infobox_label_port_boblight": "Server Boblight:",
"dashboard_infobox_label_port_flat": "Flatbuffer:",
"dashboard_infobox_label_port_json": "JSON-Server:",
"dashboard_infobox_label_port_proto": "Protobuffer:",
"dashboard_infobox_label_ports": "Porte",
"dashboard_infobox_label_ports_websocket": "WebSocket (ws|wss):",
"dashboard_infobox_label_smartacc": "Accesso Smart",
"dashboard_infobox_label_statush": "Status Hyperion:",
"dashboard_infobox_label_title": "Informazioni",
@ -181,6 +218,7 @@
"dashboard_newsbox_readmore": "Leggi ancora",
"dashboard_newsbox_visitblog": "Visita Hyperion-Blog",
"edt_append_degree": "°",
"edt_append_frames": "frames",
"edt_append_hz": "Hz",
"edt_append_leds": "LEDs",
"edt_append_ms": "ms",
@ -216,6 +254,8 @@
"edt_conf_color_blue_title": "Blu",
"edt_conf_color_brightnessComp_expl": "Compensa la differenza di luminosità tra rosso verde blu, ciano magneta giallo e bianco. 100 significa massima compensazione, 0 nessuna compensazione",
"edt_conf_color_brightnessComp_title": "Compensazione luminosità",
"edt_conf_color_brightnessGain_expl": "Regola la luminosità dei colori. 1.0 significa nessun cambiamento, oltre 1.0 aumenta la luminosità, sotto 1.0 diminuisce la luminosità",
"edt_conf_color_brightnessGain_title": "Intensità della luminosità",
"edt_conf_color_brightness_expl": "Imposta la luminosità complessiva dei leds",
"edt_conf_color_brightness_title": "Luminosità",
"edt_conf_color_channelAdjustment_header_expl": "Crea profilo di colore che può essere assegnato a un componente specifico. Regola colore, gamma, luminosità, compensazione e altro.",
@ -242,6 +282,8 @@
"edt_conf_color_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Il valore calibrato del rosso.",
"edt_conf_color_red_title": "Rosso",
"edt_conf_color_saturationGain_expl": "Regola la saturazione dei colori. 1.0 significa nessun cambiamento, oltre 1.0 aumenta la saturazione, sotto 1.0 desatura.",
"edt_conf_color_saturationGain_title": "Intensità della saturazione",
"edt_conf_color_white_expl": "Il valore calibrato del bianco.",
"edt_conf_color_white_title": "Bianco",
"edt_conf_color_yellow_expl": "Il valore calibrato del giallo.",
@ -253,10 +295,13 @@
"edt_conf_effp_paths_expl": "Puoi definire altre cartelle che contengono effetti aggiuntivi. Il configuratore di effetti salva sempre all'interno della prima cartella.",
"edt_conf_effp_paths_itemtitle": "Percorso",
"edt_conf_effp_paths_title": "Percorso Effetti",
"edt_conf_enum_BOTH": "Orizzontale e Verticale",
"edt_conf_enum_HORIZONTAL": "Orizzontale",
"edt_conf_enum_NO_CHANGE": "Auto",
"edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL",
"edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_VERTICAL": "Verticale",
"edt_conf_enum_automatic": "Automatico",
"edt_conf_enum_bbclassic": "Classico",
"edt_conf_enum_bbdefault": "Default",
@ -287,12 +332,15 @@
"edt_conf_enum_logverbose": "Verboso",
"edt_conf_enum_logwarn": "Avvertimento",
"edt_conf_enum_multicolor_mean": "Multicolore",
"edt_conf_enum_please_select": "Seleziona",
"edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB",
"edt_conf_enum_right_left": "Da destra a sinistra",
"edt_conf_enum_top_down": "Dall'alto verso il basso",
"edt_conf_enum_transeffect_smooth": "Dolce",
"edt_conf_enum_transeffect_sudden": "Repentina",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Monocromatico",
"edt_conf_fbs_heading_title": "Server Flatbuffers",
"edt_conf_fbs_timeout_expl": "Se nessuna informazione viene ricevuta per un dato periodo, il componente verrà disabilitato (soft).",
@ -321,11 +369,19 @@
"edt_conf_fge_type_title": "Tipo",
"edt_conf_fw_flat_expl": "Una destinazione flatbuffer per riga. Contiene IP:PORTA:(Esempio: 127.0.0.1:19401)",
"edt_conf_fw_flat_itemtitle": "Destinatario flatbuffer",
"edt_conf_fw_flat_services_discovered_expl": "Obiettivi flatbuffer scoperti",
"edt_conf_fw_flat_services_discovered_title": "Obiettivi flatbuffer scoperti",
"edt_conf_fw_flat_title": "Lista dei client flatbuffer",
"edt_conf_fw_heading_title": "Forwarder",
"edt_conf_fw_json_expl": "Una destinazione json per riga. Contiene IP:PORTA:(Esempio: 127.0.0.1:19446)",
"edt_conf_fw_json_itemtitle": "Destinatario json",
"edt_conf_fw_json_services_discovered_expl": "Server Hyperion scoperti che forniscono servizi JSON-API",
"edt_conf_fw_json_services_discovered_title": "Target JSON rilevati",
"edt_conf_fw_json_title": "Lista dei client json",
"edt_conf_fw_remote_service_discovered_none": "Nessun servizio remoto rilevato",
"edt_conf_fw_service_name_expl": "Nome del fornitore di servizi",
"edt_conf_fw_service_name_title": "Nome di Servizio",
"edt_conf_gen_configVersion_title": "Versione di configurazione",
"edt_conf_gen_heading_title": "impostazioni Generali",
"edt_conf_gen_name_expl": "Un nome definito dall'utente che viene utilizzato per riconoscere Hyperion. (Utile con più di un'istanza di Hyperion)",
"edt_conf_gen_name_title": "Nome configurazione",
@ -339,10 +395,19 @@
"edt_conf_general_port_title": "Porta",
"edt_conf_general_priority_expl": "Priorità di questo componente",
"edt_conf_general_priority_title": "Priorità canale.",
"edt_conf_grabber_discovered_expl": "Seleziona il tuo dispositivo di acquisizione scoperto",
"edt_conf_grabber_discovered_none": "Nessun dispositivo di acquisizione rilevato",
"edt_conf_grabber_discovered_title": "Dispositivo scoperto",
"edt_conf_grabber_discovered_title_info": "Seleziona il tuo dispositivo di acquisizione scoperto",
"edt_conf_grabber_discovery_inprogress": "ricerca in corso",
"edt_conf_instC_screen_grabber_device_expl": "l dispositivo di cattura dello schermo utilizzato",
"edt_conf_instC_screen_grabber_device_title": "Dispositivo di cattura dello schermo",
"edt_conf_instC_systemEnable_expl": "Abilita la cattura della piattaforma per questa istanza di hardware led",
"edt_conf_instC_systemEnable_title": "Abilita cattura piattaforma",
"edt_conf_instC_v4lEnable_expl": "Abilita la cattura USB per questa istanza di hardware led",
"edt_conf_instC_v4lEnable_title": "Abilita cattura USB",
"edt_conf_instC_video_grabber_device_expl": "Il dispositivo di acquisizione video utilizzato",
"edt_conf_instC_video_grabber_device_title": "Dispositivo di acquisizione video",
"edt_conf_instCapture_heading_title": "Cattura Istanza",
"edt_conf_js_heading_title": "Server JSON",
"edt_conf_log_heading_title": "Logging",
@ -374,8 +439,6 @@
"edt_conf_smooth_heading_title": "Sfumatura",
"edt_conf_smooth_interpolationRate_expl": "Velocità di calcolo dei regolari frame intermedi",
"edt_conf_smooth_interpolationRate_title": "Tasso di interpolazione",
"edt_conf_smooth_outputRate_expl": "Velocità di uscita al tuo controller LED.",
"edt_conf_smooth_outputRate_title": "Velocità di uscita",
"edt_conf_smooth_time_ms_expl": "Quanto a lungo la sfumatura dovrebbe raggiungere le immagini?",
"edt_conf_smooth_time_ms_title": "Durata",
"edt_conf_smooth_type_expl": "Tipo di sfumatura.",
@ -390,21 +453,41 @@
"edt_conf_v4l2_cecDetection_title": "Rilevamento CEC",
"edt_conf_v4l2_cropBottom_expl": "Numero di pixels in basso che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropBottom_title": "Ritaglia in basso",
"edt_conf_v4l2_cropHeightValidation_error": "Ritaglia in alto + Ritaglia in basso non può essere maggiore di Altezza ($ 1)",
"edt_conf_v4l2_cropLeft_expl": "Numero di pixels sulla sinistra che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropLeft_title": "Ritaglia sinistra",
"edt_conf_v4l2_cropRight_expl": "Numero di pixels sulla destra che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropRight_title": "Ritaglia destra",
"edt_conf_v4l2_cropTop_expl": "Numero di pixels in alto che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropTop_title": "Ritaglia in alto",
"edt_conf_v4l2_cropWidthValidation_error": "Ritaglia a sinistra + Ritaglia a destra non può essere maggiore di Larghezza ($1)",
"edt_conf_v4l2_device_expl": "Percorso del dispositivo di cattura USB. Imposta su 'Automatico' per riconoscimento automatico. Esempio: '/dev/video0'",
"edt_conf_v4l2_device_title": "Dispositivo",
"edt_conf_v4l2_encoding_expl": "Forza la codifica video per i grabber multiformato",
"edt_conf_v4l2_encoding_title": "Formato di codifica",
"edt_conf_v4l2_flip_expl": "Ciò consente di capovolgere l'immagine orizzontalmente, verticalmente o entrambi.",
"edt_conf_v4l2_flip_title": "Capovolgimento dell'immagine",
"edt_conf_v4l2_fpsSoftwareDecimation_expl": "Per risparmiare risorse verrà elaborato solo ogni n-esimo frame. Per es. se il grabber è impostato a 30fps con questa opzione impostata a 5 il risultato finale sarà di circa 6fps",
"edt_conf_v4l2_fpsSoftwareDecimation_title": "Salto del frame del software",
"edt_conf_v4l2_framerate_expl": "Frame al secondo supportati per il dispositivo attivo",
"edt_conf_v4l2_framerate_title": "Frame al secondo",
"edt_conf_v4l2_greenSignalThreshold_expl": "Scurisce valori bassi di verde (riconosciuti come nero)",
"edt_conf_v4l2_greenSignalThreshold_title": "Soglia segnale verde",
"edt_conf_v4l2_hardware_brightness_expl": "Imposta la luminosità dell'hardware",
"edt_conf_v4l2_hardware_brightness_title": "Controllo hardware della luminosità",
"edt_conf_v4l2_hardware_contrast_expl": "Imposta il contrasto hardware",
"edt_conf_v4l2_hardware_contrast_title": "Controllo hardware del contrasto",
"edt_conf_v4l2_hardware_hue_expl": "Imposta la tonalità dell'hardware",
"edt_conf_v4l2_hardware_hue_title": "Controllo della tonalità hardware",
"edt_conf_v4l2_hardware_saturation_expl": "Imposta la saturazione hardware",
"edt_conf_v4l2_hardware_saturation_title": "Controllo della saturazione hardware",
"edt_conf_v4l2_hardware_set_defaults": "Controlli hardware predefiniti",
"edt_conf_v4l2_hardware_set_defaults_tip": "Imposta i valori predefiniti del dispositivo per luminosità, contrasto, tonalità e saturazione",
"edt_conf_v4l2_heading_title": "Cattura USB",
"edt_conf_v4l2_input_expl": "Seleziona l'input video per il tuo dispositivo. 'Automatico' mantiene il Valero scelto dall'interfaccia v4l2.",
"edt_conf_v4l2_input_title": "Input",
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Conteggio dei fotogrammi (controllalo con l'attuale modalità FPS del grabber) dopo il quale viene attivato il segnale di assenza",
"edt_conf_v4l2_noSignalCounterThreshold_title": "Soglia del contatore di segnale",
"edt_conf_v4l2_redSignalThreshold_expl": "Scurisce valori bassi di rosso (riconosciuti come nero)",
"edt_conf_v4l2_redSignalThreshold_title": "Soglia segnale rosso",
"edt_conf_v4l2_resolution_expl": "Lista Risoluzioni supportate per il dispositivo attivo",
@ -435,12 +518,21 @@
"edt_conf_webc_sslport_expl": "Porta del webserver HTTPS",
"edt_conf_webc_sslport_title": "Porta HTTPS",
"edt_dev_auth_key_title": "Token di autenticazione",
"edt_dev_auth_key_title_info": "Token di autenticazione necessario per accedere al dispositivo",
"edt_dev_enum_sub_min_cool_adjust": "Regolazione freddo min",
"edt_dev_enum_sub_min_warm_adjust": "Regolazione calore min",
"edt_dev_enum_subtract_minimum": "Sottrai minimo",
"edt_dev_enum_white_off": "Bianco off",
"edt_dev_general_autostart_title": "avvio automatico",
"edt_dev_general_autostart_title_info": "Il dispositivo LED è acceso durante l'avvio oppure no",
"edt_dev_general_colorOrder_title": "Ordine byte RGB",
"edt_dev_general_colorOrder_title_info": "L'ordine dei colori del dispositivo",
"edt_dev_general_enableAttemptsInterval_title": "Intervallo tra tentativi",
"edt_dev_general_enableAttemptsInterval_title_info": "Intervallo tra due tentativi di connessione.",
"edt_dev_general_enableAttempts_title": "Tentativi di connessione",
"edt_dev_general_enableAttempts_title_info": "Numero di tentativi di connessione di un dispositivo prima che entri in uno stato di errore.",
"edt_dev_general_hardwareLedCount_title": "Numero di LED Hardware",
"edt_dev_general_hardwareLedCount_title_info": "Il numero di LED fisici disponibili per il dispositivo specificato",
"edt_dev_general_heading_title": "Impostazioni Generali",
"edt_dev_general_name_title": "Nome configurazione",
"edt_dev_general_rewriteTime_title": "Tempo di ricarica",
@ -449,21 +541,28 @@
"edt_dev_spec_FCsetConfig_title": "Imposta configurazione Fadecandy",
"edt_dev_spec_LBap102Mode_title": "Modalità LightBerry APA102",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
"edt_dev_spec_ada_mode_title": "Adalight - Standard",
"edt_dev_spec_awa_mode_title": "HyperSerial - Alta velocità",
"edt_dev_spec_baudrate_title": "Baudrate",
"edt_dev_spec_blackLightsTimeout_title": "Timeout rilevamento segnale su nero",
"edt_dev_spec_brightnessFactor_title": "Fattore luminosità",
"edt_dev_spec_brightnessMax_title": "Luminosità massima",
"edt_dev_spec_brightnessMin_title": "Luminosità minima",
"edt_dev_spec_brightnessMin_title": "Luminosità minima",
"edt_dev_spec_brightnessOverwrite_title": "Sovrascrivi luminosità",
"edt_dev_spec_brightnessThreshold_title": "Luminosità minima per rilevamento segnale",
"edt_dev_spec_brightness_title": "Luminosità",
"edt_dev_spec_candyGamma_title": "Modalità \"Candy\" (doppia correzione gamma)",
"edt_dev_spec_chanperfixture_title": "Canali per dispositivo",
"edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Clientkey",
"edt_dev_spec_colorComponent_title": "Componente colore",
"edt_dev_spec_debugLevel_title": "Livello Debug",
"edt_dev_spec_debugStreamer_title": "Debug Streamer",
"edt_dev_spec_delayAfterConnect_title": "Ritardo dopo la connessione",
"edt_dev_spec_devices_discovered_none": "Nessun dispositivo rilevato",
"edt_dev_spec_devices_discovered_title": "Dispositivi trovato",
"edt_dev_spec_devices_discovered_title_info": "Seleziona il tuo dispositivo LED trovato",
"edt_dev_spec_devices_discovered_title_info_custom": "Seleziona il tuo dispositivo LED trovato o configurane uno personalizzato",
"edt_dev_spec_devices_discovery_inprogress": "Ricerca in corso",
"edt_dev_spec_dithering_title": "Dithering",
"edt_dev_spec_dmaNumber_title": "Canale DMA",
"edt_dev_spec_gamma_title": "Gamma",
@ -478,6 +577,7 @@
"edt_dev_spec_intervall_title": "Intervallo",
"edt_dev_spec_invert_title": "Inverti segnale",
"edt_dev_spec_latchtime_title": "Tempo di latch",
"edt_dev_spec_latchtime_title_info": "Il tempo di latch è l'intervallo di tempo richiesto da un dispositivo fino all'elaborazione del successivo aggiornamento. Durante tale periodo di tempo, tutti gli aggiornamenti effettuati vengono ignorati.",
"edt_dev_spec_ledIndex_title": "Indice LED",
"edt_dev_spec_ledType_title": "Tipo LED",
"edt_dev_spec_lightid_itemtitle": "ID",
@ -491,6 +591,8 @@
"edt_dev_spec_networkDeviceName_title": "Nome dispositivo di rete",
"edt_dev_spec_networkDevicePort_title": "Porta",
"edt_dev_spec_numberOfLeds_title": "Numero di LEDs",
"edt_dev_spec_onBlackTimeToPowerOff": "È ora di spegnere la lampada se viene attivato il livello di nero",
"edt_dev_spec_onBlackTimeToPowerOn": "È ora di accendere la lampada se il segnale viene ripristinato",
"edt_dev_spec_orbIds_title": "Orb ID",
"edt_dev_spec_order_left_right_title": "2.",
"edt_dev_spec_order_top_down_title": "1.",
@ -498,18 +600,29 @@
"edt_dev_spec_panel_start_position": "Pannello d'inizio [0-max panels]",
"edt_dev_spec_panelorganisation_title": "Sequenza numerazione pannelli",
"edt_dev_spec_pid_title": "PID",
"edt_dev_spec_port_expl": "Porta di servizio [1-65535]",
"edt_dev_spec_port_title": "Porta",
"edt_dev_spec_printTimeStamp_title": "Aggiungi timestamp",
"edt_dev_spec_pwmChannel_title": "Canale PWM",
"edt_dev_spec_razer_device_title": "Dispositivo Razer Chroma",
"edt_dev_spec_restoreOriginalState_title": "Ripristina lo stato delle luci",
"edt_dev_spec_restoreOriginalState_title_info": "Ripristina lo stato originale del dispositivo quando il dispositivo è disabilitato",
"edt_dev_spec_rgbw_calibration_blue": "Aspetto del canale blu/bianco",
"edt_dev_spec_rgbw_calibration_enable": "Calibrazione del canale del bianco (solo RGBW)",
"edt_dev_spec_rgbw_calibration_green": "Aspetto canale verde/bianco",
"edt_dev_spec_rgbw_calibration_limit": "Limite canale bianco",
"edt_dev_spec_rgbw_calibration_red": "Aspetto del canale rosso/bianco",
"edt_dev_spec_serial_title": "Numero seriale",
"edt_dev_spec_spipath_title": "Percorso SPI",
"edt_dev_spec_sslHSTimeoutMax_title": "Timeout massimo handkshake streamer",
"edt_dev_spec_sslHSTimeoutMin_title": "Timeout minimo handkshake streamer",
"edt_dev_spec_sslReadTimeout_title": "Timeout lettura streamer",
"edt_dev_spec_stream_protocol_title": "Protocollo di streaming",
"edt_dev_spec_switchOffOnBlack_title": "Spegni o accendi il nero",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Spegni, sotto il minimo",
"edt_dev_spec_syncOverwrite_title": "Disabilita la sincronizzazione",
"edt_dev_spec_targetIpHost_expl": "Nome host (DNS/mDNS) o indirizzo IP (IPv4 o IPv6)",
"edt_dev_spec_targetIpHost_title": "IP di destinazione/nome host",
"edt_dev_spec_targetIpHost_title_info": "Il nome host o l'indirizzo IP del dispositivo",
"edt_dev_spec_targetIp_title": "IP di destinazione",
"edt_dev_spec_transeffect_title": "Effetto Transizione",
"edt_dev_spec_transistionTimeExtra_title": "Tempo extra di buio",
@ -573,9 +686,14 @@
"edt_eff_frequency": "Frequenza",
"edt_eff_gif_header": "GIF",
"edt_eff_gif_header_desc": "Questo effetto riproduce file .gif. Fornisci un semplice loop video come effetto.",
"edt_eff_grayscale": "Scala di grigi",
"edt_eff_height": "Altezza",
"edt_eff_huechange": "Cambiamento colore",
"edt_eff_image": "file immagine",
"edt_eff_image_source": "Fonte immagine",
"edt_eff_image_source_file": "File locale",
"edt_eff_image_source_url": "URL",
"edt_eff_initial_blink": "Flash per attirare l'attenzione",
"edt_eff_interval": "Intervallo",
"edt_eff_knightrider_header": "Supercar",
"edt_eff_knightrider_header_desc": "K.I.T.T. è tornato! Lo scanner frontale della nota automobile, questa volta non solo in rosso.",
@ -613,6 +731,7 @@
"edt_eff_reversedirection": "Inverti direzione",
"edt_eff_rotationtime": "Tempo di rotazione",
"edt_eff_saturation": "Saturazione",
"edt_eff_set_post_color": "Imposta il colore del post dopo l'allarme",
"edt_eff_showseconds": "Mostra secondi",
"edt_eff_sleeptime": "Tempo di riposo",
"edt_eff_smooth_custom": "Abilita sfumatura",
@ -631,6 +750,7 @@
"edt_eff_traces_header_desc": "Necessita redesign",
"edt_eff_trails_header": "Scie",
"edt_eff_trails_header_desc": "Stelle colorate che cadono dall'alto verso il basso",
"edt_eff_url": "Indirizzo dell'immagine",
"edt_eff_waves_header": "Onde",
"edt_eff_waves_header_desc": "Onde di colore! Scegli i colori, tempo di rotazione, direzione e altro.",
"edt_eff_whichleds": "Quali Leds",
@ -655,6 +775,10 @@
"edt_msg_error_disallow": "Il valore non deve essere di tipo $1",
"edt_msg_error_disallow_union": "Il valore non deve essere di uno dei tipi forniti non ammessi",
"edt_msg_error_enum": "Il valore deve essere uno tra quelli enumerati",
"edt_msg_error_hostname": "Il nome host ha il formato errato",
"edt_msg_error_invalid_epoch": "La data deve essere successiva al 1° gennaio 1970",
"edt_msg_error_ipv4": "Il valore deve essere un indirizzo IPv4 valido sotto forma di 4 numeri compresi tra 0 e 255, separati da punti",
"edt_msg_error_ipv6": "Il valore deve essere un indirizzo IPv6 valido",
"edt_msg_error_maxItems": "Il valore deve contenere al massimo $1 elementi",
"edt_msg_error_maxLength": "Il valore deve essere al massimo lungo $1 caratteri",
"edt_msg_error_maxProperties": "L'oggetto deve avere al massimo $1 proprietà",
@ -675,6 +799,8 @@
"edt_msg_error_type": "Il valore deve essere di tipo $1",
"edt_msg_error_type_union": "Il valore deve essere di uno dei tipi forniti",
"edt_msg_error_uniqueItems": "Il vettore deve contenere elementi diversi",
"edt_msgcust_error_hostname_ip": "Non è un nome host valido né IPv4 né IPv6",
"edt_msgcust_error_hostname_ip4": "Non è un nome host valido né IPv4",
"effectsconfigurator_button_conttest": "Test continuo",
"effectsconfigurator_button_deleffect": "Cancella Effetto",
"effectsconfigurator_button_editeffect": "Carica Effetto",
@ -686,7 +812,7 @@
"effectsconfigurator_label_effectname": "Nome Effetto",
"effectsconfigurator_label_intro": "Crea nuovi effetti calibrati a tuo piacimento a partire dagli effetti base. A seconda dell'effetto sono disponibili opzioni come colore, velocità, direzione e altri.",
"general_access_advanced": "Avanzate",
"general_access_default": "Default",
"general_access_default": "Predefinito",
"general_access_expert": "Esperto",
"general_btn_back": "Indietro",
"general_btn_cancel": "Annulla",
@ -699,6 +825,7 @@
"general_btn_off": "Off",
"general_btn_ok": "OK",
"general_btn_on": "On",
"general_btn_overwrite": "Sovrascrivi",
"general_btn_rename": "Rinomina",
"general_btn_restarthyperion": "Riavvia Hyperion",
"general_btn_save": "Salva",
@ -718,7 +845,7 @@
"general_comp_FORWARDER": "Forwarder",
"general_comp_GRABBER": "Cattura di Sistema",
"general_comp_LEDDEVICE": "Dispositivo LED",
"general_comp_PROTOSERVER": "Server Protocol Buffers",
"general_comp_PROTOSERVER": "Buffer del protocollo del server",
"general_comp_SMOOTHING": "Sfumatura",
"general_comp_V4L": "Cattura USB",
"general_country_cn": "Cina",
@ -730,14 +857,23 @@
"general_country_ru": "Russia",
"general_country_uk": "Regno Unito",
"general_country_us": "Stati Uniti",
"general_disabled": "disabilitato",
"general_enabled": "abilitato",
"general_speech_ca": "Catalano",
"general_speech_cs": "Czech",
"general_speech_da": "Danimarca",
"general_speech_de": "Tedesco",
"general_speech_el": "Greco",
"general_speech_en": "Inglese",
"general_speech_es": "Spagnolo",
"general_speech_fr": "Francese",
"general_speech_hu": "Ungheria",
"general_speech_it": "Italiano",
"general_speech_ja": "Giapponese",
"general_speech_nb": "Norvegese (Bokmål)",
"general_speech_nl": "Olandese",
"general_speech_pl": "Polacco",
"general_speech_pt": "Portoghese",
"general_speech_ro": "Rumeno",
"general_speech_ru": "Russo",
"general_speech_sv": "Svedese",
@ -757,6 +893,10 @@
"infoDialog_import_confirm_title": "Conferma import",
"infoDialog_import_hyperror_text": "Il file di configurazione selezionato \"$1\" non può essere importato. Non è compatibile con Hyperion 2.0 e superiori!",
"infoDialog_import_jsonerror_text": "Il file di configurazione selezionato \"$1\" non è un file .json o è corrotto. Messaggio di errore: ($2)",
"infoDialog_password_current_text": "Password attuale",
"infoDialog_password_minimum_length": "Le password devono contenere almeno 8 caratteri.",
"infoDialog_password_new_text": "Nuova Password",
"infoDialog_username_text": "Nome utente",
"infoDialog_wizrgb_text": "L'ordine dei Byte RGB è già impostato correttamente.",
"infoDialog_writeconf_error_text": "Salvataggio della configurazione fallito.",
"infoDialog_writeimage_error_text": "Il file selezionato \"$1\" non è un immagine o è corrotto! Seleziona un altro file.",
@ -776,6 +916,7 @@
"main_ledsim_btn_togglelednumber": "Numeri LED",
"main_ledsim_btn_toggleleds": "Mostra LEDs",
"main_ledsim_btn_togglelivevideo": "Video live",
"main_ledsim_btn_togglesigdetect": "Area di rilevamento del segnale",
"main_ledsim_text": "Visualizzazione live dei colori dei led e, opzionalmente, lo stream video dal dispositivo di cattura.",
"main_ledsim_title": "Visualizzazione LED",
"main_menu_about_token": "Info su Hyperion",
@ -787,6 +928,7 @@
"main_menu_general_conf_token": "Generale",
"main_menu_grabber_conf_token": "Hardware di cattura",
"main_menu_input_selection_token": "Selezione Input",
"main_menu_instcapture_conf_token": "Fonti",
"main_menu_leds_conf_token": "Hardware LED",
"main_menu_logging_token": "Log",
"main_menu_network_conf_token": "Servizi di Rete",
@ -886,6 +1028,7 @@
"wiz_cc_testintrok": "Premi il bottone qui sotto per iniziare un test video.",
"wiz_cc_testintrowok": "Guarda il link di seguito per il download dei video di test",
"wiz_cc_title": "Assistente calibrazione colore",
"wiz_cc_try_connect": "Connessione...",
"wiz_cololight_desc2": "Ora scegli quali Cololights dovrebbero essere aggiunti. Per identificare le singole luci, premere il pulsante a destra.",
"wiz_cololight_intro1": "Questa procedura guidata configura Hyperion per il sistema Cololight. Le caratteristiche sono il rilevamento automatico di Cololight e la regolazione automatica delle impostazioni di Hyperion! In breve: bastano pochi clic e il gioco è fatto!<br />Nota: in caso di una Strip Cololight, potrebbe essere necessario correggere manualmente il numero e il layout dei LED",
"wiz_cololight_noprops": "Impossibile ottenere le proprietà del dispositivo: definire manualmente il numero dei LED",
@ -921,6 +1064,7 @@
"wiz_hue_username": "ID Utente",
"wiz_identify": "Identifica",
"wiz_identify_light": "Identifica $1",
"wiz_identify_tip": "Identifica il dispositivo configurato accendendolo",
"wiz_ids_disabled": "Disattivato",
"wiz_ids_entire": "Immagine intera",
"wiz_noLights": "Nessun $1 trovato! Per favore connetti le luci alla rete o configurale manualmente",

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "Interfejs WebUI zostanie odblokowany automatycznie po rozwiązaniu problemu!",
"InfoDialog_nowrite_text": "Hyperion nie może zapisać do aktualnie załadowanego pliku konfiguracyjnego. zmień uprawnienia zapisu plików, aby kontynuować.",
"InfoDialog_nowrite_title": "Nie masz uprawnień do zapisu!",
"InfoDialog_systemRestart_title": "Restart",
"InfoDialog_systemResume_title": "Wznów",
"InfoDialog_systemSuspend_title": "Zawieś",
"about_3rd_party_licenses": "Licencje stron trzecich",
"about_3rd_party_licenses_error": "Mieliśmy problem z gromadzeniem informacji o licencjach stron trzecich z sieci. <br /> Kliknij ten link do zasobu GitHub.",
"about_build": "Build:",
@ -254,6 +257,8 @@
"edt_conf_color_blue_title": "Niebieski",
"edt_conf_color_brightnessComp_expl": "Kompensuje różnice jasności między czerwonym, zielonym, niebieskim, cyjanowym, purpurowym, żółtym i białym. 100 oznacza pełną kompensację, 0 oznacza brak kompensacji",
"edt_conf_color_brightnessComp_title": "Kompensacja jasności",
"edt_conf_color_brightnessGain_expl": "Reguluje jasność kolorów. 1.0 oznacza brak zmian, powyżej 1.0 zwiększa jasność, poniżej 1.0 zmniejsza jasność.",
"edt_conf_color_brightnessGain_title": "Wzmocnienie jasności",
"edt_conf_color_brightness_expl": "Ustaw jasność LEDów",
"edt_conf_color_brightness_title": "Jasność",
"edt_conf_color_channelAdjustment_header_expl": "Utwórz profile kolorów, które można przypisać do określonego profilu. Dostosuj kolor, gamma, jasność, kompensację i więcej",
@ -280,6 +285,8 @@
"edt_conf_color_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Skalibrowana wartość koloru czerwonego",
"edt_conf_color_red_title": "Czerwony",
"edt_conf_color_saturationGain_expl": "Reguluje nasycenie kolorów. 1.0 oznacza brak zmian, powyżej 1.0 zwiększa nasycenie, poniżej 1.0 zmniejsza nasycenie.",
"edt_conf_color_saturationGain_title": "Wzmocnienie nasycenia",
"edt_conf_color_white_expl": "Skalibrowana wartość koloru białego.",
"edt_conf_color_white_title": "Biały",
"edt_conf_color_yellow_expl": "Skalibrowana wartość koloru żółtego",
@ -393,7 +400,7 @@
"edt_conf_general_priority_title": "Kanał priorytetowy",
"edt_conf_grabber_discovered_expl": "Wybierz wykryte urządzenie do przechwytywania",
"edt_conf_grabber_discovered_none": "Nie wykryto urządzenia do przechwytywania",
"edt_conf_grabber_discovered_title": "Wykryto urządzenie",
"edt_conf_grabber_discovered_title": "Wykryte urządzenia",
"edt_conf_grabber_discovered_title_info": "Wybierz wykryte urządzenie do przechwytywania",
"edt_conf_grabber_discovery_inprogress": "Wykrywanie w toku",
"edt_conf_instC_screen_grabber_device_expl": "Używane urządzenie do przechwytywania ekranu",
@ -435,8 +442,6 @@
"edt_conf_smooth_heading_title": "Wygładzanie",
"edt_conf_smooth_interpolationRate_expl": "Szybkość obliczania wygładzanych ramek pośrednich.",
"edt_conf_smooth_interpolationRate_title": "Współczynnik interpolacji",
"edt_conf_smooth_outputRate_expl": "Prędkość wyjściowa do twojego kontrolera led.",
"edt_conf_smooth_outputRate_title": "Szybkość wyjściowa",
"edt_conf_smooth_time_ms_expl": "Jak długo wygładzanie powinno zbierać obrazy?",
"edt_conf_smooth_time_ms_title": "Czas",
"edt_conf_smooth_type_expl": "Rodzaj wygładzania.",
@ -539,9 +544,13 @@
"edt_dev_spec_FCsetConfig_title": "Ustaw konfigurację zanikania",
"edt_dev_spec_LBap102Mode_title": "Tryb \"LightBerry APA102\"",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
"edt_dev_spec_ada_mode_title": "Adalight - Standard",
"edt_dev_spec_awa_mode_title": "HyperSerial - High speed",
"edt_dev_spec_baudrate_title": "Prędkość (Baudrate)",
"edt_dev_spec_blackLightsTimeout_title": "Limit czasu wykrywania sygnału na czarno",
"edt_dev_spec_brightnessFactor_title": "Współczynnik jasności",
"edt_dev_spec_brightnessMax_title": "Maksymalna jasność",
"edt_dev_spec_brightnessMin_title": "Minimalna jasność",
"edt_dev_spec_brightnessOverwrite_title": "Nadpisz jasność",
"edt_dev_spec_brightnessThreshold_title": "Minimalna jasność wykrywania sygnału",
"edt_dev_spec_brightness_title": "Jasność",
@ -601,6 +610,11 @@
"edt_dev_spec_razer_device_title": "Urządzenie Razer Chroma",
"edt_dev_spec_restoreOriginalState_title": "Przywróć oryginalny stan świateł, gdy są wyłączone",
"edt_dev_spec_restoreOriginalState_title_info": "Przywróć pierwotny stan urządzenia, gdy jest ono wyłączone",
"edt_dev_spec_rgbw_calibration_blue": "Wygląd kanału niebieskiego/białego",
"edt_dev_spec_rgbw_calibration_enable": "Kalibracja kanału bieli (tylko RGBW)",
"edt_dev_spec_rgbw_calibration_green": "Wygląd kanału zielonego/białego",
"edt_dev_spec_rgbw_calibration_limit": "Limit kanału białego",
"edt_dev_spec_rgbw_calibration_red": "Wygląd kanału czerwonego/białego",
"edt_dev_spec_serial_title": "Numer seryjny",
"edt_dev_spec_spipath_title": "SPI path",
"edt_dev_spec_sslHSTimeoutMax_title": "Maksymalny limit czasu uzgadniania streamera",
@ -848,9 +862,11 @@
"general_country_us": "Stany Zjednoczone (United States)",
"general_disabled": "wyłączony",
"general_enabled": "włączony",
"general_speech_ca": "Kataloński",
"general_speech_cs": "Czeski (Czech)",
"general_speech_da": "Duński",
"general_speech_de": "Niemiecki (German)",
"general_speech_el": "Grecki",
"general_speech_en": "Angielski (English)",
"general_speech_es": "Hiszpański (Spanish)",
"general_speech_fr": "Francuski (French)",

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "Веб-интерфейс будет разблокирован автоматически после того, как вы решите проблему!",
"InfoDialog_nowrite_text": "Hyperion не может выполнять запись в ваш текущий загруженный файл конфигурации. Чтобы продолжить, восстановите права доступа к файлу.",
"InfoDialog_nowrite_title": "Ошибка разрешения записи!",
"InfoDialog_systemRestart_title": "Перезапустить",
"InfoDialog_systemResume_title": "Продолжить",
"InfoDialog_systemSuspend_title": "Отключить",
"about_3rd_party_licenses": "Сторонние лицензии",
"about_3rd_party_licenses_error": "У нас возникли проблемы со сбором информации о сторонних лицензиях из Интернета. <br /> Перейдите по этой ссылке на GitHub.",
"about_build": "Сборка",
@ -51,6 +54,8 @@
"conf_leds_contr_label_contrtype": "Тип контроллера:",
"conf_leds_device_info_log": "Если ваши светодиоды не работают, проверьте здесь наличие ошибок:",
"conf_leds_device_intro": "Hyperion поддерживает множество контроллеров для передачи данных на целевое устройство. Выберите светодиодный контроллер из списка и настройте его. Мы выбрали лучшие настройки по умолчанию для каждого устройства.",
"conf_leds_error_get_properties_text": "Ошибка при работе с устройством. Проверьте настройки.",
"conf_leds_error_get_properties_title": "Настройки устройства",
"conf_leds_error_hwled_gt_layout": "Количество светодиодов оборудования ($1) больше, чем количество светодиодов, настроенных с помощью макета ($2), <br> $3 {{plural:$3|Светодиод|Светодиоды}} останутся черными, если вы продолжите.",
"conf_leds_error_hwled_gt_maxled": "Количество светодиодов оборудования ($1) превышает максимальное количество светодиодов, поддерживаемое устройством ($2). <br> Счетчик аппаратных светодиодов установлен на ($3).",
"conf_leds_error_hwled_lt_layout": "Количество светодиодных индикаторов оборудования ($1) меньше, чем количество светодиодов, настроенных с помощью макета ($2). <br> Количество светодиодов, настроенных в макете, не должно превышать количество доступных светодиодов",
@ -62,6 +67,7 @@
"conf_leds_layout_blacklist_start_title": "Начальный светодиод",
"conf_leds_layout_blacklistleds_title": "Светодиоды из черного списка",
"conf_leds_layout_btn_checklist": "Показать сверку",
"conf_leds_layout_btn_keystone": "Коррекция трапеции",
"conf_leds_layout_button_savelay": "Сохранить раскладку",
"conf_leds_layout_button_updsim": "Просмотр обновления",
"conf_leds_layout_checkp1": "Черный светодиод — это ваш первый светодиод, первый светодиод — это точка, в которую вы вводите сигнал данных.",
@ -81,6 +87,16 @@
"conf_leds_layout_cl_leftbottom": "Левый 50% - 100% снизу",
"conf_leds_layout_cl_leftmiddle": "Левый 25% - 75% посередине",
"conf_leds_layout_cl_lefttop": "Слева 0% - 50% сверху",
"conf_leds_layout_cl_lightPosBottomLeft11": "Низ: 75 - 100% слева",
"conf_leds_layout_cl_lightPosBottomLeft112": "Низ: 0 - 50% слева",
"conf_leds_layout_cl_lightPosBottomLeft12": "Низ: 25 - 50% слева",
"conf_leds_layout_cl_lightPosBottomLeft121": "Низ: 50 - 100% слева",
"conf_leds_layout_cl_lightPosBottomLeft14": "Низ: 0 - 25% слева",
"conf_leds_layout_cl_lightPosBottomLeft34": "Низ: 50 - 75% слева",
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "Низ: 25 - 75% слева",
"conf_leds_layout_cl_lightPosTopLeft112": "Верх: 0 - 50% слева",
"conf_leds_layout_cl_lightPosTopLeft121": "Верх: 50 - 100% слева",
"conf_leds_layout_cl_lightPosTopLeftNewMid": "Верх: 25 - 75% слева",
"conf_leds_layout_cl_overlap": "Нахлёст",
"conf_leds_layout_cl_reversdir": "Обратное направление",
"conf_leds_layout_cl_right": "Справа",
@ -95,6 +111,7 @@
"conf_leds_layout_generatedconf": "Сгенерированная/Текущая Конфигурация LED",
"conf_leds_layout_intro": "Вам также нужна LED-раскладка, которая отражает положение ваших светодиодов. Классическая раскладка обычно представляет ТВ-рамку, но поддерживается и LED-матрица (LED-стена). Вид на этой раскладке ВСЕГДА СПЕРЕДИ вашего ТВ.",
"conf_leds_layout_ma_cabling": "Подключение",
"conf_leds_layout_ma_direction": "Направление",
"conf_leds_layout_ma_horiz": "Горизонтально",
"conf_leds_layout_ma_optbottomleft": "Низ слева",
"conf_leds_layout_ma_optbottomright": "Низ справа",
@ -181,6 +198,7 @@
"dashboard_infobox_label_instance": "Пример:",
"dashboard_infobox_label_latesthyp": "Последняя версия Hyperion:",
"dashboard_infobox_label_platform": "Платформа:",
"dashboard_infobox_label_port_boblight": "Boblight сервер:",
"dashboard_infobox_label_port_flat": "Плоский буфер:",
"dashboard_infobox_label_port_json": "JSON-сервер",
"dashboard_infobox_label_port_proto": "Протобуфер:",
@ -213,6 +231,7 @@
"edt_append_percent_v": "% верт.",
"edt_append_pixel": "Пиксель",
"edt_append_s": "сек",
"edt_append_sdegree": "с/градус",
"edt_conf_bb_blurRemoveCnt_expl": "Количество пикселей, которые удаляются с обнаруженной границы, чтобы убрать размытие.",
"edt_conf_bb_blurRemoveCnt_title": "Размытие пикселя",
"edt_conf_bb_borderFrameCnt_expl": "Количество кадров до установки согласованной обнаруженной границы.",
@ -238,6 +257,8 @@
"edt_conf_color_blue_title": "Синий",
"edt_conf_color_brightnessComp_expl": "Компенсирует разницу в яркости между красным, зеленым, синим, голубым, пурпурным, жёлтым и белым. 100 означает полную компенсацию, 0 без компенсации",
"edt_conf_color_brightnessComp_title": "Компенсация яркости",
"edt_conf_color_brightnessGain_expl": "Настройка яркости. 1.0 - без коррекции, больше 1.0 - повышает, а меньше 1.0 уменьшает яркость.",
"edt_conf_color_brightnessGain_title": "Яркость",
"edt_conf_color_brightness_expl": "установить общую яркость светодиодов",
"edt_conf_color_brightness_title": "Яркость",
"edt_conf_color_channelAdjustment_header_expl": "Создавайте цветовые профили, которые можно назначить конкретному компоненту. Отрегулируйте цвет, гамму, яркость, компенсацию и многое другое.",
@ -264,6 +285,8 @@
"edt_conf_color_magenta_title": "Пурпурный",
"edt_conf_color_red_expl": "Откалиброванное значение красного.",
"edt_conf_color_red_title": "Красный",
"edt_conf_color_saturationGain_expl": "Настройка насыщенности цветов. 1.0 - без коррекции, больше 1.0 - повышает, а меньше 1.0 уменьшает насыщенность.",
"edt_conf_color_saturationGain_title": "Насыщенность",
"edt_conf_color_white_expl": "Калиброванное значение белого.",
"edt_conf_color_white_title": "Белый",
"edt_conf_color_yellow_expl": "Откалиброванное значение жёлтого цвета.",
@ -319,6 +342,8 @@
"edt_conf_enum_top_down": "Сверху вниз",
"edt_conf_enum_transeffect_smooth": "Сглаживание",
"edt_conf_enum_transeffect_sudden": "Внезапный",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Одноцветный",
"edt_conf_fbs_heading_title": "Сервер Flatbuffers",
"edt_conf_fbs_timeout_expl": "Если данные за указанный период не поступают, компонент будет (мягко) отключён.",
@ -347,11 +372,18 @@
"edt_conf_fge_type_title": "Тип",
"edt_conf_fw_flat_expl": "Одна цель плоского буфера на строку. Содержит IP: ПОРТ (Пример: 127.0.0.1:19401)",
"edt_conf_fw_flat_itemtitle": "цель плоского буфера",
"edt_conf_fw_flat_services_discovered_expl": "Обнаруженные Hyperion сервера с flatbuffer сервисами",
"edt_conf_fw_flat_services_discovered_title": "Найденные Flatbuffer цели",
"edt_conf_fw_flat_title": "Список целей плоского буфера",
"edt_conf_fw_heading_title": "Экспедитор",
"edt_conf_fw_json_expl": "Одна json цель на строку. Содержит IP:PORT (Пример: 127.0.0.1:19446)",
"edt_conf_fw_json_itemtitle": "Цель Json",
"edt_conf_fw_json_itemtitle": "Цель JSON",
"edt_conf_fw_json_services_discovered_expl": "Обнаруженные Hyperion сервера с JSON-API сервисами",
"edt_conf_fw_json_services_discovered_title": "Найденные JSON цели",
"edt_conf_fw_json_title": "Список целей json",
"edt_conf_fw_remote_service_discovered_none": "Не найдено никаких сервисов",
"edt_conf_fw_service_name_expl": "Название провайдера",
"edt_conf_fw_service_name_title": "Название сервиса",
"edt_conf_gen_configVersion_title": "Версия конфигурации",
"edt_conf_gen_heading_title": "Общие настройки",
"edt_conf_gen_name_expl": "Пользовательское имя, которое используется для обнаружения Hyperion. (Полезно с более чем одним экземпляром Hyperion)",
@ -410,8 +442,6 @@
"edt_conf_smooth_heading_title": "Сглаживание",
"edt_conf_smooth_interpolationRate_expl": "Скорость расчета плавных промежуточных кадров.",
"edt_conf_smooth_interpolationRate_title": "Скорость интерполяции",
"edt_conf_smooth_outputRate_expl": "Скорость вывода на ваш светодиодный контроллер.",
"edt_conf_smooth_outputRate_title": "Выходная скорость",
"edt_conf_smooth_time_ms_expl": "Как долго сглаживание должно собирать картинки?",
"edt_conf_smooth_time_ms_title": "Время",
"edt_conf_smooth_type_expl": "Тип сглаживания.",
@ -497,8 +527,13 @@
"edt_dev_enum_subtract_minimum": "Уменьшить минимум",
"edt_dev_enum_white_off": "Выключить белый ",
"edt_dev_general_autostart_title": "Автозапуск",
"edt_dev_general_autostart_title_info": "Включать LED устройство при загрузке или нет",
"edt_dev_general_colorOrder_title": "Порядок байтов RGB",
"edt_dev_general_colorOrder_title_info": "Порядок цвета устройства",
"edt_dev_general_enableAttemptsInterval_title": "Задержка",
"edt_dev_general_enableAttemptsInterval_title_info": "Задержка между попытками подключения",
"edt_dev_general_enableAttempts_title": "Количество попыток",
"edt_dev_general_enableAttempts_title_info": "Количество попыток подключения к устройству до аварийного состояния.",
"edt_dev_general_hardwareLedCount_title": "Количество светодиодных индикаторов оборудования",
"edt_dev_general_hardwareLedCount_title_info": "Количество физических светодиодов, доступных для данного устройства",
"edt_dev_general_heading_title": "Общие настройки",
@ -509,12 +544,17 @@
"edt_dev_spec_FCsetConfig_title": "Установить конфигурацию fadecandy",
"edt_dev_spec_LBap102Mode_title": "Режим LightBerry APA102",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
"edt_dev_spec_ada_mode_title": "Adalight - Стандартно",
"edt_dev_spec_awa_mode_title": "HyperSerial - Высокая скорость",
"edt_dev_spec_baudrate_title": "Скорость",
"edt_dev_spec_blackLightsTimeout_title": "Тайм-аут обнаружения сигнала на черном",
"edt_dev_spec_brightnessFactor_title": "Фактор яркости",
"edt_dev_spec_brightnessMax_title": "Максимальная яркость",
"edt_dev_spec_brightnessMin_title": "Минимальная яркость",
"edt_dev_spec_brightnessOverwrite_title": "Перезаписать яркость",
"edt_dev_spec_brightnessThreshold_title": "Минимальная яркость обнаружения сигнала",
"edt_dev_spec_brightness_title": "Яркость",
"edt_dev_spec_candyGamma_title": "'Candy' режим (двойная гамма коррекция)",
"edt_dev_spec_chanperfixture_title": "Каналов на прибор",
"edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Клиентский ключ",
@ -530,6 +570,7 @@
"edt_dev_spec_dmaNumber_title": "Канал DMA",
"edt_dev_spec_gamma_title": "Гамма",
"edt_dev_spec_globalBrightnessControlMaxLevel_title": "Максимальный текущий уровень",
"edt_dev_spec_globalBrightnessControlThreshold_title": "Адаптивный контроль тока",
"edt_dev_spec_gpioBcm_title": "Вывод GPIO",
"edt_dev_spec_gpioMap_title": "Отображение GPIO",
"edt_dev_spec_gpioNumber_title": "Номер GPIO",
@ -553,6 +594,8 @@
"edt_dev_spec_networkDeviceName_title": "Сетевое имя устройства",
"edt_dev_spec_networkDevicePort_title": "Порт",
"edt_dev_spec_numberOfLeds_title": "Количество светодиодов",
"edt_dev_spec_onBlackTimeToPowerOff": "Время до отключения подсветки если сработала проверка уровня черного",
"edt_dev_spec_onBlackTimeToPowerOn": "Время до включения подсветки после восстановления сигнала",
"edt_dev_spec_orbIds_title": "ID сфер",
"edt_dev_spec_order_left_right_title": "2.",
"edt_dev_spec_order_top_down_title": "1.",
@ -560,19 +603,27 @@
"edt_dev_spec_panel_start_position": "Стартовая панель [0-макс панели]",
"edt_dev_spec_panelorganisation_title": "Последовательность нумерации панелей",
"edt_dev_spec_pid_title": "PID",
"edt_dev_spec_port_expl": "Сервисный порт [1-65535]",
"edt_dev_spec_port_title": "Порт",
"edt_dev_spec_printTimeStamp_title": "Добавить отметку времени",
"edt_dev_spec_pwmChannel_title": "Канал ШИМ (PWM)",
"edt_dev_spec_razer_device_title": "Устройство Razer Chroma",
"edt_dev_spec_restoreOriginalState_title": "Восстановить состояние огней",
"edt_dev_spec_restoreOriginalState_title_info": "Восстановить исходное состояние устройства, когда устройство отключено",
"edt_dev_spec_rgbw_calibration_blue": "Соотношение Синего/Белого каналов",
"edt_dev_spec_rgbw_calibration_enable": "Калибровка белого (только для RGBW)",
"edt_dev_spec_rgbw_calibration_green": "Соотношение Зеленого/Белого каналов",
"edt_dev_spec_rgbw_calibration_limit": "Ограничение белого",
"edt_dev_spec_rgbw_calibration_red": "Соотношение Красного/Белого каналов",
"edt_dev_spec_serial_title": "Серийный номер",
"edt_dev_spec_spipath_title": "Устройство SPI",
"edt_dev_spec_sslHSTimeoutMax_title": "Максимальное время ожидания подтверждения стримером",
"edt_dev_spec_sslHSTimeoutMin_title": "Минимальное время ожидания подтверждения стримером",
"edt_dev_spec_stream_protocol_title": "Протокол",
"edt_dev_spec_switchOffOnBlack_title": "Выключить черный",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Отключение, ниже минимального",
"edt_dev_spec_syncOverwrite_title": "Выключить синхронизацию",
"edt_dev_spec_targetIpHost_expl": "Имя хоста (DNS/mDNS) или IP адрес (IPv4 or IPv6)",
"edt_dev_spec_targetIpHost_title": "Целевое имя хоста/IP-адрес",
"edt_dev_spec_targetIpHost_title_info": "Имя хоста или IP-адрес устройства",
"edt_dev_spec_targetIp_title": "Целевой IP-адрес",
@ -811,14 +862,17 @@
"general_country_us": "Соединённые Штаты Америки",
"general_disabled": "отключено",
"general_enabled": "включено",
"general_speech_ca": "Каталонский",
"general_speech_cs": "Чешский",
"general_speech_da": "Danish",
"general_speech_da": "Датский",
"general_speech_de": "Немецкий",
"general_speech_el": "Греческий",
"general_speech_en": "Английский",
"general_speech_es": "Испанский",
"general_speech_fr": "французский",
"general_speech_hu": "Hungarian",
"general_speech_hu": "Венгерский",
"general_speech_it": "Итальянский",
"general_speech_ja": "Японский",
"general_speech_nb": "норвежский",
"general_speech_nl": "Dutch",
"general_speech_pl": "Polish",

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "Webbkonfigurationen kommer att släppas igen automatiskt så snart problemet har åtgärdats!",
"InfoDialog_nowrite_text": "Hyperion har inte skrivbehörighet till den för närvarande inlästa konfigurationen. Korrigera filbehörigheterna för att fortsätta.",
"InfoDialog_nowrite_title": "Skrivåtkomstfel!",
"InfoDialog_systemRestart_title": "Starta om",
"InfoDialog_systemResume_title": "Återuppta",
"InfoDialog_systemSuspend_title": "Stäng av",
"about_3rd_party_licenses": "Tredjepartslicenser",
"about_3rd_party_licenses_error": "Vi hade problem med att ladda tredjepartslicenserna från internet. <br />Klicka här för att komma åt filen på GitHub.",
"about_build": "Bygge",
@ -55,7 +58,9 @@
"conf_leds_error_get_properties_title": "Enhetsegenskaper",
"conf_leds_error_hwled_gt_layout": "Antalet givna hårdvarulysdioder ($1) är större än antalet definierade i LED-layouten ($2), $3 {{plural:$3|LEDs kommer|LEDs kommer att vara}} förblir svarta.",
"conf_leds_error_hwled_gt_maxled": "Antalet LED-lampor för hårdvara ($1) är större än det maximala antalet lysdioder som stöds av enheten ($2). <br> Antalet LED-lampor för hårdvara är inställt på ($3).",
"conf_leds_error_hwled_gt_maxled_protocol": "Antalet LED-lampor för hårdvara ($1) är större än det maximala antalet lysdioder som stöds av streamingprotokollet ($2). <br> Strömningsprotokollet kommer att ändras till ($3).",
"conf_leds_error_hwled_lt_layout": "Antalet givna hårdvarulysdioder ($1) är mindre än antalet definierade i LED-layouten ($2). <br> I LED-layouten får inte fler lysdioder konfigureras än vad som är tillgängligt.",
"conf_leds_error_wled_segment_missing": "Det för närvarande konfigurerade segmentet ($1) är inte konfigurerat på din WLED-enhet.<br>Du kan behöva kontrollera WLED-konfigurationen!<br>Konfigurationssidan representerar den aktuella WLED-inställningen.",
"conf_leds_info_ws281x": "Hyperion måste köras med 'root'-privilegier för denna styrenhetstyp!",
"conf_leds_layout_advanced": "Utökade alternativ",
"conf_leds_layout_blacklist_num_title": "Antal lysdioder",
@ -439,8 +444,6 @@
"edt_conf_smooth_heading_title": "Utjämning",
"edt_conf_smooth_interpolationRate_expl": "Frekvens i vilken mellanliggande utjämningssteg beräknas.",
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrekvens",
"edt_conf_smooth_outputRate_expl": "Utgångsfrekvensen till LED-enheten",
"edt_conf_smooth_outputRate_title": "Utfrekvens",
"edt_conf_smooth_time_ms_expl": "Hur länge ska utjämningen samla bilder?",
"edt_conf_smooth_time_ms_title": "Tid",
"edt_conf_smooth_type_expl": "Utjämningsalgoritm.",
@ -614,10 +617,17 @@
"edt_dev_spec_rgbw_calibration_green": "Grön/Vit kanalförhållande",
"edt_dev_spec_rgbw_calibration_limit": "Vit kanalgräns",
"edt_dev_spec_rgbw_calibration_red": "Röd/Vit kanalförhållande",
"edt_dev_spec_segmentId_title": "Segment-ID",
"edt_dev_spec_segmentsOverlapValidation_error": "Korrigera WLED-inställningen! Segmentet får inte överlappa med {{plural:$1| segment|segments}}: \"$2\".",
"edt_dev_spec_segmentsSwitchOffOthers_title": "Stäng av övriga segment",
"edt_dev_spec_segments_disabled_title": "Segmentströmning inaktiverad vid WLED.",
"edt_dev_spec_segments_title": "Streama till segment",
"edt_dev_spec_serial_title": "Serienummer",
"edt_dev_spec_spipath_title": "SPI Pfad",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handskakning maximal timeout",
"edt_dev_spec_sslHSTimeoutMin_title": "Minsta timeout för Streamerhandslag",
"edt_dev_spec_stayOnAfterStreaming_title": "Förbli på efter streaming",
"edt_dev_spec_stayOnAfterStreaming_title_info": "Enheten förblir på efter streaming eller återställning.",
"edt_dev_spec_stream_protocol_title": "Strömningsprotokoll",
"edt_dev_spec_switchOffOnBlack_title": "Av på svart",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Av vid lägsta",
@ -861,9 +871,11 @@
"general_country_us": "USA",
"general_disabled": "Inaktiverad",
"general_enabled": "Aktiverad",
"general_speech_ca": "Katalanska",
"general_speech_cs": "Tjeckiska",
"general_speech_da": "Danska",
"general_speech_de": "Tyska",
"general_speech_el": "Grekiska",
"general_speech_en": "Engelska",
"general_speech_es": "Spanska",
"general_speech_fr": "Franska",

View File

@ -24,8 +24,10 @@
<script src="js/lib/jquery/jquery-migrate-3.3.2.min.js"></script>
<!-- jQuery - Dev -->
<!script src="js/lib/jquery/dev/jquery-3.6.0.js"></script>
<!script src="js/lib/jquery/dev/jquery-migrate-3.3.2.js"></script>
<!--
<script src="js/lib/jquery/dev/jquery-3.6.0.js"></script>
<script src="js/lib/jquery/dev/jquery-migrate-3.3.2.js"></script>
-->
<!-- SemVer -->
<script src='js/lib/semver.js'></script>

View File

@ -58,7 +58,8 @@ $(document).ready(function () {
if (components[idx].name != "ALL") {
if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(components[idx].name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable))
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable) ||
(components[idx].name === "AUDIO" && !window.serverConfig.grabberAudio.enable))
continue;
var comp_enabled = components[idx].enabled ? "checked" : "";
@ -104,8 +105,9 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
if (screenGrabberAvailable || videoGrabberAvailable) {
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
if (screenGrabberAvailable) {
var screenGrabber = window.serverConfig.framegrabber.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
@ -120,6 +122,13 @@ $(document).ready(function () {
} else {
$("#dash_video_grabber_row").hide();
}
if (audioGrabberAvailable) {
const audioGrabber = window.serverConfig.grabberAudio.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
$('#dash_audio_grabber').html(audioGrabber);
} else {
$("#dash_audio_grabber_row").hide();
}
} else {
$("#dash_capture_hw").hide();
}

View File

@ -4,9 +4,11 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1);
var conf_editor_video = null;
var conf_editor_audio = null;
var conf_editor_screen = null;
var configuredDevice = "";
@ -38,6 +40,22 @@ $(document).ready(function () {
}
}
// Audio-Grabber
if (audioGrabberAvailable) {
$('#conf_cont').append(createRow('conf_cont_audio'));
$('#conf_cont_audio').append(createOptPanel('fa-volume', $.i18n("edt_conf_audio_heading_title"), 'editor_container_audiograbber', 'btn_submit_audiograbber', 'panel-system', 'audiograbberPanelId'));
if (storedAccess === 'expert') {
const conf_cont_audio_footer = document.getElementById("editor_container_audiograbber").nextElementSibling;
$(conf_cont_audio_footer).prepend('<button class="btn btn-primary mdi-24px" id="btn_audiograbber_set_effect_defaults" disabled data-toggle="tooltip" data-placement="top" title="' + $.i18n("edt_conf_audio_hardware_set_defaults_tip") + '">'
+ '<i class= "fa fa-fw fa-undo" ></i >' + $.i18n("edt_conf_audio_effect_set_defaults") + '</button > ');
}
if (window.showOptHelp) {
$('#conf_cont_audio').append(createHelpTable(window.schema.grabberAudio.properties, $.i18n("edt_conf_audio_heading_title"), "audiograbberHelpPanelId"));
}
}
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
var errors = [];
@ -694,6 +712,121 @@ $(document).ready(function () {
});
}
// External Input Sources (Audio-Grabbers)
if (audioGrabberAvailable) {
conf_editor_audio = createJsonEditor('editor_container_audiograbber', {
grabberAudio: window.schema.grabberAudio
}, true, true);
conf_editor_audio.on('ready', () => {
// Trigger conf_editor_audio.watch - 'root.grabberAudio.enable'
const audioEnable = window.serverConfig.grabberAudio.enable;
conf_editor_audio.getEditor("root.grabberAudio.enable").setValue(audioEnable);
});
conf_editor_audio.on('change', () => {
// Validate the current editor's content
if (!conf_editor_audio.validate().length) {
const deviceSelected = conf_editor_audio.getEditor("root.grabberAudio.available_devices").getValue();
switch (deviceSelected) {
case "SELECT":
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
break;
case "NONE":
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
break;
default:
window.readOnlyMode ? $('#btn_submit_audiograbber').prop('disabled', true) : $('#btn_submit_audiograbber').prop('disabled', false);
break;
}
}
else {
$('#btn_submit_audiograbber').prop('disabled', true);
}
});
// Enable
conf_editor_audio.watch('root.grabberAudio.enable', () => {
const audioEnable = conf_editor_audio.getEditor("root.grabberAudio.enable").getValue();
if (audioEnable)
{
showInputOptionsForKey(conf_editor_audio, "grabberAudio", "enable", true);
$('#btn_audiograbber_set_effect_defaults').show();
if (window.showOptHelp) {
$('#audiograbberHelpPanelId').show();
}
discoverInputSources("audio");
}
else
{
$('#btn_submit_audiograbber').prop('disabled', false);
$('#btn_audiograbber_set_effect_defaults').hide();
showInputOptionsForKey(conf_editor_audio, "grabberAudio", "enable", false);
$('#audiograbberHelpPanelId').hide();
}
});
// Available Devices
conf_editor_audio.watch('root.grabberAudio.available_devices', () => {
const deviceSelected = conf_editor_audio.getEditor("root.grabberAudio.available_devices").getValue();
if (deviceSelected === "SELECT" || deviceSelected === "NONE" || deviceSelected === "") {
$('#btn_submit_audiograbber').prop('disabled', true);
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
}
else
{
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], true);
const deviceProperties = getPropertiesOfDevice("audio", deviceSelected);
//Update hidden input element
conf_editor_audio.getEditor("root.grabberAudio.device").setValue(deviceProperties.device);
//Enfore configured JSON-editor dependencies
conf_editor_audio.notifyWatchers("root.grabberAudio.audioEffect");
//Enable set defaults button
$('#btn_audiograbber_set_effect_defaults').prop('disabled', false);
if (conf_editor_audio.validate().length && !window.readOnlyMode) {
$('#btn_submit_audiograbber').prop('disabled', false);
}
}
});
$('#btn_submit_audiograbber').off().on('click', function () {
const saveOptions = conf_editor_audio.getValue();
const instCaptOptions = window.serverConfig.instCapture;
instCaptOptions.audioEnable = true;
saveOptions.instCapture = instCaptOptions;
requestWriteConfig(saveOptions);
});
// ------------------------------------------------------------------
$('#btn_audiograbber_set_effect_defaults').off().on('click', function () {
const currentEffect = conf_editor_audio.getEditor("root.grabberAudio.audioEffect").getValue();
var effectEditor = conf_editor_audio.getEditor("root.grabberAudio." + currentEffect);
var defaultProperties = effectEditor.schema.defaultProperties;
var default_values = {};
for (const item of defaultProperties) {
default_values[item] = effectEditor.schema.properties[item].default;
}
effectEditor.setValue(default_values);
});
}
// ------------------------------------------------------------------
//////////////////////////////////////////////////
@ -706,6 +839,9 @@ $(document).ready(function () {
if (videoGrabberAvailable) {
createHint("intro", $.i18n('conf_grabber_v4l_intro'), "editor_container_videograbber");
}
if (audioGrabberAvailable) {
createHint("intro", $.i18n('conf_grabber_audio_intro'), "editor_container_audiograbber");
}
}
removeOverlay();
@ -773,6 +909,38 @@ $(document).ready(function () {
}
};
// build dynamic audio input enum
const updateAudioSourcesList = function (type, discoveryInfo) {
const enumVals = [];
const enumTitelVals = [];
let enumDefaultVal = "";
let addSelect = false;
if (jQuery.isEmptyObject(discoveryInfo)) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_conf_grabber_discovered_none'));
}
else {
for (const device of discoveryInfo) {
enumVals.push(device.device_name);
}
conf_editor_audio.getEditor('root.grabberAudio').enable();
configuredDevice = window.serverConfig.grabberAudio.available_devices;
if ($.inArray(configuredDevice, enumVals) != -1) {
enumDefaultVal = configuredDevice;
}
else {
addSelect = true;
}
}
if (enumVals.length > 0) {
updateJsonEditorSelection(conf_editor_audio, 'root.grabberAudio',
'available_devices', {}, enumVals, enumTitelVals, enumDefaultVal, addSelect, false);
}
};
async function discoverInputSources(type, params) {
const result = await requestInputSourcesDiscovery(type, params);
@ -782,7 +950,8 @@ $(document).ready(function () {
}
else {
discoveryResult = {
"video_sources": []
"video_sources": [],
"audio_soruces": []
};
}
@ -799,6 +968,12 @@ $(document).ready(function () {
updateVideoSourcesList(type, discoveredInputSources.video);
}
break;
case "audio":
discoveredInputSources.audio = discoveryResult.audio_sources;
if (audioGrabberAvailable) {
updateAudioSourcesList(type, discoveredInputSources.audio);
}
break;
}
}

View File

@ -278,8 +278,9 @@ $(document).ready(function () {
if (getStorage('lastSelectedInstance'))
removeStorage('lastSelectedInstance')
currentHyperionInstance = 0;
currentHyperionInstanceName = getInstanceNameByIndex(0);
window.currentHyperionInstance = 0;
window.currentHyperionInstanceName = getInstanceNameByIndex(0);
requestServerConfig();
setTimeout(requestServerInfo, 100)
setTimeout(requestTokenInfo, 200)
@ -293,7 +294,7 @@ $(document).ready(function () {
$('#btn_hypinstanceswitch').toggle(false)
// update listing for button
updateUiOnInstance(currentHyperionInstance);
updateUiOnInstance(window.currentHyperionInstance);
updateHyperionInstanceListing();
});

View File

@ -3,6 +3,7 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
@ -15,7 +16,7 @@ $(document).ready(function () {
// Instance Capture
if (window.showOptHelp) {
if (screenGrabberAvailable || videoGrabberAvailable) {
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
$('#conf_cont').append(createRow('conf_cont_instCapt'));
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
@ -29,7 +30,7 @@ $(document).ready(function () {
}
else {
$('#conf_cont').addClass('row');
if (screenGrabberAvailable || videoGrabberAvailable) {
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
}
if (BOBLIGHT_ENABLED) {
@ -37,7 +38,7 @@ $(document).ready(function () {
}
}
if (screenGrabberAvailable || videoGrabberAvailable) {
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
// Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
@ -81,12 +82,29 @@ $(document).ready(function () {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false);
}
if (audioGrabberAvailable) {
if (!window.serverConfig.grabberAudio.enable) {
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").setValue(false);
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").disable();
}
else {
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").setValue(window.serverConfig.instCapture.audioEnable);
}
} else {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioGrabberDevice", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioEnable", false);
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioPriority", false);
}
});
conf_editor_instCapt.on('change', function () {
if (!conf_editor_instCapt.validate().length) {
if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) {
if (!window.serverConfig.framegrabber.enable &&
!window.serverConfig.grabberV4L2.enable &&
!window.serverConfig.grabberAudio.enable) {
$('#btn_submit_instCapt').prop('disabled', true);
} else {
window.readOnlyMode ? $('#btn_submit_instCapt').prop('disabled', true) : $('#btn_submit_instCapt').prop('disabled', false);
@ -130,6 +148,23 @@ $(document).ready(function () {
}
});
conf_editor_instCapt.watch('root.instCapture.audioEnable', () => {
const audioEnable = conf_editor_instCapt.getEditor("root.instCapture.audioEnable").getValue();
if (audioEnable) {
conf_editor_instCapt.getEditor("root.instCapture.audioGrabberDevice").setValue(window.serverConfig.grabberAudio.available_devices);
conf_editor_instCapt.getEditor("root.instCapture.audioGrabberDevice").disable();
showInputOptions("instCapture", ["audioGrabberDevice"], true);
showInputOptions("instCapture", ["audioPriority"], true);
}
else {
if (!window.serverConfig.grabberAudio.enable) {
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").disable();
}
showInputOptions("instCapture", ["audioGrabberDevice"], false);
showInputOptions("instCapture", ["audioPriority"], false);
}
});
$('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue());
});

View File

@ -1002,6 +1002,21 @@ $(document).ready(function () {
addJsonEditorHostValidation();
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
var errors = [];
if (path === "root.specificOptions.segments.segmentList") {
var overlapSegNames = validateWledSegmentConfig(value);
if (overlapSegNames.length > 0) {
errors.push({
path: "root.specificOptions.segments",
message: $.i18n('edt_dev_spec_segmentsOverlapValidation_error', overlapSegNames.length, overlapSegNames.join(', '))
});
}
}
return errors;
});
$("#leddevices").off().on("change", function () {
var generalOptions = window.serverSchema.properties.device;
@ -1080,8 +1095,8 @@ $(document).ready(function () {
$('#btn_test_controller').hide();
switch (ledType) {
case "cololight":
case "wled":
case "cololight":
case "nanoleaf":
showAllDeviceInputOptions("hostList", false);
case "apa102":
@ -1107,7 +1122,22 @@ $(document).ready(function () {
if (storedAccess === 'expert') {
filter.discoverAll = true;
}
discover_device(ledType, filter);
$('#btn_submit_controller').prop('disabled', true);
discover_device(ledType, filter)
.then(discoveryResult => {
updateOutputSelectList(ledType, discoveryResult);
})
.then(discoveryResult => {
if (ledType === "wled") {
updateElementsWled(ledType);
}
})
.catch(error => {
showNotification('danger', "Device discovery for " + ledType + " failed with error:" + error);
});
hwLedCountDefault = 1;
colorOrderDefault = "rgb";
break;
@ -1211,8 +1241,8 @@ $(document).ready(function () {
}
break;
case "cololight":
case "wled":
case "cololight":
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
if (hostList !== "SELECT") {
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
@ -1339,7 +1369,9 @@ $(document).ready(function () {
break;
case "wled":
params = { host: host, filter: "info" };
//Ensure that elements are defaulted for new host
updateElementsWled(ledType, host);
params = { host: host };
getProperties_device(ledType, host, params);
break;
@ -1452,6 +1484,10 @@ $(document).ready(function () {
}
conf_editor.getEditor("root.specificOptions.rateList").setValue(rate);
break;
case "wled":
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
validateWledLedCount(hardwareLedCount);
break;
default:
}
});
@ -1547,12 +1583,54 @@ $(document).ready(function () {
}
});
//WLED
conf_editor.watch('root.specificOptions.segments.segmentList', () => {
//Update hidden streamSegmentId element
var selectedSegment = conf_editor.getEditor("root.specificOptions.segments.segmentList").getValue();
var streamSegmentId = parseInt(selectedSegment);
conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").setValue(streamSegmentId);
if (devicesProperties[ledType]) {
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
var ledDeviceProperties = devicesProperties[ledType][host];
if (ledDeviceProperties) {
var hardwareLedCount = 1;
if (streamSegmentId > -1) {
// Set hardware LED count to segment length
if (ledDeviceProperties.state) {
var segments = ledDeviceProperties.state.seg;
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
hardwareLedCount = segmentConfig.len;
}
} else {
//"Use main segment only" is disabled, i.e. stream to all LEDs
if (ledDeviceProperties.info) {
hardwareLedCount = ledDeviceProperties.info.leds.count;
}
}
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
}
}
});
//Handle Hardware Led Count constraint list
conf_editor.watch('root.generalOptions.hardwareLedCountList', () => {
var hwLedCountSelected = conf_editor.getEditor("root.generalOptions.hardwareLedCountList").getValue();
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(Number(hwLedCountSelected));
});
//Handle Hardware Led update and constraints
conf_editor.watch('root.generalOptions.hardwareLedCount', () => {
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
switch (ledType) {
case "wled":
validateWledLedCount(hardwareLedCount);
break;
default:
}
});
});
//philipshueentertainment backward fix
@ -1798,8 +1876,8 @@ function saveLedConfig(genDefLayout = false) {
location.reload();
}
// build dynamic enum
var updateSelectList = function (ledType, discoveryInfo) {
// build dynamic enum for hosts or output paths
var updateOutputSelectList = function (ledType, discoveryInfo) {
// Only update, if ledType is equal of selected controller type and discovery info exists
if (ledType !== $("#leddevices").val() || !discoveryInfo.devices) {
return;
@ -1810,7 +1888,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
var key;
var enumVals = [];
var enumTitelVals = [];
var enumTitleVals = [];
var enumDefaultVal = "";
var addSelect = false;
var addCustom = false;
@ -1835,7 +1913,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length === 0) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
}
else {
var name;
@ -1876,7 +1954,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
}
enumVals.push(host);
enumTitelVals.push(name);
enumTitleVals.push(name);
}
//Always allow to add custom configuration
@ -1904,7 +1982,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
$('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false);
}
@ -1922,7 +2000,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
} else {
enumVals.push(device.portName);
}
enumTitelVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
enumTitleVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
}
// Select configured device
@ -1951,7 +2029,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
$('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false);
}
@ -1970,7 +2048,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
case "piblaster":
for (const device of discoveryInfo.devices) {
enumVals.push(device.systemLocation);
enumTitelVals.push(device.deviceName + " (" + device.systemLocation + ")");
enumTitleVals.push(device.deviceName + " (" + device.systemLocation + ")");
}
// Select configured device
@ -1993,7 +2071,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
$('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false);
@ -2004,18 +2082,19 @@ var updateSelectList = function (ledType, discoveryInfo) {
}
if (enumVals.length > 0) {
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitelVals, enumDefaultVal, addSelect, addCustom);
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitleVals, enumDefaultVal, addSelect, addCustom);
}
};
async function discover_device(ledType, params) {
$('#btn_submit_controller').prop('disabled', true);
const result = await requestLedDeviceDiscovery(ledType, params);
var discoveryResult;
if (result && !result.error) {
var discoveryResult = {};
if (result) {
if (result.error) {
throw (result.error);
}
discoveryResult = result.info;
}
else {
@ -2024,8 +2103,7 @@ async function discover_device(ledType, params) {
ledDevicetype: ledType
}
}
updateSelectList(ledType, discoveryResult);
return discoveryResult;
}
async function getProperties_device(ledType, key, params) {
@ -2089,23 +2167,7 @@ function updateElements(ledType, key) {
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
break;
case "wled":
var ledProperties = devicesProperties[ledType][key];
if (ledProperties && ledProperties.leds) {
hardwareLedCount = ledProperties.leds.count;
if (ledProperties.maxLedCount) {
var maxLedCount = ledProperties.maxLedCount;
if (hardwareLedCount > maxLedCount) {
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
hardwareLedCount = maxLedCount;
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
//Workaround, as value seems to getting updated property when a 'getEditor("root.specificOptions").getValue()' is done during save
var editor = conf_editor.getEditor("root.specificOptions");
editor.value["streamProtocol"] = "RAW";
}
}
}
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
updateElementsWled(ledType, key);
break;
case "nanoleaf":
@ -2190,3 +2252,168 @@ function disableAutoResolvedGeneralOptions() {
conf_editor.getEditor("root.generalOptions.colorOrder").disable();
}
function validateWledSegmentConfig(streamSegmentId) {
var overlapSegNames = [];
if (streamSegmentId > -1) {
if (!jQuery.isEmptyObject(devicesProperties)) {
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
var ledProperties = devicesProperties['wled'][host];
if (ledProperties && ledProperties.state) {
var segments = ledProperties.state.seg;
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
var overlappingSegments = segments.filter((seg) => {
if (seg.id != streamSegmentId) {
if ((segmentConfig.start >= seg.stop) || (segmentConfig.start < seg.start && segmentConfig.stop <= seg.start)) {
return false;
}
return true;
}
});
if (overlappingSegments.length > 0) {
var overlapSegNames = [];
for (const segment of overlappingSegments) {
if (segment.n) {
overlapSegNames.push(segment.n);
} else {
overlapSegNames.push("Segment " + segment.id);
}
}
}
}
}
}
return overlapSegNames;
}
function validateWledLedCount(hardwareLedCount) {
if (!jQuery.isEmptyObject(devicesProperties)) {
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
var ledDeviceProperties = devicesProperties["wled"][host];
if (ledDeviceProperties) {
var streamProtocol = conf_editor.getEditor("root.specificOptions.streamProtocol").getValue();
if (streamProtocol === "RAW") {
var maxLedCount = 490;
if (ledDeviceProperties.maxLedCount) {
//WLED not DDP ready
maxLedCount = ledDeviceProperties.maxLedCount;
if (hardwareLedCount > maxLedCount) {
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
hardwareLedCount = maxLedCount;
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
}
} else {
//WLED is DDP ready
if (hardwareLedCount > maxLedCount) {
var newStreamingProtocol = "DDP";
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled_protocol', hardwareLedCount, maxLedCount, newStreamingProtocol));
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue(newStreamingProtocol);
}
}
}
}
}
}
function updateElementsWled(ledType, key) {
// Get configured device's details
var configuredDeviceType = window.serverConfig.device.type;
var configuredHost = window.serverConfig.device.host;
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
//New segment selection list values
var enumSegSelectVals = [];
var enumSegSelectTitleVals = [];
var enumSegSelectDefaultVal = "";
if (devicesProperties[ledType] && devicesProperties[ledType][key]) {
var ledDeviceProperties = devicesProperties[ledType][key];
if (!jQuery.isEmptyObject(ledDeviceProperties)) {
if (ledDeviceProperties.info) {
if (ledDeviceProperties.info.liveseg && ledDeviceProperties.info.liveseg < 0) {
// "Use main segment only" is disabled
var defaultSegmentId = "-1";
enumSegSelectVals.push(defaultSegmentId);
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
enumSegSelectDefaultVal = defaultSegmentId;
} else {
if (ledDeviceProperties.state) {
//Prepare new segment selection list
var segments = ledDeviceProperties.state.seg;
for (const segment of segments) {
enumSegSelectVals.push(segment.id.toString());
if (segment.n) {
enumSegSelectTitleVals.push(segment.n);
} else {
enumSegSelectTitleVals.push("Segment " + segment.id);
}
}
var currentSegmentId = conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").getValue().toString();
enumSegSelectDefaultVal = currentSegmentId;
}
}
// Check if currently configured segment is available at WLED
var configuredDeviceType = window.serverConfig.device.type;
var configuredHost = window.serverConfig.device.host;
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
if (configuredDeviceType == ledType && configuredHost == host) {
var configuredStreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
var segmentIdFound = enumSegSelectVals.filter(segId => segId == configuredStreamSegmentId).length;
if (!segmentIdFound) {
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_wled_segment_missing', configuredStreamSegmentId));
}
}
}
}
} else {
//If failed to get properties
var hardwareLedCount;
var segmentConfig = false;
if (configuredDeviceType == ledType && configuredHost == host) {
// Populate elements from existing configuration
if (window.serverConfig.device.segments) {
segmentConfig = true;
}
hardwareLedCount = window.serverConfig.device.hardwareLedCount;
} else {
// Populate elements with default values
hardwareLedCount = 1;
}
if (segmentConfig) {
var configuredstreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
enumSegSelectVals = [configuredstreamSegmentId];
enumSegSelectTitleVals = ["Segment " + configuredstreamSegmentId];
enumSegSelectDefaultVal = configuredstreamSegmentId;
} else {
defaultSegmentId = "-1";
enumSegSelectVals.push(defaultSegmentId);
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
enumSegSelectDefaultVal = defaultSegmentId;
}
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
}
updateJsonEditorSelection(conf_editor, 'root.specificOptions.segments',
'segmentList', {}, enumSegSelectVals, enumSegSelectTitleVals, enumSegSelectDefaultVal, false, false);
//Show additional configuration options, if more than one segment is available
var showAdditionalOptions = false;
if (enumSegSelectVals.length > 1) {
showAdditionalOptions = true;
}
showInputOptionForItem(conf_editor, "root.specificOptions.segments", "switchOffOtherSegments", showAdditionalOptions);
}

View File

@ -35,7 +35,8 @@ $(document).ready(function () {
function infoSummary() {
var info = "";
info += 'Hyperion System Summary Report (' + window.serverConfig.general.name + '), Reported instance: ' + window.currentHyperionInstanceName + '\n';
info += 'Hyperion System Summary Report (' + window.serverConfig.general.name + ')\n';
info += 'Reported instance: [' + window.currentHyperionInstance + '] - ' + window.currentHyperionInstanceName + '\n';
info += "\n< ----- System information -------------------- >\n";
info += getSystemInfo() + '\n';
@ -43,22 +44,36 @@ $(document).ready(function () {
info += "\n< ----- Configured Instances ------------------ >\n";
var instances = window.serverInfo.instance;
for (var i = 0; i < instances.length; i++) {
info += instances[i].instance + ': ' + instances[i].friendly_name + ' Running: ' + instances[i].running + '\n';
info += instances[i].instance + ': ' + instances[i].friendly_name + ', Running: ' + instances[i].running + '\n';
}
info += "\n< ----- This instance's priorities ------------ >\n";
var prios = window.serverInfo.priorities;
for (var i = 0; i < prios.length; i++) {
info += prios[i].priority + ': ';
if (prios[i].visible) {
info += ' VISIBLE!';
if (prios.length > 0) {
for (var i = 0; i < prios.length; i++) {
var prio = prios[i].priority.toString().padStart(3, '0');
info += prio + ': ';
if (prios[i].visible) {
info += ' VISIBLE -';
}
else {
info += ' INVISIBLE -';
}
info += ' (' + prios[i].componentId + ')';
if (prios[i].owner) {
info += ' (Owner: ' + prios[i].owner + ')';
}
info += '\n';
}
else {
info += ' ';
}
info += ' (' + prios[i].componentId + ') Owner: ' + prios[i].owner + '\n';
} else {
info += 'The current priority list is empty!\n';
}
info += 'priorities_autoselect: ' + window.serverInfo.priorities_autoselect + '\n';
info += 'Autoselect: ' + window.serverInfo.priorities_autoselect + '\n';
info += "\n< ----- This instance components' status ------->\n";
var comps = window.serverInfo.components;
@ -67,7 +82,7 @@ $(document).ready(function () {
}
info += "\n< ----- This instance's configuration --------- >\n";
info += JSON.stringify(window.serverConfig) + '\n';
info += JSON.stringify(window.serverConfig, null, 2) + '\n';
info += "\n< ----- Current Log --------------------------- >\n";
var logMsgs = document.getElementById("logmessages").textContent;
@ -193,18 +208,19 @@ $(document).ready(function () {
});
// toggle fullscreen button in log output
$(".fullscreen-btn").mousedown(function(e) {
$(".fullscreen-btn").mousedown(function (e) {
e.preventDefault();
});
$(".fullscreen-btn").click(function(e) {
$(".fullscreen-btn").click(function (e) {
e.preventDefault();
$(this).children('i')
.toggleClass('fa-expand')
.toggleClass('fa-compress');
$('#conf_cont').toggle();
$('#logmessages').css('max-height', $('#logmessages').css('max-height') !== 'none' ? 'none' : '400px' );
$('#logmessages').css('max-height', $('#logmessages').css('max-height') !== 'none' ? 'none' : '400px');
});
removeOverlay();
});

View File

@ -98,7 +98,7 @@ $(document).ready(function () {
}
function updateInputSelect() {
$('.sstbody').html("");
$('.sstbody').empty();
var prios = window.serverInfo.priorities;
var clearAll = false;
@ -155,6 +155,9 @@ $(document).ready(function () {
case "V4L":
owner = $.i18n('general_comp_V4L') + ': (' + owner + ')';
break;
case "AUDIO":
owner = $.i18n('general_comp_AUDIO') + ': (' + owner + ')';
break;
case "BOBLIGHTSERVER":
owner = $.i18n('general_comp_BOBLIGHTSERVER');
break;
@ -220,7 +223,8 @@ $(document).ready(function () {
for (const comp of components) {
if (comp.name === "ALL" || (comp.name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(comp.name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
(comp.name === "V4L" && !window.serverConfig.grabberV4L2.enable))
(comp.name === "V4L" && !window.serverConfig.grabberV4L2.enable) ||
(comp.name === "AUDIO" && !window.serverConfig.grabberAudio.enable))
continue;
const enable_style = (comp.enabled ? "checked" : "");

View File

@ -171,6 +171,10 @@ function initLanguageSelection() {
}
function updateUiOnInstance(inst) {
window.currentHyperionInstance = inst;
window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
$("#active_instance_friendly_name").text(getInstanceNameByIndex(inst));
if (window.serverInfo.instance.filter(entry => entry.running).length > 1) {
$('#btn_hypinstanceswitch').toggle(true);
@ -316,7 +320,7 @@ function showInfoDialog(type, header, message) {
$(document).on('click', '[data-dismiss-modal]', function () {
var target = $(this).attr('data-dismiss-modal');
$(target).modal('hide'); // lgtm [js/xss-through-dom]
$.find(target).modal('hide');
});
}
@ -407,6 +411,32 @@ function isJsonString(str) {
return "";
}
const getObjectProperty = (obj, path) => path.split(".").reduce((o, key) => o && typeof o[key] !== 'undefined' ? o[key] : undefined, obj);
const setObjectProperty = (object, path, value) => {
const parts = path.split('.');
const limit = parts.length - 1;
for (let i = 0; i < limit; ++i) {
const key = parts[i];
if (key === "__proto__" || key === "constructor") continue;
object = object[key] ?? (object[key] = {});
}
const key = parts[limit];
object[key] = value;
};
function getLongPropertiesPath(path) {
if (path) {
var path = path.replace('root.', '');
const parts = path.split('.');
parts.forEach(function (part, index) {
this[index] += ".properties";
}, parts);
path = parts.join('.') + '.';
}
return path;
}
function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
$('#' + container).off();
$('#' + container).html("");
@ -527,7 +557,8 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
//Update schema properties for validator
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
editor.removeObjectProperty(key);
delete editor.cached_editors[key];
@ -596,7 +627,8 @@ function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newE
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
//Update schema properties for validator
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
editor.removeObjectProperty(key);
delete editor.cached_editors[key];
@ -644,7 +676,8 @@ function updateJsonEditorRange(rootEditor, path, key, minimum, maximum, defaultV
editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key];
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
//Update schema properties for validator
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
editor.removeObjectProperty(key);
delete editor.cached_editors[key];
@ -1187,6 +1220,7 @@ function getSystemInfo() {
//info += '- Log lvl: ' + window.serverConfig.logger.level + '\n';
info += '- Avail Screen Cap.: ' + window.serverInfo.grabbers.screen.available + '\n';
info += '- Avail Video Cap.: ' + window.serverInfo.grabbers.video.available + '\n';
info += '- Avail Audio Cap.: ' + window.serverInfo.grabbers.audio.available + '\n';
info += '- Avail Services: ' + window.serverInfo.services + '\n';
info += '- Config path: ' + shy.rootPath + '\n';
info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n';
@ -1246,15 +1280,26 @@ function isAccessLevelCompliant(accessLevel) {
}
function showInputOptions(path, elements, state) {
if (!path.startsWith("root.")) {
path = ["root", path].join('.');
}
for (var i = 0; i < elements.length; i++) {
$('[data-schemapath="root.' + path + '.' + elements[i] + '"]').toggle(state);
$('[data-schemapath="' + path + '.' + elements[i] + '"]').toggle(state);
}
}
function showInputOptionForItem(editor, path, item, state) {
var accessLevel = editor.schema.properties[path].properties[item].access;
//Get access level for full path and item
var accessLevel = getObjectProperty(editor.schema.properties, getLongPropertiesPath(path) + item + ".access");
// Enable element only, if access level compliant
if (!state || isAccessLevelCompliant(accessLevel)) {
if (!path) {
debugger;
path = editor.path;
}
showInputOptions(path, [item], state);
}
}
@ -1269,17 +1314,26 @@ function showInputOptionsForKey(editor, item, showForKeys, state) {
if (typeof showForKeys === 'string') {
keysToshow.push(showForKeys);
} else {
return
return;
}
}
for (var key in editor.schema.properties[item].properties) {
for (let key in editor.schema.properties[item].properties) {
if ($.inArray(key, keysToshow) === -1) {
var accessLevel = editor.schema.properties[item].properties[key].access;
const accessLevel = editor.schema.properties[item].properties[key].access;
var hidden = false;
if (editor.schema.properties[item].properties[key].options) {
hidden = editor.schema.properties[item].properties[key].options.hidden;
if (typeof hidden === 'undefined') {
hidden = false;
}
}
//Always disable all elements, but only enable elements, if access level compliant
if (!state || isAccessLevelCompliant(accessLevel)) {
elements.push(key);
if (!hidden) {
elements.push(key);
}
}
}
}
@ -1314,7 +1368,7 @@ function isValidIPv6(value) {
function isValidHostname(value) {
if (value.match(
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$' //lgtm [js/redos]
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$'
))
return true;
else
@ -1323,7 +1377,7 @@ function isValidHostname(value) {
function isValidServicename(value) {
if (value.match(
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9 -]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$' //lgtm [js/redos]
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9 -]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$'
))
return true;
else

View File

@ -864,6 +864,7 @@ function useGroupId(id) {
get_hue_lights();
}
async function discover_hue_bridges() {
$('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress'));
$('#wiz_hue_discovered').html("")
@ -1021,11 +1022,11 @@ function beginWizardHue() {
$('#host').val(host);
var port = eV("port");
if (port > 0) {
$('#port').val(port);
if (port == 0) {
$('#port').val(80);
}
else {
$('#port').val('');
$('#port').val(port);
}
hueIPs.unshift({ host: host, port: port });
@ -1042,7 +1043,13 @@ function beginWizardHue() {
hueIPs = [];
hueIPsinc = 0;
hueIPs.push({ host: $('#host').val(), port: $('#port').val() });
var port = $('#port').val();
if (isNaN(port) || port < 1 || port > 65535) {
port = 80;
$('#port').val(80);
}
hueIPs.push({ host: $('#host').val(), port: port });
}
else {
discover_hue_bridges();

View File

@ -1,4 +1,4 @@
# - Find the Windows SDK aka Platform SDK
# - Find the Windows SDK aka Platform SDK (from https://github.com/rpavlik/cmake-modules)
#
# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
#
@ -49,10 +49,11 @@
# http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC
#
# Copyright Iowa State University 2012.
# Copyright 2012, Iowa State University
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
set(_preferred_sdk_dirs) # pre-output
set(_win_sdk_dirs) # pre-output
@ -76,6 +77,9 @@ endmacro()
# although version numbers listed on that page don't necessarily match the directory
# used by the installer.
set(_winsdk_win10vers
10.0.22000.0
10.0.20348.0
10.0.19041.0
10.0.18362.0 # Win10 1903 "19H1"
10.0.17763.0 # Win10 1809 "October 2018 Update"
10.0.17134.0 # Redstone 4 aka Win10 1803 "April 2018 Update"

View File

@ -155,6 +155,9 @@ if(ENABLE_FLATBUF_CONNECT)
if(ENABLE_V4L2)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_v4l2" )
endif()
if(ENABLE_AUDIO)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_audio" )
endif()
if(ENABLE_X11)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_x11" )
endif()

View File

@ -1,240 +1,237 @@
{
"general" :
{
"name" : "My Hyperion Config",
"configVersion": "configVersionValue",
"previousVersion": "previousVersionValue",
"watchedVersionBranch" : "Stable",
"showOptHelp" : true
"general": {
"name": "My Hyperion Config",
"configVersion": "configVersionValue",
"previousVersion": "previousVersionValue",
"watchedVersionBranch": "Stable",
"showOptHelp": true
},
"logger" :
{
"level" : "warn"
"logger": {
"level": "warn"
},
"device" :
{
"type" : "file",
"hardwareLedCount" : 1,
"autoStart" : true,
"output" : "/dev/null",
"colorOrder" : "rgb",
"latchTime" : 0,
"device": {
"type": "file",
"hardwareLedCount": 1,
"autoStart": true,
"output": "/dev/null",
"colorOrder": "rgb",
"latchTime": 0,
"rewriteTime": 0,
"enableAttempts": 6,
"enableAttemptsInterval": 15
},
"color" :
{
"imageToLedMappingType" : "multicolor_mean",
"channelAdjustment" :
[
"color": {
"imageToLedMappingType": "multicolor_mean",
"channelAdjustment": [
{
"id" : "default",
"leds" : "*",
"white" : [255,255,255],
"red" : [255,0,0],
"green" : [0,255,0],
"blue" : [0,0,255],
"cyan" : [0,255,255],
"magenta" : [255,0,255],
"yellow" : [255,255,0],
"gammaRed" : 2.2,
"gammaGreen" : 2.2,
"gammaBlue" : 2.2,
"backlightThreshold" : 0,
"backlightColored" : false,
"brightness" : 100,
"brightnessCompensation" : 100,
"saturationGain" : 1.0,
"brightnessGain" : 1.0,
"id": "default",
"leds": "*",
"white": [ 255, 255, 255 ],
"red": [ 255, 0, 0 ],
"green": [ 0, 255, 0 ],
"blue": [ 0, 0, 255 ],
"cyan": [ 0, 255, 255 ],
"magenta": [ 255, 0, 255 ],
"yellow": [ 255, 255, 0 ],
"gammaRed": 2.2,
"gammaGreen": 2.2,
"gammaBlue": 2.2,
"backlightThreshold": 0,
"backlightColored": false,
"brightness": 100,
"brightnessCompensation": 100,
"saturationGain": 1.0,
"brightnessGain": 1.0,
"temperature" : 6600
}
]
},
"smoothing" :
{
"enable" : true,
"type" : "linear",
"time_ms" : 200,
"updateFrequency" : 25.0000,
"interpolationRate" : 25.0000,
"decay" : 1,
"dithering" : false,
"updateDelay" : 0
"smoothing": {
"enable": true,
"type": "linear",
"time_ms": 150,
"updateFrequency": 25.0000,
"interpolationRate": 25.0000,
"decay": 1,
"dithering": false,
"updateDelay": 0
},
"grabberV4L2" :
{
"enable" : false,
"device" : "none",
"input" : 0,
"encoding" : "NO_CHANGE",
"width" : 0,
"height" : 0,
"fps" : 15,
"flip" : "NO_CHANGE",
"fpsSoftwareDecimation" : 0,
"sizeDecimation" : 8,
"cropLeft" : 0,
"cropRight" : 0,
"cropTop" : 0,
"cropBottom" : 0,
"redSignalThreshold" : 0,
"greenSignalThreshold" : 100,
"blueSignalThreshold" : 0,
"signalDetection" : false,
"noSignalCounterThreshold" : 200,
"cecDetection" : false,
"sDVOffsetMin" : 0.1,
"sDVOffsetMax" : 0.9,
"sDHOffsetMin" : 0.4,
"sDHOffsetMax" : 0.46,
"hardware_brightness" : 0,
"hardware_contrast" : 0,
"hardware_saturation" : 0,
"hardware_hue" : 0
"grabberV4L2": {
"enable": false,
"device": "none",
"input": 0,
"encoding": "NO_CHANGE",
"width": 0,
"height": 0,
"fps": 15,
"flip": "NO_CHANGE",
"fpsSoftwareDecimation": 0,
"sizeDecimation": 8,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"cropBottom": 0,
"redSignalThreshold": 0,
"greenSignalThreshold": 100,
"blueSignalThreshold": 0,
"signalDetection": false,
"noSignalCounterThreshold": 200,
"cecDetection": false,
"sDVOffsetMin": 0.1,
"sDVOffsetMax": 0.9,
"sDHOffsetMin": 0.4,
"sDHOffsetMax": 0.46,
"hardware_brightness": 0,
"hardware_contrast": 0,
"hardware_saturation": 0,
"hardware_hue": 0
},
"framegrabber" :
{
"enable" : false,
"device" : "auto",
"input" : 0,
"width" : 80,
"height" : 45,
"fps" : 10,
"pixelDecimation" : 8,
"cropLeft" : 0,
"cropRight" : 0,
"cropTop" : 0,
"cropBottom" : 0
"grabberAudio": {
"enable": false,
"device": "auto",
"audioEffect": "vuMeter",
"vuMeter": {
"flip": "NO_CHANGE",
"hotColor": [ 255, 0, 0 ],
"multiplier": 1,
"safeColor": [ 0, 255, 0 ],
"safeValue": 45,
"tolerance": 5,
"warnColor": [ 255, 255, 0 ],
"warnValue": 80
}
},
"blackborderdetector" :
{
"enable" : true,
"threshold" : 5,
"unknownFrameCnt" : 600,
"borderFrameCnt" : 50,
"maxInconsistentCnt" : 10,
"blurRemoveCnt" : 1,
"mode" : "default"
"framegrabber": {
"enable": false,
"device": "auto",
"input": 0,
"width": 80,
"height": 45,
"fps": 10,
"pixelDecimation": 8,
"cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"cropBottom": 0
},
"foregroundEffect" :
{
"enable" : true,
"type" : "effect",
"color" : [0,0,255],
"effect" : "Rainbow swirl fast",
"duration_ms" : 3000
"blackborderdetector": {
"enable": true,
"threshold": 5,
"unknownFrameCnt": 600,
"borderFrameCnt": 50,
"maxInconsistentCnt": 10,
"blurRemoveCnt": 1,
"mode": "default"
},
"backgroundEffect" :
{
"enable" : false,
"type" : "effect",
"color" : [255,138,0],
"effect" : "Warm mood blobs"
"foregroundEffect": {
"enable": true,
"type": "effect",
"color": [ 0, 0, 255 ],
"effect": "Rainbow swirl fast",
"duration_ms": 3000
},
"forwarder" :
{
"enable" : false,
"jsonapi" : [],
"flatbuffer" : []
"backgroundEffect": {
"enable": false,
"type": "effect",
"color": [ 255, 138, 0 ],
"effect": "Warm mood blobs"
},
"jsonServer" :
{
"port" : 19444
"forwarder": {
"enable": false,
"jsonapi": [],
"flatbuffer": []
},
"flatbufServer" :
{
"enable" : true,
"port" : 19400,
"timeout" : 5
"jsonServer": {
"port": 19444
},
"protoServer" :
{
"enable" : true,
"port" : 19445,
"timeout" : 5
"flatbufServer": {
"enable": true,
"port": 19400,
"timeout": 5
},
"boblightServer" :
{
"enable" : false,
"port" : 19333,
"priority" : 128
"protoServer": {
"enable": true,
"port": 19445,
"timeout": 5
},
"webConfig" :
{
"document_root" : "",
"port" : 8090,
"sslPort" : 8092,
"crtPath" : "",
"keyPath" : "",
"keyPassPhrase" : ""
"boblightServer": {
"enable": false,
"port": 19333,
"priority": 128
},
"effects" :
{
"paths" : ["$ROOT/custom-effects"],
"disable": [""]
"webConfig": {
"document_root": "",
"port": 8090,
"sslPort": 8092,
"crtPath": "",
"keyPath": "",
"keyPassPhrase": ""
},
"instCapture" :
{
"systemEnable" : false,
"systemGrabberDevice" : "NONE",
"systemPriority" : 250,
"v4lEnable" : false,
"v4lGrabberDevice" : "NONE",
"v4lPriority" : 240
"effects": {
"paths": [ "$ROOT/custom-effects" ],
"disable": [ "" ]
},
"network" :
{
"internetAccessAPI" : false,
"restirctedInternetAccessAPI" : false,
"ipWhitelist" : [],
"apiAuth" : true,
"localApiAuth" : false,
"instCapture": {
"systemEnable": false,
"systemGrabberDevice": "NONE",
"systemPriority": 250,
"v4lEnable": false,
"v4lGrabberDevice": "NONE",
"v4lPriority": 240,
"audioEnable": false,
"audioGrabberDevice": "NONE",
"audioPriority": 230
},
"network": {
"internetAccessAPI": false,
"restirctedInternetAccessAPI": false,
"ipWhitelist": [],
"apiAuth": true,
"localApiAuth": false,
"localAdminAuth": true
},
"ledConfig" :
{
"classic":
{
"top" : 1,
"bottom" : 0,
"left" : 0,
"right" : 0,
"glength" : 0,
"gpos" : 0,
"position" : 0,
"reverse" : false,
"hdepth" : 8,
"vdepth" : 5,
"overlap" : 0,
"edgegap" : 0,
"ptlh" : 0,
"ptlv" : 0,
"ptrh" : 100,
"ptrv" : 0,
"pblh" : 0,
"pblv" : 100,
"pbrh" : 100,
"pbrv" : 100
},
"ledConfig": {
"classic": {
"top": 1,
"bottom": 0,
"left": 0,
"right": 0,
"glength": 0,
"gpos": 0,
"position": 0,
"reverse": false,
"hdepth": 8,
"vdepth": 5,
"overlap": 0,
"edgegap": 0,
"ptlh": 0,
"ptlv": 0,
"ptrh": 100,
"ptrv": 0,
"pblh": 0,
"pblv": 100,
"pbrh": 100,
"pbrv": 100
},
"matrix": {
"ledshoriz": 1,
@ -245,8 +242,7 @@
}
},
"leds":
[
"leds": [
{
"hmax": 1,
"hmin": 0,

View File

@ -1,37 +0,0 @@
cmake_minimum_required(VERSION 3.2)
project(mbedtls)
set(DOWNLOAD_DIR "@MBEDTLS_DOWNLOAD_DIR@")
set(SOURCE_DIR "@MBEDTLS_SOURCE_DIR@")
set(BINARY_DIR "@MBEDTLS_BINARY_DIR@")
set(INSTALL_DIR "@MBEDTLS_INSTALL_DIR@")
set(CMAKE_ARGS "@MBEDTLS_CMAKE_ARGS@")
set(LOGGING "@MBEDTLS_LOGGING@")
include(ExternalProject)
ExternalProject_Add(
mbedtls
GIT_REPOSITORY "https://github.com/ARMmbed/mbedtls.git"
GIT_TAG "v3.1.0" # Bump versions manually if necessary, do not rely on origin/master to be stable
BUILD_ALWAYS OFF
DOWNLOAD_DIR "${DOWNLOAD_DIR}"
SOURCE_DIR "${SOURCE_DIR}"
BINARY_DIR "${BINARY_DIR}"
INSTALL_DIR "${INSTALL_DIR}"
CMAKE_ARGS ${CMAKE_ARGS}
CONFIGURE_COMMAND ""
UPDATE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ${LOGGING}
LOG_UPDATE ${LOGGING}
LOG_PATCH ${LOGGING}
LOG_CONFIGURE ${LOGGING}
LOG_BUILD ${LOGGING}
LOG_INSTALL ${LOGGING}
LOG_TEST ${LOGGING}
LOG_MERGED_STDOUTERR ${LOGGING}
LOG_OUTPUT_ON_FAILURE ${LOGGING}
)

View File

@ -150,7 +150,7 @@ if(ENABLE_PROTOBUF_SERVER)
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static")
endif()
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/cmake")
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf")
# define the include for the protobuf library
set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src")
@ -265,15 +265,19 @@ if(ENABLE_DEV_NETWORK)
set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF PARENT_SCOPE)
set(USE_SYSTEM_MBEDTLS_LIBS OFF)
endif (NOT MBEDTLS_FOUND)
endif (USE_SYSTEM_MBEDTLS_LIBS)
if (NOT USE_SYSTEM_MBEDTLS_LIBS)
else()
cmake_minimum_required(VERSION 3.2)
set(CMAKE_POLICY_DEFAULT_CMP0071 NEW)
set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF CACHE BOOL "system mbedtls libraries not found, disable use system mbedtls libraries")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared mbedtls libraries")
set(ENABLE_TESTING OFF CACHE BOOL "Disable mbedTLS tests")
set(GEN_FILES OFF CACHE BOOL "Disable mbedTLS auto-generated files")
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable mbedTLS programs")
#set(LINK_WITH_PTHREAD ON CACHE BOOL "Enable mbedTLS library linked to pthread.")
set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "Disable mbedTLS shared libraries")
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static libraries")
@ -287,69 +291,7 @@ if(ENABLE_DEV_NETWORK)
set(MBEDTLS_LOGGING 0)
endif ()
set(MBEDTLS_CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${MBEDTLS_INSTALL_DIR}
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DUSE_SHARED_MBEDTLS_LIBRARY:BOOL=OFF
-DUSE_STATIC_MBEDTLS_LIBRARY:BOOL=ON
-DENABLE_TESTING:BOOL=OFF
-DENABLE_PROGRAMS:BOOL=OFF
-DLINK_WITH_PTHREAD:BOOL=ON
-Wno-dev
#-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE
)
set(ENABLE_MBEDTLS_FETCH_CONTENT ON)
if (ENABLE_MBEDTLS_FETCH_CONTENT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.11)
include(FetchContent)
FetchContent_Declare(
mbedtls
GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git
GIT_TAG "v3.1.0" # Bump versions manually if necessary, do not rely on origin/master to be stable
BUILD_ALWAYS OFF
GIT_PROGRESS 1
DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}"
SOURCE_DIR "${MBEDTLS_SOURCE_DIR}"
BINARY_DIR "${MBEDTLS_BINARY_DIR}"
INSTALL_DIR "${MBEDTLS_INSTALL_DIR}"
CMAKE_ARGS ${MBEDTLS_CMAKE_ARGS}
BUILD_COMMAND ""
INSTALL_COMMAND ""
LOG_DOWNLOAD ${MBEDTLS_LOGGING}
LOG_UPDATE ${MBEDTLS_LOGGING}
LOG_PATCH ${MBEDTLS_LOGGING}
LOG_CONFIGURE ${MBEDTLS_LOGGING}
LOG_BUILD ${MBEDTLS_LOGGING}
LOG_INSTALL ${MBEDTLS_LOGGING}
LOG_TEST ${MBEDTLS_LOGGING}
LOG_MERGED_STDOUTERR ${MBEDTLS_LOGGING}
LOG_OUTPUT_ON_FAILURE ${MBEDTLS_LOGGING}
)
if (CMAKE_VERSION VERSION_LESS 3.14)
macro (FetchContent_MakeAvailable NAME)
FetchContent_GetProperties(${NAME})
if (NOT ${NAME}_POPULATED)
FetchContent_Populate(${NAME})
add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR})
endif ()
endmacro ()
endif ()
FetchContent_MakeAvailable(mbedtls)
else ()
set(ENABLE_MBEDTLS_FETCH_CONTENT OFF PARENT_SCOPE)
if(NOT DEFINED BUILD_MBEDTLS_ONCE)
set(BUILD_MBEDTLS_ONCE CACHE INTERNAL "Done")
configure_file(${CMAKE_SOURCE_DIR}/dependencies/CMakeLists-mbedtls.txt.in ${MBEDTLS_DOWNLOAD_DIR}/CMakeLists.txt @ONLY)
execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX:PATH=${MBEDTLS_INSTALL_DIR} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${MBEDTLS_DOWNLOAD_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${MBEDTLS_DOWNLOAD_DIR})
add_subdirectory(${MBEDTLS_SOURCE_DIR} ${MBEDTLS_BINARY_DIR})
endif()
endif ()
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/mbedtls)
set (MBEDTLS_INCLUDE_DIR "${MBEDTLS_SOURCE_DIR}/include")
set (MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR} PARENT_SCOPE)
@ -387,5 +329,5 @@ if(ENABLE_DEV_NETWORK)
mark_as_advanced (MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_SSL_LIBRARY MBEDTLS_X509_LIBRARY MBEDTLS_CRYPTO_LIBRARY)
endif (NOT USE_SYSTEM_MBEDTLS_LIBS)
endif (USE_SYSTEM_MBEDTLS_LIBS)
endif(ENABLE_DEV_NETWORK)

1
dependencies/external/mbedtls vendored Submodule

@ -0,0 +1 @@
Subproject commit 8c89224991adff88d53cd380f42a2baa36f91454

@ -1 +1 @@
Subproject commit 5ad0697c868235f24559dc07f8a42870d160af76
Subproject commit f0dc78d7e6e331b8c6bb2d5283e06aa26883ca7c

View File

@ -87,14 +87,14 @@ cd $HYPERION_HOME
```console
sudo apt-get update
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libturbojpeg0-dev libjpeg-dev libssl-dev
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev
```
**Ubuntu (22.04+) - Qt6 based**
```console
sudo apt-get update
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config
```
**For Linux X11/XCB grabber support**
@ -136,7 +136,7 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB
The following dependencies are needed to build hyperion.ng on fedora.
```console
sudo dnf -y groupinstall "Development Tools"
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel
```
After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..).

View File

@ -0,0 +1,42 @@
# Build a new Release
## Preparation
- Check, if new Ubuntu or Debian versions need to be added as build environment or remove unsupported (optional)
- Merge all outstading PRs or changes valid to be included in the release. Address any github-code-scanning findings before.
- Update missing non-English translations in Poeditor (optional)
- Export translations provided since last release from Poeditor into Hyperion-Git
- Update the `.version` file with the new release version
- Update the `CHANGELOG.md` with missing documentation and change from "Unreleased" to new release version.
- Push updated `.version` & `CHANGELOG.md` to master or create an PR (in case you might want to add some minor, late fixes)
## Execution
- Push a new tag to the master branch of hyperion-project/hyperion.ng repository, e.g. `git push origin 2.0.15`
The push will create a draft release including an update to Hyperion's apt repository
- On Hyperion's apt repository,
- Backup the main directory, in case a fall back is requried (optional)
- Move the content of the `draft-release` directory into the main diectory
- On GitHub, edit the draft release's description and publish the release
(this triggers the HyperBian build on top of the release)
- Check the HyperBian is build sucessfully with the correct release
## Rollover
Prepare next beta release and nighly builds
- Update the `.version` file with the next release version incl. beta.1, e.g. `2.0.16-beta.1`
- Add an "Unreleased" selection to `CHANGELOG.md`, plus empty sections to allow capturing changes.
- Push updated `.version` & `CHANGELOG.md` to master

View File

@ -1,54 +1,54 @@
import hyperion, time
# Get the parameters
speed = float(hyperion.args.get('speed', 1.0))
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
color = hyperion.args.get('color', (255,0,0))
# Check parameters
speed = max(0.0001, speed)
fadeFactor = max(0.0, min(fadeFactor, 1.0))
# Initialize the led data
width = 25
imageData = bytearray(width * (0,0,0))
imageData[0] = color[0]
imageData[1] = color[1]
imageData[2] = color[2]
# Calculate the sleep time and rotation increment
increment = 1
sleepTime = 1.0 / (speed * width)
while sleepTime < 0.05:
increment *= 2
sleepTime *= 2
# Start the write data loop
position = 0
direction = 1
while not hyperion.abort():
hyperion.setImage(width, 1, imageData)
# Move data into next state
for i in range(increment):
position += direction
if position == -1:
position = 1
direction = 1
elif position == width:
position = width-2
direction = -1
# Fade the old data
for j in range(width):
imageData[3*j] = int(fadeFactor * imageData[3*j])
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
# Insert new data
imageData[3*position] = color[0]
imageData[3*position+1] = color[1]
imageData[3*position+2] = color[2]
# Sleep for a while
time.sleep(sleepTime)
import hyperion, time
# Get the parameters
speed = float(hyperion.args.get('speed', 1.0))
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
color = hyperion.args.get('color', (255,0,0))
# Check parameters
speed = max(0.0001, speed)
fadeFactor = max(0.0, min(fadeFactor, 1.0))
# Initialize the led data
width = 25
imageData = bytearray(width * (0,0,0))
imageData[0] = color[0]
imageData[1] = color[1]
imageData[2] = color[2]
# Calculate the sleep time and rotation increment
increment = 1
sleepTime = 1.0 / (speed * width)
while sleepTime < 0.05:
increment *= 2
sleepTime *= 2
# Start the write data loop
position = 0
direction = 1
while not hyperion.abort():
hyperion.setImage(width, 1, imageData)
# Move data into next state
for unused in range(increment):
position += direction
if position == -1:
position = 1
direction = 1
elif position == width:
position = width-2
direction = -1
# Fade the old data
for j in range(width):
imageData[3*j] = int(fadeFactor * imageData[3*j])
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
# Insert new data
imageData[3*position] = color[0]
imageData[3*position+1] = color[1]
imageData[3*position+2] = color[2]
# Sleep for a while
time.sleep(sleepTime)

View File

@ -1,23 +1,23 @@
import hyperion, time, colorsys
# Get the parameters
rotationTime = float(hyperion.args.get('rotation-time', 30.0))
brightness = float(hyperion.args.get('brightness', 100))/100.0
saturation = float(hyperion.args.get('saturation', 100))/100.0
reverse = bool(hyperion.args.get('reverse', False))
# Calculate the sleep time and hue increment
sleepTime = 0.1
hueIncrement = sleepTime / rotationTime
# Switch direction if needed
if reverse:
increment = -increment
# Start the write data loop
hue = 0.0
while not hyperion.abort():
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
hyperion.setColor(int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))
hue = (hue + hueIncrement) % 1.0
time.sleep(sleepTime)
import hyperion, time, colorsys
# Get the parameters
rotationTime = float(hyperion.args.get('rotation-time', 30.0))
brightness = float(hyperion.args.get('brightness', 100))/100.0
saturation = float(hyperion.args.get('saturation', 100))/100.0
reverse = bool(hyperion.args.get('reverse', False))
# Calculate the sleep time and hue increment
sleepTime = 0.1
hueIncrement = sleepTime / rotationTime
# Switch direction if needed
if reverse:
hueIncrement = -hueIncrement
# Start the write data loop
hue = 0.0
while not hyperion.abort():
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
hyperion.setColor(int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))
hue = (hue + hueIncrement) % 1.0
time.sleep(sleepTime)

View File

@ -1,43 +1,43 @@
import hyperion, time
# get options from args
sleepTime = float(hyperion.args.get('speed', 1.5)) * 0.005
whiteLevel = int(hyperion.args.get('whiteLevel', 0))
lvl = int(hyperion.args.get('colorLevel', 220))
# check value
whiteLevel = min( whiteLevel, 254 )
lvl = min( lvl, 255 )
if whiteLevel >= lvl:
lvl = 255
# Initialize the led data
ledData = bytearray()
for i in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
runners = [
{ "pos":0, "step": 4, "lvl":lvl},
{ "pos":1, "step": 5, "lvl":lvl},
{ "pos":2, "step": 6, "lvl":lvl},
{ "pos":0, "step": 7, "lvl":lvl},
{ "pos":1, "step": 8, "lvl":lvl},
{ "pos":2, "step": 9, "lvl":lvl},
#{ "pos":0, "step":10, "lvl":lvl},
#{ "pos":1, "step":11, "lvl":lvl},
#{ "pos":2, "step":12, "lvl":lvl},
]
# Start the write data loop
counter = 0
while not hyperion.abort():
counter += 1
for r in runners:
if counter % r["step"] == 0:
ledData[r["pos"]] = whiteLevel
r["pos"] = (r["pos"]+3) % (hyperion.ledCount*3)
ledData[r["pos"]] = r["lvl"]
hyperion.setColor(ledData)
time.sleep(sleepTime)
import hyperion, time
# get options from args
sleepTime = float(hyperion.args.get('speed', 1.5)) * 0.005
whiteLevel = int(hyperion.args.get('whiteLevel', 0))
lvl = int(hyperion.args.get('colorLevel', 220))
# check value
whiteLevel = min( whiteLevel, 254 )
lvl = min( lvl, 255 )
if whiteLevel >= lvl:
lvl = 255
# Initialize the led data
ledData = bytearray()
for unused in range(hyperion.ledCount):
ledData += bytearray((0,0,0))
runners = [
{ "pos":0, "step": 4, "lvl":lvl},
{ "pos":1, "step": 5, "lvl":lvl},
{ "pos":2, "step": 6, "lvl":lvl},
{ "pos":0, "step": 7, "lvl":lvl},
{ "pos":1, "step": 8, "lvl":lvl},
{ "pos":2, "step": 9, "lvl":lvl},
#{ "pos":0, "step":10, "lvl":lvl},
#{ "pos":1, "step":11, "lvl":lvl},
#{ "pos":2, "step":12, "lvl":lvl},
]
# Start the write data loop
counter = 0
while not hyperion.abort():
counter += 1
for r in runners:
if counter % r["step"] == 0:
ledData[r["pos"]] = whiteLevel
r["pos"] = (r["pos"]+3) % (hyperion.ledCount*3)
ledData[r["pos"]] = r["lvl"]
hyperion.setColor(ledData)
time.sleep(sleepTime)

View File

@ -42,7 +42,7 @@ def buildGradient(cc, closeCircle = True):
pos = 0
if len(cc[0]) == 4:
withAlpha = True
for c in cc:
if withAlpha:
alpha = int(c[3]*255)
@ -50,7 +50,7 @@ def buildGradient(cc, closeCircle = True):
alpha = 255
pos += posfac
ba += bytearray([pos,c[0],c[1],c[2],alpha])
if closeCircle:
# last color as first color
lC = cc[-1]
@ -61,6 +61,7 @@ def buildGradient(cc, closeCircle = True):
ba += bytearray([0,lC[0],lC[1],lC[2],alpha])
return ba
return bytearray()
def rotateAngle( increment = 1):
global angle

View File

@ -1,98 +1,98 @@
import hyperion
import time
import colorsys
import random
min_len = int(hyperion.args.get('min_len', 3))
max_len = int(hyperion.args.get('max_len', 3))
#iHeight = int(hyperion.args.get('iHeight', 8))
trails = int(hyperion.args.get('int', 8))
sleepTime = float(hyperion.args.get('speed', 1)) / 1000.0
color = list(hyperion.args.get('color', (255,255,255)))
randomise = bool(hyperion.args.get('random', False))
iWidth = hyperion.imageWidth()
iHeight = hyperion.imageHeight()
class trail:
def __init__(self):
return
def start(self, x, y, step, color, _len, _h):
self.pos = 0.0
self.step = step
self.h = _h
self.x = x
self.data = []
brigtness = color[2]
step_brigtness = color[2] / _len
for i in range(0, _len):
rgb = colorsys.hsv_to_rgb(color[0], color[1], brigtness)
self.data.insert(0, (int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
brigtness -= step_brigtness
self.data.extend([(0,0,0)]*(_h-y))
if len(self.data) < _h:
for i in range (_h-len(self.data)):
self.data.insert(0, (0,0,0))
def getdata(self):
self.pos += self.step
if self.pos > 1.0:
self.pos = 0.0
self.data.pop()
self.data.insert(0, (0,0,0))
return self.x, self.data[-self.h:], all(x == self.data[0] for x in self.data)
tr = []
for i in range(trails):
r = {'exec': trail()}
if randomise:
col = (random.uniform(0.0, 1.0),1,1)
else:
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
r['exec'].start(
random.randint(0, iWidth),
random.randint(0, iHeight),
random.uniform(0.2, 0.8),
col,
random.randint(min_len, max_len),
iHeight
)
tr.append(r)
# Start the write data loop
while not hyperion.abort():
ledData = bytearray()
for r in tr:
r['x'], r['data'], c = r['exec'].getdata()
if c:
if randomise:
col = (random.uniform(0.0, 1.0),1,1)
else:
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
r['exec'].start(
random.randint(0, iWidth),
random.randint(0, iHeight),
random.uniform(0.2, 0.8),
col,
random.randint(min_len, max_len),
iHeight
)
for y in range(0, iHeight):
for x in range(0, iWidth):
for r in tr:
if x == r['x']:
led = bytearray(r['data'][y])
break
led = bytearray((0,0,0))
ledData += led
hyperion.setImage(iWidth,iHeight,ledData)
time.sleep(sleepTime)
import hyperion
import time
import colorsys
import random
min_len = int(hyperion.args.get('min_len', 3))
max_len = int(hyperion.args.get('max_len', 3))
#iHeight = int(hyperion.args.get('iHeight', 8))
trails = int(hyperion.args.get('int', 8))
sleepTime = float(hyperion.args.get('speed', 1)) / 1000.0
color = list(hyperion.args.get('color', (255,255,255)))
randomise = bool(hyperion.args.get('random', False))
iWidth = hyperion.imageWidth()
iHeight = hyperion.imageHeight()
class trail:
def __init__(self):
return
def start(self, x, y, step, color, _len, _h):
self.pos = 0.0
self.step = step
self.h = _h
self.x = x
self.data = []
brigtness = color[2]
step_brigtness = color[2] / _len
for i in range(0, _len):
rgb = colorsys.hsv_to_rgb(color[0], color[1], brigtness)
self.data.insert(0, (int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
brigtness -= step_brigtness
self.data.extend([(0,0,0)]*(_h-y))
if len(self.data) < _h:
for i in range (_h-len(self.data)):
self.data.insert(0, (0,0,0))
def getdata(self):
self.pos += self.step
if self.pos > 1.0:
self.pos = 0.0
self.data.pop()
self.data.insert(0, (0,0,0))
return self.x, self.data[-self.h:], all(x == self.data[0] for x in self.data)
tr = []
for unused in range(trails):
r = {'exec': trail()}
if randomise:
col = (random.uniform(0.0, 1.0),1,1)
else:
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
r['exec'].start(
random.randint(0, iWidth),
random.randint(0, iHeight),
random.uniform(0.2, 0.8),
col,
random.randint(min_len, max_len),
iHeight
)
tr.append(r)
# Start the write data loop
while not hyperion.abort():
ledData = bytearray()
for r in tr:
r['x'], r['data'], c = r['exec'].getdata()
if c:
if randomise:
col = (random.uniform(0.0, 1.0),1,1)
else:
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
r['exec'].start(
random.randint(0, iWidth),
random.randint(0, iHeight),
random.uniform(0.2, 0.8),
col,
random.randint(min_len, max_len),
iHeight
)
for y in range(0, iHeight):
for x in range(0, iWidth):
for r in tr:
if x == r['x']:
led = bytearray(r['data'][y])
break
led = bytearray((0,0,0))
ledData += led
hyperion.setImage(iWidth,iHeight,ledData)
time.sleep(sleepTime)

View File

@ -1,77 +1,71 @@
import hyperion, time, math, random
randomCenter = bool(hyperion.args.get('random-center', False))
centerX = float(hyperion.args.get('center_x', -0.15))
centerY = float(hyperion.args.get('center_y', -0.25))
rotationTime = float(hyperion.args.get('rotation_time', 90))
colors = hyperion.args.get('colors', ((255,0,0),(255,255,0),(0,255,0),(0,255,255),(0,0,255),(255,0,255)))
reverse = bool(hyperion.args.get('reverse', False))
reverseTime = int(hyperion.args.get('reverse_time', 0))
#rotate = bool(hyperion.args.get('rotate', True))
positions = []
# calc center if random
if randomCenter:
centerX = random.uniform(0.0, 1.0)
centerY = random.uniform(0.0, 1.0)
rCenterX = int(round(float(hyperion.imageWidth())*centerX))
rCenterY = int(round(float(hyperion.imageHeight())*centerY))
#calc interval
sleepTime = max(1/(255/rotationTime), 0.016)
#calc diagonal
if centerX < 0.5:
cX = 1.0-centerX
else:
cX = 0.0+centerX
if centerY < 0.5:
cY = 1.0-centerY
else:
cY = 0.0+centerY
diag = int(round(math.sqrt(((cX*hyperion.imageWidth())**2)+((cY*hyperion.imageHeight())**2))))
# some diagonal overhead
diag = int(diag*1.3)
# calc positions
pos = 0
step = int(255/len(colors))
for _ in colors:
positions.append(pos)
pos += step
# target time
targetTime = time.time()+float(reverseTime)
#hyperion.imageCOffset(int(hyperion.imageWidth()/2), int(hyperion.imageHeight()/2))
while not hyperion.abort():
# verify reverseTime, randomize reverseTime based on reverseTime up to reversedTime*2
if reverseTime >= 1:
now = time.time()
if now > targetTime:
reverse = not reverse
targetTime = time.time()+random.uniform(float(reverseTime), float(reverseTime*2.0))
# apply rotate
#if rotate:
# hyperion.imageCRotate(1)
# prepare bytearray with colors and positions
gradientBa = bytearray()
it = 0
for color in colors:
gradientBa += bytearray((positions[it],color[0],color[1],color[2]))
it += 1
hyperion.imageRadialGradient(rCenterX,rCenterY, diag, gradientBa,0)
# increment positions
for i, pos in enumerate(positions):
if reverse:
positions[i] = pos - 1 if pos >= 1 else 255
else:
positions[i] = pos + 1 if pos <= 254 else 0
hyperion.imageShow()
time.sleep(sleepTime)
import hyperion, time, math, random
randomCenter = bool(hyperion.args.get('random-center', False))
centerX = float(hyperion.args.get('center_x', -0.15))
centerY = float(hyperion.args.get('center_y', -0.25))
rotationTime = float(hyperion.args.get('rotation_time', 90))
colors = hyperion.args.get('colors', ((255,0,0),(255,255,0),(0,255,0),(0,255,255),(0,0,255),(255,0,255)))
reverse = bool(hyperion.args.get('reverse', False))
reverseTime = int(hyperion.args.get('reverse_time', 0))
positions = []
# calc center if random
if randomCenter:
centerX = random.uniform(0.0, 1.0)
centerY = random.uniform(0.0, 1.0)
rCenterX = int(round(float(hyperion.imageWidth())*centerX))
rCenterY = int(round(float(hyperion.imageHeight())*centerY))
#calc interval
sleepTime = max(1/(255/rotationTime), 0.016)
#calc diagonal
if centerX < 0.5:
cX = 1.0-centerX
else:
cX = 0.0+centerX
if centerY < 0.5:
cY = 1.0-centerY
else:
cY = 0.0+centerY
diag = int(round(math.hypot(cX*hyperion.imageWidth(),cY*hyperion.imageHeight())))
# some diagonal overhead
diag = int(diag*1.3)
# calc positions
pos = 0
step = int(255/len(colors))
for _ in colors:
positions.append(pos)
pos += step
# target time
targetTime = time.time()+float(reverseTime)
while not hyperion.abort():
# verify reverseTime, randomize reverseTime based on reverseTime up to reversedTime*2
if reverseTime >= 1:
now = time.time()
if now > targetTime:
reverse = not reverse
targetTime = time.time()+random.uniform(float(reverseTime), float(reverseTime*2.0))
# prepare bytearray with colors and positions
gradientBa = bytearray()
it = 0
for color in colors:
gradientBa += bytearray((positions[it],color[0],color[1],color[2]))
it += 1
hyperion.imageRadialGradient(rCenterX,rCenterY, diag, gradientBa,0)
# increment positions
for i, pos in enumerate(positions):
if reverse:
positions[i] = pos - 1 if pos >= 1 else 255
else:
positions[i] = pos + 1 if pos <= 254 else 0
hyperion.imageShow()
time.sleep(sleepTime)

View File

@ -1,30 +1,30 @@
import hyperion, time
# Get the parameters
sleepTime = float(hyperion.args.get('sleepTime', 1000))/1000.0
length = hyperion.args.get('length', 1)
color1 = hyperion.args.get('color1', (255,255,255))
color2 = hyperion.args.get('color2', (255,0,0))
# Initialize the led data
i = 0
ledDataOdd = bytearray()
while i < hyperion.ledCount:
for l in range(length):
if i<hyperion.ledCount:
ledDataOdd += bytearray((int(color1[0]), int(color1[1]), int(color1[2])))
i += 1
for l in range(length):
if i<hyperion.ledCount:
ledDataOdd += bytearray((int(color2[0]), int(color2[1]), int(color2[2])))
i += 1
ledDataEven = ledDataOdd[3*length:] + ledDataOdd[0:3*length]
# Start the write data loop
while not hyperion.abort():
hyperion.setColor(ledDataOdd)
time.sleep(sleepTime)
hyperion.setColor(ledDataEven)
time.sleep(sleepTime)
import hyperion, time
# Get the parameters
sleepTime = float(hyperion.args.get('sleepTime', 1000))/1000.0
length = hyperion.args.get('length', 1)
color1 = hyperion.args.get('color1', (255,255,255))
color2 = hyperion.args.get('color2', (255,0,0))
# Initialize the led data
i = 0
ledDataOdd = bytearray()
while i < hyperion.ledCount:
for unused in range(length):
if i<hyperion.ledCount:
ledDataOdd += bytearray((int(color1[0]), int(color1[1]), int(color1[2])))
i += 1
for unused in range(length):
if i<hyperion.ledCount:
ledDataOdd += bytearray((int(color2[0]), int(color2[1]), int(color2[2])))
i += 1
ledDataEven = ledDataOdd[3*length:] + ledDataOdd[0:3*length]
# Start the write data loop
while not hyperion.abort():
hyperion.setColor(ledDataOdd)
time.sleep(sleepTime)
hyperion.setColor(ledDataEven)
time.sleep(sleepTime)

View File

@ -157,12 +157,7 @@ protected:
///
bool setHyperionInstance(quint8 inst);
///
/// @brief Get all contrable components and their state
///
std::map<hyperion::Components, bool> getAllComponents();
///
///
/// @brief Check if Hyperion ist enabled
/// @return True when enabled else false
///

View File

@ -1,4 +1,3 @@
//#include <iostream>
#pragma once
// Utils includes
@ -219,7 +218,6 @@ namespace hyperion
|| !isBlack(image((width - x), y))
|| !isBlack(image((width - x), (height - y))))
{
// std::cout << "y " << y << " lt " << int(isBlack(color1)) << " lb " << int(isBlack(color2)) << " rt " << int(isBlack(color3)) << " rb " << int(isBlack(color4)) << std::endl;
firstNonBlackYPixelIndex = y;
break;
}

View File

@ -111,7 +111,7 @@ public:
// server port services
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
// capture
<< "framegrabber" << "grabberV4L2"
<< "framegrabber" << "grabberV4L2" << "grabberAudio"
// other
<< "logger" << "general";

View File

@ -0,0 +1,196 @@
#ifndef AUDIOGRABBER_H
#define AUDIOGRABBER_H
#include <QObject>
#include <QColor>
#include <cmath>
// Hyperion-utils includes
#include <utils/ColorRgb.h>
#include <hyperion/Grabber.h>
#include <utils/Logger.h>
///
/// Base Audio Grabber Class
///
/// This class is extended by the windows audio grabber to provied DirectX9 access to the audio devices
/// This class is extended by the linux audio grabber to provide ALSA access to the audio devices
///
/// @brief The DirectX9 capture implementation
///
class AudioGrabber : public Grabber
{
Q_OBJECT
public:
///
/// Device properties
///
/// this structure holds the name, id, and inputs of the enumerated audio devices.
///
struct DeviceProperties
{
QString name = QString();
QString id = QString();
QMultiMap<QString, int> inputs = QMultiMap<QString, int>();
};
AudioGrabber();
~AudioGrabber() override;
///
/// Start audio capturing session
///
/// @returns true if successful
virtual bool start();
///
/// Stop audio capturing session
///
virtual void stop();
///
/// Restart the audio capturing session
///
void restart();
Logger* getLog();
///
/// Set Device
///
/// configures the audio device used by the grabber
///
/// @param[in] device identifier of audio device
void setDevice(const QString& device);
///
/// Set Configuration
///
/// sets the audio grabber's configuration parameters
///
/// @param[in] config object of configuration parameters
void setConfiguration(const QJsonObject& config);
///
/// Reset Multiplier
///
/// resets the calcualted audio multiplier so that it is recalculated
/// currently the multiplier is only reduced based on loudness.
///
/// TODO: also calculate a low signal and reset the multiplier
///
void resetMultiplier();
///
/// Discover
///
/// discovers audio devices in the system
///
/// @param[in] params discover parameters
/// @return array of audio devices
virtual QJsonArray discover(const QJsonObject& params);
signals:
void newFrame(const Image<ColorRgb>& image);
protected:
///
/// Process Audio Frame
///
/// this functions takes in an audio buffer and emits a visual representation of the audio data
///
/// @param[in] buffer The audio buffer to process
/// @param[in] length The length of audio data in the buffer
void processAudioFrame(int16_t* buffer, int length);
///
/// Audio device id / properties map
///
/// properties include information such as name, inputs, and etc...
///
QMap<QString, AudioGrabber::DeviceProperties> _deviceProperties;
///
/// Current device
///
QString _device;
///
/// Hot Color
///
/// the color of the leds when the signal is high or hot
///
QColor _hotColor;
///
/// Warn value
///
/// The maximum value of the warning color. above this threshold the signal is considered hot
///
int _warnValue;
///
/// Warn color
///
/// the color of the leds when the signal is in between the safe and warn value threshold
///
QColor _warnColor;
///
/// Save value
///
/// The maximum value of the safe color. above this threshold the signal enteres the warn zone.
/// below the signal is in the safe zone.
///
int _safeValue;
///
/// Safe color
///
/// the color of the leds when the signal is below the safe threshold
///
QColor _safeColor;
///
/// Multiplier
///
/// this value is used to multiply the input signal value. Some inputs may have a very low signal
/// and the multiplier is used to get the desired visualization.
///
/// When the multiplier is configured to 0, the multiplier is automatically configured based off of the average
/// signal amplitude and tolernace.
///
double _multiplier;
///
/// Tolerance
///
/// The tolerance is used to calculate what percentage of the top end part of the signal to ignore when
/// calculating the multiplier. This enables the effect to reach the hot zone with an auto configured multiplier
///
int _tolerance;
///
/// Dynamic Multiplier
///
/// This is the current value of the automatically configured multiplier.
///
double _dynamicMultiplier;
///
/// Started
///
/// true if the capturing session has started.
///
bool _started;
private:
///
/// @brief free the _screen pointer
///
void freeResources();
};
#endif // AUDIOGRABBER_H

View File

@ -0,0 +1,91 @@
#ifndef AUDIOGRABBERLINUX_H
#define AUDIOGRABBERLINUX_H
#include <pthread.h>
#include <sched.h>
#include <alsa/asoundlib.h>
// Hyperion-utils includes
#include <grabber/AudioGrabber.h>
///
/// @brief The Linux Audio capture implementation
///
class AudioGrabberLinux : public AudioGrabber
{
public:
AudioGrabberLinux();
~AudioGrabberLinux() override;
///
/// Process audio buffer
///
void processAudioBuffer(snd_pcm_sframes_t frames);
///
/// Is Running Flag
///
std::atomic<bool> _isRunning;
///
/// Current capture device
///
snd_pcm_t * _captureDevice;
public slots:
///
/// Start audio capturing session
///
/// @returns true if successful
bool start() override;
///
/// Stop audio capturing session
///
void stop() override;
///
/// Discovery audio devices
///
QJsonArray discover(const QJsonObject& params) override;
private:
///
/// Refresh audio devices
///
void refreshDevices();
///
/// Configure current audio capture interface
///
bool configureCaptureInterface();
///
/// Get device name from path
///
QString getDeviceName(const QString& devicePath) const;
///
/// Current sample rate
///
unsigned int _sampleRate;
///
/// Audio capture thread
///
pthread_t _audioThread;
///
/// ALSA device configuration parameters
///
snd_pcm_hw_params_t * _captureDeviceConfig;
};
///
/// Audio processing thread function
///
static void* AudioThreadRunner(void* params);
#endif // AUDIOGRABBERLINUX_H

View File

@ -0,0 +1,81 @@
#ifndef AUDIOGRABBERWINDOWS_H
#define AUDIOGRABBERWINDOWS_H
// Hyperion-utils includes
#include <grabber/AudioGrabber.h>
#include <DSound.h>
///
/// @brief The Windows Audio capture implementation
///
class AudioGrabberWindows : public AudioGrabber
{
public:
AudioGrabberWindows();
~AudioGrabberWindows() override;
public slots:
bool start() override;
void stop() override;
QJsonArray discover(const QJsonObject& params) override;
private:
void refreshDevices();
bool configureCaptureInterface();
QString getDeviceName(const QString& devicePath) const;
void processAudioBuffer();
LPDIRECTSOUNDCAPTURE8 recordingDevice;
LPDIRECTSOUNDCAPTUREBUFFER8 recordingBuffer;
HANDLE audioThread;
DWORD bufferCapturePosition;
DWORD bufferCaptureSize;
DWORD notificationSize;
static DWORD WINAPI AudioThreadRunner(LPVOID param);
HANDLE notificationEvent;
std::atomic<bool> isRunning{ false };
static BOOL CALLBACK DirectSoundEnumProcessor(LPGUID deviceIdGuid, LPCTSTR deviceDescStr,
LPCTSTR deviceModelStr, LPVOID context)
{
// Skip undefined audio devices
if (deviceIdGuid == NULL)
return TRUE;
QMap<QString, AudioGrabber::DeviceProperties>* devices = (QMap<QString, AudioGrabber::DeviceProperties>*)context;
AudioGrabber::DeviceProperties device;
// Process Device ID
LPOLESTR deviceIdStr;
HRESULT res = StringFromCLSID(*deviceIdGuid, &deviceIdStr);
if (FAILED(res))
{
Error(Logger::getInstance("AUDIOGRABBER"), "Failed to get CLSID-string for %s with error: 0x%08x: %s", deviceDescStr, res, std::system_category().message(res).c_str());
return FALSE;
}
QString deviceId = QString::fromWCharArray(deviceIdStr);
CoTaskMemFree(deviceIdStr);
// Process Device Information
QString deviceName = QString::fromLocal8Bit(deviceDescStr);
Debug(Logger::getInstance("AUDIOGRABBER"), "Found Audio Device: %s", deviceDescStr);
device.id = deviceId;
device.name = deviceName;
devices->insert(deviceId, device);
return TRUE;
}
};
#endif // AUDIOGRABBERWINDOWS_H

View File

@ -0,0 +1,69 @@
#pragma once
#include <hyperion/GrabberWrapper.h>
#ifdef WIN32
#include <grabber/AudioGrabberWindows.h>
#endif
#ifdef __linux__
#include <grabber/AudioGrabberLinux.h>
#endif
///
/// Audio Grabber wrapper
///
class AudioWrapper : public GrabberWrapper
{
public:
// The AudioWrapper has no params...
///
/// Constructs the Audio grabber with a specified grab size and update rate.
///
/// @param[in] device Audio Device Identifier
/// @param[in] updateRate_Hz The audio grab rate [Hz]
///
AudioWrapper();
///
/// Destructor of this Audio grabber. Releases any claimed resources.
///
~AudioWrapper() override;
///
/// Settings update handler
///
void handleSettingsUpdate(settings::type type, const QJsonDocument& config) override;
public slots:
///
/// Performs a single frame grab and computes the led-colors
///
void action() override;
///
/// Start audio capturing session
///
/// @returns true if successful
bool start() override;
///
/// Stop audio capturing session
///
void stop() override;
private:
void newFrame(const Image<ColorRgb>& image);
/// The actual grabber
#ifdef WIN32
AudioGrabberWindows _grabber;
#endif
#ifdef __linux__
AudioGrabberLinux _grabber;
#endif
};

View File

@ -4,12 +4,14 @@
enum class GrabberType {
SCREEN,
VIDEO,
AUDIO,
};
enum class GrabberTypeFilter {
ALL,
SCREEN,
VIDEO,
AUDIO,
};
#endif // GRABBERTYPE_H

View File

@ -20,6 +20,7 @@ public:
void setSystemCaptureEnable(bool enable);
void setV4LCaptureEnable(bool enable);
void setAudioCaptureEnable(bool enable);
private slots:
///
@ -48,11 +49,22 @@ private slots:
///
void handleV4lImage(const QString& name, const Image<ColorRgb> & image);
///
/// @brief forward audio image
/// @param image The image
///
void handleAudioImage(const QString& name, const Image<ColorRgb>& image);
///
/// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive
///
void setV4lInactive();
///
/// @brief Is called from _audioInactiveTimer to set source after specific time to inactive
///
void setAudioInactive();
///
/// @brief Is called from _systemInactiveTimer to set source after specific time to inactive
///
@ -73,4 +85,10 @@ private:
quint8 _v4lCaptPrio;
QString _v4lCaptName;
QTimer* _v4lInactiveTimer;
/// Reflect state of audio capture and prio
bool _audioCaptEnabled;
quint8 _audioCaptPrio;
QString _audioCaptName;
QTimer* _audioInactiveTimer;
};

View File

@ -43,8 +43,10 @@ public:
static QMap<int, QString> GRABBER_SYS_CLIENTS;
static QMap<int, QString> GRABBER_V4L_CLIENTS;
static QMap<int, QString> GRABBER_AUDIO_CLIENTS;
static bool GLOBAL_GRABBER_SYS_ENABLE;
static bool GLOBAL_GRABBER_V4L_ENABLE;
static bool GLOBAL_GRABBER_AUDIO_ENABLE;
///
/// Starts the grabber which produces led values with the specified update rate
@ -78,6 +80,8 @@ public:
void setSysGrabberState(bool sysGrabberState){ GLOBAL_GRABBER_SYS_ENABLE = sysGrabberState; }
bool getV4lGrabberState() const { return GLOBAL_GRABBER_V4L_ENABLE; }
void setV4lGrabberState(bool v4lGrabberState){ GLOBAL_GRABBER_V4L_ENABLE = v4lGrabberState; }
bool getAudioGrabberState() const { return GLOBAL_GRABBER_AUDIO_ENABLE; }
void setAudioGrabberState(bool audioGrabberState) { GLOBAL_GRABBER_AUDIO_ENABLE = audioGrabberState; }
static QStringList availableGrabbers(GrabberTypeFilter type = GrabberTypeFilter::ALL);
@ -147,10 +151,7 @@ private slots:
void handleSourceRequest(hyperion::Components component, int hyperionInd, bool listen);
///
/// @brief Update Update capture rate
/// @param type interval between frames in milliseconds
///
void updateTimer(int interval);
protected:
@ -168,6 +169,11 @@ protected:
///
virtual bool close() { return true; }
/// @brief Update Update capture rate
/// @param type interval between frames in milliseconds
///
void updateTimer(int interval);
QString _grabberName;

View File

@ -471,6 +471,9 @@ signals:
/// Signal which is emitted, when a new V4l proto image should be forwarded
void forwardV4lProtoMessage(const QString&, const Image<ColorRgb>&);
/// Signal which is emitted, when a new Audio proto image should be forwarded
void forwardAudioProtoMessage(const QString&, const Image<ColorRgb>&);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
/// Signal which is emitted, when a new Flat-/Proto- Buffer image should be forwarded
void forwardBufferMessage(const QString&, const Image<ColorRgb>&);

View File

@ -1,6 +1,7 @@
#pragma once
#include <QString>
#include <QSharedPointer>
// Utils includes
#include <utils/Image.h>
@ -46,7 +47,7 @@ public:
/// @param[in] width The new width of the buffer-image
/// @param[in] height The new height of the buffer-image
///
void setSize(unsigned width, unsigned height);
void setSize(int width, int height);
///
/// @brief Update the led string (eg on settings change)
@ -56,6 +57,19 @@ public:
/// Returns state of black border detector
bool blackBorderDetectorEnabled() const;
///
/// Factor to reduce the number of pixels evaluated during processing
///
/// @param[in] count Use every "count" pixel
void setReducedPixelSetFactorFactor(int count);
///
/// Set the accuracy used during processing
/// (only for selected types)
///
/// @param[in] level The accuracy level (0-4)
void setAccuracyLevel(int level);
/// Returns the current _userMappingType, this may not be the current applied type!
int getUserLedMappingType() const { return _userMappingType; }
@ -98,30 +112,45 @@ public:
}
///
/// Processes the image to a list of led colors. This will update the size of the buffer-image
/// if required and call the image-to-leds mapping to determine the mean color per led.
/// Processes the image to a list of LED colors. This will update the size of the buffer-image
/// if required and call the image-to-LEDs mapping to determine the color per LED.
///
/// @param[in] image The image to translate to led values
/// @param[in] image The image to translate to LED values
///
/// @return The color value per led
/// @return The color value per LED
///
template <typename Pixel_T>
std::vector<ColorRgb> process(const Image<Pixel_T>& image)
{
std::vector<ColorRgb> colors;
if (image.width()>0 && image.height()>0)
{
// Ensure that the buffer-image is the proper size
setSize(image);
assert(!_imageToLedColors.isNull());
// Check black border detection
verifyBorder(image);
// Create a result vector and call the 'in place' function
switch (_mappingType)
{
case 1: colors = _imageToLeds->getUniLedColor(image); break;
default: colors = _imageToLeds->getMeanLedColor(image);
case 1:
colors = _imageToLedColors->getUniLedColor(image);
break;
case 2:
colors = _imageToLedColors->getMeanLedColorSqrt(image);
break;
case 3:
colors = _imageToLedColors->getDominantLedColor(image);
break;
case 4:
colors = _imageToLedColors->getDominantLedColorAdv(image);
break;
default:
colors = _imageToLedColors->getMeanLedColor(image);
}
}
else
@ -136,8 +165,8 @@ public:
///
/// Determines the led colors of the image in the buffer.
///
/// @param[in] image The image to translate to led values
/// @param[out] ledColors The color value per led
/// @param[in] image The image to translate to LED values
/// @param[out] ledColors The color value per LED
///
template <typename Pixel_T>
void process(const Image<Pixel_T>& image, std::vector<ColorRgb>& ledColors)
@ -153,8 +182,20 @@ public:
// Determine the mean or uni colors of each led (using the existing mapping)
switch (_mappingType)
{
case 1: _imageToLeds->getUniLedColor(image, ledColors); break;
default: _imageToLeds->getMeanLedColor(image, ledColors);
case 1:
_imageToLedColors->getUniLedColor(image, ledColors);
break;
case 2:
_imageToLedColors->getMeanLedColorSqrt(image, ledColors);
break;
case 3:
_imageToLedColors->getDominantLedColor(image, ledColors);
break;
case 4:
_imageToLedColors->getDominantLedColorAdv(image, ledColors);
break;
default:
_imageToLedColors->getMeanLedColor(image, ledColors);
}
}
else
@ -164,9 +205,9 @@ public:
}
///
/// Get the hscan and vscan parameters for a single led
/// Get the hscan and vscan parameters for a single LED
///
/// @param[in] led Index of the led
/// @param[in] led Index of the LED
/// @param[out] hscanBegin begin of the hscan
/// @param[out] hscanEnd end of the hscan
/// @param[out] vscanBegin begin of the hscan
@ -175,6 +216,13 @@ public:
bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
private:
void registerProcessingUnit(
int width,
int height,
int horizontalBorder,
int verticalBorder);
///
/// Performs black-border detection (if enabled) on the given image
///
@ -183,34 +231,25 @@ private:
template <typename Pixel_T>
void verifyBorder(const Image<Pixel_T> & image)
{
if (!_borderProcessor->enabled() && ( _imageToLeds->horizontalBorder()!=0 || _imageToLeds->verticalBorder()!=0 ))
if (!_borderProcessor->enabled() && ( _imageToLedColors->horizontalBorder()!=0 || _imageToLedColors->verticalBorder()!=0 ))
{
Debug(_log, "Reset border");
_borderProcessor->process(image);
delete _imageToLeds;
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
registerProcessingUnit(image.width(), image.height(), 0, 0);
}
if(_borderProcessor->enabled() && _borderProcessor->process(image))
{
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
// Clean up the old mapping
delete _imageToLeds;
if (border.unknown)
{
// Construct a new buffer and mapping
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
registerProcessingUnit(image.width(), image.height(), 0, 0);
}
else
{
// Construct a new buffer and mapping
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, _ledString.leds());
registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize);
}
//Debug(Logger::getInstance("BLACKBORDER"), "CURRENT BORDER TYPE: unknown=%d hor.size=%d vert.size=%d",
// border.unknown, border.horizontalSize, border.verticalSize );
}
}
@ -218,6 +257,7 @@ private slots:
void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
private:
Logger * _log;
/// The Led-string specification
LedString _ledString;
@ -226,15 +266,18 @@ private:
hyperion::BlackBorderProcessor * _borderProcessor;
/// The mapping of image-pixels to LEDs
hyperion::ImageToLedsMap* _imageToLeds;
QSharedPointer<hyperion::ImageToLedsMap> _imageToLedColors;
/// Type of image 2 led mapping
/// Type of image to LED mapping
int _mappingType;
/// Type of last requested user type
int _userMappingType;
/// Type of last requested hard type
int _hardMappingType;
int _accuraryLevel;
int _reducedPixelSetFactorFactor;
/// Hyperion instance pointer
Hyperion* _hyperion;
};

View File

@ -1,72 +1,90 @@
#pragma once
#ifndef IMAGETOLEDSMAP_H
#define IMAGETOLEDSMAP_H
// STL includes
#include <cassert>
#include <memory>
#include <sstream>
#include <cmath>
// hyperion-utils includes
#include <utils/Image.h>
#include <utils/Logger.h>
#include <utils/ColorRgbScalar.h>
#include <utils/ColorSys.h>
// hyperion includes
#include <hyperion/LedString.h>
namespace hyperion
{
///
/// The ImageToLedsMap holds a mapping of indices into an image to leds. It can be used to
/// calculate the average (or mean) color per led for a specific region.
/// The ImageToLedsMap holds a mapping of indices into an image to LEDs. It can be used to
/// calculate the average (aka mean) or dominant color per LED for a given region.
///
class ImageToLedsMap
class ImageToLedsMap : public QObject
{
Q_OBJECT
public:
///
/// Constructs an mapping from the absolute indices in an image to each led based on the border
/// definition given in the list of leds. The map holds absolute indices to any given image,
/// Constructs an mapping from the absolute indices in an image to each LED based on the border
/// definition given in the list of LEDs. The map holds absolute indices to any given image,
/// provided that it is row-oriented.
/// The mapping is created purely on size (width and height). The given borders are excluded
/// from indexing.
///
/// @param[in] log Logger
/// @param[in] width The width of the indexed image
/// @param[in] height The width of the indexed image
/// @param[in] horizontalBorder The size of the horizontal border (0=no border)
/// @param[in] verticalBorder The size of the vertical border (0=no border)
/// @param[in] leds The list with led specifications
/// @param[in] reducedProcessingFactor Factor to reduce the number of pixels evaluated during processing
/// @param[in] accuraryLevel The accuracy used during processing (only for selected types)
///
ImageToLedsMap(
const unsigned width,
const unsigned height,
const unsigned horizontalBorder,
const unsigned verticalBorder,
const std::vector<Led> & leds);
Logger* log,
int width,
int height,
int horizontalBorder,
int verticalBorder,
const std::vector<Led> & leds,
int reducedProcessingFactor = 0,
int accuraryLevel = 0);
///
/// Returns the width of the indexed image
///
/// @return The width of the indexed image [pixels]
///
unsigned width() const;
int width() const;
///
/// Returns the height of the indexed image
///
/// @return The height of the indexed image [pixels]
///
unsigned height() const;
int height() const;
unsigned horizontalBorder() const { return _horizontalBorder; }
unsigned verticalBorder() const { return _verticalBorder; }
int horizontalBorder() const { return _horizontalBorder; }
int verticalBorder() const { return _verticalBorder; }
///
/// Determines the mean color for each led using the mapping the image given
/// Set the accuracy used during processing
/// (only for selected types)
///
/// @param[in] level The accuracy level (0-4)
void setAccuracyLevel (int level);
///
/// Determines the mean color for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the led colors
///
/// @return ledColors The vector containing the output
/// @return The vector containing the output
///
template <typename Pixel_T>
std::vector<ColorRgb> getMeanLedColor(const Image<Pixel_T> & image) const
@ -77,20 +95,18 @@ namespace hyperion
}
///
/// Determines the mean color for each led using the mapping the image given
/// Determines the mean color for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the led colors
/// @param[in] image The image from which to extract the LED colors
/// @param[out] ledColors The vector containing the output
///
template <typename Pixel_T>
void getMeanLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
{
// Sanity check for the number of leds
//assert(_colorsMap.size() == ledColors.size());
if(_colorsMap.size() != ledColors.size())
{
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
return;
}
@ -104,12 +120,52 @@ namespace hyperion
}
///
/// Determines the uni color for each led using the mapping the image given
/// Determines the mean color squared for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the led colors
///
/// @return ledColors The vector containing the output
/// @return The vector containing the output
///
template <typename Pixel_T>
std::vector<ColorRgb> getMeanLedColorSqrt(const Image<Pixel_T> & image) const
{
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
getMeanLedColorSqrt(image, colors);
return colors;
}
///
/// Determines the mean color squared for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the LED colors
/// @param[out] ledColors The vector containing the output
///
template <typename Pixel_T>
void getMeanLedColorSqrt(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
{
if(_colorsMap.size() != ledColors.size())
{
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
return;
}
// Iterate each led and compute the mean
auto led = ledColors.begin();
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
{
const ColorRgb color = calcMeanColorSqrt(image, *colors);
*led = color;
}
}
///
/// Determines the mean color of the image and assigns it to all LEDs
///
/// @param[in] image The image from which to extract the led color
///
/// @return The vector containing the output
///
template <typename Pixel_T>
std::vector<ColorRgb> getUniLedColor(const Image<Pixel_T> & image) const
@ -120,57 +176,145 @@ namespace hyperion
}
///
/// Determines the uni color for each led using the mapping the image given
/// at construction.
/// Determines the mean color of the image and assigns it to all LEDs
///
/// @param[in] image The image from which to extract the led colors
/// @param[in] image The image from which to extract the LED colors
/// @param[out] ledColors The vector containing the output
///
template <typename Pixel_T>
void getUniLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
{
// Sanity check for the number of leds
// assert(_colorsMap.size() == ledColors.size());
if(_colorsMap.size() != ledColors.size())
{
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
return;
}
// calculate uni color
const ColorRgb color = calcMeanColor(image);
//Update all LEDs with same color
std::fill(ledColors.begin(),ledColors.end(), color);
}
private:
/// The width of the indexed image
const unsigned _width;
/// The height of the indexed image
const unsigned _height;
const unsigned _horizontalBorder;
const unsigned _verticalBorder;
/// The absolute indices into the image for each led
std::vector<std::vector<int32_t>> _colorsMap;
///
/// Determines the dominant color for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the LED color
///
/// @return The vector containing the output
///
template <typename Pixel_T>
std::vector<ColorRgb> getDominantLedColor(const Image<Pixel_T> & image) const
{
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
getDominantLedColor(image, colors);
return colors;
}
///
/// Calculates the 'mean color' of the given list. This is the mean over each color-channel
/// Determines the dominant color for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the LED colors
/// @param[out] ledColors The vector containing the output
///
template <typename Pixel_T>
void getDominantLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
{
// Sanity check for the number of LEDs
if(_colorsMap.size() != ledColors.size())
{
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
return;
}
// Iterate each led and compute the dominant color
auto led = ledColors.begin();
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
{
const ColorRgb color = calculateDominantColor(image, *colors);
*led = color;
}
}
///
/// Determines the dominant color using a k-means algorithm for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the LED color
///
/// @return The vector containing the output
///
template <typename Pixel_T>
std::vector<ColorRgb> getDominantLedColorAdv(const Image<Pixel_T> & image) const
{
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
getDominantLedColorAdv(image, colors);
return colors;
}
///
/// Determines the dominant color using a k-means algorithm for each LED using the LED area mapping given
/// at construction.
///
/// @param[in] image The image from which to extract the LED colors
/// @param[out] ledColors The vector containing the output
///
template <typename Pixel_T>
void getDominantLedColorAdv(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
{
// Sanity check for the number of LEDs
if(_colorsMap.size() != ledColors.size())
{
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
return;
}
// Iterate each led and compute the dominant color
auto led = ledColors.begin();
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
{
const ColorRgb color = calculateDominantColorAdv(image, *colors);
*led = color;
}
}
private:
Logger* _log;
/// The width of the indexed image
const int _width;
/// The height of the indexed image
const int _height;
const int _horizontalBorder;
const int _verticalBorder;
/// Evaluate every "count" pixel
int _nextPixelCount;
/// Number of clusters used during dominant color advanced processing (k-means)
int _clusterCount;
/// The absolute indices into the image for each led
std::vector<std::vector<int>> _colorsMap;
///
/// Calculates the 'mean color' over the given image. This is the mean over each color-channel
/// (red, green, blue)
///
/// @param[in] image The image a section from which an average color must be computed
/// @param[in] colors The list with colors
/// @param[in] pixels The list of pixel indices for the given image to be evaluated///
///
/// @return The mean of the given list of colors (or black when empty)
///
template <typename Pixel_T>
ColorRgb calcMeanColor(const Image<Pixel_T> & image, const std::vector<int32_t> & colors) const
ColorRgb calcMeanColor(const Image<Pixel_T> & image, const std::vector<int32_t> & pixels) const
{
const auto colorVecSize = colors.size();
if (colorVecSize == 0)
const auto pixelNum = pixels.size();
if (pixelNum == 0)
{
return ColorRgb::BLACK;
}
@ -179,20 +323,20 @@ namespace hyperion
uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 0;
const auto& imgData = image.memptr();
for (const unsigned colorOffset : colors)
const auto& imgData = image.memptr();
for (const int pixelOffset : pixels)
{
const auto& pixel = imgData[colorOffset];
const auto& pixel = imgData[pixelOffset];
cummRed += pixel.red;
cummGreen += pixel.green;
cummBlue += pixel.blue;
}
// Compute the average of each color channel
const uint8_t avgRed = uint8_t(cummRed/colorVecSize);
const uint8_t avgGreen = uint8_t(cummGreen/colorVecSize);
const uint8_t avgBlue = uint8_t(cummBlue/colorVecSize);
const uint8_t avgRed = uint8_t(cummRed/pixelNum);
const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
// Return the computed color
return {avgRed, avgGreen, avgBlue};
@ -213,11 +357,11 @@ namespace hyperion
uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 0;
const unsigned imageSize = image.width() * image.height();
const unsigned pixelNum = image.width() * image.height();
const auto& imgData = image.memptr();
for (unsigned idx=0; idx<imageSize; idx++)
for (unsigned idx=0; idx<pixelNum; idx++)
{
const auto& pixel = imgData[idx];
cummRed += pixel.red;
@ -226,13 +370,289 @@ namespace hyperion
}
// Compute the average of each color channel
const uint8_t avgRed = uint8_t(cummRed/imageSize);
const uint8_t avgGreen = uint8_t(cummGreen/imageSize);
const uint8_t avgBlue = uint8_t(cummBlue/imageSize);
const uint8_t avgRed = uint8_t(cummRed/pixelNum);
const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
// Return the computed color
return {avgRed, avgGreen, avgBlue};
}
///
/// Calculates the 'mean color' squared over the given image. This is the mean over each color-channel
/// (red, green, blue)
///
/// @param[in] image The image a section from which an average color must be computed
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
///
/// @return The mean of the given list of colors (or black when empty)
///
template <typename Pixel_T>
ColorRgb calcMeanColorSqrt(const Image<Pixel_T> & image, const std::vector<int32_t> & pixels) const
{
const auto pixelNum = pixels.size();
if (pixelNum == 0)
{
return ColorRgb::BLACK;
}
// Accumulate the squared sum of each separate color channel
uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 0;
const auto& imgData = image.memptr();
for (const int colorOffset : pixels)
{
const auto& pixel = imgData[colorOffset];
cummRed += pixel.red * pixel.red;
cummGreen += pixel.green * pixel.green;
cummBlue += pixel.blue * pixel.blue;
}
// Compute the average of each color channel
const uint8_t avgRed = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummRed/pixelNum))), 255L));
const uint8_t avgGreen = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummGreen/pixelNum))), 255L));
const uint8_t avgBlue = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummBlue/pixelNum))), 255L));
// Return the computed color
return {avgRed, avgGreen, avgBlue};
}
///
/// Calculates the 'mean color' squared over the given image. This is the mean over each color-channel
/// (red, green, blue)
///
/// @param[in] image The image a section from which an average color must be computed
///
/// @return The mean of the given list of colors (or black when empty)
///
template <typename Pixel_T>
ColorRgb calcMeanColorSqrt(const Image<Pixel_T> & image) const
{
// Accumulate the squared sum of each separate color channel
uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 0;
const unsigned pixelNum = image.width() * image.height();
const auto& imgData = image.memptr();
for (int idx=0; idx<pixelNum; ++idx)
{
const auto& pixel = imgData[idx];
cummRed += pixel.red * pixel.red;
cummGreen += pixel.green * pixel.green;
cummBlue += pixel.blue * pixel.blue;
}
// Compute the average of each color channel
const uint8_t avgRed = uint8_t(std::lround(sqrt(static_cast<double>(cummRed/pixelNum))));
const uint8_t avgGreen = uint8_t(std::lround(sqrt(static_cast<double>(cummGreen/pixelNum))));
const uint8_t avgBlue = uint8_t(std::lround(sqrt(static_cast<double>(cummBlue/pixelNum))));
// Return the computed color
return {avgRed, avgGreen, avgBlue};
}
///
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
///
/// @param[in] image The image for which a dominant color is to be computed
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
///
/// @return The image area's dominant color or black, if no pixel indices provided
///
template <typename Pixel_T>
ColorRgb calculateDominantColor(const Image<Pixel_T> & image, const std::vector<int> & pixels) const
{
ColorRgb dominantColor {ColorRgb::BLACK};
const auto pixelNum = pixels.size();
if (pixelNum > 0)
{
const auto& imgData = image.memptr();
QMap<QRgb,int> colorDistributionMap;
int count = 0;
for (const int pixelOffset : pixels)
{
QRgb color = imgData[pixelOffset].rgb();
if (colorDistributionMap.contains(color)) {
colorDistributionMap[color] = colorDistributionMap[color] + 1;
}
else {
colorDistributionMap[color] = 1;
}
int colorsFound = colorDistributionMap[color];
if (colorsFound > count) {
dominantColor.setRgb(color);
count = colorsFound;
}
}
}
return dominantColor;
}
///
/// Calculates the 'dominant color' of an image
///
/// @param[in] image The image for which a dominant color is to be computed
///
/// @return The image's dominant color
///
template <typename Pixel_T>
ColorRgb calculateDominantColor(const Image<Pixel_T> & image) const
{
const unsigned pixelNum = image.width() * image.height();
std::vector<int> pixels(pixelNum);
std::iota(pixels.begin(), pixels.end(), 0);
return calculateDominantColor(image, pixels);
}
template <typename Pixel_T>
struct ColorCluster {
ColorCluster():count(0) {}
ColorCluster(Pixel_T color):count(0),color(color) {}
Pixel_T color;
Pixel_T newColor;
int count;
};
const ColorRgb DEFAULT_CLUSTER_COLORS[5] {
{ColorRgb::BLACK},
{ColorRgb::GREEN},
{ColorRgb::WHITE},
{ColorRgb::RED},
{ColorRgb::YELLOW}
};
///
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
///
/// @param[in] image The image for which a dominant color is to be computed
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
///
/// @return The image area's dominant color or black, if no pixel indices provided
///
template <typename Pixel_T>
ColorRgb calculateDominantColorAdv(const Image<Pixel_T> & image, const std::vector<int> & pixels) const
{
ColorRgb dominantColor {ColorRgb::BLACK};
const auto pixelNum = pixels.size();
if (pixelNum > 0)
{
// initial cluster with different colors
auto clusters = std::unique_ptr< ColorCluster<ColorRgbScalar> >(new ColorCluster<ColorRgbScalar>[_clusterCount]);
for(int k = 0; k < _clusterCount; ++k)
{
clusters.get()[k].newColor = DEFAULT_CLUSTER_COLORS[k];
}
// k-means
double min_rgb_euclidean {0};
double old_rgb_euclidean {0};
while(1)
{
for(int k = 0; k < _clusterCount; ++k)
{
clusters.get()[k].count = 0;
clusters.get()[k].color = clusters.get()[k].newColor;
clusters.get()[k].newColor.setRgb(ColorRgb::BLACK);
}
const auto& imgData = image.memptr();
for (const int pixelOffset : pixels)
{
const auto& pixel = imgData[pixelOffset];
min_rgb_euclidean = 255 * 255 * 255;
int clusterIndex = -1;
for(int k = 0; k < _clusterCount; ++k)
{
double euclid = ColorSys::rgb_euclidean(ColorRgbScalar(pixel), clusters.get()[k].color);
if( euclid < min_rgb_euclidean ) {
min_rgb_euclidean = euclid;
clusterIndex = k;
}
}
clusters.get()[clusterIndex].count++;
clusters.get()[clusterIndex].newColor += ColorRgbScalar(pixel);
}
min_rgb_euclidean = 0;
for(int k = 0; k < _clusterCount; ++k)
{
if (clusters.get()[k].count > 0)
{
// new color
clusters.get()[k].newColor /= clusters.get()[k].count;
double ecli = ColorSys::rgb_euclidean(clusters.get()[k].newColor, clusters.get()[k].color);
if(ecli > min_rgb_euclidean)
{
min_rgb_euclidean = ecli;
}
}
}
if( fabs(min_rgb_euclidean - old_rgb_euclidean) < 1)
{
break;
}
old_rgb_euclidean = min_rgb_euclidean;
}
int colorsFoundMax = 0;
int dominantClusterIdx {0};
for(int clusterIdx=0; clusterIdx < _clusterCount; ++clusterIdx){
int colorsFoundinCluster = clusters.get()[clusterIdx].count;
if (colorsFoundinCluster > colorsFoundMax) {
colorsFoundMax = colorsFoundinCluster;
dominantClusterIdx = clusterIdx;
}
}
dominantColor.red = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.red);
dominantColor.green = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.green);
dominantColor.blue = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.blue);
}
return dominantColor;
}
///
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
///
/// @param[in] image The image for which a dominant color is to be computed
///
/// @return The image's dominant color
///
template <typename Pixel_T>
ColorRgb calculateDominantColorAdv(const Image<Pixel_T> & image) const
{
const unsigned pixelNum = image.width() * image.height();
std::vector<int> pixels(pixelNum);
std::iota(pixels.begin(), pixels.end(), 0);
return calculateDominantColorAdv(image, pixels);
}
};
} // end namespace hyperion
#endif // IMAGETOLEDSMAP_H

View File

@ -289,6 +289,9 @@ private:
int _currentConfigId;
bool _enabled;
//The system enable state, to restore smoothing state after effect with smoothing ran
bool _enabledSystemCfg;
/// The type of smoothing to perform
SmoothingType _smoothingType;

View File

@ -438,7 +438,10 @@ protected:
uint _ledRGBWCount;
/// Does the device allow restoring the original state?
bool _isRestoreOrigState;
bool _isRestoreOrigState;
/// Does the device should be kept on after streaming
bool _isStayOnAfterStreaming;
/// Device, lights state before streaming via hyperion
QJsonObject _orignalStateValues;
@ -460,6 +463,9 @@ protected:
/// Is the device in error state and stopped?
bool _isDeviceInError;
/// Is the device in error state, but is retries might resolve the situation?
bool _isDeviceRecoverable;
/// Timestamp of last write
QDateTime _lastWriteTime;
@ -476,8 +482,9 @@ protected slots:
/// @brief Set device in error state
///
/// @param[in] errorMsg The error message to be logged
/// @param[in] isRecoverable If False, no further retries will be done
///
virtual void setInError( const QString& errorMsg);
virtual void setInError( const QString& errorMsg, bool isRecoverable=true);
private:

View File

@ -19,6 +19,7 @@
// Utility includes
#include <utils/Logger.h>
#include <utils/WeakConnect.h>
namespace {
constexpr std::chrono::milliseconds DEFAULT_DISCOVER_TIMEOUT{ 500 };
@ -103,61 +104,6 @@ private slots:
void onServiceRemoved(const QMdnsEngine::Service& service);
private:
// template <typename Func1, typename Func2, typename std::enable_if_t<std::is_member_pointer<Func2>::value, int> = 0>
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
// Func1 signal,
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
// Func2 slot)
// {
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
// QObject::disconnect(conn_normal);
// QObject::disconnect(*conn_delete);
// delete conn_delete;
// });
// return conn_normal;
// }
template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
Func1 signal,
Func2 slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
// template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
// Func1 signal,
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
// Func2 slot)
// {
// Q_UNUSED(receiver);
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
// QObject::disconnect(conn_normal);
// QObject::disconnect(*conn_delete);
// delete conn_delete;
// });
// return conn_normal;
// }
/// The logger instance for mDNS-Service
Logger* _log;

View File

@ -6,6 +6,7 @@
#include <QString>
#include <QTextStream>
#include <QRgb>
///
/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the
@ -52,6 +53,18 @@ struct ColorRgb
return a;
}
QRgb rgb() const
{
return qRgb(red,green,blue);
}
void setRgb(QRgb rgb)
{
red = static_cast<uint8_t>(qRed(rgb));
green = static_cast<uint8_t>(qGreen(rgb));
blue = static_cast<uint8_t>(qBlue(rgb));
}
QString toQString() const
{
return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue);

View File

@ -0,0 +1,203 @@
#ifndef COLORRGBSCALAR_H
#define COLORRGBSCALAR_H
// STL includes
#include <cstdint>
#include <iostream>
#include <QString>
#include <QTextStream>
#include <QRgb>
#include <utils/ColorRgb.h>
///
/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the
/// structure is exactly 3 times int for easy writing to led-device
///
struct ColorRgbScalar
{
/// The red color channel
int red;
/// The green color channel
int green;
/// The blue color channel
int blue;
/// 'Black' RgbColor (0, 0, 0)
static const ColorRgbScalar BLACK;
/// 'Red' RgbColor (255, 0, 0)
static const ColorRgbScalar RED;
/// 'Green' RgbColor (0, 255, 0)
static const ColorRgbScalar GREEN;
/// 'Blue' RgbColor (0, 0, 255)
static const ColorRgbScalar BLUE;
/// 'Yellow' RgbColor (255, 255, 0)
static const ColorRgbScalar YELLOW;
/// 'White' RgbColor (255, 255, 255)
static const ColorRgbScalar WHITE;
ColorRgbScalar() = default;
ColorRgbScalar(int _red, int _green,int _blue):
red(_red),
green(_green),
blue(_blue)
{
}
ColorRgbScalar(ColorRgb rgb):
red(rgb.red),
green(rgb.green),
blue(rgb.blue)
{
}
ColorRgbScalar operator-(const ColorRgbScalar& b) const
{
ColorRgbScalar a(*this);
a.red -= b.red;
a.green -= b.green;
a.blue -= b.blue;
return a;
}
void setRgb(QRgb rgb)
{
red = qRed(rgb);
green = qGreen(rgb);
blue = qBlue(rgb);
}
void setRgb(ColorRgb rgb)
{
red = rgb.red;
green = rgb.green;
blue = rgb.blue;
}
QString toQString() const
{
return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue);
}
};
/// Assert to ensure that the size of the structure is 'only' 3 times int
static_assert(sizeof(ColorRgbScalar) == 3 * sizeof(int), "Incorrect size of ColorRgbInt");
///
/// Stream operator to write ColorRgbInt to an outputstream (format "'{'[red]','[green]','[blue]'}'")
///
/// @param os The output stream
/// @param color The color to write
/// @return The output stream (with the color written to it)
///
inline std::ostream& operator<<(std::ostream& os, const ColorRgbScalar& color)
{
os << "{"
<< static_cast<unsigned>(color.red) << ","
<< static_cast<unsigned>(color.green) << ","
<< static_cast<unsigned>(color.blue)
<< "}";
return os;
}
///
/// Stream operator to write ColorRgbInt to a QTextStream (format "'{'[red]','[green]','[blue]'}'")
///
/// @param os The output stream
/// @param color The color to write
/// @return The output stream (with the color written to it)
///
inline QTextStream& operator<<(QTextStream &os, const ColorRgbScalar& color)
{
os << "{"
<< static_cast<unsigned>(color.red) << ","
<< static_cast<unsigned>(color.green) << ","
<< static_cast<unsigned>(color.blue)
<< "}";
return os;
}
/// Compare operator to check if a color is 'equal' to another color
inline bool operator==(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return lhs.red == rhs.red &&
lhs.green == rhs.green &&
lhs.blue == rhs.blue;
}
/// Compare operator to check if a color is 'smaller' than another color
inline bool operator<(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return lhs.red < rhs.red &&
lhs.green < rhs.green &&
lhs.blue < rhs.blue;
}
/// Compare operator to check if a color is 'not equal' to another color
inline bool operator!=(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return !(lhs == rhs);
}
/// Compare operator to check if a color is 'smaller' than or 'equal' to another color
inline bool operator<=(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return lhs.red <= rhs.red &&
lhs.green <= rhs.green &&
lhs.blue <= rhs.blue;
}
/// Compare operator to check if a color is 'greater' to another color
inline bool operator>(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return lhs.red > rhs.red &&
lhs.green > rhs.green &&
lhs.blue > rhs.blue;
}
/// Compare operator to check if a color is 'greater' than or 'equal' to another color
inline bool operator>=(const ColorRgbScalar & lhs, const ColorRgbScalar & rhs)
{
return lhs.red >= rhs.red &&
lhs.green >= rhs.green &&
lhs.blue >= rhs.blue;
}
inline ColorRgbScalar& operator+=(ColorRgbScalar& lhs, const ColorRgbScalar& rhs)
{
lhs.red += rhs.red;
lhs.green += rhs.green;
lhs.blue += rhs.blue;
return lhs;
}
inline ColorRgbScalar operator+(ColorRgbScalar lhs, const ColorRgbScalar rhs)
{
lhs += rhs;
return lhs;
}
inline ColorRgbScalar& operator/=(ColorRgbScalar& lhs, int count)
{
if (count > 0)
{
lhs.red /= count;
lhs.green /= count;
lhs.blue /= count;
}
return lhs;
}
inline ColorRgbScalar operator/(ColorRgbScalar lhs, int count)
{
lhs /= count;
return lhs;
}
#endif // COLORRGBSCALAR_H

View File

@ -30,11 +30,11 @@ struct ColorRgba
static const ColorRgba WHITE;
};
/// Assert to ensure that the size of the structure is 'only' 3 bytes
/// Assert to ensure that the size of the structure is 'only' 4 bytes
static_assert(sizeof(ColorRgba) == 4, "Incorrect size of ColorARGB");
///
/// Stream operator to write ColorRgb to an outputstream (format "'{'[alpha]', '[red]','[green]','[blue]'}'")
/// Stream operator to write ColorRgba to an outputstream (format "'{'[alpha]', '[red]','[green]','[blue]'}'")
///
/// @param os The output stream
/// @param color The color to write

View File

@ -105,6 +105,19 @@ public:
/// @note See https://bottosson.github.io/posts/colorpicker/#okhsv
///
static void okhsv2rgb(double hue, double saturation, double value, uint8_t & red, uint8_t & green, uint8_t & blue);
template <typename Pixel_T>
static double rgb_euclidean(Pixel_T p1, Pixel_T p2)
{
double val = sqrt(
(p1.red - p2.red) * (p1.red - p2.red) +
(p1.green - p2.green) * (p1.green - p2.green) +
(p1.blue - p2.blue) * (p1.blue - p2.blue)
);
return val;
}
};
#endif // COLORSYS_H

View File

@ -23,6 +23,7 @@ enum Components
#endif
COMP_GRABBER,
COMP_V4L,
COMP_AUDIO,
COMP_COLOR,
COMP_IMAGE,
COMP_EFFECT,
@ -50,6 +51,7 @@ inline const char* componentToString(Components c)
#endif
case COMP_GRABBER: return "Framegrabber";
case COMP_V4L: return "V4L capture device";
case COMP_AUDIO: return "Audio capture device";
case COMP_COLOR: return "Solid color";
case COMP_EFFECT: return "Effect";
case COMP_IMAGE: return "Image";
@ -79,6 +81,7 @@ inline const char* componentToIdString(Components c)
#endif
case COMP_GRABBER: return "GRABBER";
case COMP_V4L: return "V4L";
case COMP_AUDIO: return "AUDIO";
case COMP_COLOR: return "COLOR";
case COMP_EFFECT: return "EFFECT";
case COMP_IMAGE: return "IMAGE";
@ -107,6 +110,7 @@ inline Components stringToComponent(const QString& component)
#endif
if (cmp == "GRABBER") return COMP_GRABBER;
if (cmp == "V4L") return COMP_V4L;
if (cmp == "AUDIO") return COMP_AUDIO;
if (cmp == "COLOR") return COMP_COLOR;
if (cmp == "EFFECT") return COMP_EFFECT;
if (cmp == "IMAGE") return COMP_IMAGE;

View File

@ -56,6 +56,13 @@ signals:
void setBufferImage(const QString& name, const Image<ColorRgb>& image);
#endif
///
/// @brief PIPE audioCapture images from audioCapture over HyperionDaemon to Hyperion class
/// @param name The name of the audio capture (path) that is currently active
/// @param image The prepared image
///
void setAudioImage(const QString& name, const Image<ColorRgb>& image);
///
/// @brief PIPE the register command for a new global input over HyperionDaemon to Hyperion class
/// @param[in] priority The priority of the channel

View File

@ -51,7 +51,7 @@ namespace NetUtils {
{
if ((port <= 0 || port > MAX_PORT) && port != -1)
{
Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [0 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [1 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
return false;
}
return true;
@ -122,7 +122,7 @@ namespace NetUtils {
{
if (hostAddress.setAddress(hostname))
{
//Debug(log, "IP-address (%s) not required to be resolved.", QSTRING_CSTR(hostAddress.toString()));
// An IP-address is not required to be resolved
isHostAddressOK = true;
}
else

View File

@ -44,7 +44,7 @@ public:
int getBacklightThreshold() const;
/// @param backlightThreshold New lower brightness
void setBacklightThreshold(int backlightThreshold);
void setBacklightThreshold(double backlightThreshold);
/// @return The current state
bool getBacklightColored() const;

View File

@ -0,0 +1,63 @@
#ifndef WEAKCONNECT_H
#define WEAKCONNECT_H
#include <type_traits>
// Qt includes
#include <QObject>
template <typename Func1, typename Func2, typename std::enable_if_t<std::is_member_pointer<Func2>::value, int> = 0>
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
Func1 signal,
typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
Func2 slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
Func1 signal,
Func2 slot)
{
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
Func1 signal,
typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
Func2 slot)
{
Q_UNUSED(receiver);
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
QObject::disconnect(conn_normal);
QObject::disconnect(*conn_delete);
delete conn_delete;
});
return conn_normal;
}
#endif // WEAKCONNECT_H

View File

@ -16,14 +16,12 @@
#include <effectengine/Effect.h>
#endif
#include <QDebug>
///
/// @brief Provide utility methods for Hyperion class
///
namespace hyperion {
void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
static void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
{
#define FGCONFIG_ARRAY fgColorConfig.toArray()
@ -67,12 +65,12 @@ namespace hyperion {
#undef FGCONFIG_ARRAY
}
ColorOrder createColorOrder(const QJsonObject &deviceConfig)
static ColorOrder createColorOrder(const QJsonObject &deviceConfig)
{
return stringToColorOrder(deviceConfig["colorOrder"].toString("rgb"));
}
RgbTransform createRgbTransform(const QJsonObject& colorConfig)
static RgbTransform createRgbTransform(const QJsonObject& colorConfig)
{
const double backlightThreshold = colorConfig["backlightThreshold"].toDouble(0.0);
const bool backlightColored = colorConfig["backlightColored"].toBool(false);
@ -85,7 +83,7 @@ namespace hyperion {
return RgbTransform(gammaR, gammaG, gammaB, backlightThreshold, backlightColored, static_cast<uint8_t>(brightness), static_cast<uint8_t>(brightnessComp));
}
OkhsvTransform createOkhsvTransform(const QJsonObject& colorConfig)
static OkhsvTransform createOkhsvTransform(const QJsonObject& colorConfig)
{
const double saturationGain = colorConfig["saturationGain"].toDouble(1.0);
const double brightnessGain = colorConfig["brightnessGain"].toDouble(1.0);
@ -93,7 +91,7 @@ namespace hyperion {
return OkhsvTransform(saturationGain, brightnessGain);
}
RgbChannelAdjustment createRgbChannelAdjustment(const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB)
static RgbChannelAdjustment createRgbChannelAdjustment(const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB)
{
const QJsonArray& channelConfig = colorConfig[channelName].toArray();
return RgbChannelAdjustment(
@ -104,7 +102,7 @@ namespace hyperion {
);
}
RgbChannelCorrection* createRgbChannelCorrection(const QJsonObject& colorConfig)
static RgbChannelCorrection* createRgbChannelCorrection(const QJsonObject& colorConfig)
{
int varR = colorConfig["red"].toInt(255);
int varG = colorConfig["green"].toInt(255);
@ -114,7 +112,7 @@ namespace hyperion {
return correction;
}
ColorCorrection * createColorCorrection(const QJsonObject& correctionConfig)
static ColorCorrection * createColorCorrection(const QJsonObject& correctionConfig)
{
const QString id = correctionConfig["id"].toString("default");
@ -130,7 +128,7 @@ namespace hyperion {
return correction;
}
ColorAdjustment* createColorAdjustment(const QJsonObject & adjustmentConfig)
static ColorAdjustment* createColorAdjustment(const QJsonObject & adjustmentConfig)
{
const QString id = adjustmentConfig["id"].toString("default");
@ -150,7 +148,7 @@ namespace hyperion {
return adjustment;
}
MultiColorAdjustment * createLedColorsAdjustment(int ledCnt, const QJsonObject & colorConfig)
static MultiColorAdjustment * createLedColorsAdjustment(int ledCnt, const QJsonObject & colorConfig)
{
// Create the result, the transforms are added to this
MultiColorAdjustment * adjustment = new MultiColorAdjustment(ledCnt);
@ -170,13 +168,12 @@ namespace hyperion {
{
// Special case for indices '*' => all leds
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [0-%d]", QSTRING_CSTR(colorAdjustment->_id), ledCnt-1);
continue;
}
if (!overallExp.match(ledIndicesStr).hasMatch())
{
//Error(Logger::getInstance("HYPERION"), "Given led indices %d not correct format: %s", i, QSTRING_CSTR(ledIndicesStr));
// Given LED indices are not correctly formatted
continue;
}
@ -203,13 +200,12 @@ namespace hyperion {
ss << index;
}
}
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [%s]", QSTRING_CSTR(colorAdjustment->_id), ss.str().c_str());
}
return adjustment;
}
MultiColorCorrection * createLedColorsTemperature(int ledCnt, const QJsonObject & colorConfig)
static MultiColorCorrection * createLedColorsTemperature(int ledCnt, const QJsonObject & colorConfig)
{
// Create the result, the corrections are added to this
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
@ -226,9 +222,6 @@ namespace hyperion {
int temperature = config["temperature"].toInt();
ColorRgb rgb = getRgbFromTemperature(temperature);
qDebug() << "createLedColorsTemperature: adjustment[temperture]: " << temperature << "-> " << rgb.toQString();
QJsonObject correctionConfig {
{"red", rgb.red},
{"green", rgb.green},
@ -287,7 +280,7 @@ namespace hyperion {
* @param deviceOrder The default RGB channel ordering
* @return The constructed ledstring
*/
LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder)
static LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder)
{
LedString ledString;
const QString deviceOrderStr = colorOrderToString(deviceOrder);
@ -318,7 +311,7 @@ namespace hyperion {
return ledString;
}
QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray)
static QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray)
{
std::vector<int> midPointsX;
std::vector<int> midPointsY;

View File

@ -57,7 +57,9 @@ public:
break;
}
case QJsonValue::Object:
ret = getDefaultValue(value.toObject().find("default").value());
{
ret = getDefaultValue(value.toObject().value("default"));
}
break;
case QJsonValue::Bool:
return value.toBool() ? "True" : "False";
@ -174,9 +176,9 @@ private:
if (!path.isEmpty())
{
QJsonObject obj;
modifyValue(subValue, obj, path, newValue, property);
subValue = obj;
QJsonObject tempObj;
modifyValue(subValue, tempObj, path, newValue, property);
subValue = tempObj;
}
else if (newValue != QJsonValue::Null)
subValue = newValue;

View File

@ -19,6 +19,7 @@ namespace settings {
SYSTEMCAPTURE,
GENERAL,
V4L2,
AUDIO,
JSONSERVER,
LEDCONFIG,
LEDS,
@ -52,6 +53,7 @@ namespace settings {
case SYSTEMCAPTURE: return "framegrabber";
case GENERAL: return "general";
case V4L2: return "grabberV4L2";
case AUDIO: return "grabberAudio";
case JSONSERVER: return "jsonServer";
case LEDCONFIG: return "ledConfig";
case LEDS: return "leds";
@ -84,6 +86,7 @@ namespace settings {
else if (type == "framegrabber") return SYSTEMCAPTURE;
else if (type == "general") return GENERAL;
else if (type == "grabberV4L2") return V4L2;
else if (type == "grabberAudio") return AUDIO;
else if (type == "jsonServer") return JSONSERVER;
else if (type == "ledConfig") return LEDCONFIG;
else if (type == "leds") return LEDS;

View File

@ -294,13 +294,6 @@ bool API::setHyperionInstance(quint8 inst)
return true;
}
std::map<hyperion::Components, bool> API::getAllComponents()
{
std::map<hyperion::Components, bool> comps;
//QMetaObject::invokeMethod(_hyperion, "getAllComponents", Qt::BlockingQueuedConnection, Q_RETURN_ARG(std::map<hyperion::Components, bool>, comps));
return comps;
}
bool API::isHyperionEnabled()
{
int res;

View File

@ -21,7 +21,7 @@
"component":
{
"type" : "string",
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "BOBLIGHTSERVER", "GRABBER", "V4L", "LEDDEVICE"],
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "BOBLIGHTSERVER", "GRABBER", "V4L", "AUDIO", "LEDDEVICE"],
"required": true
},
"state":

View File

@ -12,7 +12,7 @@
},
"mappingType": {
"type" : "string",
"enum" : ["multicolor_mean", "unicolor_mean"]
"enum" : ["multicolor_mean", "unicolor_mean", "multicolor_mean_squared", "dominant_color", "dominant_color_advanced"]
}
},
"additionalProperties": false

View File

@ -21,12 +21,26 @@
#include <hyperion/GrabberWrapper.h>
#include <grabber/QtGrabber.h>
#include <utils/WeakConnect.h>
#if defined(ENABLE_MF)
#include <grabber/MFGrabber.h>
#elif defined(ENABLE_V4L2)
#include <grabber/V4L2Grabber.h>
#endif
#if defined(ENABLE_AUDIO)
#include <grabber/AudioGrabber.h>
#ifdef WIN32
#include <grabber/AudioGrabberWindows.h>
#endif
#ifdef __linux__
#include <grabber/AudioGrabberLinux.h>
#endif
#endif
#if defined(ENABLE_X11)
#include <grabber/X11Grabber.h>
#endif
@ -145,7 +159,6 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut
{
const QString ident = "JsonRpc@" + _peerAddress;
QJsonObject message;
//std::cout << "JsonAPI::handleMessage | [" << static_cast<int>(_hyperion->getInstanceIndex()) << "] Received: ["<< messageString.toStdString() << "]" << std::endl;
// parse the message
if (!JsonUtils::parse(ident, messageString, message, _log))
@ -556,27 +569,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
info["ledDevices"] = ledDevices;
QJsonObject grabbers;
// *** Deprecated ***
//QJsonArray availableGrabbers;
//if ( GrabberWrapper::getInstance() != nullptr )
//{
// QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex());
// QJsonArray activeGrabberNames;
// for (auto grabberName : activeGrabbers)
// {
// activeGrabberNames.append(grabberName);
// }
// grabbers["active"] = activeGrabberNames;
//}
//for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::ALL))
//{
// availableGrabbers.append(grabber);
//}
//grabbers["available"] = availableGrabbers;
// SCREEN
QJsonObject screenGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
@ -596,6 +589,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
}
screenGrabbers["available"] = availableScreenGrabbers;
// VIDEO
QJsonObject videoGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
@ -615,8 +609,31 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
}
videoGrabbers["available"] = availableVideoGrabbers;
// AUDIO
QJsonObject audioGrabbers;
if (GrabberWrapper::getInstance() != nullptr)
{
QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex(), GrabberTypeFilter::AUDIO);
QJsonArray activeGrabberNames;
for (auto grabberName : activeGrabbers)
{
activeGrabberNames.append(grabberName);
}
audioGrabbers["active"] = activeGrabberNames;
}
QJsonArray availableAudioGrabbers;
for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO))
{
availableAudioGrabbers.append(grabber);
}
audioGrabbers["available"] = availableAudioGrabbers;
grabbers.insert("screen", screenGrabbers);
grabbers.insert("video", videoGrabbers);
grabbers.insert("audio", audioGrabbers);
info["grabbers"] = grabbers;
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
@ -690,7 +707,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString());
obj.insert("instance", entry["instance"].toInt());
//obj.insert("last_use", entry["last_use"].toString());
obj.insert("running", entry["running"].toBool());
instanceInfo.append(obj);
}
@ -699,7 +715,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
// add leds configs
info["leds"] = _hyperion->getSetting(settings::LEDS).array();
// BEGIN | The following entries are derecated but used to ensure backward compatibility with hyperion Classic remote control
// BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperion Classic remote control
// TODO Output the real transformation information instead of default
// HOST NAME
@ -760,7 +776,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority());
if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
{
QJsonObject LEDcolor;
// check if LED Color not Black (0,0,0)
if ((priorityInfo.ledColors.begin()->red +
priorityInfo.ledColors.begin()->green +
@ -1323,8 +1338,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
// use comment
// for user authorized sessions
AuthManager::AuthDefinition def;
const QString res = API::createToken(comment, def);
if (res.isEmpty())
const QString createTokenResult = API::createToken(comment, def);
if (createTokenResult.isEmpty())
{
QJsonObject newTok;
newTok["comment"] = def.comment;
@ -1334,7 +1349,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
sendSuccessDataReply(QJsonDocument(newTok), command + "-" + subc, tan);
return;
}
sendErrorReply(res, command + "-" + subc, tan);
sendErrorReply(createTokenResult, command + "-" + subc, tan);
return;
}
@ -1342,13 +1357,13 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
if (subc == "renameToken")
{
// use id/comment
const QString res = API::renameToken(id, comment);
if (res.isEmpty())
const QString renameTokenResult = API::renameToken(id, comment);
if (renameTokenResult.isEmpty())
{
sendSuccessReply(command + "-" + subc, tan);
return;
}
sendErrorReply(res, command + "-" + subc, tan);
sendErrorReply(renameTokenResult, command + "-" + subc, tan);
return;
}
@ -1356,13 +1371,13 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
if (subc == "deleteToken")
{
// use id
const QString res = API::deleteToken(id);
if (res.isEmpty())
const QString deleteTokenResult = API::deleteToken(id);
if (deleteTokenResult.isEmpty())
{
sendSuccessReply(command + "-" + subc, tan);
return;
}
sendErrorReply(res, command + "-" + subc, tan);
sendErrorReply(deleteTokenResult, command + "-" + subc, tan);
return;
}
@ -1370,7 +1385,6 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
if (subc == "requestToken")
{
// use id/comment
const QString &comment = message["comment"].toString().trimmed();
const bool &acc = message["accept"].toBool(true);
if (acc)
API::setNewTokenRequest(comment, id, tan);
@ -1387,7 +1401,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
if (API::getPendingTokenRequests(vec))
{
QJsonArray arr;
for (const auto &entry : vec)
for (const auto &entry : qAsConst(vec))
{
QJsonObject obj;
obj["comment"] = entry.comment;
@ -1492,7 +1506,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &command, int tan)
{
const QString &subc = message["subcommand"].toString();
const quint8 &inst = message["instance"].toInt();
const quint8 &inst = static_cast<quint8>(message["instance"].toInt());
const QString &name = message["name"].toString();
if (subc == "switchTo")
@ -1510,7 +1524,12 @@ void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &c
if (subc == "startInstance")
{
connect(this, &API::onStartInstanceResponse, [=] (const int &tan) { sendSuccessReply(command + "-" + subc, tan); });
//Only send update once
weakConnect(this, &API::onStartInstanceResponse, [this, command, subc] (int tan)
{
sendSuccessReply(command + "-" + subc, tan);
});
if (!API::startInstance(inst, tan))
sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan);
@ -1570,12 +1589,8 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &
QString full_command = command + "-" + subc;
// TODO: Validate that device type is a valid one
/* if ( ! valid type )
{
sendErrorReply("Unknown device", full_command, tan);
}
else
*/ {
QJsonObject config;
config.insert("type", devType);
LedDevice* ledDevice = nullptr;
@ -1637,17 +1652,13 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
QString full_command = command + "-" + subc;
// TODO: Validate that source type is a valid one
/* if ( ! valid type )
{
sendErrorReply("Unknown device", full_command, tan);
}
else
*/ {
if (subc == "discover")
{
QJsonObject inputSourcesDiscovered;
inputSourcesDiscovered.insert("sourceType", sourceType);
QJsonArray videoInputs;
QJsonArray audioInputs;
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
@ -1664,6 +1675,24 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
}
else
#endif
#if defined(ENABLE_AUDIO)
if (sourceType == "audio")
{
AudioGrabber* grabber;
#ifdef WIN32
grabber = new AudioGrabberWindows();
#endif
#ifdef __linux__
grabber = new AudioGrabberLinux();
#endif
QJsonObject params;
audioInputs = grabber->discover(params);
delete grabber;
}
else
#endif
{
DebugIf(verbose, _log, "sourceType: [%s]", QSTRING_CSTR(sourceType));
@ -1760,6 +1789,7 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
}
inputSourcesDiscovered["video_sources"] = videoInputs;
inputSourcesDiscovered["audio_sources"] = audioInputs;
DebugIf(verbose, _log, "response: [%s]", QString(QJsonDocument(inputSourcesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
@ -1873,6 +1903,7 @@ void JsonAPI::sendSuccessReply(const QString &command, int tan)
{
// create reply
QJsonObject reply;
reply["instance"] = _hyperion->getInstanceIndex();
reply["success"] = true;
reply["command"] = command;
reply["tan"] = tan;
@ -1884,6 +1915,7 @@ void JsonAPI::sendSuccessReply(const QString &command, int tan)
void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, int tan)
{
QJsonObject reply;
reply["instance"] = _hyperion->getInstanceIndex();
reply["success"] = true;
reply["command"] = command;
reply["tan"] = tan;
@ -1899,6 +1931,7 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, int t
{
// create reply
QJsonObject reply;
reply["instance"] = _hyperion->getInstanceIndex();
reply["success"] = false;
reply["error"] = error;
reply["command"] = command;
@ -2021,6 +2054,11 @@ void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, co
handleInstanceSwitch();
}
break;
case InstanceState::H_STARTED:
case InstanceState::H_STOPPED:
case InstanceState::H_CREATED:
case InstanceState::H_DELETED:
default:
break;
}

View File

@ -148,7 +148,6 @@ void JsonCB::resetSubscriptions()
void JsonCB::setSubscriptionsTo(Hyperion* hyperion)
{
assert(hyperion);
//std::cout << "JsonCB::setSubscriptions for instance [" << static_cast<int>(hyperion->getInstanceIndex()) << "] " << std::endl;
// get current subs
QStringList currSubs(getSubscribedCommands());
@ -179,8 +178,6 @@ void JsonCB::doCallback(const QString& cmd, const QVariant& data)
else
obj["data"] = data.toJsonObject();
//std::cout << "JsonCB::doCallback | [" << static_cast<int>(_hyperion->getInstanceIndex()) << "] Send: [" << QJsonDocument(obj).toJson(QJsonDocument::Compact).toStdString() << "]" << std::endl;
emit newCallback(obj);
}
@ -398,7 +395,6 @@ void JsonCB::handleInstanceChange()
QJsonObject obj;
obj.insert("friendly_name", entry["friendly_name"].toString());
obj.insert("instance", entry["instance"].toInt());
//obj.insert("last_use", entry["last_use"].toString());
obj.insert("running", entry["running"].toBool());
arr.append(obj);
}

View File

@ -23,7 +23,5 @@ uint8_t BlackBorderDetector::calculateThreshold(double threshold) const
uint8_t blackborderThreshold = uint8_t(rgbThreshold);
//Debug(Logger::getInstance("BLACKBORDER"), "threshold set to %f (%d)", threshold , int(blackborderThreshold));
return blackborderThreshold;
}

View File

@ -1,4 +1,5 @@
#include <iostream>
#include <cmath>
#include <hyperion/Hyperion.h>
@ -33,6 +34,8 @@ BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent)
// listen for component state changes
connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest);
_detector = new BlackBorderDetector(_oldThreshold);
}
BlackBorderProcessor::~BlackBorderProcessor()
@ -60,7 +63,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson
_detectionMode = obj["mode"].toString("default");
const double newThreshold = obj["threshold"].toDouble(5.0) / 100.0;
if (_oldThreshold != newThreshold)
if (fabs(_oldThreshold - newThreshold) > std::numeric_limits<double>::epsilon())
{
_oldThreshold = newThreshold;
@ -140,8 +143,6 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
// makes it look like the border detectionn is not working - since the new 3 line detection algorithm is more precise this became a problem specialy in dark scenes
// wisc
// std::cout << "c: " << setw(2) << _currentBorder.verticalSize << " " << setw(2) << _currentBorder.horizontalSize << " p: " << setw(2) << _previousDetectedBorder.verticalSize << " " << setw(2) << _previousDetectedBorder.horizontalSize << " n: " << setw(2) << newDetectedBorder.verticalSize << " " << setw(2) << newDetectedBorder.horizontalSize << " c:i " << setw(2) << _consistentCnt << ":" << setw(2) << _inconsistentCnt << std::endl;
// set the consistency counter
if (newDetectedBorder == _previousDetectedBorder)
{

View File

@ -106,8 +106,6 @@ QString BoblightClientConnection::readMessage(const char* data, const size_t siz
const int len = end - data + 1;
const QString message = QString::fromLatin1(data, len);
//std::cout << bytes << ": \"" << message.toUtf8().constData() << "\"" << std::endl;
return message;
}
@ -124,7 +122,6 @@ void BoblightClientConnection::socketClosed()
void BoblightClientConnection::handleMessage(const QString& message)
{
//std::cout << "boblight message: " << message.toStdString() << std::endl;
QStringList messageParts = QStringUtils::split(message, ' ', QStringUtils::SplitBehavior::SkipEmptyParts);
if (!messageParts.isEmpty())
{
@ -340,7 +337,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
{
if (ok)
{
//std::cout << "FAIL L " << q << ": " << s.toUtf8().constData() << std::endl;
*ok = false;
}
return 0;
@ -348,7 +344,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
if (ok)
{
//std::cout << "OK " << d << ": " << s.toUtf8().constData() << std::endl;
*ok = true;
}

View File

@ -12,7 +12,7 @@
#include <QFile>
/* Enable to turn on detailed CEC logs */
// #define VERBOSE_CEC
#define NO_VERBOSE_CEC
CECHandler::CECHandler()
{
@ -138,9 +138,9 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
if(!_cecAdapter->Open(descriptor.strComName))
{
Error(_logger, QString("Failed to open the CEC adaper on port %1")
.arg(descriptor.strComName)
.toLocal8Bit());
Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1")
.arg(descriptor.strComName))
);
return false;
}
@ -149,9 +149,9 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
{
Info(_logger, QString("CEC Adapter:").toLocal8Bit());
Info(_logger, QString("\tName : %1").arg(descriptor.strComName).toLocal8Bit());
Info(_logger, QString("\tPath : %1").arg(descriptor.strComPath).toLocal8Bit());
Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:")));
Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName)));
Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath)));
}
QString CECHandler::scan() const
@ -180,12 +180,12 @@ QString CECHandler::scan() const
devices << device;
Info(_logger, QString("\tCECDevice: %1 / %2 / %3 / %4")
.arg(device["name"].toString())
.arg(device["vendor"].toString())
.arg(device["address"].toString())
.arg(device["power"].toString())
.toLocal8Bit());
Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
.arg(device["name"].toString(),
device["vendor"].toString(),
device["address"].toString(),
device["power"].toString()))
);
}
}
@ -305,16 +305,16 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command
{
if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH)
{
Info(handler->_logger, QString("CEC source activated: %1")
.arg(adapter->ToString(command->initiator))
.toLocal8Bit());
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
.arg(adapter->ToString(command->initiator)))
);
emit handler->cecEvent(CECEvent::On);
}
if (command->opcode == CEC::CEC_OPCODE_STANDBY)
{
Info(handler->_logger, QString("CEC source deactivated: %1")
.arg(adapter->ToString(command->initiator))
.toLocal8Bit());
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1")
.arg(adapter->ToString(command->initiator)))
);
emit handler->cecEvent(CECEvent::Off);
}
}

View File

@ -50,7 +50,7 @@ QSqlDatabase DBManager::getDB() const
db.setDatabaseName(_rootPath+"/db/"+_dbn+".db");
if(!db.open())
{
Error(_log, QSTRING_CSTR(db.lastError().text()));
Error(_log, "%s", QSTRING_CSTR(db.lastError().text()));
throw std::runtime_error("Failed to open database connection!");
}
return db;

View File

@ -121,17 +121,21 @@ void EffectEngine::handleUpdatedEffectList()
// add smoothing configurations to Hyperion
if (def.args["smoothing-custom-settings"].toBool())
{
int settlingTime_ms = def.args["smoothing-time_ms"].toInt();
double ledUpdateFrequency_hz = def.args["smoothing-updateFrequency"].toDouble();
unsigned updateDelay {0};
Debug(_log, "Effect \"%s\": Add custom smoothing settings [%d]. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(def.name), specificId, settlingTime_ms, ledUpdateFrequency_hz);
def.smoothCfg = _hyperion->updateSmoothingConfig(
++specificId,
def.args["smoothing-time_ms"].toInt(),
def.args["smoothing-updateFrequency"].toDouble(),
0 );
//Debug( _log, "Customs Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
++specificId,
settlingTime_ms,
ledUpdateFrequency_hz,
updateDelay );
}
else
{
def.smoothCfg = SmoothingConfigID::SYSTEM;
//Debug( _log, "Default Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
}
_availableEffects.push_back(def);
}
@ -157,11 +161,18 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args,
//In case smoothing information is provided dynamically use temp smoothing config item (2)
if (smoothCfg == SmoothingConfigID::SYSTEM && args["smoothing-custom-settings"].toBool())
{
int settlingTime_ms = args["smoothing-time_ms"].toInt();
double ledUpdateFrequency_hz = args["smoothing-updateFrequency"].toDouble();
unsigned updateDelay {0};
Debug(_log, "Effect \"%s\": Apply dynamic smoothing settings, if smoothing. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(effectName), settlingTime_ms, ledUpdateFrequency_hz);
smoothCfg = _hyperion->updateSmoothingConfig(
SmoothingConfigID::EFFECT_DYNAMIC,
args["smoothing-time_ms"].toInt(),
args["smoothing-updateFrequency"].toDouble(),
0 );
SmoothingConfigID::EFFECT_DYNAMIC,
settlingTime_ms,
ledUpdateFrequency_hz,
updateDelay
);
}
if (pythonScript.isEmpty())

View File

@ -53,9 +53,12 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
Py_RETURN_NOTIMPLEMENTED;
case QJsonValue::Double:
{
if (std::round(jsonData.toDouble()) != jsonData.toDouble())
double doubleIntegratlPart;
double doubleFractionalPart = std::modf(jsonData.toDouble(), &doubleIntegratlPart);
if (doubleFractionalPart > std::numeric_limits<double>::epsilon())
{
return Py_BuildValue("d", jsonData.toDouble());
}
return Py_BuildValue("i", jsonData.toInt());
}
case QJsonValue::Bool:
@ -184,7 +187,8 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
{
// bytearray of values
int width, height;
int width = 0;
int height = 0;
PyObject * bytearray = nullptr;
if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray))
{
@ -391,8 +395,10 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
int startRY = 0;
int startX = 0;
int startY = 0;
int endX, width = getEffect()->_imageSize.width();
int endY, height = getEffect()->_imageSize.height();
int width = getEffect()->_imageSize.width();
int endX {width};
int height = getEffect()->_imageSize.height();
int endY {height};
int spread = 0;
bool argsOK = false;
@ -454,7 +460,9 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
PyObject * bytearray = nullptr;
int centerX, centerY, angle;
int centerX = 0;
int centerY = 0;
int angle = 0;
int startX = 0;
int startY = 0;
int width = getEffect()->_imageSize.width();
@ -520,7 +528,13 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
PyObject * bytearray = nullptr;
int centerX, centerY, radius, focalX, focalY, focalRadius, spread;
int centerX = 0;
int centerY = 0;
int radius = 0;
int focalX = 0;
int focalY = 0;
int focalRadius =0;
int spread = 0;
int startX = 0;
int startY = 0;
int width = getEffect()->_imageSize.width();
@ -599,7 +613,9 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
PyObject * bytearray = nullptr;
int argCount = PyTuple_Size(args);
int r, g, b;
int r = 0;
int g = 0;
int b = 0;
int a = 255;
bool argsOK = false;
@ -658,7 +674,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
QString brush;
int argCount = PyTuple_Size(args);
int radius, centerX, centerY;
int radius = 0;
int centerX = 0;
int centerY = 0;
int startAngle = 0;
int spanAngle = 360;
int r = 0;
@ -749,7 +767,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int r, g, b;
int r = 0;
int g = 0;
int b = 0;
int a = 255;
int startX = 0;
int startY = 0;
@ -788,8 +808,10 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int r, g, b;
int a = 255;
int r = 0;
int g = 0;
int b = 0;
int a = 255;
int startX = 0;
int startY = 0;
int thick = 1;
@ -826,8 +848,12 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int r, g, b, x, y;
int a = 255;
int r = 0;
int g = 0;
int b = 0;
int x = 0;
int y = 0;
int a = 255;
int thick = 1;
bool argsOK = false;
@ -859,8 +885,10 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int r, g, b;
int a = 255;
int r = 0;
int g = 0;
int b = 0;
int a = 255;
int startX = 0;
int startY = 0;
int thick = 1;
@ -898,7 +926,11 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int r, g, b, x, y;
int r = 0;
int g = 0;
int b = 0;
int x = 0;
int y = 0;
if ( argCount == 5 && PyArg_ParseTuple(args, "iiiii", &x, &y, &r, &g, &b ) )
{
@ -913,7 +945,8 @@ PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int x, y;
int x = 0;
int y = 0;
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y) )
{
@ -934,7 +967,8 @@ PyObject* EffectModule::wrapImageSave(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
{
int argCount = PyTuple_Size(args);
int w, h;
int w = 0;
int h = 0;
int width = getEffect()->_imageSize.width();
int height = getEffect()->_imageSize.height();
@ -994,7 +1028,8 @@ PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageCShear(PyObject *self, PyObject *args)
{
int sh,sv;
int sh = 0;
int sv = 0;
int argCount = PyTuple_Size(args);
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv ))

View File

@ -115,6 +115,9 @@ void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
case hyperion::COMP_V4L:
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break;
case hyperion::COMP_AUDIO:
connect(_hyperion, &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break;
#if defined(ENABLE_FLATBUF_SERVER)
case hyperion::COMP_FLATBUFSERVER:
#endif
@ -138,7 +141,7 @@ void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
else
{
_forwarder_enabled = false;
Warning(_log,"No JSON- nor Flatbuffer-Forwarder configured -> Forwarding disabled", _forwarder_enabled);
Warning(_log,"No JSON- nor Flatbuffer-Forwarder configured -> Forwarding disabled");
}
}
_hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _forwarder_enabled);
@ -153,6 +156,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
switch (activeCompId) {
case hyperion::COMP_GRABBER:
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif
@ -160,11 +164,20 @@ void MessageForwarder::handlePriorityChanges(int priority)
break;
case hyperion::COMP_V4L:
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break;
case hyperion::COMP_AUDIO:
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif
connect(_hyperion, &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break;
#if defined(ENABLE_FLATBUF_SERVER)
case hyperion::COMP_FLATBUFSERVER:
#endif
@ -172,6 +185,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
case hyperion::COMP_PROTOSERVER:
#endif
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
connect(_hyperion, &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
@ -180,6 +194,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
default:
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif
@ -373,6 +388,7 @@ void MessageForwarder::stopFlatbufferTargets()
{
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif

View File

@ -33,3 +33,7 @@ endif(ENABLE_QT)
if (ENABLE_DX)
add_subdirectory(directx)
endif(ENABLE_DX)
if (ENABLE_AUDIO)
add_subdirectory(audio)
endif()

View File

@ -0,0 +1,201 @@
#include <grabber/AudioGrabber.h>
#include <math.h>
#include <QImage>
#include <QObject>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonValue>
// Constants
namespace {
const uint16_t RESOLUTION = 255;
}
#if (QT_VERSION < QT_VERSION_CHECK(5, 14, 0))
namespace QColorConstants
{
const QColor Black = QColor(0xFF, 0x00, 0x00);
const QColor Red = QColor(0xFF, 0x00, 0x00);
const QColor Green = QColor(0x00, 0xFF, 0x00);
const QColor Blue = QColor(0x00, 0x00, 0xFF);
const QColor Yellow = QColor(0xFF, 0xFF, 0x00);
}
#endif
//End of constants
AudioGrabber::AudioGrabber()
: Grabber("AudioGrabber")
, _deviceProperties()
, _device("none")
, _hotColor(QColorConstants::Red)
, _warnValue(80)
, _warnColor(QColorConstants::Yellow)
, _safeValue(45)
, _safeColor(QColorConstants::Green)
, _multiplier(0)
, _tolerance(20)
, _dynamicMultiplier(INT16_MAX)
, _started(false)
{
}
AudioGrabber::~AudioGrabber()
{
freeResources();
}
void AudioGrabber::freeResources()
{
}
void AudioGrabber::setDevice(const QString& device)
{
_device = device;
if (_started)
{
this->stop();
this->start();
}
}
void AudioGrabber::setConfiguration(const QJsonObject& config)
{
QJsonArray hotColorArray = config["hotColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(255), QVariant(0), QVariant(0) })));
QJsonArray warnColorArray = config["warnColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(255), QVariant(255), QVariant(0) })));
QJsonArray safeColorArray = config["safeColor"].toArray(QJsonArray::fromVariantList(QList<QVariant>({ QVariant(0), QVariant(255), QVariant(0) })));
_hotColor = QColor(hotColorArray.at(0).toInt(), hotColorArray.at(1).toInt(), hotColorArray.at(2).toInt());
_warnColor = QColor(warnColorArray.at(0).toInt(), warnColorArray.at(1).toInt(), warnColorArray.at(2).toInt());
_safeColor = QColor(safeColorArray.at(0).toInt(), safeColorArray.at(1).toInt(), safeColorArray.at(2).toInt());
_warnValue = config["warnValue"].toInt(80);
_safeValue = config["safeValue"].toInt(45);
_multiplier = config["multiplier"].toDouble(0);
_tolerance = config["tolerance"].toInt(20);
}
void AudioGrabber::resetMultiplier()
{
_dynamicMultiplier = INT16_MAX;
}
void AudioGrabber::processAudioFrame(int16_t* buffer, int length)
{
// Apply Visualizer and Construct Image
// TODO: Pass Audio Frame to python and let the script calculate the image.
// TODO: Support Stereo capture with different meters per side
// Default VUMeter - Later Make this pluggable for different audio effects
double averageAmplitude = 0;
// Calculate the the average amplitude value in the buffer
for (int i = 0; i < length; i++)
{
averageAmplitude += fabs(buffer[i]) / length;
}
double * currentMultiplier;
if (_multiplier < std::numeric_limits<double>::epsilon())
{
// Dynamically calculate multiplier.
const double pendingMultiplier = INT16_MAX / fmax(1.0, averageAmplitude + ((_tolerance / 100.0) * averageAmplitude));
if (pendingMultiplier < _dynamicMultiplier)
_dynamicMultiplier = pendingMultiplier;
currentMultiplier = &_dynamicMultiplier;
}
else
{
// User defined multiplier
currentMultiplier = &_multiplier;
}
// Apply multiplier to average amplitude
const double result = averageAmplitude * (*currentMultiplier);
// Calculate the average percentage
const double percentage = fmin(result / INT16_MAX, 1);
// Calculate the value
const int value = static_cast<int>(ceil(percentage * RESOLUTION));
// Draw Image
QImage image(1, RESOLUTION, QImage::Format_RGB888);
image.fill(QColorConstants::Black);
int safePixelValue = static_cast<int>(round(( _safeValue / 100.0) * RESOLUTION));
int warnPixelValue = static_cast<int>(round(( _warnValue / 100.0) * RESOLUTION));
for (int i = 0; i < RESOLUTION; i++)
{
QColor color = QColorConstants::Black;
int position = RESOLUTION - i;
if (position < safePixelValue)
{
color = _safeColor;
}
else if (position < warnPixelValue)
{
color = _warnColor;
}
else
{
color = _hotColor;
}
if (position < value)
{
image.setPixelColor(0, i, color);
}
else
{
image.setPixelColor(0, i, QColorConstants::Black);
}
}
// Convert to Image<ColorRGB>
Image<ColorRgb> finalImage (static_cast<unsigned>(image.width()), static_cast<unsigned>(image.height()));
for (int y = 0; y < image.height(); y++)
{
memcpy((unsigned char*)finalImage.memptr() + y * image.width() * 3, static_cast<unsigned char*>(image.scanLine(y)), image.width() * 3);
}
emit newFrame(finalImage);
}
Logger* AudioGrabber::getLog()
{
return _log;
}
bool AudioGrabber::start()
{
resetMultiplier();
_started = true;
return true;
}
void AudioGrabber::stop()
{
_started = false;
}
void AudioGrabber::restart()
{
stop();
start();
}
QJsonArray AudioGrabber::discover(const QJsonObject& /*params*/)
{
QJsonArray result; // Return empty result
return result;
}

View File

@ -0,0 +1,317 @@
#include <grabber/AudioGrabberLinux.h>
#include <alsa/asoundlib.h>
#include <QJsonObject>
#include <QJsonArray>
typedef void* (*THREADFUNCPTR)(void*);
AudioGrabberLinux::AudioGrabberLinux()
: AudioGrabber()
, _isRunning{ false }
, _captureDevice {nullptr}
, _sampleRate(44100)
{
}
AudioGrabberLinux::~AudioGrabberLinux()
{
this->stop();
}
void AudioGrabberLinux::refreshDevices()
{
Debug(_log, "Enumerating Audio Input Devices");
_deviceProperties.clear();
snd_ctl_t* deviceHandle;
int soundCard {-1};
int error {-1};
int cardInput {-1};
snd_ctl_card_info_t* cardInfo;
snd_pcm_info_t* deviceInfo;
snd_ctl_card_info_alloca(&cardInfo);
snd_pcm_info_alloca(&deviceInfo);
while (snd_card_next(&soundCard) > -1)
{
if (soundCard < 0)
{
break;
}
char cardId[32];
sprintf(cardId, "hw:%d", soundCard);
if ((error = snd_ctl_open(&deviceHandle, cardId, SND_CTL_NONBLOCK)) < 0)
{
Error(_log, "Erorr opening device: (%i): %s", soundCard, snd_strerror(error));
continue;
}
if ((error = snd_ctl_card_info(deviceHandle, cardInfo)) < 0)
{
Error(_log, "Erorr getting hardware info: (%i): %s", soundCard, snd_strerror(error));
snd_ctl_close(deviceHandle);
continue;
}
cardInput = -1;
while (true)
{
if (snd_ctl_pcm_next_device(deviceHandle, &cardInput) < 0)
Error(_log, "Error selecting device input");
if (cardInput < 0)
break;
snd_pcm_info_set_device(deviceInfo, static_cast<uint>(cardInput));
snd_pcm_info_set_subdevice(deviceInfo, 0);
snd_pcm_info_set_stream(deviceInfo, SND_PCM_STREAM_CAPTURE);
if ((error = snd_ctl_pcm_info(deviceHandle, deviceInfo)) < 0)
{
if (error != -ENOENT)
Error(_log, "Digital Audio Info: (%i): %s", soundCard, snd_strerror(error));
continue;
}
AudioGrabber::DeviceProperties device;
device.id = QString("hw:%1,%2").arg(snd_pcm_info_get_card(deviceInfo)).arg(snd_pcm_info_get_device(deviceInfo));
device.name = QString("%1: %2").arg(snd_ctl_card_info_get_name(cardInfo),snd_pcm_info_get_name(deviceInfo));
Debug(_log, "Found sound card (%s): %s", QSTRING_CSTR(device.id), QSTRING_CSTR(device.name));
_deviceProperties.insert(device.id, device);
}
snd_ctl_close(deviceHandle);
}
}
bool AudioGrabberLinux::configureCaptureInterface()
{
int error = -1;
QString name = (_device.isEmpty() || _device == "auto") ? "default" : (_device);
if ((error = snd_pcm_open(&_captureDevice, QSTRING_CSTR(name) , SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0)
{
Error(_log, "Failed to open audio device: %s, - %s", QSTRING_CSTR(_device), snd_strerror(error));
return false;
}
if ((error = snd_pcm_hw_params_malloc(&_captureDeviceConfig)) < 0)
{
Error(_log, "Failed to create hardware parameters: %s", snd_strerror(error));
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_hw_params_any(_captureDevice, _captureDeviceConfig)) < 0)
{
Error(_log, "Failed to initialize hardware parameters: %s", snd_strerror(error));
snd_pcm_hw_params_free(_captureDeviceConfig);
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_hw_params_set_access(_captureDevice, _captureDeviceConfig, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
{
Error(_log, "Failed to configure interleaved mode: %s", snd_strerror(error));
snd_pcm_hw_params_free(_captureDeviceConfig);
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_hw_params_set_format(_captureDevice, _captureDeviceConfig, SND_PCM_FORMAT_S16_LE)) < 0)
{
Error(_log, "Failed to configure capture format: %s", snd_strerror(error));
snd_pcm_hw_params_free(_captureDeviceConfig);
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_hw_params_set_rate_near(_captureDevice, _captureDeviceConfig, &_sampleRate, nullptr)) < 0)
{
Error(_log, "Failed to configure sample rate: %s", snd_strerror(error));
snd_pcm_hw_params_free(_captureDeviceConfig);
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_hw_params(_captureDevice, _captureDeviceConfig)) < 0)
{
Error(_log, "Failed to configure hardware parameters: %s", snd_strerror(error));
snd_pcm_hw_params_free(_captureDeviceConfig);
snd_pcm_close(_captureDevice);
return false;
}
snd_pcm_hw_params_free(_captureDeviceConfig);
if ((error = snd_pcm_prepare(_captureDevice)) < 0)
{
Error(_log, "Failed to prepare audio interface: %s", snd_strerror(error));
snd_pcm_close(_captureDevice);
return false;
}
if ((error = snd_pcm_start(_captureDevice)) < 0)
{
Error(_log, "Failed to start audio interface: %s", snd_strerror(error));
snd_pcm_close(_captureDevice);
return false;
}
return true;
}
bool AudioGrabberLinux::start()
{
if (!_isEnabled)
return false;
if (_isRunning.load(std::memory_order_acquire))
return true;
Debug(_log, "Start Audio With %s", QSTRING_CSTR(getDeviceName(_device)));
if (!configureCaptureInterface())
return false;
_isRunning.store(true, std::memory_order_release);
pthread_attr_t threadAttributes;
int threadPriority = 1;
sched_param schedulerParameter;
schedulerParameter.sched_priority = threadPriority;
if (pthread_attr_init(&threadAttributes) != 0)
{
Debug(_log, "Failed to create thread attributes");
stop();
return false;
}
if (pthread_create(&_audioThread, &threadAttributes, static_cast<THREADFUNCPTR>(&AudioThreadRunner), static_cast<void*>(this)) != 0)
{
Debug(_log, "Failed to create audio capture thread");
stop();
return false;
}
AudioGrabber::start();
return true;
}
void AudioGrabberLinux::stop()
{
if (!_isRunning.load(std::memory_order_acquire))
return;
Debug(_log, "Stopping Audio Interface");
_isRunning.store(false, std::memory_order_release);
if (_audioThread != 0) {
pthread_join(_audioThread, NULL);
}
snd_pcm_close(_captureDevice);
AudioGrabber::stop();
}
void AudioGrabberLinux::processAudioBuffer(snd_pcm_sframes_t frames)
{
if (!_isRunning.load(std::memory_order_acquire))
return;
ssize_t bytes = snd_pcm_frames_to_bytes(_captureDevice, frames);
int16_t * buffer = static_cast<int16_t*>(calloc(static_cast<size_t>(bytes / 2), sizeof(int16_t)));
if (frames == 0)
{
buffer[0] = 0;
processAudioFrame(buffer, 1);
}
else
{
snd_pcm_sframes_t framesRead = snd_pcm_readi(_captureDevice, buffer, static_cast<snd_pcm_uframes_t>(frames));
if (framesRead < frames)
{
Error(_log, "Error reading audio. Got %d frames instead of %d", framesRead, frames);
}
else
{
processAudioFrame(buffer, static_cast<int>(snd_pcm_frames_to_bytes(_captureDevice, framesRead)) / 2);
}
}
free(buffer);
}
QJsonArray AudioGrabberLinux::discover(const QJsonObject& /*params*/)
{
refreshDevices();
QJsonArray devices;
for (auto deviceIterator = _deviceProperties.begin(); deviceIterator != _deviceProperties.end(); ++deviceIterator)
{
// Device
QJsonObject device;
QJsonArray deviceInputs;
device["device"] = deviceIterator.key();
device["device_name"] = deviceIterator.value().name;
device["type"] = "audio";
devices.append(device);
}
return devices;
}
QString AudioGrabberLinux::getDeviceName(const QString& devicePath) const
{
if (devicePath.isEmpty() || devicePath == "auto")
{
return "Default Audio Device";
}
return _deviceProperties.value(devicePath).name;
}
static void * AudioThreadRunner(void* params)
{
AudioGrabberLinux* This = static_cast<AudioGrabberLinux*>(params);
Debug(This->getLog(), "Audio Thread Started");
snd_pcm_sframes_t framesAvailable = 0;
while (This->_isRunning.load(std::memory_order_acquire))
{
snd_pcm_wait(This->_captureDevice, 1000);
if ((framesAvailable = snd_pcm_avail(This->_captureDevice)) > 0)
This->processAudioBuffer(framesAvailable);
sched_yield();
}
Debug(This->getLog(), "Audio Thread Shutting Down");
return nullptr;
}

View File

@ -0,0 +1,354 @@
#include <grabber/AudioGrabberWindows.h>
#include <QImage>
#include <QJsonObject>
#include <QJsonArray>
#pragma comment(lib,"dsound.lib")
#pragma comment(lib, "dxguid.lib")
// Constants
namespace {
const int AUDIO_NOTIFICATION_COUNT{ 4 };
} //End of constants
AudioGrabberWindows::AudioGrabberWindows() : AudioGrabber()
{
}
AudioGrabberWindows::~AudioGrabberWindows()
{
this->stop();
}
void AudioGrabberWindows::refreshDevices()
{
Debug(_log, "Refreshing Audio Devices");
_deviceProperties.clear();
// Enumerate Devices
if (FAILED(DirectSoundCaptureEnumerate(DirectSoundEnumProcessor, (VOID*)&_deviceProperties)))
{
Error(_log, "Failed to enumerate audio devices.");
}
}
bool AudioGrabberWindows::configureCaptureInterface()
{
CLSID deviceId {};
if (!this->_device.isEmpty() && this->_device != "auto")
{
LPCOLESTR clsid = reinterpret_cast<const wchar_t*>(_device.utf16());
HRESULT res = CLSIDFromString(clsid, &deviceId);
if (FAILED(res))
{
Error(_log, "Failed to get CLSID for '%s' with error: 0x%08x: %s", QSTRING_CSTR(_device), res, std::system_category().message(res).c_str());
return false;
}
}
// Create Capture Device
HRESULT res = DirectSoundCaptureCreate8(&deviceId, &recordingDevice, NULL);
if (FAILED(res))
{
Error(_log, "Failed to create capture device: '%s' with error: 0x%08x: %s", QSTRING_CSTR(_device), res, std::system_category().message(res).c_str());
return false;
}
// Define Audio Format & Create Buffer
WAVEFORMATEX audioFormat { WAVE_FORMAT_PCM, 1, 44100, 88200, 2, 16, 0 };
// wFormatTag, nChannels, nSamplesPerSec, mAvgBytesPerSec,
// nBlockAlign, wBitsPerSample, cbSize
notificationSize = max(1024, audioFormat.nAvgBytesPerSec / 8);
notificationSize -= notificationSize % audioFormat.nBlockAlign;
bufferCaptureSize = notificationSize * AUDIO_NOTIFICATION_COUNT;
DSCBUFFERDESC bufferDesc;
bufferDesc.dwSize = sizeof(DSCBUFFERDESC);
bufferDesc.dwFlags = 0;
bufferDesc.dwBufferBytes = bufferCaptureSize;
bufferDesc.dwReserved = 0;
bufferDesc.lpwfxFormat = &audioFormat;
bufferDesc.dwFXCount = 0;
bufferDesc.lpDSCFXDesc = NULL;
// Create Capture Device's Buffer
LPDIRECTSOUNDCAPTUREBUFFER preBuffer;
if (FAILED(recordingDevice->CreateCaptureBuffer(&bufferDesc, &preBuffer, NULL)))
{
Error(_log, "Failed to create capture buffer: '%s'", QSTRING_CSTR(getDeviceName(_device)));
recordingDevice->Release();
return false;
}
bufferCapturePosition = 0;
// Query Capture8 Buffer
if (FAILED(preBuffer->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*)&recordingBuffer)))
{
Error(_log, "Failed to retrieve recording buffer");
preBuffer->Release();
return false;
}
preBuffer->Release();
// Create Notifications
LPDIRECTSOUNDNOTIFY8 notify;
if (FAILED(recordingBuffer->QueryInterface(IID_IDirectSoundNotify8, (LPVOID *) &notify)))
{
Error(_log, "Failed to configure buffer notifications: '%s'", QSTRING_CSTR(getDeviceName(_device)));
recordingDevice->Release();
recordingBuffer->Release();
return false;
}
// Create Events
notificationEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (notificationEvent == NULL)
{
Error(_log, "Failed to configure buffer notifications events: '%s'", QSTRING_CSTR(getDeviceName(_device)));
notify->Release();
recordingDevice->Release();
recordingBuffer->Release();
return false;
}
// Configure Notifications
DSBPOSITIONNOTIFY positionNotify[AUDIO_NOTIFICATION_COUNT];
for (int i = 0; i < AUDIO_NOTIFICATION_COUNT; i++)
{
positionNotify[i].dwOffset = (notificationSize * i) + notificationSize - 1;
positionNotify[i].hEventNotify = notificationEvent;
}
// Set Notifications
notify->SetNotificationPositions(AUDIO_NOTIFICATION_COUNT, positionNotify);
notify->Release();
return true;
}
bool AudioGrabberWindows::start()
{
if (!_isEnabled)
{
return false;
}
if (this->isRunning.load(std::memory_order_acquire))
{
return true;
}
//Test, if configured device currently exists
refreshDevices();
if (!_deviceProperties.contains(_device))
{
_device = "auto";
Warning(_log, "Configured audio device is not available. Using '%s'", QSTRING_CSTR(getDeviceName(_device)));
}
Info(_log, "Capture audio from %s", QSTRING_CSTR(getDeviceName(_device)));
if (!this->configureCaptureInterface())
{
return false;
}
if (FAILED(recordingBuffer->Start(DSCBSTART_LOOPING)))
{
Error(_log, "Failed starting audio capture from '%s'", QSTRING_CSTR(getDeviceName(_device)));
return false;
}
this->isRunning.store(true, std::memory_order_release);
DWORD threadId;
this->audioThread = CreateThread(
NULL,
16,
AudioThreadRunner,
(void *) this,
0,
&threadId
);
if (this->audioThread == NULL)
{
Error(_log, "Failed to create audio capture thread");
this->stop();
return false;
}
AudioGrabber::start();
return true;
}
void AudioGrabberWindows::stop()
{
if (!this->isRunning.load(std::memory_order_acquire))
{
return;
}
Info(_log, "Shutting down audio capture from: '%s'", QSTRING_CSTR(getDeviceName(_device)));
this->isRunning.store(false, std::memory_order_release);
if (FAILED(recordingBuffer->Stop()))
{
Error(_log, "Audio capture failed to stop: '%s'", QSTRING_CSTR(getDeviceName(_device)));
}
if (FAILED(recordingBuffer->Release()))
{
Error(_log, "Failed to release recording buffer: '%s'", QSTRING_CSTR(getDeviceName(_device)));
}
if (FAILED(recordingDevice->Release()))
{
Error(_log, "Failed to release recording device: '%s'", QSTRING_CSTR(getDeviceName(_device)));
}
CloseHandle(notificationEvent);
CloseHandle(this->audioThread);
AudioGrabber::stop();
}
DWORD WINAPI AudioGrabberWindows::AudioThreadRunner(LPVOID param)
{
AudioGrabberWindows* This = (AudioGrabberWindows*) param;
while (This->isRunning.load(std::memory_order_acquire))
{
DWORD result = WaitForMultipleObjects(1, &This->notificationEvent, true, 500);
switch (result)
{
case WAIT_OBJECT_0:
This->processAudioBuffer();
break;
}
}
Debug(This->_log, "Audio capture thread stopped.");
return 0;
}
void AudioGrabberWindows::processAudioBuffer()
{
DWORD readPosition;
DWORD capturePosition;
// Primary segment
VOID* capturedAudio;
DWORD capturedAudioLength;
// Wrap around segment
VOID* capturedAudio2;
DWORD capturedAudio2Length;
LONG lockSize;
if (FAILED(recordingBuffer->GetCurrentPosition(&capturePosition, &readPosition)))
{
// Failed to get current position
Error(_log, "Failed to get buffer position.");
return;
}
lockSize = readPosition - bufferCapturePosition;
if (lockSize < 0)
{
lockSize += bufferCaptureSize;
}
// Block Align
lockSize -= (lockSize % notificationSize);
if (lockSize == 0)
{
return;
}
// Lock Capture Buffer
if (FAILED(recordingBuffer->Lock(bufferCapturePosition, lockSize, &capturedAudio, &capturedAudioLength,
&capturedAudio2, &capturedAudio2Length, 0)))
{
// Handle Lock Error
return;
}
bufferCapturePosition += capturedAudioLength;
bufferCapturePosition %= bufferCaptureSize; // Circular Buffer
int frameSize = capturedAudioLength + capturedAudio2Length;
int16_t * readBuffer = new int16_t[frameSize];
// Buffer wrapped around, read second position
if (capturedAudio2 != NULL)
{
bufferCapturePosition += capturedAudio2Length;
bufferCapturePosition %= bufferCaptureSize; // Circular Buffer
}
// Copy Buffer into memory
CopyMemory(readBuffer, capturedAudio, capturedAudioLength);
if (capturedAudio2 != NULL)
{
CopyMemory(readBuffer + capturedAudioLength, capturedAudio2, capturedAudio2Length);
}
// Release Buffer Lock
recordingBuffer->Unlock(capturedAudio, capturedAudioLength, capturedAudio2, capturedAudio2Length);
// Process Audio Frame
this->processAudioFrame(readBuffer, frameSize);
delete[] readBuffer;
}
QJsonArray AudioGrabberWindows::discover(const QJsonObject& params)
{
refreshDevices();
QJsonArray devices;
for (auto deviceIterator = _deviceProperties.begin(); deviceIterator != _deviceProperties.end(); ++deviceIterator)
{
// Device
QJsonObject device;
QJsonArray deviceInputs;
device["device"] = deviceIterator.value().id;
device["device_name"] = deviceIterator.value().name;
device["type"] = "audio";
devices.append(device);
}
return devices;
}
QString AudioGrabberWindows::getDeviceName(const QString& devicePath) const
{
if (devicePath.isEmpty() || devicePath == "auto")
{
return "Default Device";
}
return _deviceProperties.value(devicePath).name;
}

View File

@ -0,0 +1,63 @@
#include <grabber/AudioWrapper.h>
#include <hyperion/GrabberWrapper.h>
#include <QObject>
#include <QMetaType>
AudioWrapper::AudioWrapper()
: GrabberWrapper("AudioGrabber", &_grabber)
, _grabber()
{
// register the image type
qRegisterMetaType<Image<ColorRgb>>("Image<ColorRgb>");
connect(&_grabber, &AudioGrabber::newFrame, this, &AudioWrapper::newFrame, Qt::DirectConnection);
}
AudioWrapper::~AudioWrapper()
{
stop();
}
bool AudioWrapper::start()
{
return (_grabber.start() && GrabberWrapper::start());
}
void AudioWrapper::stop()
{
_grabber.stop();
GrabberWrapper::stop();
}
void AudioWrapper::action()
{
// Dummy we will push the audio images
}
void AudioWrapper::newFrame(const Image<ColorRgb>& image)
{
emit systemImage(_grabberName, image);
}
void AudioWrapper::handleSettingsUpdate(settings::type type, const QJsonDocument& config)
{
if (type == settings::AUDIO)
{
const QJsonObject& obj = config.object();
// set global grabber state
setAudioGrabberState(obj["enable"].toBool(false));
if (getAudioGrabberState())
{
_grabber.setDevice(obj["device"].toString());
_grabber.setConfiguration(obj);
_grabber.restart();
}
else
{
stop();
}
}
}

Some files were not shown because too many files have changed in this diff Show More