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

View File

@ -2,64 +2,64 @@
{ {
"distribution": "Bionic", "distribution": "Bionic",
"architecture": "amd64", "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", "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, libturbojpeg, libcec4", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 18.04 (Bionic Beaver) (amd64)" "description": "Ubuntu 18.04 (Bionic Beaver) (amd64)"
}, },
{ {
"distribution": "Focal", "distribution": "Focal",
"architecture": "amd64", "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", "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, libturbojpeg, libcec4", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 20.04 (Focal Fossa) (amd64)" "description": "Ubuntu 20.04 (Focal Fossa) (amd64)"
}, },
{ {
"distribution": "Jammy", "distribution": "Jammy",
"architecture": "amd64", "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", "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, libturbojpeg, libcec6", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 22.04 (Jammy Jellyfish) (amd64)" "description": "Ubuntu 22.04 (Jammy Jellyfish) (amd64)"
}, },
{ {
"distribution": "Kinetic", "distribution": "Kinetic",
"architecture": "amd64", "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", "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, libturbojpeg, libcec6", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)" "description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)"
}, },
{ {
"distribution": "Stretch", "distribution": "Stretch",
"architecture": "amd64", "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", "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, libturbojpeg0, libcec4", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 9.x (Stretch) (amd64)" "description": "Debian 9.x (Stretch) (amd64)"
}, },
{ {
"distribution": "Buster", "distribution": "Buster",
"architecture": "amd64", "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", "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, libturbojpeg0, libcec4", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 10.x (Buster) (amd64)" "description": "Debian 10.x (Buster) (amd64)"
}, },
{ {
"distribution": "Bullseye", "distribution": "Bullseye",
"architecture": "amd64", "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", "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, libturbojpeg0, libcec6", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 11.x (Bullseye) (amd64)" "description": "Debian 11.x (Bullseye) (amd64)"
}, },
{ {
"distribution": "Bookworm", "distribution": "Bookworm",
"architecture": "amd64", "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", "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, libturbojpeg0, libcec6", "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", "cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
"description": "Debian 12.x (Bookworm) (amd64)" "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 reprepro -Vb nightly export
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3.0.1 uses: actions/download-artifact@v3.0.2
- name: Include artifacts into the package source - name: Include artifacts into the package source
run: | run: |
@ -171,7 +171,7 @@ jobs:
done done
- name: Upload packages to nightly server - name: Upload packages to nightly server
uses: SamKirkland/FTP-Deploy-Action@4.3.2 uses: SamKirkland/FTP-Deploy-Action@4.3.3
with: with:
server: nightly.apt.hyperion-project.org server: nightly.apt.hyperion-project.org
username: ${{ secrets.NIGHTLY_USER }} username: ${{ secrets.NIGHTLY_USER }}

View File

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

View File

@ -10,7 +10,7 @@ jobs:
steps: steps:
# Dispatch event to build new HyperBian image # Dispatch event to build new HyperBian image
- name: Dispatch HyperBian build - 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'}} if: ${{ github.repository_owner == 'hyperion-project'}}
with: with:
repository: hyperion-project/HyperBian repository: hyperion-project/HyperBian

1
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@ -12,3 +12,6 @@
[submodule "dependencies/external/qmdnsengine"] [submodule "dependencies/external/qmdnsengine"]
path = dependencies/external/qmdnsengine path = dependencies/external/qmdnsengine
url = https://github.com/nitroshare/qmdnsengine.git 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/), 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). 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 ### Breaking
### Added ### 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 - Allow to Disable / Enable all instances (#970) by
- Suspend/Resume support for Linux and Windows (#1493,#1282, #978). - Suspend/Resume support for Linux and Windows (#1493,#1282, #978).
Suspend/Resume/Restart is supported via API, UI, Systray and hyperion-remote 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). 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. 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 - 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) - Add instance# in API response (#1504)
### Changed ### 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 ### 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) - 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 ## Removed

View File

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

View File

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

View File

@ -1,6 +1,6 @@
MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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) [![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) [![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) [![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) [![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) [![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 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 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: try:
intentional_exit = False
while True: while True:
try: try:
while True: while True:
@ -180,7 +179,7 @@ to this service over the network.
# probably got disconnected # probably got disconnected
break break
except KeyboardInterrupt: except KeyboardInterrupt:
intentional_exit = True # intentional_exit
raise raise
except socket.error as msg: except socket.error as msg:
if args.develop: if args.develop:
@ -190,6 +189,7 @@ to this service over the network.
ser_to_net.socket = None ser_to_net.socket = None
sys.stderr.write('Disconnected\n') sys.stderr.write('Disconnected\n')
except KeyboardInterrupt: except KeyboardInterrupt:
# do not handle exceptions
pass pass
sys.stderr.write('\n--- exit ---\n') sys.stderr.write('\n--- exit ---\n')

View File

@ -322,6 +322,17 @@
</select> </select>
</td> </td>
</tr> </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> <tr>
<td class="ltd"> <td class="ltd">
<label class="ltdlabel" for="ip_ma_start" data-i18n="conf_leds_layout_ma_position">Input</label> <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> <a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_videograbber')" style="text-decoration: none; cursor: pointer"></a>
</td> </td>
</tr> </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> </tbody>
</table> </table>
<table id="dash_ports" class="table borderless"> <table id="dash_ports" class="table borderless">
@ -135,6 +143,7 @@
</div> </div>
<!-- /.row --> <!-- /.row -->
</div> </div>
</div>
<!-- /.container-fluid --> <!-- /.container-fluid -->
<script src="/js/content_dashboard.js"></script> <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_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_text": "Hyperion hat keinen Schreibzugriff auf die aktuell geladene Konfiguration. Bitte korrigiere die Dateizugriffsrechte, um fortzufahren.",
"InfoDialog_nowrite_title": "Fehler beim Schreibzugriff!", "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": "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_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", "about_build": "Build",
@ -41,6 +44,7 @@
"conf_general_inst_title": "LED-Hardware Instanzverwaltung", "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_intro": "Grundsätzliche Einstellungen zu Hyperion oder WebUI, die in keine andere Kategorie passen.",
"conf_general_label_title": "Allgemeine Einstellungen", "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_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_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.", "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_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_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": "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_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_info_ws281x": "Hyperion muss mit 'root' Rechten für diesen LED-Steuerungstyp laufen!",
"conf_leds_layout_advanced": "Erweiterte Optionen", "conf_leds_layout_advanced": "Erweiterte Optionen",
"conf_leds_layout_blacklist_num_title": "LED-Anzahl", "conf_leds_layout_blacklist_num_title": "LED-Anzahl",
@ -229,6 +235,27 @@
"edt_append_pixel": "Pixel", "edt_append_pixel": "Pixel",
"edt_append_s": "s", "edt_append_s": "s",
"edt_append_sdegree": "s/grad", "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_expl": "Anzahl an Pixeln, die zusätzlich vom Rand abgeschnitten werden.",
"edt_conf_bb_blurRemoveCnt_title": "Unscharfe Pixel", "edt_conf_bb_blurRemoveCnt_title": "Unscharfe Pixel",
"edt_conf_bb_borderFrameCnt_expl": "Anzahl an Bildern bis ein neuer Rand festgelegt wird.", "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_bb_unknownFrameCnt_title": "Unbekannte Bilder",
"edt_conf_bge_heading_title": "Hintergrund Effekt/Farbe", "edt_conf_bge_heading_title": "Hintergrund Effekt/Farbe",
"edt_conf_bobls_heading_title": "Boblight Server", "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_expl": "Die Hintergrundbeleuchtung kann mit oder ohne Farbanteile genutzt werden.",
"edt_conf_color_backlightColored_title": "Farbige Hintergrundbeleuchtung", "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\")", "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_heading_title": "Farbkalibrierung",
"edt_conf_color_id_expl": "Eine vom Benutzer frei angegebene ID.", "edt_conf_color_id_expl": "Eine vom Benutzer frei angegebene ID.",
"edt_conf_color_id_title": "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_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_expl": "Zugewiesen zu allen (*) LEDs oder nur zu bestimmten LED Nummern (0-17).",
"edt_conf_color_leds_title": "LED-Iindex", "edt_conf_color_leds_title": "LED-Iindex",
@ -282,6 +311,8 @@
"edt_conf_color_magenta_title": "Magenta", "edt_conf_color_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Kalibrierter Rotwert.", "edt_conf_color_red_expl": "Kalibrierter Rotwert.",
"edt_conf_color_red_title": "Rot", "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_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_saturationGain_title": "Sättigungsverstärkung",
"edt_conf_color_white_expl": "Kalibrierter Weißwert.", "edt_conf_color_white_expl": "Kalibrierter Weißwert.",
@ -313,6 +344,8 @@
"edt_conf_enum_color": "Farbe", "edt_conf_enum_color": "Farbe",
"edt_conf_enum_custom": "Benutzerdefiniert", "edt_conf_enum_custom": "Benutzerdefiniert",
"edt_conf_enum_decay": "Dämpfung", "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_error": "nur Fehler",
"edt_conf_enum_dl_informational": "informativ", "edt_conf_enum_dl_informational": "informativ",
"edt_conf_enum_dl_nodebug": "keine Debugausgabe", "edt_conf_enum_dl_nodebug": "keine Debugausgabe",
@ -321,9 +354,12 @@
"edt_conf_enum_dl_verbose1": "Stufe 1", "edt_conf_enum_dl_verbose1": "Stufe 1",
"edt_conf_enum_dl_verbose2": "Stufe 2", "edt_conf_enum_dl_verbose2": "Stufe 2",
"edt_conf_enum_dl_verbose3": "Stufe 3", "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_effect": "Effekt",
"edt_conf_enum_gbr": "GBR", "edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB", "edt_conf_enum_grb": "GRB",
"edt_conf_enum_high": "Hoch",
"edt_conf_enum_hsv": "HSV", "edt_conf_enum_hsv": "HSV",
"edt_conf_enum_left_right": "von links nach rechts", "edt_conf_enum_left_right": "von links nach rechts",
"edt_conf_enum_linear": "Linear", "edt_conf_enum_linear": "Linear",
@ -331,7 +367,10 @@
"edt_conf_enum_logsilent": "Stille", "edt_conf_enum_logsilent": "Stille",
"edt_conf_enum_logverbose": "Ausführlich", "edt_conf_enum_logverbose": "Ausführlich",
"edt_conf_enum_logwarn": "Warnung", "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_please_select": "Bitte auswählen",
"edt_conf_enum_rbg": "RBG", "edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB", "edt_conf_enum_rgb": "RGB",
@ -341,7 +380,7 @@
"edt_conf_enum_transeffect_sudden": "Sofort", "edt_conf_enum_transeffect_sudden": "Sofort",
"edt_conf_enum_udp_ddp": "DDP", "edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW", "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_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_expl": "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
"edt_conf_fbs_timeout_title": "Zeitüberschreitung", "edt_conf_fbs_timeout_title": "Zeitüberschreitung",
@ -400,11 +439,13 @@
"edt_conf_grabber_discovered_title": "Gefundenes Aufnahmegerät", "edt_conf_grabber_discovered_title": "Gefundenes Aufnahmegerät",
"edt_conf_grabber_discovered_title_info": "Wähle dein gefundenes Aufnahmegerät aus.", "edt_conf_grabber_discovered_title_info": "Wähle dein gefundenes Aufnahmegerät aus.",
"edt_conf_grabber_discovery_inprogress": "Suche Aufnahmegeräte", "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_expl": "Das verwendet Bildschirmaufnahmegerät",
"edt_conf_instC_screen_grabber_device_title": "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_expl": "Aktiviert die Bildschirm Aufnahme für diese LED Hardware Instanz.",
"edt_conf_instC_systemEnable_title": "Aktiviere Bildschirm Aufnahme", "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_v4lEnable_title": "Aktiviere USB-Aufnahme",
"edt_conf_instC_video_grabber_device_expl": "Das verwendete Videoaufnahmegerät.", "edt_conf_instC_video_grabber_device_expl": "Das verwendete Videoaufnahmegerät.",
"edt_conf_instC_video_grabber_device_title": "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_heading_title": "Glättung",
"edt_conf_smooth_interpolationRate_expl": "Frequenz in der Zwischenschritte zur Glättung berechnet werden.", "edt_conf_smooth_interpolationRate_expl": "Frequenz in der Zwischenschritte zur Glättung berechnet werden.",
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrequenz", "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_expl": "Wie lange soll die Glättung Bilder sammeln?",
"edt_conf_smooth_time_ms_title": "Zeit", "edt_conf_smooth_time_ms_title": "Zeit",
"edt_conf_smooth_type_expl": "Algorithmus der Glättung.", "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_green": "Grün/Weiß-Kanal Aspekt",
"edt_dev_spec_rgbw_calibration_limit": "Grenzwert für Weißkanal", "edt_dev_spec_rgbw_calibration_limit": "Grenzwert für Weißkanal",
"edt_dev_spec_rgbw_calibration_red": "Rot/Weiß-Kanal Aspekt", "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_serial_title": "Seriennummer",
"edt_dev_spec_spipath_title": "SPI Pfad", "edt_dev_spec_spipath_title": "SPI Pfad",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer Handshake maximum Timeout", "edt_dev_spec_sslHSTimeoutMax_title": "Streamer Handshake maximum Timeout",
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer Handshake minimum 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_stream_protocol_title": "Streaming-Protokoll",
"edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz", "edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Aus bei Minimum", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Aus bei Minimum",
@ -841,6 +887,7 @@
"general_col_blue": "blau", "general_col_blue": "blau",
"general_col_green": "grün", "general_col_green": "grün",
"general_col_red": "rot", "general_col_red": "rot",
"general_comp_AUDIO": "Audioaufnahme",
"general_comp_BLACKBORDER": "Schwarze Balken Erkennung", "general_comp_BLACKBORDER": "Schwarze Balken Erkennung",
"general_comp_BOBLIGHTSERVER": "Boblight Server", "general_comp_BOBLIGHTSERVER": "Boblight Server",
"general_comp_FLATBUFSERVER": "Flatbuffers Server", "general_comp_FLATBUFSERVER": "Flatbuffers Server",
@ -964,8 +1011,11 @@
"remote_losthint": "Notiz: Alle Änderungen gehen nach einem Neustart verloren.", "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_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": "LED-Bereich Zuordnung",
"remote_maptype_label_multicolor_mean": "Mehrfarbig", "remote_maptype_label_dominant_color": "Dominante Farbe",
"remote_maptype_label_unicolor_mean": "Einfarbig", "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_syseffets": "Mitgelieferte Effekte",
"remote_optgroup_templates_custom": "Nutzer Vorlage", "remote_optgroup_templates_custom": "Nutzer Vorlage",
"remote_optgroup_templates_system": "System 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_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_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_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_expl": "Explanation",
"conf_helptable_option": "Option", "conf_helptable_option": "Option",
"conf_leds_config_error": "Error in LED/LED layout configuration", "conf_leds_config_error": "Error in LED/LED layout configuration",
@ -58,11 +59,13 @@
"conf_leds_contr_label_contrtype": "Controller type:", "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_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_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_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_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_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_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": "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_info_ws281x": "Hyperion must run with 'root' privileges for this controller type!",
"conf_leds_layout_advanced": "Advanced Settings", "conf_leds_layout_advanced": "Advanced Settings",
"conf_leds_layout_blacklist_num_title": "Number of LEDs", "conf_leds_layout_blacklist_num_title": "Number of LEDs",
@ -252,6 +255,8 @@
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames", "edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
"edt_conf_bge_heading_title": "Background Effect/Color", "edt_conf_bge_heading_title": "Background Effect/Color",
"edt_conf_bobls_heading_title": "Boblight Server", "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_expl": "Add some color to your backlight.",
"edt_conf_color_backlightColored_title": "Colored 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\"", "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_heading_title": "Color Calibration",
"edt_conf_color_id_expl": "User given name", "edt_conf_color_id_expl": "User given name",
"edt_conf_color_id_title": "ID", "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_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_expl": "Assign this adjustment to all LEDs (*) or just some (0-24).",
"edt_conf_color_leds_title": "LED index", "edt_conf_color_leds_title": "LED index",
@ -294,6 +299,8 @@
"edt_conf_color_temperature_title": "Temperature", "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_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_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_expl": "The calibrated white value.",
"edt_conf_color_white_title": "White", "edt_conf_color_white_title": "White",
"edt_conf_color_yellow_expl": "The calibrated yellow value.", "edt_conf_color_yellow_expl": "The calibrated yellow value.",
@ -323,6 +330,8 @@
"edt_conf_enum_color": "Color", "edt_conf_enum_color": "Color",
"edt_conf_enum_custom": "Custom", "edt_conf_enum_custom": "Custom",
"edt_conf_enum_decay": "Decay", "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_error": "Error",
"edt_conf_enum_dl_informational": "Informational", "edt_conf_enum_dl_informational": "Informational",
"edt_conf_enum_dl_nodebug": "No Debug output", "edt_conf_enum_dl_nodebug": "No Debug output",
@ -331,9 +340,12 @@
"edt_conf_enum_dl_verbose1": "Verbosity level 1", "edt_conf_enum_dl_verbose1": "Verbosity level 1",
"edt_conf_enum_dl_verbose2": "Verbosity level 2", "edt_conf_enum_dl_verbose2": "Verbosity level 2",
"edt_conf_enum_dl_verbose3": "Verbosity level 3", "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_effect": "Effect",
"edt_conf_enum_gbr": "GBR", "edt_conf_enum_gbr": "GBR",
"edt_conf_enum_grb": "GRB", "edt_conf_enum_grb": "GRB",
"edt_conf_enum_high": "High",
"edt_conf_enum_hsv": "HSV", "edt_conf_enum_hsv": "HSV",
"edt_conf_enum_left_right": "Left to right", "edt_conf_enum_left_right": "Left to right",
"edt_conf_enum_linear": "Linear", "edt_conf_enum_linear": "Linear",
@ -341,7 +353,10 @@
"edt_conf_enum_logsilent": "Silent", "edt_conf_enum_logsilent": "Silent",
"edt_conf_enum_logverbose": "Verbose", "edt_conf_enum_logverbose": "Verbose",
"edt_conf_enum_logwarn": "Warning", "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_please_select": "Please Select",
"edt_conf_enum_rbg": "RBG", "edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB", "edt_conf_enum_rgb": "RGB",
@ -351,7 +366,7 @@
"edt_conf_enum_transeffect_sudden": "Sudden", "edt_conf_enum_transeffect_sudden": "Sudden",
"edt_conf_enum_udp_ddp": "DDP", "edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW", "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_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_expl": "If no data is received for the given period, the component will be (soft) disabled.",
"edt_conf_fbs_timeout_title": "Timeout", "edt_conf_fbs_timeout_title": "Timeout",
@ -416,6 +431,8 @@
"edt_conf_instC_systemEnable_title": "Enable screen capture", "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_expl": "Enables the USB capture for this LED hardware instance",
"edt_conf_instC_v4lEnable_title": "Enable USB capture", "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_expl": "The video capture device used",
"edt_conf_instC_video_grabber_device_title": "Video capture device", "edt_conf_instC_video_grabber_device_title": "Video capture device",
"edt_conf_instCapture_heading_title": "Capture Devices", "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_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_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_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_expl": "Path to the certification file (format should be PEM)",
"edt_conf_webc_crtPath_title": "Certificate path", "edt_conf_webc_crtPath_title": "Certificate path",
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)", "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_brightnessOverwrite_title": "Overwrite brightness",
"edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum", "edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum",
"edt_dev_spec_brightness_title": "Brightness", "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_chanperfixture_title": "Channels per Fixture",
"edt_dev_spec_cid_title": "CID", "edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Clientkey", "edt_dev_spec_clientKey_title": "Clientkey",
@ -620,15 +658,22 @@
"edt_dev_spec_razer_device_title": "Razer Chroma Device", "edt_dev_spec_razer_device_title": "Razer Chroma Device",
"edt_dev_spec_restoreOriginalState_title": "Restore lights' state", "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_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_enable": "White channel calibration (RGBW only)",
"edt_dev_spec_rgbw_calibration_limit" : "White channel limit", "edt_dev_spec_rgbw_calibration_limit": "White channel limit",
"edt_dev_spec_rgbw_calibration_red" : "Red/White channel aspect", "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_green": "Green/White channel aspect",
"edt_dev_spec_rgbw_calibration_blue" : "Blue/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_serial_title": "Serial number",
"edt_dev_spec_spipath_title": "SPI Device", "edt_dev_spec_spipath_title": "SPI Device",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum", "edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum",
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer handshake timeout minimum", "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_switchOffOnBlack_title": "Switch off on black",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum",
"edt_dev_spec_syncOverwrite_title": "Disable synchronisation", "edt_dev_spec_syncOverwrite_title": "Disable synchronisation",
@ -860,6 +905,7 @@
"general_comp_PROTOSERVER": "Protocol Buffers Server", "general_comp_PROTOSERVER": "Protocol Buffers Server",
"general_comp_SMOOTHING": "Smoothing", "general_comp_SMOOTHING": "Smoothing",
"general_comp_V4L": "Capture USB-Input", "general_comp_V4L": "Capture USB-Input",
"general_comp_AUDIO": "Audio Capture",
"general_country_cn": "China", "general_country_cn": "China",
"general_country_de": "Germany", "general_country_de": "Germany",
"general_country_es": "Spain", "general_country_es": "Spain",
@ -871,11 +917,11 @@
"general_country_us": "United States", "general_country_us": "United States",
"general_disabled": "disabled", "general_disabled": "disabled",
"general_enabled": "enabled", "general_enabled": "enabled",
"general_speech_ca": "Catalan", "general_speech_ca": "Catalan",
"general_speech_cs": "Czech", "general_speech_cs": "Czech",
"general_speech_da": "Danish", "general_speech_da": "Danish",
"general_speech_de": "German", "general_speech_de": "German",
"general_speech_el": "Greek", "general_speech_el": "Greek",
"general_speech_en": "English", "general_speech_en": "English",
"general_speech_es": "Spanish", "general_speech_es": "Spanish",
"general_speech_fr": "French", "general_speech_fr": "French",
@ -970,8 +1016,11 @@
"remote_losthint": "Note: All changes will be lost after a restart.", "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_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": "Mapping type",
"remote_maptype_label_multicolor_mean": "Multicolor", "remote_maptype_label_dominant_color": "Dominant Color",
"remote_maptype_label_unicolor_mean": "Unicolor", "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_syseffets": "System Effects",
"remote_optgroup_templates_custom": "User Templates", "remote_optgroup_templates_custom": "User Templates",
"remote_optgroup_templates_system": "System 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_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_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_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": "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_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", "about_build": "Build",
@ -55,7 +58,9 @@
"conf_leds_error_get_properties_title": "Propiedades del dispositivo", "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_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": "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_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_info_ws281x": "Hyperion debe ejecutarse con privilegios 'root' para este tipo de controlador.",
"conf_leds_layout_advanced": "Ajustes Avanzados", "conf_leds_layout_advanced": "Ajustes Avanzados",
"conf_leds_layout_blacklist_num_title": "Número de LEDs", "conf_leds_layout_blacklist_num_title": "Número de LEDs",
@ -439,8 +444,6 @@
"edt_conf_smooth_heading_title": "Suavizado", "edt_conf_smooth_heading_title": "Suavizado",
"edt_conf_smooth_interpolationRate_expl": "Velocidad de cálculo de los fotogramas intermedios suaves.", "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_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_expl": "¿Cuánto tiempo debe recoger las imágenes el suavizado?",
"edt_conf_smooth_time_ms_title": "Tiempo", "edt_conf_smooth_time_ms_title": "Tiempo",
"edt_conf_smooth_type_expl": "Tipo de suavizado", "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_green": "Aspecto del canal Verde/Blanco",
"edt_dev_spec_rgbw_calibration_limit": "Límite del canal 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_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_serial_title": "Número de serie",
"edt_dev_spec_spipath_title": "Dispositivo SPI", "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_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_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_stream_protocol_title": "Protocolo de streaming",
"edt_dev_spec_switchOffOnBlack_title": "Apagar en negro", "edt_dev_spec_switchOffOnBlack_title": "Apagar en negro",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Apagado, por debajo del mínimo", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Apagado, por debajo del mínimo",
@ -861,9 +871,11 @@
"general_country_us": "Estados Unidos", "general_country_us": "Estados Unidos",
"general_disabled": "Deshabilitado", "general_disabled": "Deshabilitado",
"general_enabled": "Habilitado", "general_enabled": "Habilitado",
"general_speech_ca": "Catalán",
"general_speech_cs": "Czech", "general_speech_cs": "Czech",
"general_speech_da": "Danés", "general_speech_da": "Danés",
"general_speech_de": "Alemán", "general_speech_de": "Alemán",
"general_speech_el": "Griego",
"general_speech_en": "Inglés", "general_speech_en": "Inglés",
"general_speech_es": "Español", "general_speech_es": "Español",
"general_speech_fr": "Francés", "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_expl": "Assombrie les valeurs vertes faibles (reconnues comme noires)",
"edt_conf_v4l2_greenSignalThreshold_title": "Seuil de signal vert", "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_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_expl": "Définie le contraste matériel",
"edt_conf_v4l2_hardware_contrast_title": "Contrôle 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_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_expl": "Définie la saturation matérielle",
"edt_conf_v4l2_hardware_saturation_title": "Contrôle la saturation matériel", "edt_conf_v4l2_hardware_saturation_title": "Contrôle la saturation matériel",
"edt_conf_v4l2_heading_title": "Capture USB", "edt_conf_v4l2_heading_title": "Capture USB",

View File

@ -5,6 +5,8 @@
"InfoDialog_changePassword_title": "Cambia Password", "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_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_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_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_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!", "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_intro": "Impostazioni base di Hyperion e WebUI che non rientrano in altre categorie.",
"conf_general_label_title": "Impostazioni generali", "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_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_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_expl": "Spiegazione",
"conf_helptable_option": "Opzioni", "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_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_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_advanced": "Impostazioni Avanzate",
"conf_leds_layout_blacklist_num_title": "Numero di LEDs", "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_checklist": "Mostra lista",
"conf_leds_layout_btn_keystone": "Correzione trapezoidale",
"conf_leds_layout_button_savelay": "Salva Layout", "conf_leds_layout_button_savelay": "Salva Layout",
"conf_leds_layout_button_updsim": "Aggiorna Anteprima", "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", "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_leftbottom": "Sinistra 0% - 100% Sotto",
"conf_leds_layout_cl_leftmiddle": "Sinistra 0% - 75% Centro", "conf_leds_layout_cl_leftmiddle": "Sinistra 0% - 75% Centro",
"conf_leds_layout_cl_lefttop": "Sinistra 0% - 50% Sopra", "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_overlap": "Overlap",
"conf_leds_layout_cl_reversdir": "Inverti direzione", "conf_leds_layout_cl_reversdir": "Inverti direzione",
"conf_leds_layout_cl_right": "Destra", "conf_leds_layout_cl_right": "Destra",
@ -81,6 +108,7 @@
"conf_leds_layout_generatedconf": "Configurazione LED Generata/Attuale", "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_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_cabling": "Cablaggio",
"conf_leds_layout_ma_direction": "Direzione",
"conf_leds_layout_ma_horiz": "Orizzontale", "conf_leds_layout_ma_horiz": "Orizzontale",
"conf_leds_layout_ma_optbottomleft": "Basso a sinistra", "conf_leds_layout_ma_optbottomleft": "Basso a sinistra",
"conf_leds_layout_ma_optbottomright": "Basso a destra", "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_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_ledcontroller": "Controller LED",
"conf_leds_nav_label_ledlayout": "Layout 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_RPiGPIO": "RPi GPIO",
"conf_leds_optgroup_RPiPWM": "RPi PWM", "conf_leds_optgroup_RPiPWM": "RPi PWM",
"conf_leds_optgroup_RPiSPI": "RPi SPI", "conf_leds_optgroup_RPiSPI": "RPi SPI",
"conf_leds_optgroup_debug": "Debug",
"conf_leds_optgroup_network": "Rete", "conf_leds_optgroup_network": "Rete",
"conf_leds_optgroup_other": "Altro", "conf_leds_optgroup_other": "Altro",
"conf_leds_optgroup_usb": "USB/Seriale", "conf_leds_optgroup_usb": "USB/Seriale",
"conf_logging_btn_autoscroll": "Auto scrolling", "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_btn_pbupload": "Invia segnalazione per richieste di supporto",
"conf_logging_contpolicy": "Policy per la Privacy delle segnalazioni", "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_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_lastreports": "Segnalazioni precedenti",
"conf_logging_logoutput": "Log output",
"conf_logging_nomessage": "Nessun messaggio di log disponibile.", "conf_logging_nomessage": "Nessun messaggio di log disponibile.",
"conf_logging_report": "Segnala", "conf_logging_report": "Segnala",
"conf_logging_uplfailed": "Upload fallito! Controlla la tua connessione a internet!", "conf_logging_uplfailed": "Upload fallito! Controlla la tua connessione a internet!",
@ -163,7 +195,12 @@
"dashboard_infobox_label_instance": "Istanza:", "dashboard_infobox_label_instance": "Istanza:",
"dashboard_infobox_label_latesthyp": "L'ultima versione di Hyperion:", "dashboard_infobox_label_latesthyp": "L'ultima versione di Hyperion:",
"dashboard_infobox_label_platform": "Piattaforma:", "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": "Porte",
"dashboard_infobox_label_ports_websocket": "WebSocket (ws|wss):",
"dashboard_infobox_label_smartacc": "Accesso Smart", "dashboard_infobox_label_smartacc": "Accesso Smart",
"dashboard_infobox_label_statush": "Status Hyperion:", "dashboard_infobox_label_statush": "Status Hyperion:",
"dashboard_infobox_label_title": "Informazioni", "dashboard_infobox_label_title": "Informazioni",
@ -181,6 +218,7 @@
"dashboard_newsbox_readmore": "Leggi ancora", "dashboard_newsbox_readmore": "Leggi ancora",
"dashboard_newsbox_visitblog": "Visita Hyperion-Blog", "dashboard_newsbox_visitblog": "Visita Hyperion-Blog",
"edt_append_degree": "°", "edt_append_degree": "°",
"edt_append_frames": "frames",
"edt_append_hz": "Hz", "edt_append_hz": "Hz",
"edt_append_leds": "LEDs", "edt_append_leds": "LEDs",
"edt_append_ms": "ms", "edt_append_ms": "ms",
@ -216,6 +254,8 @@
"edt_conf_color_blue_title": "Blu", "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_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_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_expl": "Imposta la luminosità complessiva dei leds",
"edt_conf_color_brightness_title": "Luminosità", "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.", "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_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Il valore calibrato del rosso.", "edt_conf_color_red_expl": "Il valore calibrato del rosso.",
"edt_conf_color_red_title": "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_expl": "Il valore calibrato del bianco.",
"edt_conf_color_white_title": "Bianco", "edt_conf_color_white_title": "Bianco",
"edt_conf_color_yellow_expl": "Il valore calibrato del giallo.", "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_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_itemtitle": "Percorso",
"edt_conf_effp_paths_title": "Percorso Effetti", "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_NO_CHANGE": "Auto",
"edt_conf_enum_NTSC": "NTSC", "edt_conf_enum_NTSC": "NTSC",
"edt_conf_enum_PAL": "PAL", "edt_conf_enum_PAL": "PAL",
"edt_conf_enum_SECAM": "SECAM", "edt_conf_enum_SECAM": "SECAM",
"edt_conf_enum_VERTICAL": "Verticale",
"edt_conf_enum_automatic": "Automatico", "edt_conf_enum_automatic": "Automatico",
"edt_conf_enum_bbclassic": "Classico", "edt_conf_enum_bbclassic": "Classico",
"edt_conf_enum_bbdefault": "Default", "edt_conf_enum_bbdefault": "Default",
@ -287,12 +332,15 @@
"edt_conf_enum_logverbose": "Verboso", "edt_conf_enum_logverbose": "Verboso",
"edt_conf_enum_logwarn": "Avvertimento", "edt_conf_enum_logwarn": "Avvertimento",
"edt_conf_enum_multicolor_mean": "Multicolore", "edt_conf_enum_multicolor_mean": "Multicolore",
"edt_conf_enum_please_select": "Seleziona",
"edt_conf_enum_rbg": "RBG", "edt_conf_enum_rbg": "RBG",
"edt_conf_enum_rgb": "RGB", "edt_conf_enum_rgb": "RGB",
"edt_conf_enum_right_left": "Da destra a sinistra", "edt_conf_enum_right_left": "Da destra a sinistra",
"edt_conf_enum_top_down": "Dall'alto verso il basso", "edt_conf_enum_top_down": "Dall'alto verso il basso",
"edt_conf_enum_transeffect_smooth": "Dolce", "edt_conf_enum_transeffect_smooth": "Dolce",
"edt_conf_enum_transeffect_sudden": "Repentina", "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_enum_unicolor_mean": "Monocromatico",
"edt_conf_fbs_heading_title": "Server Flatbuffers", "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).", "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_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_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_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_flat_title": "Lista dei client flatbuffer",
"edt_conf_fw_heading_title": "Forwarder", "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_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_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_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_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_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", "edt_conf_gen_name_title": "Nome configurazione",
@ -339,10 +395,19 @@
"edt_conf_general_port_title": "Porta", "edt_conf_general_port_title": "Porta",
"edt_conf_general_priority_expl": "Priorità di questo componente", "edt_conf_general_priority_expl": "Priorità di questo componente",
"edt_conf_general_priority_title": "Priorità canale.", "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_expl": "Abilita la cattura della piattaforma per questa istanza di hardware led",
"edt_conf_instC_systemEnable_title": "Abilita cattura piattaforma", "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_expl": "Abilita la cattura USB per questa istanza di hardware led",
"edt_conf_instC_v4lEnable_title": "Abilita cattura USB", "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_instCapture_heading_title": "Cattura Istanza",
"edt_conf_js_heading_title": "Server JSON", "edt_conf_js_heading_title": "Server JSON",
"edt_conf_log_heading_title": "Logging", "edt_conf_log_heading_title": "Logging",
@ -374,8 +439,6 @@
"edt_conf_smooth_heading_title": "Sfumatura", "edt_conf_smooth_heading_title": "Sfumatura",
"edt_conf_smooth_interpolationRate_expl": "Velocità di calcolo dei regolari frame intermedi", "edt_conf_smooth_interpolationRate_expl": "Velocità di calcolo dei regolari frame intermedi",
"edt_conf_smooth_interpolationRate_title": "Tasso di interpolazione", "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_expl": "Quanto a lungo la sfumatura dovrebbe raggiungere le immagini?",
"edt_conf_smooth_time_ms_title": "Durata", "edt_conf_smooth_time_ms_title": "Durata",
"edt_conf_smooth_type_expl": "Tipo di sfumatura.", "edt_conf_smooth_type_expl": "Tipo di sfumatura.",
@ -390,21 +453,41 @@
"edt_conf_v4l2_cecDetection_title": "Rilevamento CEC", "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_expl": "Numero di pixels in basso che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropBottom_title": "Ritaglia in basso", "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_expl": "Numero di pixels sulla sinistra che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropLeft_title": "Ritaglia sinistra", "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_expl": "Numero di pixels sulla destra che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropRight_title": "Ritaglia destra", "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_expl": "Numero di pixels in alto che vengono rimossi dall'immagine.",
"edt_conf_v4l2_cropTop_title": "Ritaglia in alto", "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_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_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_expl": "Frame al secondo supportati per il dispositivo attivo",
"edt_conf_v4l2_framerate_title": "Frame al secondo", "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_expl": "Scurisce valori bassi di verde (riconosciuti come nero)",
"edt_conf_v4l2_greenSignalThreshold_title": "Soglia segnale verde", "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_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_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_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_expl": "Scurisce valori bassi di rosso (riconosciuti come nero)",
"edt_conf_v4l2_redSignalThreshold_title": "Soglia segnale rosso", "edt_conf_v4l2_redSignalThreshold_title": "Soglia segnale rosso",
"edt_conf_v4l2_resolution_expl": "Lista Risoluzioni supportate per il dispositivo attivo", "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_expl": "Porta del webserver HTTPS",
"edt_conf_webc_sslport_title": "Porta HTTPS", "edt_conf_webc_sslport_title": "Porta HTTPS",
"edt_dev_auth_key_title": "Token di autenticazione", "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_cool_adjust": "Regolazione freddo min",
"edt_dev_enum_sub_min_warm_adjust": "Regolazione calore min", "edt_dev_enum_sub_min_warm_adjust": "Regolazione calore min",
"edt_dev_enum_subtract_minimum": "Sottrai minimo", "edt_dev_enum_subtract_minimum": "Sottrai minimo",
"edt_dev_enum_white_off": "Bianco off", "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": "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": "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_heading_title": "Impostazioni Generali",
"edt_dev_general_name_title": "Nome configurazione", "edt_dev_general_name_title": "Nome configurazione",
"edt_dev_general_rewriteTime_title": "Tempo di ricarica", "edt_dev_general_rewriteTime_title": "Tempo di ricarica",
@ -449,21 +541,28 @@
"edt_dev_spec_FCsetConfig_title": "Imposta configurazione Fadecandy", "edt_dev_spec_FCsetConfig_title": "Imposta configurazione Fadecandy",
"edt_dev_spec_LBap102Mode_title": "Modalità LightBerry APA102", "edt_dev_spec_LBap102Mode_title": "Modalità LightBerry APA102",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo", "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_baudrate_title": "Baudrate",
"edt_dev_spec_blackLightsTimeout_title": "Timeout rilevamento segnale su nero", "edt_dev_spec_blackLightsTimeout_title": "Timeout rilevamento segnale su nero",
"edt_dev_spec_brightnessFactor_title": "Fattore luminosità", "edt_dev_spec_brightnessFactor_title": "Fattore luminosità",
"edt_dev_spec_brightnessMax_title": "Luminosità massima", "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_brightnessOverwrite_title": "Sovrascrivi luminosità",
"edt_dev_spec_brightnessThreshold_title": "Luminosità minima per rilevamento segnale", "edt_dev_spec_brightnessThreshold_title": "Luminosità minima per rilevamento segnale",
"edt_dev_spec_brightness_title": "Luminosità", "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_chanperfixture_title": "Canali per dispositivo",
"edt_dev_spec_cid_title": "CID", "edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Clientkey", "edt_dev_spec_clientKey_title": "Clientkey",
"edt_dev_spec_colorComponent_title": "Componente colore", "edt_dev_spec_colorComponent_title": "Componente colore",
"edt_dev_spec_debugLevel_title": "Livello Debug", "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_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_dithering_title": "Dithering",
"edt_dev_spec_dmaNumber_title": "Canale DMA", "edt_dev_spec_dmaNumber_title": "Canale DMA",
"edt_dev_spec_gamma_title": "Gamma", "edt_dev_spec_gamma_title": "Gamma",
@ -478,6 +577,7 @@
"edt_dev_spec_intervall_title": "Intervallo", "edt_dev_spec_intervall_title": "Intervallo",
"edt_dev_spec_invert_title": "Inverti segnale", "edt_dev_spec_invert_title": "Inverti segnale",
"edt_dev_spec_latchtime_title": "Tempo di latch", "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_ledIndex_title": "Indice LED",
"edt_dev_spec_ledType_title": "Tipo LED", "edt_dev_spec_ledType_title": "Tipo LED",
"edt_dev_spec_lightid_itemtitle": "ID", "edt_dev_spec_lightid_itemtitle": "ID",
@ -491,6 +591,8 @@
"edt_dev_spec_networkDeviceName_title": "Nome dispositivo di rete", "edt_dev_spec_networkDeviceName_title": "Nome dispositivo di rete",
"edt_dev_spec_networkDevicePort_title": "Porta", "edt_dev_spec_networkDevicePort_title": "Porta",
"edt_dev_spec_numberOfLeds_title": "Numero di LEDs", "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_orbIds_title": "Orb ID",
"edt_dev_spec_order_left_right_title": "2.", "edt_dev_spec_order_left_right_title": "2.",
"edt_dev_spec_order_top_down_title": "1.", "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_panel_start_position": "Pannello d'inizio [0-max panels]",
"edt_dev_spec_panelorganisation_title": "Sequenza numerazione pannelli", "edt_dev_spec_panelorganisation_title": "Sequenza numerazione pannelli",
"edt_dev_spec_pid_title": "PID", "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_port_title": "Porta",
"edt_dev_spec_printTimeStamp_title": "Aggiungi timestamp", "edt_dev_spec_printTimeStamp_title": "Aggiungi timestamp",
"edt_dev_spec_pwmChannel_title": "Canale PWM", "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": "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_serial_title": "Numero seriale",
"edt_dev_spec_spipath_title": "Percorso SPI", "edt_dev_spec_spipath_title": "Percorso SPI",
"edt_dev_spec_sslHSTimeoutMax_title": "Timeout massimo handkshake streamer", "edt_dev_spec_sslHSTimeoutMax_title": "Timeout massimo handkshake streamer",
"edt_dev_spec_sslHSTimeoutMin_title": "Timeout minimo 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_switchOffOnBlack_title": "Spegni o accendi il nero",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Spegni, sotto il minimo", "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": "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_targetIp_title": "IP di destinazione",
"edt_dev_spec_transeffect_title": "Effetto Transizione", "edt_dev_spec_transeffect_title": "Effetto Transizione",
"edt_dev_spec_transistionTimeExtra_title": "Tempo extra di buio", "edt_dev_spec_transistionTimeExtra_title": "Tempo extra di buio",
@ -573,9 +686,14 @@
"edt_eff_frequency": "Frequenza", "edt_eff_frequency": "Frequenza",
"edt_eff_gif_header": "GIF", "edt_eff_gif_header": "GIF",
"edt_eff_gif_header_desc": "Questo effetto riproduce file .gif. Fornisci un semplice loop video come effetto.", "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_height": "Altezza",
"edt_eff_huechange": "Cambiamento colore", "edt_eff_huechange": "Cambiamento colore",
"edt_eff_image": "file immagine", "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_interval": "Intervallo",
"edt_eff_knightrider_header": "Supercar", "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.", "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_reversedirection": "Inverti direzione",
"edt_eff_rotationtime": "Tempo di rotazione", "edt_eff_rotationtime": "Tempo di rotazione",
"edt_eff_saturation": "Saturazione", "edt_eff_saturation": "Saturazione",
"edt_eff_set_post_color": "Imposta il colore del post dopo l'allarme",
"edt_eff_showseconds": "Mostra secondi", "edt_eff_showseconds": "Mostra secondi",
"edt_eff_sleeptime": "Tempo di riposo", "edt_eff_sleeptime": "Tempo di riposo",
"edt_eff_smooth_custom": "Abilita sfumatura", "edt_eff_smooth_custom": "Abilita sfumatura",
@ -631,6 +750,7 @@
"edt_eff_traces_header_desc": "Necessita redesign", "edt_eff_traces_header_desc": "Necessita redesign",
"edt_eff_trails_header": "Scie", "edt_eff_trails_header": "Scie",
"edt_eff_trails_header_desc": "Stelle colorate che cadono dall'alto verso il basso", "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": "Onde",
"edt_eff_waves_header_desc": "Onde di colore! Scegli i colori, tempo di rotazione, direzione e altro.", "edt_eff_waves_header_desc": "Onde di colore! Scegli i colori, tempo di rotazione, direzione e altro.",
"edt_eff_whichleds": "Quali Leds", "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": "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_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_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_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_maxLength": "Il valore deve essere al massimo lungo $1 caratteri",
"edt_msg_error_maxProperties": "L'oggetto deve avere al massimo $1 proprietà", "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": "Il valore deve essere di tipo $1",
"edt_msg_error_type_union": "Il valore deve essere di uno dei tipi forniti", "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_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_conttest": "Test continuo",
"effectsconfigurator_button_deleffect": "Cancella Effetto", "effectsconfigurator_button_deleffect": "Cancella Effetto",
"effectsconfigurator_button_editeffect": "Carica Effetto", "effectsconfigurator_button_editeffect": "Carica Effetto",
@ -686,7 +812,7 @@
"effectsconfigurator_label_effectname": "Nome Effetto", "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.", "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_advanced": "Avanzate",
"general_access_default": "Default", "general_access_default": "Predefinito",
"general_access_expert": "Esperto", "general_access_expert": "Esperto",
"general_btn_back": "Indietro", "general_btn_back": "Indietro",
"general_btn_cancel": "Annulla", "general_btn_cancel": "Annulla",
@ -699,6 +825,7 @@
"general_btn_off": "Off", "general_btn_off": "Off",
"general_btn_ok": "OK", "general_btn_ok": "OK",
"general_btn_on": "On", "general_btn_on": "On",
"general_btn_overwrite": "Sovrascrivi",
"general_btn_rename": "Rinomina", "general_btn_rename": "Rinomina",
"general_btn_restarthyperion": "Riavvia Hyperion", "general_btn_restarthyperion": "Riavvia Hyperion",
"general_btn_save": "Salva", "general_btn_save": "Salva",
@ -718,7 +845,7 @@
"general_comp_FORWARDER": "Forwarder", "general_comp_FORWARDER": "Forwarder",
"general_comp_GRABBER": "Cattura di Sistema", "general_comp_GRABBER": "Cattura di Sistema",
"general_comp_LEDDEVICE": "Dispositivo LED", "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_SMOOTHING": "Sfumatura",
"general_comp_V4L": "Cattura USB", "general_comp_V4L": "Cattura USB",
"general_country_cn": "Cina", "general_country_cn": "Cina",
@ -730,14 +857,23 @@
"general_country_ru": "Russia", "general_country_ru": "Russia",
"general_country_uk": "Regno Unito", "general_country_uk": "Regno Unito",
"general_country_us": "Stati Uniti", "general_country_us": "Stati Uniti",
"general_disabled": "disabilitato",
"general_enabled": "abilitato",
"general_speech_ca": "Catalano",
"general_speech_cs": "Czech", "general_speech_cs": "Czech",
"general_speech_da": "Danimarca",
"general_speech_de": "Tedesco", "general_speech_de": "Tedesco",
"general_speech_el": "Greco",
"general_speech_en": "Inglese", "general_speech_en": "Inglese",
"general_speech_es": "Spagnolo", "general_speech_es": "Spagnolo",
"general_speech_fr": "Francese", "general_speech_fr": "Francese",
"general_speech_hu": "Ungheria",
"general_speech_it": "Italiano", "general_speech_it": "Italiano",
"general_speech_ja": "Giapponese",
"general_speech_nb": "Norvegese (Bokmål)",
"general_speech_nl": "Olandese", "general_speech_nl": "Olandese",
"general_speech_pl": "Polacco", "general_speech_pl": "Polacco",
"general_speech_pt": "Portoghese",
"general_speech_ro": "Rumeno", "general_speech_ro": "Rumeno",
"general_speech_ru": "Russo", "general_speech_ru": "Russo",
"general_speech_sv": "Svedese", "general_speech_sv": "Svedese",
@ -757,6 +893,10 @@
"infoDialog_import_confirm_title": "Conferma import", "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_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_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_wizrgb_text": "L'ordine dei Byte RGB è già impostato correttamente.",
"infoDialog_writeconf_error_text": "Salvataggio della configurazione fallito.", "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.", "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_togglelednumber": "Numeri LED",
"main_ledsim_btn_toggleleds": "Mostra LEDs", "main_ledsim_btn_toggleleds": "Mostra LEDs",
"main_ledsim_btn_togglelivevideo": "Video live", "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_text": "Visualizzazione live dei colori dei led e, opzionalmente, lo stream video dal dispositivo di cattura.",
"main_ledsim_title": "Visualizzazione LED", "main_ledsim_title": "Visualizzazione LED",
"main_menu_about_token": "Info su Hyperion", "main_menu_about_token": "Info su Hyperion",
@ -787,6 +928,7 @@
"main_menu_general_conf_token": "Generale", "main_menu_general_conf_token": "Generale",
"main_menu_grabber_conf_token": "Hardware di cattura", "main_menu_grabber_conf_token": "Hardware di cattura",
"main_menu_input_selection_token": "Selezione Input", "main_menu_input_selection_token": "Selezione Input",
"main_menu_instcapture_conf_token": "Fonti",
"main_menu_leds_conf_token": "Hardware LED", "main_menu_leds_conf_token": "Hardware LED",
"main_menu_logging_token": "Log", "main_menu_logging_token": "Log",
"main_menu_network_conf_token": "Servizi di Rete", "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_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_testintrowok": "Guarda il link di seguito per il download dei video di test",
"wiz_cc_title": "Assistente calibrazione colore", "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_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_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", "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_hue_username": "ID Utente",
"wiz_identify": "Identifica", "wiz_identify": "Identifica",
"wiz_identify_light": "Identifica $1", "wiz_identify_light": "Identifica $1",
"wiz_identify_tip": "Identifica il dispositivo configurato accendendolo",
"wiz_ids_disabled": "Disattivato", "wiz_ids_disabled": "Disattivato",
"wiz_ids_entire": "Immagine intera", "wiz_ids_entire": "Immagine intera",
"wiz_noLights": "Nessun $1 trovato! Per favore connetti le luci alla rete o configurale manualmente", "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_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_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_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": "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_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:", "about_build": "Build:",
@ -254,6 +257,8 @@
"edt_conf_color_blue_title": "Niebieski", "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_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_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_expl": "Ustaw jasność LEDów",
"edt_conf_color_brightness_title": "Jasność", "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", "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_magenta_title": "Magenta",
"edt_conf_color_red_expl": "Skalibrowana wartość koloru czerwonego", "edt_conf_color_red_expl": "Skalibrowana wartość koloru czerwonego",
"edt_conf_color_red_title": "Czerwony", "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_expl": "Skalibrowana wartość koloru białego.",
"edt_conf_color_white_title": "Biały", "edt_conf_color_white_title": "Biały",
"edt_conf_color_yellow_expl": "Skalibrowana wartość koloru żółtego", "edt_conf_color_yellow_expl": "Skalibrowana wartość koloru żółtego",
@ -393,7 +400,7 @@
"edt_conf_general_priority_title": "Kanał priorytetowy", "edt_conf_general_priority_title": "Kanał priorytetowy",
"edt_conf_grabber_discovered_expl": "Wybierz wykryte urządzenie do przechwytywania", "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_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_discovered_title_info": "Wybierz wykryte urządzenie do przechwytywania",
"edt_conf_grabber_discovery_inprogress": "Wykrywanie w toku", "edt_conf_grabber_discovery_inprogress": "Wykrywanie w toku",
"edt_conf_instC_screen_grabber_device_expl": "Używane urządzenie do przechwytywania ekranu", "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_heading_title": "Wygładzanie",
"edt_conf_smooth_interpolationRate_expl": "Szybkość obliczania wygładzanych ramek pośrednich.", "edt_conf_smooth_interpolationRate_expl": "Szybkość obliczania wygładzanych ramek pośrednich.",
"edt_conf_smooth_interpolationRate_title": "Współczynnik interpolacji", "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_expl": "Jak długo wygładzanie powinno zbierać obrazy?",
"edt_conf_smooth_time_ms_title": "Czas", "edt_conf_smooth_time_ms_title": "Czas",
"edt_conf_smooth_type_expl": "Rodzaj wygładzania.", "edt_conf_smooth_type_expl": "Rodzaj wygładzania.",
@ -539,9 +544,13 @@
"edt_dev_spec_FCsetConfig_title": "Ustaw konfigurację zanikania", "edt_dev_spec_FCsetConfig_title": "Ustaw konfigurację zanikania",
"edt_dev_spec_LBap102Mode_title": "Tryb \"LightBerry APA102\"", "edt_dev_spec_LBap102Mode_title": "Tryb \"LightBerry APA102\"",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo", "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_baudrate_title": "Prędkość (Baudrate)",
"edt_dev_spec_blackLightsTimeout_title": "Limit czasu wykrywania sygnału na czarno", "edt_dev_spec_blackLightsTimeout_title": "Limit czasu wykrywania sygnału na czarno",
"edt_dev_spec_brightnessFactor_title": "Współczynnik jasności", "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_brightnessOverwrite_title": "Nadpisz jasność",
"edt_dev_spec_brightnessThreshold_title": "Minimalna jasność wykrywania sygnału", "edt_dev_spec_brightnessThreshold_title": "Minimalna jasność wykrywania sygnału",
"edt_dev_spec_brightness_title": "Jasność", "edt_dev_spec_brightness_title": "Jasność",
@ -601,6 +610,11 @@
"edt_dev_spec_razer_device_title": "Urządzenie Razer Chroma", "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": "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_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_serial_title": "Numer seryjny",
"edt_dev_spec_spipath_title": "SPI path", "edt_dev_spec_spipath_title": "SPI path",
"edt_dev_spec_sslHSTimeoutMax_title": "Maksymalny limit czasu uzgadniania streamera", "edt_dev_spec_sslHSTimeoutMax_title": "Maksymalny limit czasu uzgadniania streamera",
@ -848,9 +862,11 @@
"general_country_us": "Stany Zjednoczone (United States)", "general_country_us": "Stany Zjednoczone (United States)",
"general_disabled": "wyłączony", "general_disabled": "wyłączony",
"general_enabled": "włączony", "general_enabled": "włączony",
"general_speech_ca": "Kataloński",
"general_speech_cs": "Czeski (Czech)", "general_speech_cs": "Czeski (Czech)",
"general_speech_da": "Duński", "general_speech_da": "Duński",
"general_speech_de": "Niemiecki (German)", "general_speech_de": "Niemiecki (German)",
"general_speech_el": "Grecki",
"general_speech_en": "Angielski (English)", "general_speech_en": "Angielski (English)",
"general_speech_es": "Hiszpański (Spanish)", "general_speech_es": "Hiszpański (Spanish)",
"general_speech_fr": "Francuski (French)", "general_speech_fr": "Francuski (French)",

View File

@ -10,6 +10,9 @@
"InfoDialog_nowrite_foottext": "Веб-интерфейс будет разблокирован автоматически после того, как вы решите проблему!", "InfoDialog_nowrite_foottext": "Веб-интерфейс будет разблокирован автоматически после того, как вы решите проблему!",
"InfoDialog_nowrite_text": "Hyperion не может выполнять запись в ваш текущий загруженный файл конфигурации. Чтобы продолжить, восстановите права доступа к файлу.", "InfoDialog_nowrite_text": "Hyperion не может выполнять запись в ваш текущий загруженный файл конфигурации. Чтобы продолжить, восстановите права доступа к файлу.",
"InfoDialog_nowrite_title": "Ошибка разрешения записи!", "InfoDialog_nowrite_title": "Ошибка разрешения записи!",
"InfoDialog_systemRestart_title": "Перезапустить",
"InfoDialog_systemResume_title": "Продолжить",
"InfoDialog_systemSuspend_title": "Отключить",
"about_3rd_party_licenses": "Сторонние лицензии", "about_3rd_party_licenses": "Сторонние лицензии",
"about_3rd_party_licenses_error": "У нас возникли проблемы со сбором информации о сторонних лицензиях из Интернета. <br /> Перейдите по этой ссылке на GitHub.", "about_3rd_party_licenses_error": "У нас возникли проблемы со сбором информации о сторонних лицензиях из Интернета. <br /> Перейдите по этой ссылке на GitHub.",
"about_build": "Сборка", "about_build": "Сборка",
@ -51,6 +54,8 @@
"conf_leds_contr_label_contrtype": "Тип контроллера:", "conf_leds_contr_label_contrtype": "Тип контроллера:",
"conf_leds_device_info_log": "Если ваши светодиоды не работают, проверьте здесь наличие ошибок:", "conf_leds_device_info_log": "Если ваши светодиоды не работают, проверьте здесь наличие ошибок:",
"conf_leds_device_intro": "Hyperion поддерживает множество контроллеров для передачи данных на целевое устройство. Выберите светодиодный контроллер из списка и настройте его. Мы выбрали лучшие настройки по умолчанию для каждого устройства.", "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_layout": "Количество светодиодов оборудования ($1) больше, чем количество светодиодов, настроенных с помощью макета ($2), <br> $3 {{plural:$3|Светодиод|Светодиоды}} останутся черными, если вы продолжите.",
"conf_leds_error_hwled_gt_maxled": "Количество светодиодов оборудования ($1) превышает максимальное количество светодиодов, поддерживаемое устройством ($2). <br> Счетчик аппаратных светодиодов установлен на ($3).", "conf_leds_error_hwled_gt_maxled": "Количество светодиодов оборудования ($1) превышает максимальное количество светодиодов, поддерживаемое устройством ($2). <br> Счетчик аппаратных светодиодов установлен на ($3).",
"conf_leds_error_hwled_lt_layout": "Количество светодиодных индикаторов оборудования ($1) меньше, чем количество светодиодов, настроенных с помощью макета ($2). <br> Количество светодиодов, настроенных в макете, не должно превышать количество доступных светодиодов", "conf_leds_error_hwled_lt_layout": "Количество светодиодных индикаторов оборудования ($1) меньше, чем количество светодиодов, настроенных с помощью макета ($2). <br> Количество светодиодов, настроенных в макете, не должно превышать количество доступных светодиодов",
@ -62,6 +67,7 @@
"conf_leds_layout_blacklist_start_title": "Начальный светодиод", "conf_leds_layout_blacklist_start_title": "Начальный светодиод",
"conf_leds_layout_blacklistleds_title": "Светодиоды из черного списка", "conf_leds_layout_blacklistleds_title": "Светодиоды из черного списка",
"conf_leds_layout_btn_checklist": "Показать сверку", "conf_leds_layout_btn_checklist": "Показать сверку",
"conf_leds_layout_btn_keystone": "Коррекция трапеции",
"conf_leds_layout_button_savelay": "Сохранить раскладку", "conf_leds_layout_button_savelay": "Сохранить раскладку",
"conf_leds_layout_button_updsim": "Просмотр обновления", "conf_leds_layout_button_updsim": "Просмотр обновления",
"conf_leds_layout_checkp1": "Черный светодиод — это ваш первый светодиод, первый светодиод — это точка, в которую вы вводите сигнал данных.", "conf_leds_layout_checkp1": "Черный светодиод — это ваш первый светодиод, первый светодиод — это точка, в которую вы вводите сигнал данных.",
@ -81,6 +87,16 @@
"conf_leds_layout_cl_leftbottom": "Левый 50% - 100% снизу", "conf_leds_layout_cl_leftbottom": "Левый 50% - 100% снизу",
"conf_leds_layout_cl_leftmiddle": "Левый 25% - 75% посередине", "conf_leds_layout_cl_leftmiddle": "Левый 25% - 75% посередине",
"conf_leds_layout_cl_lefttop": "Слева 0% - 50% сверху", "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_overlap": "Нахлёст",
"conf_leds_layout_cl_reversdir": "Обратное направление", "conf_leds_layout_cl_reversdir": "Обратное направление",
"conf_leds_layout_cl_right": "Справа", "conf_leds_layout_cl_right": "Справа",
@ -95,6 +111,7 @@
"conf_leds_layout_generatedconf": "Сгенерированная/Текущая Конфигурация LED", "conf_leds_layout_generatedconf": "Сгенерированная/Текущая Конфигурация LED",
"conf_leds_layout_intro": "Вам также нужна LED-раскладка, которая отражает положение ваших светодиодов. Классическая раскладка обычно представляет ТВ-рамку, но поддерживается и LED-матрица (LED-стена). Вид на этой раскладке ВСЕГДА СПЕРЕДИ вашего ТВ.", "conf_leds_layout_intro": "Вам также нужна LED-раскладка, которая отражает положение ваших светодиодов. Классическая раскладка обычно представляет ТВ-рамку, но поддерживается и LED-матрица (LED-стена). Вид на этой раскладке ВСЕГДА СПЕРЕДИ вашего ТВ.",
"conf_leds_layout_ma_cabling": "Подключение", "conf_leds_layout_ma_cabling": "Подключение",
"conf_leds_layout_ma_direction": "Направление",
"conf_leds_layout_ma_horiz": "Горизонтально", "conf_leds_layout_ma_horiz": "Горизонтально",
"conf_leds_layout_ma_optbottomleft": "Низ слева", "conf_leds_layout_ma_optbottomleft": "Низ слева",
"conf_leds_layout_ma_optbottomright": "Низ справа", "conf_leds_layout_ma_optbottomright": "Низ справа",
@ -181,6 +198,7 @@
"dashboard_infobox_label_instance": "Пример:", "dashboard_infobox_label_instance": "Пример:",
"dashboard_infobox_label_latesthyp": "Последняя версия Hyperion:", "dashboard_infobox_label_latesthyp": "Последняя версия Hyperion:",
"dashboard_infobox_label_platform": "Платформа:", "dashboard_infobox_label_platform": "Платформа:",
"dashboard_infobox_label_port_boblight": "Boblight сервер:",
"dashboard_infobox_label_port_flat": "Плоский буфер:", "dashboard_infobox_label_port_flat": "Плоский буфер:",
"dashboard_infobox_label_port_json": "JSON-сервер", "dashboard_infobox_label_port_json": "JSON-сервер",
"dashboard_infobox_label_port_proto": "Протобуфер:", "dashboard_infobox_label_port_proto": "Протобуфер:",
@ -213,6 +231,7 @@
"edt_append_percent_v": "% верт.", "edt_append_percent_v": "% верт.",
"edt_append_pixel": "Пиксель", "edt_append_pixel": "Пиксель",
"edt_append_s": "сек", "edt_append_s": "сек",
"edt_append_sdegree": "с/градус",
"edt_conf_bb_blurRemoveCnt_expl": "Количество пикселей, которые удаляются с обнаруженной границы, чтобы убрать размытие.", "edt_conf_bb_blurRemoveCnt_expl": "Количество пикселей, которые удаляются с обнаруженной границы, чтобы убрать размытие.",
"edt_conf_bb_blurRemoveCnt_title": "Размытие пикселя", "edt_conf_bb_blurRemoveCnt_title": "Размытие пикселя",
"edt_conf_bb_borderFrameCnt_expl": "Количество кадров до установки согласованной обнаруженной границы.", "edt_conf_bb_borderFrameCnt_expl": "Количество кадров до установки согласованной обнаруженной границы.",
@ -238,6 +257,8 @@
"edt_conf_color_blue_title": "Синий", "edt_conf_color_blue_title": "Синий",
"edt_conf_color_brightnessComp_expl": "Компенсирует разницу в яркости между красным, зеленым, синим, голубым, пурпурным, жёлтым и белым. 100 означает полную компенсацию, 0 без компенсации", "edt_conf_color_brightnessComp_expl": "Компенсирует разницу в яркости между красным, зеленым, синим, голубым, пурпурным, жёлтым и белым. 100 означает полную компенсацию, 0 без компенсации",
"edt_conf_color_brightnessComp_title": "Компенсация яркости", "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_expl": "установить общую яркость светодиодов",
"edt_conf_color_brightness_title": "Яркость", "edt_conf_color_brightness_title": "Яркость",
"edt_conf_color_channelAdjustment_header_expl": "Создавайте цветовые профили, которые можно назначить конкретному компоненту. Отрегулируйте цвет, гамму, яркость, компенсацию и многое другое.", "edt_conf_color_channelAdjustment_header_expl": "Создавайте цветовые профили, которые можно назначить конкретному компоненту. Отрегулируйте цвет, гамму, яркость, компенсацию и многое другое.",
@ -264,6 +285,8 @@
"edt_conf_color_magenta_title": "Пурпурный", "edt_conf_color_magenta_title": "Пурпурный",
"edt_conf_color_red_expl": "Откалиброванное значение красного.", "edt_conf_color_red_expl": "Откалиброванное значение красного.",
"edt_conf_color_red_title": "Красный", "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_expl": "Калиброванное значение белого.",
"edt_conf_color_white_title": "Белый", "edt_conf_color_white_title": "Белый",
"edt_conf_color_yellow_expl": "Откалиброванное значение жёлтого цвета.", "edt_conf_color_yellow_expl": "Откалиброванное значение жёлтого цвета.",
@ -319,6 +342,8 @@
"edt_conf_enum_top_down": "Сверху вниз", "edt_conf_enum_top_down": "Сверху вниз",
"edt_conf_enum_transeffect_smooth": "Сглаживание", "edt_conf_enum_transeffect_smooth": "Сглаживание",
"edt_conf_enum_transeffect_sudden": "Внезапный", "edt_conf_enum_transeffect_sudden": "Внезапный",
"edt_conf_enum_udp_ddp": "DDP",
"edt_conf_enum_udp_raw": "RAW",
"edt_conf_enum_unicolor_mean": "Одноцветный", "edt_conf_enum_unicolor_mean": "Одноцветный",
"edt_conf_fbs_heading_title": "Сервер Flatbuffers", "edt_conf_fbs_heading_title": "Сервер Flatbuffers",
"edt_conf_fbs_timeout_expl": "Если данные за указанный период не поступают, компонент будет (мягко) отключён.", "edt_conf_fbs_timeout_expl": "Если данные за указанный период не поступают, компонент будет (мягко) отключён.",
@ -347,11 +372,18 @@
"edt_conf_fge_type_title": "Тип", "edt_conf_fge_type_title": "Тип",
"edt_conf_fw_flat_expl": "Одна цель плоского буфера на строку. Содержит IP: ПОРТ (Пример: 127.0.0.1:19401)", "edt_conf_fw_flat_expl": "Одна цель плоского буфера на строку. Содержит IP: ПОРТ (Пример: 127.0.0.1:19401)",
"edt_conf_fw_flat_itemtitle": "цель плоского буфера", "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_flat_title": "Список целей плоского буфера",
"edt_conf_fw_heading_title": "Экспедитор", "edt_conf_fw_heading_title": "Экспедитор",
"edt_conf_fw_json_expl": "Одна json цель на строку. Содержит IP:PORT (Пример: 127.0.0.1:19446)", "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_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_configVersion_title": "Версия конфигурации",
"edt_conf_gen_heading_title": "Общие настройки", "edt_conf_gen_heading_title": "Общие настройки",
"edt_conf_gen_name_expl": "Пользовательское имя, которое используется для обнаружения Hyperion. (Полезно с более чем одним экземпляром Hyperion)", "edt_conf_gen_name_expl": "Пользовательское имя, которое используется для обнаружения Hyperion. (Полезно с более чем одним экземпляром Hyperion)",
@ -410,8 +442,6 @@
"edt_conf_smooth_heading_title": "Сглаживание", "edt_conf_smooth_heading_title": "Сглаживание",
"edt_conf_smooth_interpolationRate_expl": "Скорость расчета плавных промежуточных кадров.", "edt_conf_smooth_interpolationRate_expl": "Скорость расчета плавных промежуточных кадров.",
"edt_conf_smooth_interpolationRate_title": "Скорость интерполяции", "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_expl": "Как долго сглаживание должно собирать картинки?",
"edt_conf_smooth_time_ms_title": "Время", "edt_conf_smooth_time_ms_title": "Время",
"edt_conf_smooth_type_expl": "Тип сглаживания.", "edt_conf_smooth_type_expl": "Тип сглаживания.",
@ -497,8 +527,13 @@
"edt_dev_enum_subtract_minimum": "Уменьшить минимум", "edt_dev_enum_subtract_minimum": "Уменьшить минимум",
"edt_dev_enum_white_off": "Выключить белый ", "edt_dev_enum_white_off": "Выключить белый ",
"edt_dev_general_autostart_title": "Автозапуск", "edt_dev_general_autostart_title": "Автозапуск",
"edt_dev_general_autostart_title_info": "Включать LED устройство при загрузке или нет",
"edt_dev_general_colorOrder_title": "Порядок байтов RGB", "edt_dev_general_colorOrder_title": "Порядок байтов RGB",
"edt_dev_general_colorOrder_title_info": "Порядок цвета устройства", "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": "Количество светодиодных индикаторов оборудования",
"edt_dev_general_hardwareLedCount_title_info": "Количество физических светодиодов, доступных для данного устройства", "edt_dev_general_hardwareLedCount_title_info": "Количество физических светодиодов, доступных для данного устройства",
"edt_dev_general_heading_title": "Общие настройки", "edt_dev_general_heading_title": "Общие настройки",
@ -509,12 +544,17 @@
"edt_dev_spec_FCsetConfig_title": "Установить конфигурацию fadecandy", "edt_dev_spec_FCsetConfig_title": "Установить конфигурацию fadecandy",
"edt_dev_spec_LBap102Mode_title": "Режим LightBerry APA102", "edt_dev_spec_LBap102Mode_title": "Режим LightBerry APA102",
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo", "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_baudrate_title": "Скорость",
"edt_dev_spec_blackLightsTimeout_title": "Тайм-аут обнаружения сигнала на черном", "edt_dev_spec_blackLightsTimeout_title": "Тайм-аут обнаружения сигнала на черном",
"edt_dev_spec_brightnessFactor_title": "Фактор яркости", "edt_dev_spec_brightnessFactor_title": "Фактор яркости",
"edt_dev_spec_brightnessMax_title": "Максимальная яркость",
"edt_dev_spec_brightnessMin_title": "Минимальная яркость",
"edt_dev_spec_brightnessOverwrite_title": "Перезаписать яркость", "edt_dev_spec_brightnessOverwrite_title": "Перезаписать яркость",
"edt_dev_spec_brightnessThreshold_title": "Минимальная яркость обнаружения сигнала", "edt_dev_spec_brightnessThreshold_title": "Минимальная яркость обнаружения сигнала",
"edt_dev_spec_brightness_title": "Яркость", "edt_dev_spec_brightness_title": "Яркость",
"edt_dev_spec_candyGamma_title": "'Candy' режим (двойная гамма коррекция)",
"edt_dev_spec_chanperfixture_title": "Каналов на прибор", "edt_dev_spec_chanperfixture_title": "Каналов на прибор",
"edt_dev_spec_cid_title": "CID", "edt_dev_spec_cid_title": "CID",
"edt_dev_spec_clientKey_title": "Клиентский ключ", "edt_dev_spec_clientKey_title": "Клиентский ключ",
@ -530,6 +570,7 @@
"edt_dev_spec_dmaNumber_title": "Канал DMA", "edt_dev_spec_dmaNumber_title": "Канал DMA",
"edt_dev_spec_gamma_title": "Гамма", "edt_dev_spec_gamma_title": "Гамма",
"edt_dev_spec_globalBrightnessControlMaxLevel_title": "Максимальный текущий уровень", "edt_dev_spec_globalBrightnessControlMaxLevel_title": "Максимальный текущий уровень",
"edt_dev_spec_globalBrightnessControlThreshold_title": "Адаптивный контроль тока",
"edt_dev_spec_gpioBcm_title": "Вывод GPIO", "edt_dev_spec_gpioBcm_title": "Вывод GPIO",
"edt_dev_spec_gpioMap_title": "Отображение GPIO", "edt_dev_spec_gpioMap_title": "Отображение GPIO",
"edt_dev_spec_gpioNumber_title": "Номер GPIO", "edt_dev_spec_gpioNumber_title": "Номер GPIO",
@ -553,6 +594,8 @@
"edt_dev_spec_networkDeviceName_title": "Сетевое имя устройства", "edt_dev_spec_networkDeviceName_title": "Сетевое имя устройства",
"edt_dev_spec_networkDevicePort_title": "Порт", "edt_dev_spec_networkDevicePort_title": "Порт",
"edt_dev_spec_numberOfLeds_title": "Количество светодиодов", "edt_dev_spec_numberOfLeds_title": "Количество светодиодов",
"edt_dev_spec_onBlackTimeToPowerOff": "Время до отключения подсветки если сработала проверка уровня черного",
"edt_dev_spec_onBlackTimeToPowerOn": "Время до включения подсветки после восстановления сигнала",
"edt_dev_spec_orbIds_title": "ID сфер", "edt_dev_spec_orbIds_title": "ID сфер",
"edt_dev_spec_order_left_right_title": "2.", "edt_dev_spec_order_left_right_title": "2.",
"edt_dev_spec_order_top_down_title": "1.", "edt_dev_spec_order_top_down_title": "1.",
@ -560,19 +603,27 @@
"edt_dev_spec_panel_start_position": "Стартовая панель [0-макс панели]", "edt_dev_spec_panel_start_position": "Стартовая панель [0-макс панели]",
"edt_dev_spec_panelorganisation_title": "Последовательность нумерации панелей", "edt_dev_spec_panelorganisation_title": "Последовательность нумерации панелей",
"edt_dev_spec_pid_title": "PID", "edt_dev_spec_pid_title": "PID",
"edt_dev_spec_port_expl": "Сервисный порт [1-65535]",
"edt_dev_spec_port_title": "Порт", "edt_dev_spec_port_title": "Порт",
"edt_dev_spec_printTimeStamp_title": "Добавить отметку времени", "edt_dev_spec_printTimeStamp_title": "Добавить отметку времени",
"edt_dev_spec_pwmChannel_title": "Канал ШИМ (PWM)", "edt_dev_spec_pwmChannel_title": "Канал ШИМ (PWM)",
"edt_dev_spec_razer_device_title": "Устройство Razer Chroma", "edt_dev_spec_razer_device_title": "Устройство Razer Chroma",
"edt_dev_spec_restoreOriginalState_title": "Восстановить состояние огней", "edt_dev_spec_restoreOriginalState_title": "Восстановить состояние огней",
"edt_dev_spec_restoreOriginalState_title_info": "Восстановить исходное состояние устройства, когда устройство отключено", "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_serial_title": "Серийный номер",
"edt_dev_spec_spipath_title": "Устройство SPI", "edt_dev_spec_spipath_title": "Устройство SPI",
"edt_dev_spec_sslHSTimeoutMax_title": "Максимальное время ожидания подтверждения стримером", "edt_dev_spec_sslHSTimeoutMax_title": "Максимальное время ожидания подтверждения стримером",
"edt_dev_spec_sslHSTimeoutMin_title": "Минимальное время ожидания подтверждения стримером", "edt_dev_spec_sslHSTimeoutMin_title": "Минимальное время ожидания подтверждения стримером",
"edt_dev_spec_stream_protocol_title": "Протокол",
"edt_dev_spec_switchOffOnBlack_title": "Выключить черный", "edt_dev_spec_switchOffOnBlack_title": "Выключить черный",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Отключение, ниже минимального", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Отключение, ниже минимального",
"edt_dev_spec_syncOverwrite_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": "Целевое имя хоста/IP-адрес",
"edt_dev_spec_targetIpHost_title_info": "Имя хоста или IP-адрес устройства", "edt_dev_spec_targetIpHost_title_info": "Имя хоста или IP-адрес устройства",
"edt_dev_spec_targetIp_title": "Целевой IP-адрес", "edt_dev_spec_targetIp_title": "Целевой IP-адрес",
@ -811,14 +862,17 @@
"general_country_us": "Соединённые Штаты Америки", "general_country_us": "Соединённые Штаты Америки",
"general_disabled": "отключено", "general_disabled": "отключено",
"general_enabled": "включено", "general_enabled": "включено",
"general_speech_ca": "Каталонский",
"general_speech_cs": "Чешский", "general_speech_cs": "Чешский",
"general_speech_da": "Danish", "general_speech_da": "Датский",
"general_speech_de": "Немецкий", "general_speech_de": "Немецкий",
"general_speech_el": "Греческий",
"general_speech_en": "Английский", "general_speech_en": "Английский",
"general_speech_es": "Испанский", "general_speech_es": "Испанский",
"general_speech_fr": "французский", "general_speech_fr": "французский",
"general_speech_hu": "Hungarian", "general_speech_hu": "Венгерский",
"general_speech_it": "Итальянский", "general_speech_it": "Итальянский",
"general_speech_ja": "Японский",
"general_speech_nb": "норвежский", "general_speech_nb": "норвежский",
"general_speech_nl": "Dutch", "general_speech_nl": "Dutch",
"general_speech_pl": "Polish", "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_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_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_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": "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_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", "about_build": "Bygge",
@ -55,7 +58,9 @@
"conf_leds_error_get_properties_title": "Enhetsegenskaper", "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_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": "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_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_info_ws281x": "Hyperion måste köras med 'root'-privilegier för denna styrenhetstyp!",
"conf_leds_layout_advanced": "Utökade alternativ", "conf_leds_layout_advanced": "Utökade alternativ",
"conf_leds_layout_blacklist_num_title": "Antal lysdioder", "conf_leds_layout_blacklist_num_title": "Antal lysdioder",
@ -439,8 +444,6 @@
"edt_conf_smooth_heading_title": "Utjämning", "edt_conf_smooth_heading_title": "Utjämning",
"edt_conf_smooth_interpolationRate_expl": "Frekvens i vilken mellanliggande utjämningssteg beräknas.", "edt_conf_smooth_interpolationRate_expl": "Frekvens i vilken mellanliggande utjämningssteg beräknas.",
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrekvens", "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_expl": "Hur länge ska utjämningen samla bilder?",
"edt_conf_smooth_time_ms_title": "Tid", "edt_conf_smooth_time_ms_title": "Tid",
"edt_conf_smooth_type_expl": "Utjämningsalgoritm.", "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_green": "Grön/Vit kanalförhållande",
"edt_dev_spec_rgbw_calibration_limit": "Vit kanalgräns", "edt_dev_spec_rgbw_calibration_limit": "Vit kanalgräns",
"edt_dev_spec_rgbw_calibration_red": "Röd/Vit kanalförhållande", "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_serial_title": "Serienummer",
"edt_dev_spec_spipath_title": "SPI Pfad", "edt_dev_spec_spipath_title": "SPI Pfad",
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handskakning maximal timeout", "edt_dev_spec_sslHSTimeoutMax_title": "Streamer handskakning maximal timeout",
"edt_dev_spec_sslHSTimeoutMin_title": "Minsta timeout för Streamerhandslag", "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_stream_protocol_title": "Strömningsprotokoll",
"edt_dev_spec_switchOffOnBlack_title": "Av på svart", "edt_dev_spec_switchOffOnBlack_title": "Av på svart",
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Av vid lägsta", "edt_dev_spec_switchOffOnbelowMinBrightness_title": "Av vid lägsta",
@ -861,9 +871,11 @@
"general_country_us": "USA", "general_country_us": "USA",
"general_disabled": "Inaktiverad", "general_disabled": "Inaktiverad",
"general_enabled": "Aktiverad", "general_enabled": "Aktiverad",
"general_speech_ca": "Katalanska",
"general_speech_cs": "Tjeckiska", "general_speech_cs": "Tjeckiska",
"general_speech_da": "Danska", "general_speech_da": "Danska",
"general_speech_de": "Tyska", "general_speech_de": "Tyska",
"general_speech_el": "Grekiska",
"general_speech_en": "Engelska", "general_speech_en": "Engelska",
"general_speech_es": "Spanska", "general_speech_es": "Spanska",
"general_speech_fr": "Franska", "general_speech_fr": "Franska",

View File

@ -24,8 +24,10 @@
<script src="js/lib/jquery/jquery-migrate-3.3.2.min.js"></script> <script src="js/lib/jquery/jquery-migrate-3.3.2.min.js"></script>
<!-- jQuery - Dev --> <!-- 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 --> <!-- SemVer -->
<script src='js/lib/semver.js'></script> <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 != "ALL") {
if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) || if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) ||
(components[idx].name === "GRABBER" && !window.serverConfig.framegrabber.enable) || (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; continue;
var comp_enabled = components[idx].enabled ? "checked" : ""; var comp_enabled = components[idx].enabled ? "checked" : "";
@ -104,8 +105,9 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.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) { if (screenGrabberAvailable) {
var screenGrabber = window.serverConfig.framegrabber.enable ? $.i18n('general_enabled') : $.i18n('general_disabled'); var screenGrabber = window.serverConfig.framegrabber.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
@ -120,6 +122,13 @@ $(document).ready(function () {
} else { } else {
$("#dash_video_grabber_row").hide(); $("#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 { } else {
$("#dash_capture_hw").hide(); $("#dash_capture_hw").hide();
} }

View File

@ -4,9 +4,11 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.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 CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1);
var conf_editor_video = null; var conf_editor_video = null;
var conf_editor_audio = null;
var conf_editor_screen = null; var conf_editor_screen = null;
var configuredDevice = ""; 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) { JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
var errors = []; 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) { if (videoGrabberAvailable) {
createHint("intro", $.i18n('conf_grabber_v4l_intro'), "editor_container_videograbber"); createHint("intro", $.i18n('conf_grabber_v4l_intro'), "editor_container_videograbber");
} }
if (audioGrabberAvailable) {
createHint("intro", $.i18n('conf_grabber_audio_intro'), "editor_container_audiograbber");
}
} }
removeOverlay(); 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) { async function discoverInputSources(type, params) {
const result = await requestInputSourcesDiscovery(type, params); const result = await requestInputSourcesDiscovery(type, params);
@ -782,7 +950,8 @@ $(document).ready(function () {
} }
else { else {
discoveryResult = { discoveryResult = {
"video_sources": [] "video_sources": [],
"audio_soruces": []
}; };
} }
@ -799,6 +968,12 @@ $(document).ready(function () {
updateVideoSourcesList(type, discoveredInputSources.video); updateVideoSourcesList(type, discoveredInputSources.video);
} }
break; 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')) if (getStorage('lastSelectedInstance'))
removeStorage('lastSelectedInstance') removeStorage('lastSelectedInstance')
currentHyperionInstance = 0; window.currentHyperionInstance = 0;
currentHyperionInstanceName = getInstanceNameByIndex(0); window.currentHyperionInstanceName = getInstanceNameByIndex(0);
requestServerConfig(); requestServerConfig();
setTimeout(requestServerInfo, 100) setTimeout(requestServerInfo, 100)
setTimeout(requestTokenInfo, 200) setTimeout(requestTokenInfo, 200)
@ -293,7 +294,7 @@ $(document).ready(function () {
$('#btn_hypinstanceswitch').toggle(false) $('#btn_hypinstanceswitch').toggle(false)
// update listing for button // update listing for button
updateUiOnInstance(currentHyperionInstance); updateUiOnInstance(window.currentHyperionInstance);
updateHyperionInstanceListing(); updateHyperionInstanceListing();
}); });

View File

@ -3,6 +3,7 @@ $(document).ready(function () {
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0); var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
var videoGrabberAvailable = (window.serverInfo.grabbers.video.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); var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
@ -15,7 +16,7 @@ $(document).ready(function () {
// Instance Capture // Instance Capture
if (window.showOptHelp) { if (window.showOptHelp) {
if (screenGrabberAvailable || videoGrabberAvailable) { if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
$('#conf_cont').append(createRow('conf_cont_instCapt')); $('#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(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"))); $('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
@ -29,7 +30,7 @@ $(document).ready(function () {
} }
else { else {
$('#conf_cont').addClass('row'); $('#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', '')); $('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
} }
if (BOBLIGHT_ENABLED) { if (BOBLIGHT_ENABLED) {
@ -37,7 +38,7 @@ $(document).ready(function () {
} }
} }
if (screenGrabberAvailable || videoGrabberAvailable) { if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
// Instance Capture // Instance Capture
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', { conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
@ -81,12 +82,29 @@ $(document).ready(function () {
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false); 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 () { conf_editor_instCapt.on('change', function () {
if (!conf_editor_instCapt.validate().length) { 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); $('#btn_submit_instCapt').prop('disabled', true);
} else { } else {
window.readOnlyMode ? $('#btn_submit_instCapt').prop('disabled', true) : $('#btn_submit_instCapt').prop('disabled', false); 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 () { $('#btn_submit_instCapt').off().on('click', function () {
requestWriteConfig(conf_editor_instCapt.getValue()); requestWriteConfig(conf_editor_instCapt.getValue());
}); });

View File

@ -1002,6 +1002,21 @@ $(document).ready(function () {
addJsonEditorHostValidation(); 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 () { $("#leddevices").off().on("change", function () {
var generalOptions = window.serverSchema.properties.device; var generalOptions = window.serverSchema.properties.device;
@ -1080,8 +1095,8 @@ $(document).ready(function () {
$('#btn_test_controller').hide(); $('#btn_test_controller').hide();
switch (ledType) { switch (ledType) {
case "cololight":
case "wled": case "wled":
case "cololight":
case "nanoleaf": case "nanoleaf":
showAllDeviceInputOptions("hostList", false); showAllDeviceInputOptions("hostList", false);
case "apa102": case "apa102":
@ -1107,7 +1122,22 @@ $(document).ready(function () {
if (storedAccess === 'expert') { if (storedAccess === 'expert') {
filter.discoverAll = true; 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; hwLedCountDefault = 1;
colorOrderDefault = "rgb"; colorOrderDefault = "rgb";
break; break;
@ -1211,8 +1241,8 @@ $(document).ready(function () {
} }
break; break;
case "cololight":
case "wled": case "wled":
case "cololight":
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue(); var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
if (hostList !== "SELECT") { if (hostList !== "SELECT") {
var host = conf_editor.getEditor("root.specificOptions.host").getValue(); var host = conf_editor.getEditor("root.specificOptions.host").getValue();
@ -1339,7 +1369,9 @@ $(document).ready(function () {
break; break;
case "wled": 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); getProperties_device(ledType, host, params);
break; break;
@ -1452,6 +1484,10 @@ $(document).ready(function () {
} }
conf_editor.getEditor("root.specificOptions.rateList").setValue(rate); conf_editor.getEditor("root.specificOptions.rateList").setValue(rate);
break; break;
case "wled":
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
validateWledLedCount(hardwareLedCount);
break;
default: 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 //Handle Hardware Led Count constraint list
conf_editor.watch('root.generalOptions.hardwareLedCountList', () => { conf_editor.watch('root.generalOptions.hardwareLedCountList', () => {
var hwLedCountSelected = conf_editor.getEditor("root.generalOptions.hardwareLedCountList").getValue(); var hwLedCountSelected = conf_editor.getEditor("root.generalOptions.hardwareLedCountList").getValue();
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(Number(hwLedCountSelected)); 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 //philipshueentertainment backward fix
@ -1798,8 +1876,8 @@ function saveLedConfig(genDefLayout = false) {
location.reload(); location.reload();
} }
// build dynamic enum // build dynamic enum for hosts or output paths
var updateSelectList = function (ledType, discoveryInfo) { var updateOutputSelectList = function (ledType, discoveryInfo) {
// Only update, if ledType is equal of selected controller type and discovery info exists // Only update, if ledType is equal of selected controller type and discovery info exists
if (ledType !== $("#leddevices").val() || !discoveryInfo.devices) { if (ledType !== $("#leddevices").val() || !discoveryInfo.devices) {
return; return;
@ -1810,7 +1888,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
var key; var key;
var enumVals = []; var enumVals = [];
var enumTitelVals = []; var enumTitleVals = [];
var enumDefaultVal = ""; var enumDefaultVal = "";
var addSelect = false; var addSelect = false;
var addCustom = false; var addCustom = false;
@ -1835,7 +1913,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length === 0) { if (discoveryInfo.devices.length === 0) {
enumVals.push("NONE"); enumVals.push("NONE");
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none')); enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
} }
else { else {
var name; var name;
@ -1876,7 +1954,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
} }
enumVals.push(host); enumVals.push(host);
enumTitelVals.push(name); enumTitleVals.push(name);
} }
//Always allow to add custom configuration //Always allow to add custom configuration
@ -1904,7 +1982,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) { if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE"); 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); $('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false); showAllDeviceInputOptions(key, false);
} }
@ -1922,7 +2000,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
} else { } else {
enumVals.push(device.portName); 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 // Select configured device
@ -1951,7 +2029,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) { if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE"); 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); $('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false); showAllDeviceInputOptions(key, false);
} }
@ -1970,7 +2048,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
case "piblaster": case "piblaster":
for (const device of discoveryInfo.devices) { for (const device of discoveryInfo.devices) {
enumVals.push(device.systemLocation); enumVals.push(device.systemLocation);
enumTitelVals.push(device.deviceName + " (" + device.systemLocation + ")"); enumTitleVals.push(device.deviceName + " (" + device.systemLocation + ")");
} }
// Select configured device // Select configured device
@ -1993,7 +2071,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
if (discoveryInfo.devices.length == 0) { if (discoveryInfo.devices.length == 0) {
enumVals.push("NONE"); 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); $('#btn_submit_controller').prop('disabled', true);
showAllDeviceInputOptions(key, false); showAllDeviceInputOptions(key, false);
@ -2004,18 +2082,19 @@ var updateSelectList = function (ledType, discoveryInfo) {
} }
if (enumVals.length > 0) { 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) { async function discover_device(ledType, params) {
$('#btn_submit_controller').prop('disabled', true);
const result = await requestLedDeviceDiscovery(ledType, params); const result = await requestLedDeviceDiscovery(ledType, params);
var discoveryResult; var discoveryResult = {};
if (result && !result.error) { if (result) {
if (result.error) {
throw (result.error);
}
discoveryResult = result.info; discoveryResult = result.info;
} }
else { else {
@ -2024,8 +2103,7 @@ async function discover_device(ledType, params) {
ledDevicetype: ledType ledDevicetype: ledType
} }
} }
return discoveryResult;
updateSelectList(ledType, discoveryResult);
} }
async function getProperties_device(ledType, key, params) { async function getProperties_device(ledType, key, params) {
@ -2089,23 +2167,7 @@ function updateElements(ledType, key) {
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount); conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
break; break;
case "wled": case "wled":
var ledProperties = devicesProperties[ledType][key]; updateElementsWled(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);
break; break;
case "nanoleaf": case "nanoleaf":
@ -2190,3 +2252,168 @@ function disableAutoResolvedGeneralOptions() {
conf_editor.getEditor("root.generalOptions.colorOrder").disable(); 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() { function infoSummary() {
var info = ""; 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 += "\n< ----- System information -------------------- >\n";
info += getSystemInfo() + '\n'; info += getSystemInfo() + '\n';
@ -43,22 +44,36 @@ $(document).ready(function () {
info += "\n< ----- Configured Instances ------------------ >\n"; info += "\n< ----- Configured Instances ------------------ >\n";
var instances = window.serverInfo.instance; var instances = window.serverInfo.instance;
for (var i = 0; i < instances.length; i++) { 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"; info += "\n< ----- This instance's priorities ------------ >\n";
var prios = window.serverInfo.priorities; var prios = window.serverInfo.priorities;
for (var i = 0; i < prios.length; i++) {
info += prios[i].priority + ': '; if (prios.length > 0) {
if (prios[i].visible) {
info += ' VISIBLE!'; 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 { } else {
info += ' '; info += 'The current priority list is empty!\n';
}
info += ' (' + prios[i].componentId + ') Owner: ' + prios[i].owner + '\n';
} }
info += 'priorities_autoselect: ' + window.serverInfo.priorities_autoselect + '\n'; info += 'Autoselect: ' + window.serverInfo.priorities_autoselect + '\n';
info += "\n< ----- This instance components' status ------->\n"; info += "\n< ----- This instance components' status ------->\n";
var comps = window.serverInfo.components; var comps = window.serverInfo.components;
@ -67,7 +82,7 @@ $(document).ready(function () {
} }
info += "\n< ----- This instance's configuration --------- >\n"; 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"; info += "\n< ----- Current Log --------------------------- >\n";
var logMsgs = document.getElementById("logmessages").textContent; var logMsgs = document.getElementById("logmessages").textContent;
@ -193,18 +208,19 @@ $(document).ready(function () {
}); });
// toggle fullscreen button in log output // toggle fullscreen button in log output
$(".fullscreen-btn").mousedown(function(e) { $(".fullscreen-btn").mousedown(function (e) {
e.preventDefault(); e.preventDefault();
}); });
$(".fullscreen-btn").click(function(e) { $(".fullscreen-btn").click(function (e) {
e.preventDefault(); e.preventDefault();
$(this).children('i') $(this).children('i')
.toggleClass('fa-expand') .toggleClass('fa-expand')
.toggleClass('fa-compress'); .toggleClass('fa-compress');
$('#conf_cont').toggle(); $('#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(); removeOverlay();
}); });

View File

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

View File

@ -171,6 +171,10 @@ function initLanguageSelection() {
} }
function updateUiOnInstance(inst) { function updateUiOnInstance(inst) {
window.currentHyperionInstance = inst;
window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
$("#active_instance_friendly_name").text(getInstanceNameByIndex(inst)); $("#active_instance_friendly_name").text(getInstanceNameByIndex(inst));
if (window.serverInfo.instance.filter(entry => entry.running).length > 1) { if (window.serverInfo.instance.filter(entry => entry.running).length > 1) {
$('#btn_hypinstanceswitch').toggle(true); $('#btn_hypinstanceswitch').toggle(true);
@ -316,7 +320,7 @@ function showInfoDialog(type, header, message) {
$(document).on('click', '[data-dismiss-modal]', function () { $(document).on('click', '[data-dismiss-modal]', function () {
var target = $(this).attr('data-dismiss-modal'); 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 ""; 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) { function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
$('#' + container).off(); $('#' + container).off();
$('#' + container).html(""); $('#' + container).html("");
@ -527,7 +557,8 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
editor.original_schema.properties[key] = orginalProperties; editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; 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); editor.removeObjectProperty(key);
delete editor.cached_editors[key]; delete editor.cached_editors[key];
@ -596,7 +627,8 @@ function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newE
editor.original_schema.properties[key] = orginalProperties; editor.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; 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); editor.removeObjectProperty(key);
delete editor.cached_editors[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.original_schema.properties[key] = orginalProperties;
editor.schema.properties[key] = newSchema[key]; 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); editor.removeObjectProperty(key);
delete editor.cached_editors[key]; delete editor.cached_editors[key];
@ -1187,6 +1220,7 @@ function getSystemInfo() {
//info += '- Log lvl: ' + window.serverConfig.logger.level + '\n'; //info += '- Log lvl: ' + window.serverConfig.logger.level + '\n';
info += '- Avail Screen Cap.: ' + window.serverInfo.grabbers.screen.available + '\n'; info += '- Avail Screen Cap.: ' + window.serverInfo.grabbers.screen.available + '\n';
info += '- Avail Video Cap.: ' + window.serverInfo.grabbers.video.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 += '- Avail Services: ' + window.serverInfo.services + '\n';
info += '- Config path: ' + shy.rootPath + '\n'; info += '- Config path: ' + shy.rootPath + '\n';
info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n'; info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n';
@ -1246,15 +1280,26 @@ function isAccessLevelCompliant(accessLevel) {
} }
function showInputOptions(path, elements, state) { function showInputOptions(path, elements, state) {
if (!path.startsWith("root.")) {
path = ["root", path].join('.');
}
for (var i = 0; i < elements.length; i++) { 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) { 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 // Enable element only, if access level compliant
if (!state || isAccessLevelCompliant(accessLevel)) { if (!state || isAccessLevelCompliant(accessLevel)) {
if (!path) {
debugger;
path = editor.path;
}
showInputOptions(path, [item], state); showInputOptions(path, [item], state);
} }
} }
@ -1269,17 +1314,26 @@ function showInputOptionsForKey(editor, item, showForKeys, state) {
if (typeof showForKeys === 'string') { if (typeof showForKeys === 'string') {
keysToshow.push(showForKeys); keysToshow.push(showForKeys);
} else { } 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) { 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 //Always disable all elements, but only enable elements, if access level compliant
if (!state || isAccessLevelCompliant(accessLevel)) { if (!state || isAccessLevelCompliant(accessLevel)) {
elements.push(key); if (!hidden) {
elements.push(key);
}
} }
} }
} }
@ -1314,7 +1368,7 @@ function isValidIPv6(value) {
function isValidHostname(value) { function isValidHostname(value) {
if (value.match( 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; return true;
else else
@ -1323,7 +1377,7 @@ function isValidHostname(value) {
function isValidServicename(value) { function isValidServicename(value) {
if (value.match( 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; return true;
else else

View File

@ -864,6 +864,7 @@ function useGroupId(id) {
get_hue_lights(); get_hue_lights();
} }
async function discover_hue_bridges() { async function discover_hue_bridges() {
$('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress')); $('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress'));
$('#wiz_hue_discovered').html("") $('#wiz_hue_discovered').html("")
@ -1021,11 +1022,11 @@ function beginWizardHue() {
$('#host').val(host); $('#host').val(host);
var port = eV("port"); var port = eV("port");
if (port > 0) { if (port == 0) {
$('#port').val(port); $('#port').val(80);
} }
else { else {
$('#port').val(''); $('#port').val(port);
} }
hueIPs.unshift({ host: host, port: port }); hueIPs.unshift({ host: host, port: port });
@ -1042,7 +1043,13 @@ function beginWizardHue() {
hueIPs = []; hueIPs = [];
hueIPsinc = 0; 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 { else {
discover_hue_bridges(); 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 # Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
# #
@ -49,10 +49,11 @@
# http://academic.cleardefinition.com # http://academic.cleardefinition.com
# Iowa State University HCI Graduate Program/VRAC # 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. # Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at # (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt) # http://www.boost.org/LICENSE_1_0.txt)
# SPDX-License-Identifier: BSL-1.0
set(_preferred_sdk_dirs) # pre-output set(_preferred_sdk_dirs) # pre-output
set(_win_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 # although version numbers listed on that page don't necessarily match the directory
# used by the installer. # used by the installer.
set(_winsdk_win10vers set(_winsdk_win10vers
10.0.22000.0
10.0.20348.0
10.0.19041.0
10.0.18362.0 # Win10 1903 "19H1" 10.0.18362.0 # Win10 1903 "19H1"
10.0.17763.0 # Win10 1809 "October 2018 Update" 10.0.17763.0 # Win10 1809 "October 2018 Update"
10.0.17134.0 # Redstone 4 aka Win10 1803 "April 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) if(ENABLE_V4L2)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_v4l2" ) SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_v4l2" )
endif() endif()
if(ENABLE_AUDIO)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_audio" )
endif()
if(ENABLE_X11) if(ENABLE_X11)
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_x11" ) SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_x11" )
endif() endif()

View File

@ -1,240 +1,237 @@
{ {
"general" : "general": {
{ "name": "My Hyperion Config",
"name" : "My Hyperion Config", "configVersion": "configVersionValue",
"configVersion": "configVersionValue", "previousVersion": "previousVersionValue",
"previousVersion": "previousVersionValue", "watchedVersionBranch": "Stable",
"watchedVersionBranch" : "Stable", "showOptHelp": true
"showOptHelp" : true
}, },
"logger" : "logger": {
{ "level": "warn"
"level" : "warn"
}, },
"device" : "device": {
{ "type": "file",
"type" : "file", "hardwareLedCount": 1,
"hardwareLedCount" : 1, "autoStart": true,
"autoStart" : true, "output": "/dev/null",
"output" : "/dev/null", "colorOrder": "rgb",
"colorOrder" : "rgb", "latchTime": 0,
"latchTime" : 0,
"rewriteTime": 0, "rewriteTime": 0,
"enableAttempts": 6, "enableAttempts": 6,
"enableAttemptsInterval": 15 "enableAttemptsInterval": 15
}, },
"color" : "color": {
{ "imageToLedMappingType": "multicolor_mean",
"imageToLedMappingType" : "multicolor_mean", "channelAdjustment": [
"channelAdjustment" :
[
{ {
"id" : "default", "id": "default",
"leds" : "*", "leds": "*",
"white" : [255,255,255], "white": [ 255, 255, 255 ],
"red" : [255,0,0], "red": [ 255, 0, 0 ],
"green" : [0,255,0], "green": [ 0, 255, 0 ],
"blue" : [0,0,255], "blue": [ 0, 0, 255 ],
"cyan" : [0,255,255], "cyan": [ 0, 255, 255 ],
"magenta" : [255,0,255], "magenta": [ 255, 0, 255 ],
"yellow" : [255,255,0], "yellow": [ 255, 255, 0 ],
"gammaRed" : 2.2, "gammaRed": 2.2,
"gammaGreen" : 2.2, "gammaGreen": 2.2,
"gammaBlue" : 2.2, "gammaBlue": 2.2,
"backlightThreshold" : 0, "backlightThreshold": 0,
"backlightColored" : false, "backlightColored": false,
"brightness" : 100, "brightness": 100,
"brightnessCompensation" : 100, "brightnessCompensation": 100,
"saturationGain" : 1.0, "saturationGain": 1.0,
"brightnessGain" : 1.0, "brightnessGain": 1.0,
"temperature" : 6600 "temperature" : 6600
} }
] ]
}, },
"smoothing" : "smoothing": {
{ "enable": true,
"enable" : true, "type": "linear",
"type" : "linear", "time_ms": 150,
"time_ms" : 200, "updateFrequency": 25.0000,
"updateFrequency" : 25.0000, "interpolationRate": 25.0000,
"interpolationRate" : 25.0000, "decay": 1,
"decay" : 1, "dithering": false,
"dithering" : false, "updateDelay": 0
"updateDelay" : 0
}, },
"grabberV4L2" : "grabberV4L2": {
{ "enable": false,
"enable" : false, "device": "none",
"device" : "none", "input": 0,
"input" : 0, "encoding": "NO_CHANGE",
"encoding" : "NO_CHANGE", "width": 0,
"width" : 0, "height": 0,
"height" : 0, "fps": 15,
"fps" : 15, "flip": "NO_CHANGE",
"flip" : "NO_CHANGE", "fpsSoftwareDecimation": 0,
"fpsSoftwareDecimation" : 0, "sizeDecimation": 8,
"sizeDecimation" : 8, "cropLeft": 0,
"cropLeft" : 0, "cropRight": 0,
"cropRight" : 0, "cropTop": 0,
"cropTop" : 0, "cropBottom": 0,
"cropBottom" : 0, "redSignalThreshold": 0,
"redSignalThreshold" : 0, "greenSignalThreshold": 100,
"greenSignalThreshold" : 100, "blueSignalThreshold": 0,
"blueSignalThreshold" : 0, "signalDetection": false,
"signalDetection" : false, "noSignalCounterThreshold": 200,
"noSignalCounterThreshold" : 200, "cecDetection": false,
"cecDetection" : false, "sDVOffsetMin": 0.1,
"sDVOffsetMin" : 0.1, "sDVOffsetMax": 0.9,
"sDVOffsetMax" : 0.9, "sDHOffsetMin": 0.4,
"sDHOffsetMin" : 0.4, "sDHOffsetMax": 0.46,
"sDHOffsetMax" : 0.46, "hardware_brightness": 0,
"hardware_brightness" : 0, "hardware_contrast": 0,
"hardware_contrast" : 0, "hardware_saturation": 0,
"hardware_saturation" : 0, "hardware_hue": 0
"hardware_hue" : 0
}, },
"framegrabber" : "grabberAudio": {
{ "enable": false,
"enable" : false, "device": "auto",
"device" : "auto", "audioEffect": "vuMeter",
"input" : 0, "vuMeter": {
"width" : 80, "flip": "NO_CHANGE",
"height" : 45, "hotColor": [ 255, 0, 0 ],
"fps" : 10, "multiplier": 1,
"pixelDecimation" : 8, "safeColor": [ 0, 255, 0 ],
"cropLeft" : 0, "safeValue": 45,
"cropRight" : 0, "tolerance": 5,
"cropTop" : 0, "warnColor": [ 255, 255, 0 ],
"cropBottom" : 0 "warnValue": 80
}
}, },
"blackborderdetector" : "framegrabber": {
{ "enable": false,
"enable" : true, "device": "auto",
"threshold" : 5, "input": 0,
"unknownFrameCnt" : 600, "width": 80,
"borderFrameCnt" : 50, "height": 45,
"maxInconsistentCnt" : 10, "fps": 10,
"blurRemoveCnt" : 1, "pixelDecimation": 8,
"mode" : "default" "cropLeft": 0,
"cropRight": 0,
"cropTop": 0,
"cropBottom": 0
}, },
"foregroundEffect" : "blackborderdetector": {
{ "enable": true,
"enable" : true, "threshold": 5,
"type" : "effect", "unknownFrameCnt": 600,
"color" : [0,0,255], "borderFrameCnt": 50,
"effect" : "Rainbow swirl fast", "maxInconsistentCnt": 10,
"duration_ms" : 3000 "blurRemoveCnt": 1,
"mode": "default"
}, },
"backgroundEffect" : "foregroundEffect": {
{ "enable": true,
"enable" : false, "type": "effect",
"type" : "effect", "color": [ 0, 0, 255 ],
"color" : [255,138,0], "effect": "Rainbow swirl fast",
"effect" : "Warm mood blobs" "duration_ms": 3000
}, },
"forwarder" : "backgroundEffect": {
{ "enable": false,
"enable" : false, "type": "effect",
"jsonapi" : [], "color": [ 255, 138, 0 ],
"flatbuffer" : [] "effect": "Warm mood blobs"
}, },
"jsonServer" : "forwarder": {
{ "enable": false,
"port" : 19444 "jsonapi": [],
"flatbuffer": []
}, },
"flatbufServer" : "jsonServer": {
{ "port": 19444
"enable" : true,
"port" : 19400,
"timeout" : 5
}, },
"protoServer" : "flatbufServer": {
{ "enable": true,
"enable" : true, "port": 19400,
"port" : 19445, "timeout": 5
"timeout" : 5
}, },
"boblightServer" : "protoServer": {
{ "enable": true,
"enable" : false, "port": 19445,
"port" : 19333, "timeout": 5
"priority" : 128
}, },
"webConfig" : "boblightServer": {
{ "enable": false,
"document_root" : "", "port": 19333,
"port" : 8090, "priority": 128
"sslPort" : 8092,
"crtPath" : "",
"keyPath" : "",
"keyPassPhrase" : ""
}, },
"effects" : "webConfig": {
{ "document_root": "",
"paths" : ["$ROOT/custom-effects"], "port": 8090,
"disable": [""] "sslPort": 8092,
"crtPath": "",
"keyPath": "",
"keyPassPhrase": ""
}, },
"instCapture" : "effects": {
{ "paths": [ "$ROOT/custom-effects" ],
"systemEnable" : false, "disable": [ "" ]
"systemGrabberDevice" : "NONE",
"systemPriority" : 250,
"v4lEnable" : false,
"v4lGrabberDevice" : "NONE",
"v4lPriority" : 240
}, },
"network" : "instCapture": {
{ "systemEnable": false,
"internetAccessAPI" : false, "systemGrabberDevice": "NONE",
"restirctedInternetAccessAPI" : false, "systemPriority": 250,
"ipWhitelist" : [], "v4lEnable": false,
"apiAuth" : true, "v4lGrabberDevice": "NONE",
"localApiAuth" : false, "v4lPriority": 240,
"audioEnable": false,
"audioGrabberDevice": "NONE",
"audioPriority": 230
},
"network": {
"internetAccessAPI": false,
"restirctedInternetAccessAPI": false,
"ipWhitelist": [],
"apiAuth": true,
"localApiAuth": false,
"localAdminAuth": true "localAdminAuth": true
}, },
"ledConfig" : "ledConfig": {
{ "classic": {
"classic": "top": 1,
{ "bottom": 0,
"top" : 1, "left": 0,
"bottom" : 0, "right": 0,
"left" : 0, "glength": 0,
"right" : 0, "gpos": 0,
"glength" : 0, "position": 0,
"gpos" : 0, "reverse": false,
"position" : 0, "hdepth": 8,
"reverse" : false, "vdepth": 5,
"hdepth" : 8, "overlap": 0,
"vdepth" : 5, "edgegap": 0,
"overlap" : 0, "ptlh": 0,
"edgegap" : 0, "ptlv": 0,
"ptlh" : 0, "ptrh": 100,
"ptlv" : 0, "ptrv": 0,
"ptrh" : 100, "pblh": 0,
"ptrv" : 0, "pblv": 100,
"pblh" : 0, "pbrh": 100,
"pblv" : 100, "pbrv": 100
"pbrh" : 100, },
"pbrv" : 100
},
"matrix": { "matrix": {
"ledshoriz": 1, "ledshoriz": 1,
@ -245,8 +242,7 @@
} }
}, },
"leds": "leds": [
[
{ {
"hmax": 1, "hmax": 1,
"hmin": 0, "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") set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static")
endif() 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 # define the include for the protobuf library
set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src") 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(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF PARENT_SCOPE)
set(USE_SYSTEM_MBEDTLS_LIBS OFF) set(USE_SYSTEM_MBEDTLS_LIBS OFF)
endif (NOT MBEDTLS_FOUND) endif (NOT MBEDTLS_FOUND)
endif (USE_SYSTEM_MBEDTLS_LIBS) else()
if (NOT USE_SYSTEM_MBEDTLS_LIBS)
cmake_minimum_required(VERSION 3.2) 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(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(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared mbedtls libraries")
set(ENABLE_TESTING OFF CACHE BOOL "Disable mbedTLS tests") 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(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_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "Disable mbedTLS shared libraries")
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static 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) set(MBEDTLS_LOGGING 0)
endif () endif ()
set(MBEDTLS_CMAKE_ARGS add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/mbedtls)
-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 ()
set (MBEDTLS_INCLUDE_DIR "${MBEDTLS_SOURCE_DIR}/include") set (MBEDTLS_INCLUDE_DIR "${MBEDTLS_SOURCE_DIR}/include")
set (MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR} PARENT_SCOPE) 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) 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) 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 ```console
sudo apt-get update 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** **Ubuntu (22.04+) - Qt6 based**
```console ```console
sudo apt-get update 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** **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. The following dependencies are needed to build hyperion.ng on fedora.
```console ```console
sudo dnf -y groupinstall "Development Tools" 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..). 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 import hyperion, time
# Get the parameters # Get the parameters
speed = float(hyperion.args.get('speed', 1.0)) speed = float(hyperion.args.get('speed', 1.0))
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7)) fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
color = hyperion.args.get('color', (255,0,0)) color = hyperion.args.get('color', (255,0,0))
# Check parameters # Check parameters
speed = max(0.0001, speed) speed = max(0.0001, speed)
fadeFactor = max(0.0, min(fadeFactor, 1.0)) fadeFactor = max(0.0, min(fadeFactor, 1.0))
# Initialize the led data # Initialize the led data
width = 25 width = 25
imageData = bytearray(width * (0,0,0)) imageData = bytearray(width * (0,0,0))
imageData[0] = color[0] imageData[0] = color[0]
imageData[1] = color[1] imageData[1] = color[1]
imageData[2] = color[2] imageData[2] = color[2]
# Calculate the sleep time and rotation increment # Calculate the sleep time and rotation increment
increment = 1 increment = 1
sleepTime = 1.0 / (speed * width) sleepTime = 1.0 / (speed * width)
while sleepTime < 0.05: while sleepTime < 0.05:
increment *= 2 increment *= 2
sleepTime *= 2 sleepTime *= 2
# Start the write data loop # Start the write data loop
position = 0 position = 0
direction = 1 direction = 1
while not hyperion.abort(): while not hyperion.abort():
hyperion.setImage(width, 1, imageData) hyperion.setImage(width, 1, imageData)
# Move data into next state # Move data into next state
for i in range(increment): for unused in range(increment):
position += direction position += direction
if position == -1: if position == -1:
position = 1 position = 1
direction = 1 direction = 1
elif position == width: elif position == width:
position = width-2 position = width-2
direction = -1 direction = -1
# Fade the old data # Fade the old data
for j in range(width): for j in range(width):
imageData[3*j] = int(fadeFactor * imageData[3*j]) imageData[3*j] = int(fadeFactor * imageData[3*j])
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1]) imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2]) imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
# Insert new data # Insert new data
imageData[3*position] = color[0] imageData[3*position] = color[0]
imageData[3*position+1] = color[1] imageData[3*position+1] = color[1]
imageData[3*position+2] = color[2] imageData[3*position+2] = color[2]
# Sleep for a while # Sleep for a while
time.sleep(sleepTime) time.sleep(sleepTime)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,3 @@
//#include <iostream>
#pragma once #pragma once
// Utils includes // Utils includes
@ -219,7 +218,6 @@ namespace hyperion
|| !isBlack(image((width - x), y)) || !isBlack(image((width - x), y))
|| !isBlack(image((width - x), (height - 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; firstNonBlackYPixelIndex = y;
break; break;
} }

View File

@ -111,7 +111,7 @@ public:
// server port services // server port services
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network" list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
// capture // capture
<< "framegrabber" << "grabberV4L2" << "framegrabber" << "grabberV4L2" << "grabberAudio"
// other // other
<< "logger" << "general"; << "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 { enum class GrabberType {
SCREEN, SCREEN,
VIDEO, VIDEO,
AUDIO,
}; };
enum class GrabberTypeFilter { enum class GrabberTypeFilter {
ALL, ALL,
SCREEN, SCREEN,
VIDEO, VIDEO,
AUDIO,
}; };
#endif // GRABBERTYPE_H #endif // GRABBERTYPE_H

View File

@ -20,6 +20,7 @@ public:
void setSystemCaptureEnable(bool enable); void setSystemCaptureEnable(bool enable);
void setV4LCaptureEnable(bool enable); void setV4LCaptureEnable(bool enable);
void setAudioCaptureEnable(bool enable);
private slots: private slots:
/// ///
@ -48,11 +49,22 @@ private slots:
/// ///
void handleV4lImage(const QString& name, const Image<ColorRgb> & image); 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 /// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive
/// ///
void setV4lInactive(); 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 /// @brief Is called from _systemInactiveTimer to set source after specific time to inactive
/// ///
@ -73,4 +85,10 @@ private:
quint8 _v4lCaptPrio; quint8 _v4lCaptPrio;
QString _v4lCaptName; QString _v4lCaptName;
QTimer* _v4lInactiveTimer; 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_SYS_CLIENTS;
static QMap<int, QString> GRABBER_V4L_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_SYS_ENABLE;
static bool GLOBAL_GRABBER_V4L_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 /// 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; } void setSysGrabberState(bool sysGrabberState){ GLOBAL_GRABBER_SYS_ENABLE = sysGrabberState; }
bool getV4lGrabberState() const { return GLOBAL_GRABBER_V4L_ENABLE; } bool getV4lGrabberState() const { return GLOBAL_GRABBER_V4L_ENABLE; }
void setV4lGrabberState(bool v4lGrabberState){ GLOBAL_GRABBER_V4L_ENABLE = v4lGrabberState; } 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); static QStringList availableGrabbers(GrabberTypeFilter type = GrabberTypeFilter::ALL);
@ -147,10 +151,7 @@ private slots:
void handleSourceRequest(hyperion::Components component, int hyperionInd, bool listen); 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: protected:
@ -168,6 +169,11 @@ protected:
/// ///
virtual bool close() { return true; } virtual bool close() { return true; }
/// @brief Update Update capture rate
/// @param type interval between frames in milliseconds
///
void updateTimer(int interval);
QString _grabberName; QString _grabberName;

View File

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

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QString> #include <QString>
#include <QSharedPointer>
// Utils includes // Utils includes
#include <utils/Image.h> #include <utils/Image.h>
@ -46,7 +47,7 @@ public:
/// @param[in] width The new width of the buffer-image /// @param[in] width The new width of the buffer-image
/// @param[in] height The new height 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) /// @brief Update the led string (eg on settings change)
@ -56,6 +57,19 @@ public:
/// Returns state of black border detector /// Returns state of black border detector
bool blackBorderDetectorEnabled() const; 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! /// Returns the current _userMappingType, this may not be the current applied type!
int getUserLedMappingType() const { return _userMappingType; } 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 /// 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. /// 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> template <typename Pixel_T>
std::vector<ColorRgb> process(const Image<Pixel_T>& image) std::vector<ColorRgb> process(const Image<Pixel_T>& image)
{ {
std::vector<ColorRgb> colors; std::vector<ColorRgb> colors;
if (image.width()>0 && image.height()>0) if (image.width()>0 && image.height()>0)
{ {
// Ensure that the buffer-image is the proper size // Ensure that the buffer-image is the proper size
setSize(image); setSize(image);
assert(!_imageToLedColors.isNull());
// Check black border detection // Check black border detection
verifyBorder(image); verifyBorder(image);
// Create a result vector and call the 'in place' function // Create a result vector and call the 'in place' function
switch (_mappingType) switch (_mappingType)
{ {
case 1: colors = _imageToLeds->getUniLedColor(image); break; case 1:
default: colors = _imageToLeds->getMeanLedColor(image); 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 else
@ -136,8 +165,8 @@ public:
/// ///
/// Determines the led colors of the image in the buffer. /// Determines the led colors of the image in the buffer.
/// ///
/// @param[in] image The image to translate to led values /// @param[in] image The image to translate to LED values
/// @param[out] ledColors The color value per led /// @param[out] ledColors The color value per LED
/// ///
template <typename Pixel_T> template <typename Pixel_T>
void process(const Image<Pixel_T>& image, std::vector<ColorRgb>& ledColors) 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) // Determine the mean or uni colors of each led (using the existing mapping)
switch (_mappingType) switch (_mappingType)
{ {
case 1: _imageToLeds->getUniLedColor(image, ledColors); break; case 1:
default: _imageToLeds->getMeanLedColor(image, ledColors); _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 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] hscanBegin begin of the hscan
/// @param[out] hscanEnd end of the hscan /// @param[out] hscanEnd end of the hscan
/// @param[out] vscanBegin begin 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; bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
private: private:
void registerProcessingUnit(
int width,
int height,
int horizontalBorder,
int verticalBorder);
/// ///
/// Performs black-border detection (if enabled) on the given image /// Performs black-border detection (if enabled) on the given image
/// ///
@ -183,34 +231,25 @@ private:
template <typename Pixel_T> template <typename Pixel_T>
void verifyBorder(const Image<Pixel_T> & image) 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"); Debug(_log, "Reset border");
_borderProcessor->process(image); _borderProcessor->process(image);
delete _imageToLeds; registerProcessingUnit(image.width(), image.height(), 0, 0);
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
} }
if(_borderProcessor->enabled() && _borderProcessor->process(image)) if(_borderProcessor->enabled() && _borderProcessor->process(image))
{ {
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder(); const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
// Clean up the old mapping
delete _imageToLeds;
if (border.unknown) if (border.unknown)
{ {
// Construct a new buffer and mapping registerProcessingUnit(image.width(), image.height(), 0, 0);
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
} }
else else
{ {
// Construct a new buffer and mapping registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize);
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, _ledString.leds());
} }
//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); void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
private: private:
Logger * _log; Logger * _log;
/// The Led-string specification /// The Led-string specification
LedString _ledString; LedString _ledString;
@ -226,15 +266,18 @@ private:
hyperion::BlackBorderProcessor * _borderProcessor; hyperion::BlackBorderProcessor * _borderProcessor;
/// The mapping of image-pixels to LEDs /// 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; int _mappingType;
/// Type of last requested user type /// Type of last requested user type
int _userMappingType; int _userMappingType;
/// Type of last requested hard type /// Type of last requested hard type
int _hardMappingType; int _hardMappingType;
int _accuraryLevel;
int _reducedPixelSetFactorFactor;
/// Hyperion instance pointer /// Hyperion instance pointer
Hyperion* _hyperion; Hyperion* _hyperion;
}; };

View File

@ -1,72 +1,90 @@
#ifndef IMAGETOLEDSMAP_H
#pragma once #define IMAGETOLEDSMAP_H
// STL includes // STL includes
#include <cassert> #include <cassert>
#include <memory>
#include <sstream> #include <sstream>
#include <cmath>
// hyperion-utils includes // hyperion-utils includes
#include <utils/Image.h> #include <utils/Image.h>
#include <utils/Logger.h> #include <utils/Logger.h>
#include <utils/ColorRgbScalar.h>
#include <utils/ColorSys.h>
// hyperion includes // hyperion includes
#include <hyperion/LedString.h> #include <hyperion/LedString.h>
namespace hyperion namespace hyperion
{ {
/// ///
/// The ImageToLedsMap holds a mapping of indices into an image to leds. It can be used to /// 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. /// calculate the average (aka mean) or dominant color per LED for a given region.
/// ///
class ImageToLedsMap class ImageToLedsMap : public QObject
{ {
Q_OBJECT
public: public:
/// ///
/// Constructs an mapping from the absolute indices in an image to each led based on the border /// 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, /// definition given in the list of LEDs. The map holds absolute indices to any given image,
/// provided that it is row-oriented. /// provided that it is row-oriented.
/// The mapping is created purely on size (width and height). The given borders are excluded /// The mapping is created purely on size (width and height). The given borders are excluded
/// from indexing. /// from indexing.
/// ///
/// @param[in] log Logger
/// @param[in] width The width of the indexed image /// @param[in] width The width of the indexed image
/// @param[in] height 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] horizontalBorder The size of the horizontal border (0=no border)
/// @param[in] verticalBorder The size of the vertical 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] 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( ImageToLedsMap(
const unsigned width, Logger* log,
const unsigned height, int width,
const unsigned horizontalBorder, int height,
const unsigned verticalBorder, int horizontalBorder,
const std::vector<Led> & leds); int verticalBorder,
const std::vector<Led> & leds,
int reducedProcessingFactor = 0,
int accuraryLevel = 0);
/// ///
/// Returns the width of the indexed image /// Returns the width of the indexed image
/// ///
/// @return The width of the indexed image [pixels] /// @return The width of the indexed image [pixels]
/// ///
unsigned width() const; int width() const;
/// ///
/// Returns the height of the indexed image /// Returns the height of the indexed image
/// ///
/// @return The height of the indexed image [pixels] /// @return The height of the indexed image [pixels]
/// ///
unsigned height() const; int height() const;
unsigned horizontalBorder() const { return _horizontalBorder; } int horizontalBorder() const { return _horizontalBorder; }
unsigned verticalBorder() const { return _verticalBorder; } 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. /// 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
/// ///
/// @return ledColors The vector containing the output /// @return The vector containing the output
/// ///
template <typename Pixel_T> template <typename Pixel_T>
std::vector<ColorRgb> getMeanLedColor(const Image<Pixel_T> & image) const 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. /// 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 /// @param[out] ledColors The vector containing the output
/// ///
template <typename Pixel_T> template <typename Pixel_T>
void getMeanLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const 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()) 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; 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. /// 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
/// ///
/// @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> template <typename Pixel_T>
std::vector<ColorRgb> getUniLedColor(const Image<Pixel_T> & image) const 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 /// Determines the mean color of the image and assigns it to all LEDs
/// 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 /// @param[out] ledColors The vector containing the output
/// ///
template <typename Pixel_T> template <typename Pixel_T>
void getUniLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const 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()) 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; return;
} }
// calculate uni color // calculate uni color
const ColorRgb color = calcMeanColor(image); const ColorRgb color = calcMeanColor(image);
//Update all LEDs with same color
std::fill(ledColors.begin(),ledColors.end(), color); std::fill(ledColors.begin(),ledColors.end(), color);
} }
private: ///
/// The width of the indexed image /// Determines the dominant color for each LED using the LED area mapping given
const unsigned _width; /// at construction.
/// The height of the indexed image ///
const unsigned _height; /// @param[in] image The image from which to extract the LED color
///
const unsigned _horizontalBorder; /// @return The vector containing the output
///
const unsigned _verticalBorder; template <typename Pixel_T>
std::vector<ColorRgb> getDominantLedColor(const Image<Pixel_T> & image) const
/// The absolute indices into the image for each led {
std::vector<std::vector<int32_t>> _colorsMap; 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) /// (red, green, blue)
/// ///
/// @param[in] image The image a section from which an average color must be computed /// @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) /// @return The mean of the given list of colors (or black when empty)
/// ///
template <typename Pixel_T> 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(); const auto pixelNum = pixels.size();
if (pixelNum == 0)
if (colorVecSize == 0)
{ {
return ColorRgb::BLACK; return ColorRgb::BLACK;
} }
@ -179,20 +323,20 @@ namespace hyperion
uint_fast32_t cummRed = 0; uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0; uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 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; cummRed += pixel.red;
cummGreen += pixel.green; cummGreen += pixel.green;
cummBlue += pixel.blue; cummBlue += pixel.blue;
} }
// Compute the average of each color channel // Compute the average of each color channel
const uint8_t avgRed = uint8_t(cummRed/colorVecSize); const uint8_t avgRed = uint8_t(cummRed/pixelNum);
const uint8_t avgGreen = uint8_t(cummGreen/colorVecSize); const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
const uint8_t avgBlue = uint8_t(cummBlue/colorVecSize); const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
// Return the computed color // Return the computed color
return {avgRed, avgGreen, avgBlue}; return {avgRed, avgGreen, avgBlue};
@ -213,11 +357,11 @@ namespace hyperion
uint_fast32_t cummRed = 0; uint_fast32_t cummRed = 0;
uint_fast32_t cummGreen = 0; uint_fast32_t cummGreen = 0;
uint_fast32_t cummBlue = 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(); const auto& imgData = image.memptr();
for (unsigned idx=0; idx<imageSize; idx++) for (unsigned idx=0; idx<pixelNum; idx++)
{ {
const auto& pixel = imgData[idx]; const auto& pixel = imgData[idx];
cummRed += pixel.red; cummRed += pixel.red;
@ -226,13 +370,289 @@ namespace hyperion
} }
// Compute the average of each color channel // Compute the average of each color channel
const uint8_t avgRed = uint8_t(cummRed/imageSize); const uint8_t avgRed = uint8_t(cummRed/pixelNum);
const uint8_t avgGreen = uint8_t(cummGreen/imageSize); const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
const uint8_t avgBlue = uint8_t(cummBlue/imageSize); const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
// Return the computed color // Return the computed color
return {avgRed, avgGreen, avgBlue}; 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 } // end namespace hyperion
#endif // IMAGETOLEDSMAP_H

View File

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

View File

@ -438,7 +438,10 @@ protected:
uint _ledRGBWCount; uint _ledRGBWCount;
/// Does the device allow restoring the original state? /// 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 /// Device, lights state before streaming via hyperion
QJsonObject _orignalStateValues; QJsonObject _orignalStateValues;
@ -460,6 +463,9 @@ protected:
/// Is the device in error state and stopped? /// Is the device in error state and stopped?
bool _isDeviceInError; bool _isDeviceInError;
/// Is the device in error state, but is retries might resolve the situation?
bool _isDeviceRecoverable;
/// Timestamp of last write /// Timestamp of last write
QDateTime _lastWriteTime; QDateTime _lastWriteTime;
@ -476,8 +482,9 @@ protected slots:
/// @brief Set device in error state /// @brief Set device in error state
/// ///
/// @param[in] errorMsg The error message to be logged /// @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: private:

View File

@ -19,6 +19,7 @@
// Utility includes // Utility includes
#include <utils/Logger.h> #include <utils/Logger.h>
#include <utils/WeakConnect.h>
namespace { namespace {
constexpr std::chrono::milliseconds DEFAULT_DISCOVER_TIMEOUT{ 500 }; constexpr std::chrono::milliseconds DEFAULT_DISCOVER_TIMEOUT{ 500 };
@ -103,61 +104,6 @@ private slots:
void onServiceRemoved(const QMdnsEngine::Service& service); void onServiceRemoved(const QMdnsEngine::Service& service);
private: 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 /// The logger instance for mDNS-Service
Logger* _log; Logger* _log;

View File

@ -6,6 +6,7 @@
#include <QString> #include <QString>
#include <QTextStream> #include <QTextStream>
#include <QRgb>
/// ///
/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the /// Plain-Old-Data structure containing the red-green-blue color specification. Size of the
@ -52,6 +53,18 @@ struct ColorRgb
return a; 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 QString toQString() const
{ {
return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue); 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; 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"); 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 os The output stream
/// @param color The color to write /// @param color The color to write

View File

@ -105,6 +105,19 @@ public:
/// @note See https://bottosson.github.io/posts/colorpicker/#okhsv /// @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); 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 #endif // COLORSYS_H

View File

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

View File

@ -56,6 +56,13 @@ signals:
void setBufferImage(const QString& name, const Image<ColorRgb>& image); void setBufferImage(const QString& name, const Image<ColorRgb>& image);
#endif #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 /// @brief PIPE the register command for a new global input over HyperionDaemon to Hyperion class
/// @param[in] priority The priority of the channel /// @param[in] priority The priority of the channel

View File

@ -51,7 +51,7 @@ namespace NetUtils {
{ {
if ((port <= 0 || port > MAX_PORT) && port != -1) 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 false;
} }
return true; return true;
@ -122,7 +122,7 @@ namespace NetUtils {
{ {
if (hostAddress.setAddress(hostname)) 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; isHostAddressOK = true;
} }
else else

View File

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

View File

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

View File

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

View File

@ -294,13 +294,6 @@ bool API::setHyperionInstance(quint8 inst)
return true; 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() bool API::isHyperionEnabled()
{ {
int res; int res;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,5 @@
#include <iostream> #include <iostream>
#include <cmath>
#include <hyperion/Hyperion.h> #include <hyperion/Hyperion.h>
@ -33,6 +34,8 @@ BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent)
// listen for component state changes // listen for component state changes
connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest); connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest);
_detector = new BlackBorderDetector(_oldThreshold);
} }
BlackBorderProcessor::~BlackBorderProcessor() BlackBorderProcessor::~BlackBorderProcessor()
@ -60,7 +63,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson
_detectionMode = obj["mode"].toString("default"); _detectionMode = obj["mode"].toString("default");
const double newThreshold = obj["threshold"].toDouble(5.0) / 100.0; const double newThreshold = obj["threshold"].toDouble(5.0) / 100.0;
if (_oldThreshold != newThreshold) if (fabs(_oldThreshold - newThreshold) > std::numeric_limits<double>::epsilon())
{ {
_oldThreshold = newThreshold; _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 // 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 // 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 // set the consistency counter
if (newDetectedBorder == _previousDetectedBorder) 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 int len = end - data + 1;
const QString message = QString::fromLatin1(data, len); const QString message = QString::fromLatin1(data, len);
//std::cout << bytes << ": \"" << message.toUtf8().constData() << "\"" << std::endl;
return message; return message;
} }
@ -124,7 +122,6 @@ void BoblightClientConnection::socketClosed()
void BoblightClientConnection::handleMessage(const QString& message) void BoblightClientConnection::handleMessage(const QString& message)
{ {
//std::cout << "boblight message: " << message.toStdString() << std::endl;
QStringList messageParts = QStringUtils::split(message, ' ', QStringUtils::SplitBehavior::SkipEmptyParts); QStringList messageParts = QStringUtils::split(message, ' ', QStringUtils::SplitBehavior::SkipEmptyParts);
if (!messageParts.isEmpty()) if (!messageParts.isEmpty())
{ {
@ -340,7 +337,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
{ {
if (ok) if (ok)
{ {
//std::cout << "FAIL L " << q << ": " << s.toUtf8().constData() << std::endl;
*ok = false; *ok = false;
} }
return 0; return 0;
@ -348,7 +344,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
if (ok) if (ok)
{ {
//std::cout << "OK " << d << ": " << s.toUtf8().constData() << std::endl;
*ok = true; *ok = true;
} }

View File

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

View File

@ -50,7 +50,7 @@ QSqlDatabase DBManager::getDB() const
db.setDatabaseName(_rootPath+"/db/"+_dbn+".db"); db.setDatabaseName(_rootPath+"/db/"+_dbn+".db");
if(!db.open()) 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!"); throw std::runtime_error("Failed to open database connection!");
} }
return db; return db;

View File

@ -121,17 +121,21 @@ void EffectEngine::handleUpdatedEffectList()
// add smoothing configurations to Hyperion // add smoothing configurations to Hyperion
if (def.args["smoothing-custom-settings"].toBool()) 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( def.smoothCfg = _hyperion->updateSmoothingConfig(
++specificId, ++specificId,
def.args["smoothing-time_ms"].toInt(), settlingTime_ms,
def.args["smoothing-updateFrequency"].toDouble(), ledUpdateFrequency_hz,
0 ); updateDelay );
//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);
} }
else else
{ {
def.smoothCfg = SmoothingConfigID::SYSTEM; 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); _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) //In case smoothing information is provided dynamically use temp smoothing config item (2)
if (smoothCfg == SmoothingConfigID::SYSTEM && args["smoothing-custom-settings"].toBool()) 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( smoothCfg = _hyperion->updateSmoothingConfig(
SmoothingConfigID::EFFECT_DYNAMIC, SmoothingConfigID::EFFECT_DYNAMIC,
args["smoothing-time_ms"].toInt(), settlingTime_ms,
args["smoothing-updateFrequency"].toDouble(), ledUpdateFrequency_hz,
0 ); updateDelay
);
} }
if (pythonScript.isEmpty()) if (pythonScript.isEmpty())

View File

@ -53,9 +53,12 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
Py_RETURN_NOTIMPLEMENTED; Py_RETURN_NOTIMPLEMENTED;
case QJsonValue::Double: 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("d", jsonData.toDouble());
}
return Py_BuildValue("i", jsonData.toInt()); return Py_BuildValue("i", jsonData.toInt());
} }
case QJsonValue::Bool: case QJsonValue::Bool:
@ -184,7 +187,8 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args) PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
{ {
// bytearray of values // bytearray of values
int width, height; int width = 0;
int height = 0;
PyObject * bytearray = nullptr; PyObject * bytearray = nullptr;
if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray)) if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray))
{ {
@ -391,8 +395,10 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
int startRY = 0; int startRY = 0;
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
int endX, width = getEffect()->_imageSize.width(); int width = getEffect()->_imageSize.width();
int endY, height = getEffect()->_imageSize.height(); int endX {width};
int height = getEffect()->_imageSize.height();
int endY {height};
int spread = 0; int spread = 0;
bool argsOK = false; bool argsOK = false;
@ -454,7 +460,9 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
PyObject * bytearray = nullptr; PyObject * bytearray = nullptr;
int centerX, centerY, angle; int centerX = 0;
int centerY = 0;
int angle = 0;
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
int width = getEffect()->_imageSize.width(); int width = getEffect()->_imageSize.width();
@ -520,7 +528,13 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
PyObject * bytearray = nullptr; 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 startX = 0;
int startY = 0; int startY = 0;
int width = getEffect()->_imageSize.width(); int width = getEffect()->_imageSize.width();
@ -599,7 +613,9 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
PyObject * bytearray = nullptr; PyObject * bytearray = nullptr;
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int r, g, b; int r = 0;
int g = 0;
int b = 0;
int a = 255; int a = 255;
bool argsOK = false; bool argsOK = false;
@ -658,7 +674,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
QString brush; QString brush;
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int radius, centerX, centerY; int radius = 0;
int centerX = 0;
int centerY = 0;
int startAngle = 0; int startAngle = 0;
int spanAngle = 360; int spanAngle = 360;
int r = 0; int r = 0;
@ -749,7 +767,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args) PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int r, g, b; int r = 0;
int g = 0;
int b = 0;
int a = 255; int a = 255;
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
@ -788,8 +808,10 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args) PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int r, g, b; int r = 0;
int a = 255; int g = 0;
int b = 0;
int a = 255;
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
int thick = 1; int thick = 1;
@ -826,8 +848,12 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args) PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int r, g, b, x, y; int r = 0;
int a = 255; int g = 0;
int b = 0;
int x = 0;
int y = 0;
int a = 255;
int thick = 1; int thick = 1;
bool argsOK = false; bool argsOK = false;
@ -859,8 +885,10 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args) PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int r, g, b; int r = 0;
int a = 255; int g = 0;
int b = 0;
int a = 255;
int startX = 0; int startX = 0;
int startY = 0; int startY = 0;
int thick = 1; int thick = 1;
@ -898,7 +926,11 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args) PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(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 ) ) 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) PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int x, y; int x = 0;
int y = 0;
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y) ) 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) PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
{ {
int argCount = PyTuple_Size(args); int argCount = PyTuple_Size(args);
int w, h; int w = 0;
int h = 0;
int width = getEffect()->_imageSize.width(); int width = getEffect()->_imageSize.width();
int height = getEffect()->_imageSize.height(); int height = getEffect()->_imageSize.height();
@ -994,7 +1028,8 @@ PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
PyObject* EffectModule::wrapImageCShear(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); int argCount = PyTuple_Size(args);
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv )) 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: case hyperion::COMP_V4L:
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break; break;
case hyperion::COMP_AUDIO:
connect(_hyperion, &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break;
#if defined(ENABLE_FLATBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER)
case hyperion::COMP_FLATBUFSERVER: case hyperion::COMP_FLATBUFSERVER:
#endif #endif
@ -138,7 +141,7 @@ void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
else else
{ {
_forwarder_enabled = false; _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); _hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _forwarder_enabled);
@ -153,6 +156,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
switch (activeCompId) { switch (activeCompId) {
case hyperion::COMP_GRABBER: case hyperion::COMP_GRABBER:
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif #endif
@ -160,11 +164,20 @@ void MessageForwarder::handlePriorityChanges(int priority)
break; break;
case hyperion::COMP_V4L: case hyperion::COMP_V4L:
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif #endif
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
break; 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) #if defined(ENABLE_FLATBUF_SERVER)
case hyperion::COMP_FLATBUFSERVER: case hyperion::COMP_FLATBUFSERVER:
#endif #endif
@ -172,6 +185,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
case hyperion::COMP_PROTOSERVER: case hyperion::COMP_PROTOSERVER:
#endif #endif
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
connect(_hyperion, &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection); connect(_hyperion, &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
@ -180,6 +194,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
default: default:
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif #endif
@ -373,6 +388,7 @@ void MessageForwarder::stopFlatbufferTargets()
{ {
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER) #if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr); disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
#endif #endif

View File

@ -33,3 +33,7 @@ endif(ENABLE_QT)
if (ENABLE_DX) if (ENABLE_DX)
add_subdirectory(directx) add_subdirectory(directx)
endif(ENABLE_DX) 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