mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Merge remote-tracking branch 'upstream/master' into temperture
This commit is contained in:
commit
2a91e2af93
4
.github/config/codeql.yml
vendored
Normal file
4
.github/config/codeql.yml
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
name: "CodeQL config"
|
||||
paths-ignore:
|
||||
- 'dependencies/external/'
|
||||
- 'assets/webconfig/js/lib'
|
4
.github/workflows/apt.yml
vendored
4
.github/workflows/apt.yml
vendored
@ -108,7 +108,7 @@ jobs:
|
||||
reprepro -Vb apt export
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3.0.1
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
|
||||
- name: Include artifacts into the package source
|
||||
run: |
|
||||
@ -121,7 +121,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Upload packages to APT server (DRAFT)
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.2
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.3
|
||||
with:
|
||||
server: apt.hyperion-project.org
|
||||
username: ${{ secrets.APT_USER }}
|
||||
|
32
.github/workflows/apt/amd64.json
vendored
32
.github/workflows/apt/amd64.json
vendored
@ -2,64 +2,64 @@
|
||||
{
|
||||
"distribution": "Bionic",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg, libcec4",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.6, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg, libcec4",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Ubuntu 18.04 (Bionic Beaver) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Focal",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg, libcec4",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.8, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg, libcec4",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Ubuntu 20.04 (Focal Fossa) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Jammy",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Ubuntu 22.04 (Jammy Jellyfish) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Kinetic",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libturbojpeg, libcec6",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.10, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls14, libasound2, libturbojpeg, libcec6",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Ubuntu 22.10 (Kinetic Kudu) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Stretch",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libturbojpeg0, libcec4",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl1.0-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.5, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls10, libasound2, libturbojpeg0, libcec4",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Debian 9.x (Stretch) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Buster",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec4",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.7, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec4",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Debian 10.x (Buster) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Bullseye",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Debian 11.x (Bullseye) (amd64)"
|
||||
},
|
||||
{
|
||||
"distribution": "Bookworm",
|
||||
"architecture": "amd64",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libturbojpeg0, libcec6",
|
||||
"build-depends": "git, cmake, build-essential, qtbase5-dev, libqt5serialport5-dev, libqt5sql5-sqlite, libqt5svg5-dev, libqt5x11extras5-dev, libusb-1.0-0-dev, python3-dev, libcec-dev, libxcb-image0-dev, libxcb-util0-dev, libxcb-shm0-dev, libxcb-render0-dev, libxcb-randr0-dev, libxrandr-dev, libxrender-dev, libasound2-dev, libturbojpeg0-dev, libjpeg-dev, libssl-dev, libmbedtls-dev",
|
||||
"package-depends": "libpython3.9, libusb-1.0-0, libqt5widgets5, libqt5x11extras5, libqt5sql5, libqt5sql5-sqlite, libqt5serialport5, libmbedtls12, libasound2, libturbojpeg0, libcec6",
|
||||
"cmake-environment": "-DUSE_SYSTEM_MBEDTLS_LIBS=ON -DENABLE_DEPLOY_DEPENDENCIES=OFF -DCMAKE_BUILD_TYPE=Release",
|
||||
"description": "Debian 12.x (Bookworm) (amd64)"
|
||||
}
|
||||
|
76
.github/workflows/codeql.yml
vendored
Normal file
76
.github/workflows/codeql.yml
vendored
Normal 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
|
||||
|
4
.github/workflows/nightly.yml
vendored
4
.github/workflows/nightly.yml
vendored
@ -158,7 +158,7 @@ jobs:
|
||||
reprepro -Vb nightly export
|
||||
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3.0.1
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
|
||||
- name: Include artifacts into the package source
|
||||
run: |
|
||||
@ -171,7 +171,7 @@ jobs:
|
||||
done
|
||||
|
||||
- name: Upload packages to nightly server
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.2
|
||||
uses: SamKirkland/FTP-Deploy-Action@4.3.3
|
||||
with:
|
||||
server: nightly.apt.hyperion-project.org
|
||||
username: ${{ secrets.NIGHTLY_USER }}
|
||||
|
2
.github/workflows/push-master.yml
vendored
2
.github/workflows/push-master.yml
vendored
@ -171,7 +171,7 @@ jobs:
|
||||
|
||||
# Download artifacts from previous build process
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v3.0.1
|
||||
uses: actions/download-artifact@v3.0.2
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
# Dispatch event to build new HyperBian image
|
||||
- name: Dispatch HyperBian build
|
||||
uses: peter-evans/repository-dispatch@v2.1.0
|
||||
uses: peter-evans/repository-dispatch@v2.1.1
|
||||
if: ${{ github.repository_owner == 'hyperion-project'}}
|
||||
with:
|
||||
repository: hyperion-project/HyperBian
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,6 +30,7 @@ libsrc/flatbufserver/hyperion_request_generated.h
|
||||
# Ignore
|
||||
.vs/*
|
||||
CMakeSettings.json
|
||||
/out
|
||||
# Allow
|
||||
!.vs/launch.vs.json
|
||||
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -12,3 +12,6 @@
|
||||
[submodule "dependencies/external/qmdnsengine"]
|
||||
path = dependencies/external/qmdnsengine
|
||||
url = https://github.com/nitroshare/qmdnsengine.git
|
||||
[submodule "dependencies/external/mbedtls"]
|
||||
path = dependencies/external/mbedtls
|
||||
url = ../../Mbed-TLS/mbedtls.git
|
||||
|
40
CHANGELOG.md
40
CHANGELOG.md
@ -4,12 +4,26 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased](https://github.com/hyperion-project/hyperion.ng/compare/2.0.14...HEAD)
|
||||
## [Unreleased](https://github.com/hyperion-project/hyperion.ng/compare/2.0.15...HEAD)
|
||||
|
||||
### Breaking
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
## Removed
|
||||
|
||||
## [2.0.15](https://github.com/hyperion-project/hyperion.ng/releases/tag/2.0.15) - 2023-02
|
||||
|
||||
### Added
|
||||
|
||||
- Audio Grabber to add audio visualization support for both Windows and Linux.
|
||||
- Support streaming to individual WLED segments (requires WLED 0.13.3+).
|
||||
To allow segment streaming, enable "Realtime - Use main segment only" in WLED's Sync Interfaces setup screen
|
||||
- Allow to keep WLED powered on after streaming and restoring state
|
||||
- Allow to Disable / Enable all instances (#970) by
|
||||
- Suspend/Resume support for Linux and Windows (#1493,#1282, #978).
|
||||
Suspend/Resume/Restart is supported via API, UI, Systray and hyperion-remote
|
||||
@ -17,13 +31,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
In Idle, all instances, components will be disabled besides the output processing (LED-Devices, smoothing).
|
||||
The current priorities will be cleared and the background effect per instance will be executed, if enabled.
|
||||
- Commands toogleSuspend and toggleIdle allow to flip between modes, e.g. might be used to trigger modes by a remote
|
||||
- Reduced pixel processing to reduce resources on big assignment areas
|
||||
- Support for squared mean color processing
|
||||
- Support for dominant color processing on assigned LED areas (#1382). A simple and advanced way is provided. Advanced and high accuracy might be combined with reduced pixel processing to lower CPU usage.
|
||||
- Add instance# in API response (#1504)
|
||||
|
||||
### Changed
|
||||
|
||||
- REST API - Increased default timeout to address "Operation cancelled" errors
|
||||
- LED Devices: Allow to differentiate between recoverable/unrecoverable errors
|
||||
- Renamed LED area assignment naming to provide clarity on the processing algorithms
|
||||
|
||||
### Fixed
|
||||
|
||||
- Effects/Smoothing: Effects with dedicated smoothing settings will now run with those settings (even if general smoothing is off)
|
||||
- No interim color update after streaming and turning off WLED
|
||||
- LED-Matrix Layout: Add Cabling direction selection element again (#1566)
|
||||
- Restart correctly, if running as service (#1368)
|
||||
- Hue-Wizard: In case auto discovery failed, port 80 was not used as default (#1544)
|
||||
- Send only one reply per Start Instance request (#1551)
|
||||
- Add instance# in JSON-API replies (aligning to Add instance in websocket response to a subscription #1504 behaviour)
|
||||
- hyperion-remote: Extracting reply for a configGet request correctly (#1555)
|
||||
- Grabber fps setting was not applied correctly
|
||||
- Smoothing: No empty updates
|
||||
|
||||
### Technical
|
||||
- Add CodeQL for GitHub code scanning
|
||||
- Update to Protocol Buffers 3.21.12
|
||||
- Update to Mbed TLS 3.3.0
|
||||
- Qt6 alignments
|
||||
- cmake support of libcec without version in lib-name
|
||||
- Refactor for Python 3.11 deprecated functions
|
||||
|
||||
## Removed
|
||||
|
||||
|
@ -67,6 +67,7 @@ SET ( DEFAULT_MF OFF )
|
||||
SET ( DEFAULT_OSX OFF )
|
||||
SET ( DEFAULT_QT ON )
|
||||
SET ( DEFAULT_V4L2 OFF )
|
||||
SET ( DEFAULT_AUDIO ON )
|
||||
SET ( DEFAULT_X11 OFF )
|
||||
SET ( DEFAULT_XCB OFF )
|
||||
|
||||
@ -172,8 +173,10 @@ if ( "${PLATFORM}" MATCHES "osx" )
|
||||
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${SUBDIRPY})
|
||||
|
||||
include_directories("/opt/X11/include/")
|
||||
SET ( DEFAULT_OSX ON )
|
||||
SET ( DEFAULT_DEV_USB_HID ON )
|
||||
SET ( DEFAULT_OSX ON )
|
||||
SET ( DEFAULT_AUDIO OFF )
|
||||
SET ( DEFAULT_DEV_USB_HID ON )
|
||||
|
||||
elseif ( "${PLATFORM}" MATCHES "rpi" )
|
||||
SET ( DEFAULT_DISPMANX ON )
|
||||
SET ( DEFAULT_DEV_WS281XPWM ON )
|
||||
@ -222,6 +225,7 @@ if (HYPERION_LIGHT)
|
||||
SET ( DEFAULT_OSX OFF )
|
||||
SET ( DEFAULT_QT OFF )
|
||||
SET ( DEFAULT_V4L2 OFF )
|
||||
SET ( DEFAULT_AUDIO OFF )
|
||||
SET ( DEFAULT_X11 OFF )
|
||||
SET ( DEFAULT_XCB OFF )
|
||||
|
||||
@ -273,6 +277,11 @@ message(STATUS "ENABLE_V4L2 = ${ENABLE_V4L2}")
|
||||
|
||||
option(ENABLE_X11 "Enable the X11 grabber" ${DEFAULT_X11})
|
||||
message(STATUS "ENABLE_X11 = ${ENABLE_X11}")
|
||||
option(ENABLE_AUDIO "Enable the AUDIO grabber" ${DEFAULT_AUDIO})
|
||||
message(STATUS "ENABLE_AUDIO = ${ENABLE_AUDIO}")
|
||||
|
||||
option(ENABLE_WS281XPWM "Enable the WS281x-PWM device" ${DEFAULT_WS281XPWM} )
|
||||
message(STATUS "ENABLE_WS281XPWM = ${ENABLE_WS281XPWM}")
|
||||
|
||||
option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB})
|
||||
message(STATUS "ENABLE_XCB = ${ENABLE_XCB}")
|
||||
|
@ -9,6 +9,10 @@
|
||||
// Define to enable the DirectX grabber
|
||||
#cmakedefine ENABLE_DX
|
||||
|
||||
// Define to enable the framebuffer grabber
|
||||
// Define to enable the Audio grabber
|
||||
#cmakedefine ENABLE_AUDIO
|
||||
|
||||
// Define to enable the Framebuffer grabber
|
||||
#cmakedefine ENABLE_FB
|
||||
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014-2022 Hyperion Project
|
||||
Copyright (c) 2014-2023 Hyperion Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
[](https://github.com/hyperion-project/hyperion.ng/releases)
|
||||
[](https://github.com/hyperion-project/hyperion.ng/actions)
|
||||
[](https://lgtm.com/projects/g/hyperion-project/hyperion.ng/context:cpp)
|
||||
[](https://github.com/hyperion-project/hyperion.ng/actions/workflows/codeql.yml)
|
||||
[](https://www.hyperion-project.org)
|
||||
[](https://docs.hyperion-project.org)
|
||||
[](https://discord.gg/khkR8Vx3ff)
|
||||
|
@ -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
|
@ -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
|
6
assets/firmware/arduino/network_bridge/udpraw_serialadalight.py
Executable file → Normal file
6
assets/firmware/arduino/network_bridge/udpraw_serialadalight.py
Executable file → Normal file
@ -153,10 +153,9 @@ to this service over the network.
|
||||
|
||||
srv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
srv.bind(('0.0.0.0', args.localport)) # lgtm [py/bind-socket-all-network-interfaces]
|
||||
srv.bind(('0.0.0.0', args.localport))
|
||||
|
||||
try:
|
||||
intentional_exit = False
|
||||
while True:
|
||||
try:
|
||||
while True:
|
||||
@ -180,7 +179,7 @@ to this service over the network.
|
||||
# probably got disconnected
|
||||
break
|
||||
except KeyboardInterrupt:
|
||||
intentional_exit = True
|
||||
# intentional_exit
|
||||
raise
|
||||
except socket.error as msg:
|
||||
if args.develop:
|
||||
@ -190,6 +189,7 @@ to this service over the network.
|
||||
ser_to_net.socket = None
|
||||
sys.stderr.write('Disconnected\n')
|
||||
except KeyboardInterrupt:
|
||||
# do not handle exceptions
|
||||
pass
|
||||
|
||||
sys.stderr.write('\n--- exit ---\n')
|
||||
|
@ -322,6 +322,17 @@
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="ltd">
|
||||
<label class="ltdlabel" for="ip_ma_direction" data-i18n="conf_leds_layout_ma_direction">Cabling</label>
|
||||
</td>
|
||||
<td class="itd">
|
||||
<select class="form-control ledMAconstr" id="ip_ma_direction">
|
||||
<option value="horizontal" data-i18n="conf_leds_layout_ma_opthoriz">Horizontal</option>
|
||||
<option value="vertical" data-i18n="conf_leds_layout_ma_optvert">Vertical</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="ltd">
|
||||
<label class="ltdlabel" for="ip_ma_start" data-i18n="conf_leds_layout_ma_position">Input</label>
|
||||
|
@ -42,6 +42,14 @@
|
||||
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_videograbber')" style="text-decoration: none; cursor: pointer"></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="dash_audio_grabber_row">
|
||||
<td></td>
|
||||
<td data-i18n="edt_conf_audio_heading_title">Audio-Grabber</td>
|
||||
<td style="text-align: right; padding-right: 0">
|
||||
<span id="dash_audio_grabber">disabled</span>
|
||||
<a class="fa fa-cog fa-fw" onclick="SwitchToMenuItem('MenuItemGrabber', 'editor_container_audiograbber')" style="text-decoration: none; cursor: pointer"></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="dash_ports" class="table borderless">
|
||||
@ -135,6 +143,7 @@
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.container-fluid -->
|
||||
|
||||
<script src="/js/content_dashboard.js"></script>
|
||||
|
@ -10,6 +10,9 @@
|
||||
"InfoDialog_nowrite_foottext": "Die Webkonfiguration wird automatisch wieder freigegeben, sobald das Problem behoben wurde!",
|
||||
"InfoDialog_nowrite_text": "Hyperion hat keinen Schreibzugriff auf die aktuell geladene Konfiguration. Bitte korrigiere die Dateizugriffsrechte, um fortzufahren.",
|
||||
"InfoDialog_nowrite_title": "Fehler beim Schreibzugriff!",
|
||||
"InfoDialog_systemRestart_title": "Neustart",
|
||||
"InfoDialog_systemResume_title": "Aktivieren",
|
||||
"InfoDialog_systemSuspend_title": "Ruhezustand",
|
||||
"about_3rd_party_licenses": "Drittanbieter Lizenzen",
|
||||
"about_3rd_party_licenses_error": "Wir hatten Probleme beim Laden der Drittanbieter Lizenzen aus dem Internet. <br />Klicke hier, um die Datei auf GitHub aufzurufen.",
|
||||
"about_build": "Build",
|
||||
@ -41,6 +44,7 @@
|
||||
"conf_general_inst_title": "LED-Hardware Instanzverwaltung",
|
||||
"conf_general_intro": "Grundsätzliche Einstellungen zu Hyperion oder WebUI, die in keine andere Kategorie passen.",
|
||||
"conf_general_label_title": "Allgemeine Einstellungen",
|
||||
"conf_grabber_audio_intro": "Bei der Audioerfassung wird ein Audioeingabegerät als Quelle für die Visualisierung verwendet.",
|
||||
"conf_grabber_fg_intro": "Bildschirm Aufnahme ist das lokale System auf dem Hyperion installiert wurde, welches als Bildquelle dient.",
|
||||
"conf_grabber_inst_grabber_config_info": "Konfiguriere deine Aufnahmegeräte bevor du sie in einer LED-Instanz benutzt.",
|
||||
"conf_grabber_v4l_intro": "USB-Aufnahme ist ein Gerät, welches via USB angeschlossen ist und als Bildquelle dient.",
|
||||
@ -55,7 +59,9 @@
|
||||
"conf_leds_error_get_properties_title": "Geräteeigenschaften",
|
||||
"conf_leds_error_hwled_gt_layout": "Die Zahl der gegebenen Hardware LEDs ($1) ist größer, als die Anzahl der im LED-Layout definierten ($2), $3 {{plural:$3|LED wird|LEDs werden}} schwarz bleiben.",
|
||||
"conf_leds_error_hwled_gt_maxled": "Die Zahl der gegebenen Hardware LEDs ($1) ist größer, als die Anzahl der durch den LED-Steuerungstyp unterstützen ($2). <br>Die Anzahl Hardware LEDs wird auf ($3) gesetzt.",
|
||||
"conf_leds_error_hwled_gt_maxled_protocol": "Die Anzahl der Hardware-LEDs ($1) ist größer als die maximale Anzahl der vom Streaming-Protokoll unterstützten LEDs ($2).<br> Das Streaming-Protokoll wird auf ($3) geändert.",
|
||||
"conf_leds_error_hwled_lt_layout": "Die Zahl der gegebenen Hardware LEDs ($1) ist kleiner, als die Anzahl der im LED-Layout definierten ($2). <br> Im LED-Layout dürfen nicht mehr LEDs konfiguriert sein, als vorhanden.",
|
||||
"conf_leds_error_wled_segment_missing": "Das aktuell konfigurierte Segment ($1) ist in WLED-Gerät nicht konfiguriert.<br> Überprüfe die WLED-Konfiguration!<br>Die Konfigurationsseite zeigt die aktuelle WLED-Einstellung.",
|
||||
"conf_leds_info_ws281x": "Hyperion muss mit 'root' Rechten für diesen LED-Steuerungstyp laufen!",
|
||||
"conf_leds_layout_advanced": "Erweiterte Optionen",
|
||||
"conf_leds_layout_blacklist_num_title": "LED-Anzahl",
|
||||
@ -229,6 +235,27 @@
|
||||
"edt_append_pixel": "Pixel",
|
||||
"edt_append_s": "s",
|
||||
"edt_append_sdegree": "s/grad",
|
||||
"edt_conf_audio_device_expl": "Ausgewähltes Audio-Eingabegerät",
|
||||
"edt_conf_audio_device_title": "Audio-Eingabegerät",
|
||||
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
|
||||
"edt_conf_audio_effect_hotcolor_expl": "Farbe die einen hohen Pegel anzeigt.",
|
||||
"edt_conf_audio_effect_hotcolor_title": "Farbe hoher Pegel",
|
||||
"edt_conf_audio_effect_multiplier_expl": "Multiplikator zur Verstärkung des Audiosignals.",
|
||||
"edt_conf_audio_effect_multiplier_title": "Multiplikator",
|
||||
"edt_conf_audio_effect_safecolor_expl": "Farbe die einen niedrigen Pegel anzeigt.",
|
||||
"edt_conf_audio_effect_safecolor_title": "Farbe niedriger Pegel",
|
||||
"edt_conf_audio_effect_safevalue_expl": "Schwellwert bis zu dem ein niedriger Pegel gegeben ist.",
|
||||
"edt_conf_audio_effect_safevalue_title": "Schwellwert niedriger Pegel",
|
||||
"edt_conf_audio_effect_set_defaults": "Zurücksetzen auf Standardwerte",
|
||||
"edt_conf_audio_effect_tolerance_expl": "Toleranz für die automatische Berechnung eines Signalmultiplikators von 0-100",
|
||||
"edt_conf_audio_effect_tolerance_title": "Toleranz",
|
||||
"edt_conf_audio_effect_warncolor_expl": "Farbe die einen mittleren Pegel anzeigt.",
|
||||
"edt_conf_audio_effect_warncolor_title": "Farbe mittlerer Pegel",
|
||||
"edt_conf_audio_effect_warnvalue_expl": "Schwellwert bis zu dem ein mittlerer Pegel gegeben ist.",
|
||||
"edt_conf_audio_effect_warnvalue_title": "Schwellwert mittlerer Pegel",
|
||||
"edt_conf_audio_effects_expl": "Wähle einen Effekt für die Umwandlung des Audiosignals.",
|
||||
"edt_conf_audio_effects_title": "Audio-Effekte",
|
||||
"edt_conf_audio_heading_title": "Audio Aufnahme",
|
||||
"edt_conf_bb_blurRemoveCnt_expl": "Anzahl an Pixeln, die zusätzlich vom Rand abgeschnitten werden.",
|
||||
"edt_conf_bb_blurRemoveCnt_title": "Unscharfe Pixel",
|
||||
"edt_conf_bb_borderFrameCnt_expl": "Anzahl an Bildern bis ein neuer Rand festgelegt wird.",
|
||||
@ -244,6 +271,8 @@
|
||||
"edt_conf_bb_unknownFrameCnt_title": "Unbekannte Bilder",
|
||||
"edt_conf_bge_heading_title": "Hintergrund Effekt/Farbe",
|
||||
"edt_conf_bobls_heading_title": "Boblight Server",
|
||||
"edt_conf_color_accuracyLevel_expl": "Stufe, wie genau dominante Farben ausgewertet werden. Eine höhere Stufe erzeugt genauere Ergebnisse, erfordert aber auch mehr Rechenleistung. Sollte mit reduzierter Pixelverarbeitung kombiniert werden.",
|
||||
"edt_conf_color_accuracyLevel_title": "Genauigkeitsstufe",
|
||||
"edt_conf_color_backlightColored_expl": "Die Hintergrundbeleuchtung kann mit oder ohne Farbanteile genutzt werden.",
|
||||
"edt_conf_color_backlightColored_title": "Farbige Hintergrundbeleuchtung",
|
||||
"edt_conf_color_backlightThreshold_expl": "Eine Beleuchtung die dauerhaft aktiv ist. (Automatisch deaktiviert bei Effekten, Farben oder im Zustand \"Aus\")",
|
||||
@ -274,7 +303,7 @@
|
||||
"edt_conf_color_heading_title": "Farbkalibrierung",
|
||||
"edt_conf_color_id_expl": "Eine vom Benutzer frei angegebene ID.",
|
||||
"edt_conf_color_id_title": "ID",
|
||||
"edt_conf_color_imageToLedMappingType_expl": "Sofern nicht \"Mehrfarbig\", wird dein LED-Layout mit einer anderen Bildzuweisung überschrieben",
|
||||
"edt_conf_color_imageToLedMappingType_expl": "Sofern nicht \"Durchschnittsfarbe einfach \", wird dein LED-Layout mit einer anderen Bildzuweisung überschrieben",
|
||||
"edt_conf_color_imageToLedMappingType_title": "LED-Bereich Zuordnungstyp",
|
||||
"edt_conf_color_leds_expl": "Zugewiesen zu allen (*) LEDs oder nur zu bestimmten LED Nummern (0-17).",
|
||||
"edt_conf_color_leds_title": "LED-Iindex",
|
||||
@ -282,6 +311,8 @@
|
||||
"edt_conf_color_magenta_title": "Magenta",
|
||||
"edt_conf_color_red_expl": "Kalibrierter Rotwert.",
|
||||
"edt_conf_color_red_title": "Rot",
|
||||
"edt_conf_color_reducedPixelSetFactorFactor_expl": "Es wird nur eine reduzierte Menge von Pixeln pro definiertem LED-Bereich ausgewertet, Niedrig ~25%, Mittel ~10%, Hoch ~6%",
|
||||
"edt_conf_color_reducedPixelSetFactorFactor_title": "Reduzierte Pixelverarbeitung",
|
||||
"edt_conf_color_saturationGain_expl": "Anpassung der Farbsättigung. 1,0 bedeutet keine Änderung, Werte größer 1,0 erhöhen die Sättigung, kleiner 1,0 verringern diese.",
|
||||
"edt_conf_color_saturationGain_title": "Sättigungsverstärkung",
|
||||
"edt_conf_color_white_expl": "Kalibrierter Weißwert.",
|
||||
@ -313,6 +344,8 @@
|
||||
"edt_conf_enum_color": "Farbe",
|
||||
"edt_conf_enum_custom": "Benutzerdefiniert",
|
||||
"edt_conf_enum_decay": "Dämpfung",
|
||||
"edt_conf_enum_delay": "Nur Verzögerung",
|
||||
"edt_conf_enum_disabled": "Deaktiviert",
|
||||
"edt_conf_enum_dl_error": "nur Fehler",
|
||||
"edt_conf_enum_dl_informational": "informativ",
|
||||
"edt_conf_enum_dl_nodebug": "keine Debugausgabe",
|
||||
@ -321,9 +354,12 @@
|
||||
"edt_conf_enum_dl_verbose1": "Stufe 1",
|
||||
"edt_conf_enum_dl_verbose2": "Stufe 2",
|
||||
"edt_conf_enum_dl_verbose3": "Stufe 3",
|
||||
"edt_conf_enum_dominant_color": "Dominante Farbe - pro LED",
|
||||
"edt_conf_enum_dominant_color_advanced": "Dominante Farbe fortgeschritten - pro LED",
|
||||
"edt_conf_enum_effect": "Effekt",
|
||||
"edt_conf_enum_gbr": "GBR",
|
||||
"edt_conf_enum_grb": "GRB",
|
||||
"edt_conf_enum_high": "Hoch",
|
||||
"edt_conf_enum_hsv": "HSV",
|
||||
"edt_conf_enum_left_right": "von links nach rechts",
|
||||
"edt_conf_enum_linear": "Linear",
|
||||
@ -331,7 +367,10 @@
|
||||
"edt_conf_enum_logsilent": "Stille",
|
||||
"edt_conf_enum_logverbose": "Ausführlich",
|
||||
"edt_conf_enum_logwarn": "Warnung",
|
||||
"edt_conf_enum_multicolor_mean": "Mehrfarbig",
|
||||
"edt_conf_enum_low": "Niedrig",
|
||||
"edt_conf_enum_medium": "Mittel",
|
||||
"edt_conf_enum_multicolor_mean": "Durchschnittsfarbe einfach - pro LED",
|
||||
"edt_conf_enum_multicolor_mean_squared": "Durchschnittsfarbe zum Quadrat - pro LED",
|
||||
"edt_conf_enum_please_select": "Bitte auswählen",
|
||||
"edt_conf_enum_rbg": "RBG",
|
||||
"edt_conf_enum_rgb": "RGB",
|
||||
@ -341,7 +380,7 @@
|
||||
"edt_conf_enum_transeffect_sudden": "Sofort",
|
||||
"edt_conf_enum_udp_ddp": "DDP",
|
||||
"edt_conf_enum_udp_raw": "RAW",
|
||||
"edt_conf_enum_unicolor_mean": "Einfarbig",
|
||||
"edt_conf_enum_unicolor_mean": "Durchschnittsfarbe Gesamtbild - auf alle LED angewandt",
|
||||
"edt_conf_fbs_heading_title": "Flatbuffers Server",
|
||||
"edt_conf_fbs_timeout_expl": "Wenn für die angegebene Zeit keine Daten empfangen werden, wird die Komponente (vorübergehend) deaktiviert",
|
||||
"edt_conf_fbs_timeout_title": "Zeitüberschreitung",
|
||||
@ -400,11 +439,13 @@
|
||||
"edt_conf_grabber_discovered_title": "Gefundenes Aufnahmegerät",
|
||||
"edt_conf_grabber_discovered_title_info": "Wähle dein gefundenes Aufnahmegerät aus.",
|
||||
"edt_conf_grabber_discovery_inprogress": "Suche Aufnahmegeräte",
|
||||
"edt_conf_instC_audioEnable_expl": "Aktiviert die Audioaufnahme für diese LED Hardware Instanz.",
|
||||
"edt_conf_instC_audioEnable_title": "Aktivieren der Audioaufnahme",
|
||||
"edt_conf_instC_screen_grabber_device_expl": "Das verwendet Bildschirmaufnahmegerät",
|
||||
"edt_conf_instC_screen_grabber_device_title": "Bildschirmaufnahmegerät",
|
||||
"edt_conf_instC_systemEnable_expl": "Aktiviert die Bildschirm Aufnahme für diese LED Hardware Instanz.",
|
||||
"edt_conf_instC_systemEnable_title": "Aktiviere Bildschirm Aufnahme",
|
||||
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED -Hardware Instanz.",
|
||||
"edt_conf_instC_v4lEnable_expl": "Aktiviert die USB Aufnahme für diese LED Hardware Instanz.",
|
||||
"edt_conf_instC_v4lEnable_title": "Aktiviere USB-Aufnahme",
|
||||
"edt_conf_instC_video_grabber_device_expl": "Das verwendete Videoaufnahmegerät.",
|
||||
"edt_conf_instC_video_grabber_device_title": "Videoaufnahmegerät",
|
||||
@ -439,8 +480,6 @@
|
||||
"edt_conf_smooth_heading_title": "Glättung",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Frequenz in der Zwischenschritte zur Glättung berechnet werden.",
|
||||
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrequenz",
|
||||
"edt_conf_smooth_outputRate_expl": "Die Ausgangfrequenz zum LED-Gerät",
|
||||
"edt_conf_smooth_outputRate_title": "Ausgabefrequenz",
|
||||
"edt_conf_smooth_time_ms_expl": "Wie lange soll die Glättung Bilder sammeln?",
|
||||
"edt_conf_smooth_time_ms_title": "Zeit",
|
||||
"edt_conf_smooth_type_expl": "Algorithmus der Glättung.",
|
||||
@ -614,10 +653,17 @@
|
||||
"edt_dev_spec_rgbw_calibration_green": "Grün/Weiß-Kanal Aspekt",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Grenzwert für Weißkanal",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Rot/Weiß-Kanal Aspekt",
|
||||
"edt_dev_spec_segmentId_title": "Segment-ID",
|
||||
"edt_dev_spec_segmentsOverlapValidation_error": "Korrigiere die WLED-Konfiguration! Das Segment darf sich nicht mit {{Plural:$1|Segment|Segmenten}} überschneiden: \"$2\".",
|
||||
"edt_dev_spec_segmentsSwitchOffOthers_title": "Abschalten anderer Segmente",
|
||||
"edt_dev_spec_segments_disabled_title": "Segment-Streaming ist in WLED deaktiviert.",
|
||||
"edt_dev_spec_segments_title": "Stream zum Segment",
|
||||
"edt_dev_spec_serial_title": "Seriennummer",
|
||||
"edt_dev_spec_spipath_title": "SPI Pfad",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer Handshake maximum Timeout",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer Handshake minimum Timeout",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title": "LED bleiben nach dem Stream an",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title_info": "Die LEDs bleiben nach dem Streaming oder der Wiederherstellung des Status eingeschaltet.",
|
||||
"edt_dev_spec_stream_protocol_title": "Streaming-Protokoll",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Aus bei schwarz",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Aus bei Minimum",
|
||||
@ -841,6 +887,7 @@
|
||||
"general_col_blue": "blau",
|
||||
"general_col_green": "grün",
|
||||
"general_col_red": "rot",
|
||||
"general_comp_AUDIO": "Audioaufnahme",
|
||||
"general_comp_BLACKBORDER": "Schwarze Balken Erkennung",
|
||||
"general_comp_BOBLIGHTSERVER": "Boblight Server",
|
||||
"general_comp_FLATBUFSERVER": "Flatbuffers Server",
|
||||
@ -964,8 +1011,11 @@
|
||||
"remote_losthint": "Notiz: Alle Änderungen gehen nach einem Neustart verloren.",
|
||||
"remote_maptype_intro": "Für gewöhnlich entscheidet dein LED-Layout welcher Bildbereich welche LED zugewiesen bekommt, dies kann hier geändert werden. $1",
|
||||
"remote_maptype_label": "LED-Bereich Zuordnung",
|
||||
"remote_maptype_label_multicolor_mean": "Mehrfarbig",
|
||||
"remote_maptype_label_unicolor_mean": "Einfarbig",
|
||||
"remote_maptype_label_dominant_color": "Dominante Farbe",
|
||||
"remote_maptype_label_dominant_color_advanced": "Dominante Farbe fortgeschritten",
|
||||
"remote_maptype_label_multicolor_mean": "Durchschnittsfarbe einfach",
|
||||
"remote_maptype_label_multicolor_mean_squared": "Durchschnittsfarbe zum Quadrat",
|
||||
"remote_maptype_label_unicolor_mean": "Durchschnittsfarbe Gesamtbild",
|
||||
"remote_optgroup_syseffets": "Mitgelieferte Effekte",
|
||||
"remote_optgroup_templates_custom": "Nutzer Vorlage",
|
||||
"remote_optgroup_templates_system": "System Vorlage",
|
||||
|
@ -51,6 +51,7 @@
|
||||
"conf_grabber_fg_intro": "Screen capture is your local system capture as input source, Hyperion is installed on.",
|
||||
"conf_grabber_inst_grabber_config_info": "Configure your capturing hardware devices to be used by the instance in advance",
|
||||
"conf_grabber_v4l_intro": "USB capture is a (capture) device connected via USB which is used to input source pictures for processing.",
|
||||
"conf_grabber_audio_intro": "Audio capture utilizes an audio input device as the source for visualization.",
|
||||
"conf_helptable_expl": "Explanation",
|
||||
"conf_helptable_option": "Option",
|
||||
"conf_leds_config_error": "Error in LED/LED layout configuration",
|
||||
@ -58,11 +59,13 @@
|
||||
"conf_leds_contr_label_contrtype": "Controller type:",
|
||||
"conf_leds_device_info_log": "In case your LEDs do not work, check here for errors:",
|
||||
"conf_leds_device_intro": "Hyperion supports a lot of controllers to transmit data to your target device. Select a LED controller out of the sorted list and configure it. We have chosen the best default settings for each device.",
|
||||
"conf_leds_error_get_properties_text" : "Failed to get the device's properties. Please check the configuration items.",
|
||||
"conf_leds_error_get_properties_title" : "Device properties",
|
||||
"conf_leds_error_get_properties_text": "Failed to get the device's properties. Please check the configuration items.",
|
||||
"conf_leds_error_get_properties_title": "Device properties",
|
||||
"conf_leds_error_hwled_gt_layout": "The hardware LED count ($1) is greater than LEDs configured via layout ($2),<br>$3 {{plural:$3|LED|LEDs}} will stay black if you continue.",
|
||||
"conf_leds_error_hwled_lt_layout": "The hardware LED count ($1) is less than LEDs configured via layout ($2). <br> The number of LEDs configured in the layout must not exceed the available LEDs",
|
||||
"conf_leds_error_hwled_gt_maxled": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the device ($2). <br> The hardware LED count is set to ($3).",
|
||||
"conf_leds_error_hwled_gt_maxled_protocol": "The hardware LED count ($1) is greater than the maximum number of LEDs supported by the streaming protocol ($2). <br> The streaming protocol will be changed to ($3).",
|
||||
"conf_leds_error_wled_segment_missing": "The currently configured segment ($1) is not configured at your WLED device.<br>You might need to check the WLED configuration!<br>The configuration page represents the current WLED setup.",
|
||||
"conf_leds_info_ws281x": "Hyperion must run with 'root' privileges for this controller type!",
|
||||
"conf_leds_layout_advanced": "Advanced Settings",
|
||||
"conf_leds_layout_blacklist_num_title": "Number of LEDs",
|
||||
@ -252,6 +255,8 @@
|
||||
"edt_conf_bb_unknownFrameCnt_title": "Unknown frames",
|
||||
"edt_conf_bge_heading_title": "Background Effect/Color",
|
||||
"edt_conf_bobls_heading_title": "Boblight Server",
|
||||
"edt_conf_color_accuracyLevel_expl": "Level how accurate dominat colors are evaluated. A higher level creates more accurate results, but also requries more processing power. Should to be combined with reduced pixel processing.",
|
||||
"edt_conf_color_accuracyLevel_title": "Accuracy level",
|
||||
"edt_conf_color_backlightColored_expl": "Add some color to your backlight.",
|
||||
"edt_conf_color_backlightColored_title": "Colored backlight",
|
||||
"edt_conf_color_backlightThreshold_expl": "The minimum amount of brightness (backlight). Disabled during effects, colors and in status \"Off\"",
|
||||
@ -282,7 +287,7 @@
|
||||
"edt_conf_color_heading_title": "Color Calibration",
|
||||
"edt_conf_color_id_expl": "User given name",
|
||||
"edt_conf_color_id_title": "ID",
|
||||
"edt_conf_color_imageToLedMappingType_expl": "Overwrites the LED area assignment of your LED layout if it's not \"multicolor\"",
|
||||
"edt_conf_color_imageToLedMappingType_expl": "Overwrites the LED area assignment of your LED layout if it's not \"Mean Color Simple\"",
|
||||
"edt_conf_color_imageToLedMappingType_title": "LED area assignment",
|
||||
"edt_conf_color_leds_expl": "Assign this adjustment to all LEDs (*) or just some (0-24).",
|
||||
"edt_conf_color_leds_title": "LED index",
|
||||
@ -294,6 +299,8 @@
|
||||
"edt_conf_color_temperature_title": "Temperature",
|
||||
"edt_conf_color_saturationGain_expl": "Adjusts the saturation of colors. 1.0 means no change, over 1.0 increases saturation, under 1.0 desaturates.",
|
||||
"edt_conf_color_saturationGain_title": "Saturation gain",
|
||||
"edt_conf_color_reducedPixelSetFactorFactor_expl": "Evaluate only a set of pixels per LED area defined, Low ~25%, Medium ~10%, High ~6%",
|
||||
"edt_conf_color_reducedPixelSetFactorFactor_title": "Reduced pixel processing",
|
||||
"edt_conf_color_white_expl": "The calibrated white value.",
|
||||
"edt_conf_color_white_title": "White",
|
||||
"edt_conf_color_yellow_expl": "The calibrated yellow value.",
|
||||
@ -323,6 +330,8 @@
|
||||
"edt_conf_enum_color": "Color",
|
||||
"edt_conf_enum_custom": "Custom",
|
||||
"edt_conf_enum_decay": "Decay",
|
||||
"edt_conf_enum_delay": "Delay only",
|
||||
"edt_conf_enum_disabled": "Disabled",
|
||||
"edt_conf_enum_dl_error": "Error",
|
||||
"edt_conf_enum_dl_informational": "Informational",
|
||||
"edt_conf_enum_dl_nodebug": "No Debug output",
|
||||
@ -331,9 +340,12 @@
|
||||
"edt_conf_enum_dl_verbose1": "Verbosity level 1",
|
||||
"edt_conf_enum_dl_verbose2": "Verbosity level 2",
|
||||
"edt_conf_enum_dl_verbose3": "Verbosity level 3",
|
||||
"edt_conf_enum_dominant_color": "Dominant Color - per LED",
|
||||
"edt_conf_enum_dominant_color_advanced": "Dominant Color Advanced - per LED",
|
||||
"edt_conf_enum_effect": "Effect",
|
||||
"edt_conf_enum_gbr": "GBR",
|
||||
"edt_conf_enum_grb": "GRB",
|
||||
"edt_conf_enum_high": "High",
|
||||
"edt_conf_enum_hsv": "HSV",
|
||||
"edt_conf_enum_left_right": "Left to right",
|
||||
"edt_conf_enum_linear": "Linear",
|
||||
@ -341,7 +353,10 @@
|
||||
"edt_conf_enum_logsilent": "Silent",
|
||||
"edt_conf_enum_logverbose": "Verbose",
|
||||
"edt_conf_enum_logwarn": "Warning",
|
||||
"edt_conf_enum_multicolor_mean": "Multicolor",
|
||||
"edt_conf_enum_low": "Low",
|
||||
"edt_conf_enum_medium": "Medium",
|
||||
"edt_conf_enum_multicolor_mean": "Mean Color Simple - per LED",
|
||||
"edt_conf_enum_multicolor_mean_squared": "Mean Color Squared - per LED",
|
||||
"edt_conf_enum_please_select": "Please Select",
|
||||
"edt_conf_enum_rbg": "RBG",
|
||||
"edt_conf_enum_rgb": "RGB",
|
||||
@ -351,7 +366,7 @@
|
||||
"edt_conf_enum_transeffect_sudden": "Sudden",
|
||||
"edt_conf_enum_udp_ddp": "DDP",
|
||||
"edt_conf_enum_udp_raw": "RAW",
|
||||
"edt_conf_enum_unicolor_mean": "Unicolor",
|
||||
"edt_conf_enum_unicolor_mean": "Mean Color Image - applied to all LEDs",
|
||||
"edt_conf_fbs_heading_title": "Flatbuffers Server",
|
||||
"edt_conf_fbs_timeout_expl": "If no data is received for the given period, the component will be (soft) disabled.",
|
||||
"edt_conf_fbs_timeout_title": "Timeout",
|
||||
@ -416,6 +431,8 @@
|
||||
"edt_conf_instC_systemEnable_title": "Enable screen capture",
|
||||
"edt_conf_instC_v4lEnable_expl": "Enables the USB capture for this LED hardware instance",
|
||||
"edt_conf_instC_v4lEnable_title": "Enable USB capture",
|
||||
"edt_conf_instC_audioEnable_expl": "Enables the Audio capture for this LED hardware instance",
|
||||
"edt_conf_instC_audioEnable_title": "Enable Audio capture",
|
||||
"edt_conf_instC_video_grabber_device_expl": "The video capture device used",
|
||||
"edt_conf_instC_video_grabber_device_title": "Video capture device",
|
||||
"edt_conf_instCapture_heading_title": "Capture Devices",
|
||||
@ -518,6 +535,27 @@
|
||||
"edt_conf_v4l2_hardware_set_defaults_tip": "Set device's default values for brightness, contrast, hue and saturation",
|
||||
"edt_conf_v4l2_noSignalCounterThreshold_title": "Signal Counter Threshold",
|
||||
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Count of frames (check that with grabber's current FPS mode) after which the no signal is triggered",
|
||||
"edt_conf_audio_device_expl": "Selected audio input device",
|
||||
"edt_conf_audio_device_title": "Audio Device",
|
||||
"edt_conf_audio_effects_expl": "Select an effect on how the audio signal is transformed to",
|
||||
"edt_conf_audio_effects_title": "Audio Effects",
|
||||
"edt_conf_audio_effect_enum_vumeter": "VU-Meter",
|
||||
"edt_conf_audio_effect_hotcolor_expl": "Hot Color",
|
||||
"edt_conf_audio_effect_hotcolor_title": "Hot Color",
|
||||
"edt_conf_audio_effect_multiplier_expl": "Audio Signal Value multiplier",
|
||||
"edt_conf_audio_effect_multiplier_title": "Multiplier",
|
||||
"edt_conf_audio_effect_safecolor_expl": "Safe Color",
|
||||
"edt_conf_audio_effect_safecolor_title": "Safe Color",
|
||||
"edt_conf_audio_effect_safevalue_expl": "Safe Threshold",
|
||||
"edt_conf_audio_effect_safevalue_title": "Safe Threshold",
|
||||
"edt_conf_audio_effect_set_defaults": "Reset to default values",
|
||||
"edt_conf_audio_effect_tolerance_expl": "Tolerance used when auto calculating a signal multipler from 0-100",
|
||||
"edt_conf_audio_effect_tolerance_title": "Tolerance",
|
||||
"edt_conf_audio_effect_warncolor_expl": "Warning Color",
|
||||
"edt_conf_audio_effect_warncolor_title": "Warning Color",
|
||||
"edt_conf_audio_effect_warnvalue_expl": "Warning Threshold",
|
||||
"edt_conf_audio_effect_warnvalue_title": "Warning Threshold",
|
||||
"edt_conf_audio_heading_title": "Audio Capture",
|
||||
"edt_conf_webc_crtPath_expl": "Path to the certification file (format should be PEM)",
|
||||
"edt_conf_webc_crtPath_title": "Certificate path",
|
||||
"edt_conf_webc_docroot_expl": "Local webinterface root path (just for webui developer)",
|
||||
@ -563,7 +601,7 @@
|
||||
"edt_dev_spec_brightnessOverwrite_title": "Overwrite brightness",
|
||||
"edt_dev_spec_brightnessThreshold_title": "Signal detection brightness minimum",
|
||||
"edt_dev_spec_brightness_title": "Brightness",
|
||||
"edt_dev_spec_candyGamma_title" : "'Candy' mode (double gamma correction)",
|
||||
"edt_dev_spec_candyGamma_title": "'Candy' mode (double gamma correction)",
|
||||
"edt_dev_spec_chanperfixture_title": "Channels per Fixture",
|
||||
"edt_dev_spec_cid_title": "CID",
|
||||
"edt_dev_spec_clientKey_title": "Clientkey",
|
||||
@ -620,15 +658,22 @@
|
||||
"edt_dev_spec_razer_device_title": "Razer Chroma Device",
|
||||
"edt_dev_spec_restoreOriginalState_title": "Restore lights' state",
|
||||
"edt_dev_spec_restoreOriginalState_title_info": "Restore the device's original state when device is disabled",
|
||||
"edt_dev_spec_rgbw_calibration_enable" : "White channel calibration (RGBW only)",
|
||||
"edt_dev_spec_rgbw_calibration_limit" : "White channel limit",
|
||||
"edt_dev_spec_rgbw_calibration_red" : "Red/White channel aspect",
|
||||
"edt_dev_spec_rgbw_calibration_green" : "Green/White channel aspect",
|
||||
"edt_dev_spec_rgbw_calibration_blue" : "Blue/White channel aspect",
|
||||
"edt_dev_spec_rgbw_calibration_enable": "White channel calibration (RGBW only)",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "White channel limit",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Red/White channel aspect",
|
||||
"edt_dev_spec_rgbw_calibration_green": "Green/White channel aspect",
|
||||
"edt_dev_spec_rgbw_calibration_blue": "Blue/White channel aspect",
|
||||
"edt_dev_spec_segments_disabled_title": "Segment streaming disabled at WLED.",
|
||||
"edt_dev_spec_segments_title": "Stream to segment",
|
||||
"edt_dev_spec_segmentId_title": "Segment-ID",
|
||||
"edt_dev_spec_segmentsSwitchOffOthers_title": "Switch-off other segments",
|
||||
"edt_dev_spec_segmentsOverlapValidation_error": "Correct the WLED setup! The segment must not overlap with {{plural:$1| segment|segments}}: \"$2\".",
|
||||
"edt_dev_spec_serial_title": "Serial number",
|
||||
"edt_dev_spec_spipath_title": "SPI Device",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handshake timeout maximum",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Streamer handshake timeout minimum",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title": "Stay on after streaming",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title_info": "The device will stay on after streaming or restoring state.",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Switch off on black",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Switch-off, below minimum",
|
||||
"edt_dev_spec_syncOverwrite_title": "Disable synchronisation",
|
||||
@ -860,6 +905,7 @@
|
||||
"general_comp_PROTOSERVER": "Protocol Buffers Server",
|
||||
"general_comp_SMOOTHING": "Smoothing",
|
||||
"general_comp_V4L": "Capture USB-Input",
|
||||
"general_comp_AUDIO": "Audio Capture",
|
||||
"general_country_cn": "China",
|
||||
"general_country_de": "Germany",
|
||||
"general_country_es": "Spain",
|
||||
@ -871,11 +917,11 @@
|
||||
"general_country_us": "United States",
|
||||
"general_disabled": "disabled",
|
||||
"general_enabled": "enabled",
|
||||
"general_speech_ca": "Catalan",
|
||||
"general_speech_ca": "Catalan",
|
||||
"general_speech_cs": "Czech",
|
||||
"general_speech_da": "Danish",
|
||||
"general_speech_de": "German",
|
||||
"general_speech_el": "Greek",
|
||||
"general_speech_el": "Greek",
|
||||
"general_speech_en": "English",
|
||||
"general_speech_es": "Spanish",
|
||||
"general_speech_fr": "French",
|
||||
@ -970,8 +1016,11 @@
|
||||
"remote_losthint": "Note: All changes will be lost after a restart.",
|
||||
"remote_maptype_intro": "Usually the LED layout defines which LED covers a specific picture area. You can change it here: $1.",
|
||||
"remote_maptype_label": "Mapping type",
|
||||
"remote_maptype_label_multicolor_mean": "Multicolor",
|
||||
"remote_maptype_label_unicolor_mean": "Unicolor",
|
||||
"remote_maptype_label_dominant_color": "Dominant Color",
|
||||
"remote_maptype_label_dominant_color_advanced": "Dominant Color Advanced",
|
||||
"remote_maptype_label_multicolor_mean": "Mean Color Simple",
|
||||
"remote_maptype_label_multicolor_mean_squared": "Mean Color Squared",
|
||||
"remote_maptype_label_unicolor_mean": "Mean Color Image",
|
||||
"remote_optgroup_syseffets": "System Effects",
|
||||
"remote_optgroup_templates_custom": "User Templates",
|
||||
"remote_optgroup_templates_system": "System Templates",
|
||||
|
@ -10,6 +10,9 @@
|
||||
"InfoDialog_nowrite_foottext": "¡El WebUI se desbloqueará automáticamente después de haber resuelto el problema!",
|
||||
"InfoDialog_nowrite_text": "Hyperion no puede escribir en el archivo de configuración cargado actual. Repara los permisos de archivo para continuar.",
|
||||
"InfoDialog_nowrite_title": "¡Error de permiso de escritura!",
|
||||
"InfoDialog_systemRestart_title": "Reiniciar",
|
||||
"InfoDialog_systemResume_title": "Retomar",
|
||||
"InfoDialog_systemSuspend_title": "Suspender",
|
||||
"about_3rd_party_licenses": "Licencias de terceros",
|
||||
"about_3rd_party_licenses_error": "Tuvimos problemas para recopilar información de licencias de terceros a través de la Web. <br />Por favor, sigue este enlace para acceder al Recurso GitHub.",
|
||||
"about_build": "Build",
|
||||
@ -55,7 +58,9 @@
|
||||
"conf_leds_error_get_properties_title": "Propiedades del dispositivo",
|
||||
"conf_leds_error_hwled_gt_layout": "El recuento de LEDs por hardware ($1) es mayor que los LEDs configurados a través de la disposición ($2),<br>$3 {{plural:$1|LED|LEDs}} se quedará en negro si continúas.",
|
||||
"conf_leds_error_hwled_gt_maxled": "El recuento de LEDs por hardware ($1) es mayor que el número máximo de LEDs soportado por el dispositivo ($2). <br> El recuento de LEDs de hardware se establece en ($3).",
|
||||
"conf_leds_error_hwled_gt_maxled_protocol": "El recuento de LEDs por hardware ($1) es mayor que el número máximo de LEDs soportado por el protocolo de streaming ($2). <br> El protocolo de streaming cambiará a ($3).",
|
||||
"conf_leds_error_hwled_lt_layout": "El recuento de LEDs por hardware ($1) es menor que los LEDs configurados a través del diseño ($2). <br> El número de LEDs configurados en el esquema no debe superar los LEDs disponibles.",
|
||||
"conf_leds_error_wled_segment_missing": "El segmento configurado actualmente ($1) no está configurado en tu dispositivo WLED.<br>¡Es posible que tengas que comprobar la configuración del WLED!<br>La página de configuración representa la configuración actual del WLED.",
|
||||
"conf_leds_info_ws281x": "Hyperion debe ejecutarse con privilegios 'root' para este tipo de controlador.",
|
||||
"conf_leds_layout_advanced": "Ajustes Avanzados",
|
||||
"conf_leds_layout_blacklist_num_title": "Número de LEDs",
|
||||
@ -439,8 +444,6 @@
|
||||
"edt_conf_smooth_heading_title": "Suavizado",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Velocidad de cálculo de los fotogramas intermedios suaves.",
|
||||
"edt_conf_smooth_interpolationRate_title": "Tasa de interpolación",
|
||||
"edt_conf_smooth_outputRate_expl": "La velocidad de salida a tu controlador de leds.",
|
||||
"edt_conf_smooth_outputRate_title": "Tasa de salida",
|
||||
"edt_conf_smooth_time_ms_expl": "¿Cuánto tiempo debe recoger las imágenes el suavizado?",
|
||||
"edt_conf_smooth_time_ms_title": "Tiempo",
|
||||
"edt_conf_smooth_type_expl": "Tipo de suavizado",
|
||||
@ -614,10 +617,17 @@
|
||||
"edt_dev_spec_rgbw_calibration_green": "Aspecto del canal Verde/Blanco",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Límite del canal blanco",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Aspecto del canal Rojo/Blanco",
|
||||
"edt_dev_spec_segmentId_title": "Segmento-ID",
|
||||
"edt_dev_spec_segmentsOverlapValidation_error": "¡Corrige la configuración de WLED! El segmento no debe solaparse con {{plural:$1| segmento|segmentos}}: \"$2\".",
|
||||
"edt_dev_spec_segmentsSwitchOffOthers_title": "Desconectar otros segmentos",
|
||||
"edt_dev_spec_segments_disabled_title": "Transmisión de segmentos desactivada en WLED.",
|
||||
"edt_dev_spec_segments_title": "Transmisión a segmento",
|
||||
"edt_dev_spec_serial_title": "Número de serie",
|
||||
"edt_dev_spec_spipath_title": "Dispositivo SPI",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Máximo tiempo de espera para el contacto con el Streamer",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Tiempo mínimo de espera para el contacto con el Streamer",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title": "Permanecer encendido después del streaming",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title_info": "El dispositivo permanecerá encendido después de transmitir o restaurar el estado.",
|
||||
"edt_dev_spec_stream_protocol_title": "Protocolo de streaming",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Apagar en negro",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Apagado, por debajo del mínimo",
|
||||
@ -861,9 +871,11 @@
|
||||
"general_country_us": "Estados Unidos",
|
||||
"general_disabled": "Deshabilitado",
|
||||
"general_enabled": "Habilitado",
|
||||
"general_speech_ca": "Catalán",
|
||||
"general_speech_cs": "Czech",
|
||||
"general_speech_da": "Danés",
|
||||
"general_speech_de": "Alemán",
|
||||
"general_speech_el": "Griego",
|
||||
"general_speech_en": "Inglés",
|
||||
"general_speech_es": "Español",
|
||||
"general_speech_fr": "Francés",
|
||||
|
@ -398,9 +398,11 @@
|
||||
"edt_conf_v4l2_greenSignalThreshold_expl": "Assombrie les valeurs vertes faibles (reconnues comme noires)",
|
||||
"edt_conf_v4l2_greenSignalThreshold_title": "Seuil de signal vert",
|
||||
"edt_conf_v4l2_hardware_brightness_expl": "Définie la luminosité matérielle",
|
||||
"edt_conf_v4l2_hardware_brightness_title": "Contrôle matériel de la luminosité",
|
||||
"edt_conf_v4l2_hardware_contrast_expl": "Définie le contraste matériel",
|
||||
"edt_conf_v4l2_hardware_contrast_title": "Contrôle le contraste matériel",
|
||||
"edt_conf_v4l2_hardware_hue_expl": "Définie le hue matériel",
|
||||
"edt_conf_v4l2_hardware_hue_title": "Contrôle matériel de la teinte",
|
||||
"edt_conf_v4l2_hardware_saturation_expl": "Définie la saturation matérielle",
|
||||
"edt_conf_v4l2_hardware_saturation_title": "Contrôle la saturation matériel",
|
||||
"edt_conf_v4l2_heading_title": "Capture USB",
|
||||
|
@ -5,6 +5,8 @@
|
||||
"InfoDialog_changePassword_title": "Cambia Password",
|
||||
"InfoDialog_iswitch_text": "Se esegui più di un Hyperion nella tua rete locale puoi passare tra configurazioni web. Seleziona l'istanza di Hyperion qui sotto e cambia!",
|
||||
"InfoDialog_iswitch_title": "Cambia Hyperion",
|
||||
"InfoDialog_nostorage_text": "Il tuo browser non supporta localStorage. Non è possibile salvare un'impostazione di lingua specifica (fallback su \"rilevamento automatico\") e livello di accesso (fallback su \"predefinito\"). Alcuni maghi potrebbero essere nascosti. Puoi ancora utilizzare l'interfaccia web senza ulteriori problemi",
|
||||
"InfoDialog_nostorage_title": "Impossibile memorizzare le impostazioni",
|
||||
"InfoDialog_nowrite_foottext": "WebUI sarà sbloccata automaticamente appena avrai risolto il problema!",
|
||||
"InfoDialog_nowrite_text": "Hyperion non può scrivere sull'attuale file di configurazione caricato. Riparare i permessi del file per procedere",
|
||||
"InfoDialog_nowrite_title": "errore dei permessi di scrittura!",
|
||||
@ -40,14 +42,29 @@
|
||||
"conf_general_intro": "Impostazioni base di Hyperion e WebUI che non rientrano in altre categorie.",
|
||||
"conf_general_label_title": "Impostazioni generali",
|
||||
"conf_grabber_fg_intro": "Cattura di sistema usa la cattura locale del sistema su cui è installato Hyperion come sorgente di input.",
|
||||
"conf_grabber_inst_grabber_config_info": "Configura in anticipo i dispositivi hardware di acquisizione in modo che vengano utilizzati dall'istanza",
|
||||
"conf_grabber_v4l_intro": "Cattura USB è un dispositivo (di cattura) connesso via USB usato come sorgente di immagini per l'elaborazione.",
|
||||
"conf_helptable_expl": "Spiegazione",
|
||||
"conf_helptable_option": "Opzioni",
|
||||
"conf_leds_config_error": "Errore nella configurazione del layout LED/LED",
|
||||
"conf_leds_config_warning": "Controlla la configurazione del layout LED/LED",
|
||||
"conf_leds_contr_label_contrtype": "Tipo di Controller",
|
||||
"conf_leds_device_info_log": "Nel caso in cui i tuoi LED non funzionino, controlla qui per gli errori:",
|
||||
"conf_leds_device_intro": "Hyperion supporta molti controller per trasmettere dati al tuo dispositivo. Seleziona un controller LED dalla lista e configuralo. Abbiamo scelto le impostazioni di default migliori per ogni dispositivo.",
|
||||
"conf_leds_error_get_properties_text": "Impossibile ottenere le proprietà del dispositivo. Si prega di controllare gli elementi di configurazione.",
|
||||
"conf_leds_error_get_properties_title": "Proprietà del dispositivo",
|
||||
"conf_leds_error_hwled_gt_layout": "Il conteggio dei LED hardware ($1) è maggiore dei LED configurati tramite layout ($2),<br>$3 {{plural:$3|LED|LED}} rimarranno neri se continui.",
|
||||
"conf_leds_error_hwled_gt_maxled": "Il numero di LED hardware ($ 1) è superiore al numero massimo di LED supportati dal dispositivo ($ 2). <br> Il conteggio dei LED hardware è impostato su ($ 3)",
|
||||
"conf_leds_error_hwled_lt_layout": "Il numero di LED hardware ($ 1) è inferiore ai LED configurati tramite layout ($ 2). <br> Il numero di LED configurati nel layout non deve superare i LED disponibili",
|
||||
"conf_leds_info_ws281x": "Hyperion deve essere eseguito con privilegi di 'root' per questo tipo di controller!",
|
||||
"conf_leds_layout_advanced": "Impostazioni Avanzate",
|
||||
"conf_leds_layout_blacklist_num_title": "Numero di LEDs",
|
||||
"conf_leds_layout_blacklist_rule_title": "Regole della lista nera",
|
||||
"conf_leds_layout_blacklist_rules_title": "Regole delle liste nere",
|
||||
"conf_leds_layout_blacklist_start_title": "LED Iniziale",
|
||||
"conf_leds_layout_blacklistleds_title": "I LED della lista nera",
|
||||
"conf_leds_layout_btn_checklist": "Mostra lista",
|
||||
"conf_leds_layout_btn_keystone": "Correzione trapezoidale",
|
||||
"conf_leds_layout_button_savelay": "Salva Layout",
|
||||
"conf_leds_layout_button_updsim": "Aggiorna Anteprima",
|
||||
"conf_leds_layout_checkp1": "Il led nero è il tuo primo led, il primo led è il punto in cui c'è l'input del segnale dati",
|
||||
@ -67,6 +84,16 @@
|
||||
"conf_leds_layout_cl_leftbottom": "Sinistra 0% - 100% Sotto",
|
||||
"conf_leds_layout_cl_leftmiddle": "Sinistra 0% - 75% Centro",
|
||||
"conf_leds_layout_cl_lefttop": "Sinistra 0% - 50% Sopra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft11": "In basso: 75 - 100% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft112": "In basso: 0 - 50% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft12": "In basso: 25 - 50% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft121": "In basso: 50 - 100% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft14": "In basso: 0 - 25% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft34": "In basso: 50 - 75% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "In basso: 25 - 75% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosTopLeft112": "In alto: 0 - 50% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosTopLeft121": "In alto: 50 - 100% da sinistra",
|
||||
"conf_leds_layout_cl_lightPosTopLeftNewMid": "In alto: 25 - 75% da sinistra",
|
||||
"conf_leds_layout_cl_overlap": "Overlap",
|
||||
"conf_leds_layout_cl_reversdir": "Inverti direzione",
|
||||
"conf_leds_layout_cl_right": "Destra",
|
||||
@ -81,6 +108,7 @@
|
||||
"conf_leds_layout_generatedconf": "Configurazione LED Generata/Attuale",
|
||||
"conf_leds_layout_intro": "è necessario anche un layout dei led che rifletta la disposizione dei tuoi led. Il layout classico è il comune bordo della tv, ma supportiamo anche la creazione di matrici led (parete led). La visuale sul layout è SEMPRE DI FRONTE alla TV.",
|
||||
"conf_leds_layout_ma_cabling": "Cablaggio",
|
||||
"conf_leds_layout_ma_direction": "Direzione",
|
||||
"conf_leds_layout_ma_horiz": "Orizzontale",
|
||||
"conf_leds_layout_ma_optbottomleft": "Basso a sinistra",
|
||||
"conf_leds_layout_ma_optbottomright": "Basso a destra",
|
||||
@ -112,17 +140,21 @@
|
||||
"conf_leds_layout_textf1": "Questo campo testo mostra di default il layout attualmente caricato e viene sovrascritto se ne generi uno nuovo con le opzioni sopra. Se vuoi puoi fare ulteriori modifiche.",
|
||||
"conf_leds_nav_label_ledcontroller": "Controller LED",
|
||||
"conf_leds_nav_label_ledlayout": "Layout LED",
|
||||
"conf_leds_note_layout_overwrite": "Nota: Overwrite crea un layout predefinito per {{plural:$1| un LED| tutti i LED $1}} dati dal conteggio dei LED hardware",
|
||||
"conf_leds_optgroup_RPiGPIO": "RPi GPIO",
|
||||
"conf_leds_optgroup_RPiPWM": "RPi PWM",
|
||||
"conf_leds_optgroup_RPiSPI": "RPi SPI",
|
||||
"conf_leds_optgroup_debug": "Debug",
|
||||
"conf_leds_optgroup_network": "Rete",
|
||||
"conf_leds_optgroup_other": "Altro",
|
||||
"conf_leds_optgroup_usb": "USB/Seriale",
|
||||
"conf_logging_btn_autoscroll": "Auto scrolling",
|
||||
"conf_logging_btn_clipboard": "Copia nel registro Appunti",
|
||||
"conf_logging_btn_pbupload": "Invia segnalazione per richieste di supporto",
|
||||
"conf_logging_contpolicy": "Policy per la Privacy delle segnalazioni",
|
||||
"conf_logging_label_intro": "Area per controllare i messaggi di log, a seconda dell'impostazione di loglevel vedrai più o meno informazioni.",
|
||||
"conf_logging_lastreports": "Segnalazioni precedenti",
|
||||
"conf_logging_logoutput": "Log output",
|
||||
"conf_logging_nomessage": "Nessun messaggio di log disponibile.",
|
||||
"conf_logging_report": "Segnala",
|
||||
"conf_logging_uplfailed": "Upload fallito! Controlla la tua connessione a internet!",
|
||||
@ -163,7 +195,12 @@
|
||||
"dashboard_infobox_label_instance": "Istanza:",
|
||||
"dashboard_infobox_label_latesthyp": "L'ultima versione di Hyperion:",
|
||||
"dashboard_infobox_label_platform": "Piattaforma:",
|
||||
"dashboard_infobox_label_port_boblight": "Server Boblight:",
|
||||
"dashboard_infobox_label_port_flat": "Flatbuffer:",
|
||||
"dashboard_infobox_label_port_json": "JSON-Server:",
|
||||
"dashboard_infobox_label_port_proto": "Protobuffer:",
|
||||
"dashboard_infobox_label_ports": "Porte",
|
||||
"dashboard_infobox_label_ports_websocket": "WebSocket (ws|wss):",
|
||||
"dashboard_infobox_label_smartacc": "Accesso Smart",
|
||||
"dashboard_infobox_label_statush": "Status Hyperion:",
|
||||
"dashboard_infobox_label_title": "Informazioni",
|
||||
@ -181,6 +218,7 @@
|
||||
"dashboard_newsbox_readmore": "Leggi ancora",
|
||||
"dashboard_newsbox_visitblog": "Visita Hyperion-Blog",
|
||||
"edt_append_degree": "°",
|
||||
"edt_append_frames": "frames",
|
||||
"edt_append_hz": "Hz",
|
||||
"edt_append_leds": "LEDs",
|
||||
"edt_append_ms": "ms",
|
||||
@ -216,6 +254,8 @@
|
||||
"edt_conf_color_blue_title": "Blu",
|
||||
"edt_conf_color_brightnessComp_expl": "Compensa la differenza di luminosità tra rosso verde blu, ciano magneta giallo e bianco. 100 significa massima compensazione, 0 nessuna compensazione",
|
||||
"edt_conf_color_brightnessComp_title": "Compensazione luminosità",
|
||||
"edt_conf_color_brightnessGain_expl": "Regola la luminosità dei colori. 1.0 significa nessun cambiamento, oltre 1.0 aumenta la luminosità, sotto 1.0 diminuisce la luminosità",
|
||||
"edt_conf_color_brightnessGain_title": "Intensità della luminosità",
|
||||
"edt_conf_color_brightness_expl": "Imposta la luminosità complessiva dei leds",
|
||||
"edt_conf_color_brightness_title": "Luminosità",
|
||||
"edt_conf_color_channelAdjustment_header_expl": "Crea profilo di colore che può essere assegnato a un componente specifico. Regola colore, gamma, luminosità, compensazione e altro.",
|
||||
@ -242,6 +282,8 @@
|
||||
"edt_conf_color_magenta_title": "Magenta",
|
||||
"edt_conf_color_red_expl": "Il valore calibrato del rosso.",
|
||||
"edt_conf_color_red_title": "Rosso",
|
||||
"edt_conf_color_saturationGain_expl": "Regola la saturazione dei colori. 1.0 significa nessun cambiamento, oltre 1.0 aumenta la saturazione, sotto 1.0 desatura.",
|
||||
"edt_conf_color_saturationGain_title": "Intensità della saturazione",
|
||||
"edt_conf_color_white_expl": "Il valore calibrato del bianco.",
|
||||
"edt_conf_color_white_title": "Bianco",
|
||||
"edt_conf_color_yellow_expl": "Il valore calibrato del giallo.",
|
||||
@ -253,10 +295,13 @@
|
||||
"edt_conf_effp_paths_expl": "Puoi definire altre cartelle che contengono effetti aggiuntivi. Il configuratore di effetti salva sempre all'interno della prima cartella.",
|
||||
"edt_conf_effp_paths_itemtitle": "Percorso",
|
||||
"edt_conf_effp_paths_title": "Percorso Effetti",
|
||||
"edt_conf_enum_BOTH": "Orizzontale e Verticale",
|
||||
"edt_conf_enum_HORIZONTAL": "Orizzontale",
|
||||
"edt_conf_enum_NO_CHANGE": "Auto",
|
||||
"edt_conf_enum_NTSC": "NTSC",
|
||||
"edt_conf_enum_PAL": "PAL",
|
||||
"edt_conf_enum_SECAM": "SECAM",
|
||||
"edt_conf_enum_VERTICAL": "Verticale",
|
||||
"edt_conf_enum_automatic": "Automatico",
|
||||
"edt_conf_enum_bbclassic": "Classico",
|
||||
"edt_conf_enum_bbdefault": "Default",
|
||||
@ -287,12 +332,15 @@
|
||||
"edt_conf_enum_logverbose": "Verboso",
|
||||
"edt_conf_enum_logwarn": "Avvertimento",
|
||||
"edt_conf_enum_multicolor_mean": "Multicolore",
|
||||
"edt_conf_enum_please_select": "Seleziona",
|
||||
"edt_conf_enum_rbg": "RBG",
|
||||
"edt_conf_enum_rgb": "RGB",
|
||||
"edt_conf_enum_right_left": "Da destra a sinistra",
|
||||
"edt_conf_enum_top_down": "Dall'alto verso il basso",
|
||||
"edt_conf_enum_transeffect_smooth": "Dolce",
|
||||
"edt_conf_enum_transeffect_sudden": "Repentina",
|
||||
"edt_conf_enum_udp_ddp": "DDP",
|
||||
"edt_conf_enum_udp_raw": "RAW",
|
||||
"edt_conf_enum_unicolor_mean": "Monocromatico",
|
||||
"edt_conf_fbs_heading_title": "Server Flatbuffers",
|
||||
"edt_conf_fbs_timeout_expl": "Se nessuna informazione viene ricevuta per un dato periodo, il componente verrà disabilitato (soft).",
|
||||
@ -321,11 +369,19 @@
|
||||
"edt_conf_fge_type_title": "Tipo",
|
||||
"edt_conf_fw_flat_expl": "Una destinazione flatbuffer per riga. Contiene IP:PORTA:(Esempio: 127.0.0.1:19401)",
|
||||
"edt_conf_fw_flat_itemtitle": "Destinatario flatbuffer",
|
||||
"edt_conf_fw_flat_services_discovered_expl": "Obiettivi flatbuffer scoperti",
|
||||
"edt_conf_fw_flat_services_discovered_title": "Obiettivi flatbuffer scoperti",
|
||||
"edt_conf_fw_flat_title": "Lista dei client flatbuffer",
|
||||
"edt_conf_fw_heading_title": "Forwarder",
|
||||
"edt_conf_fw_json_expl": "Una destinazione json per riga. Contiene IP:PORTA:(Esempio: 127.0.0.1:19446)",
|
||||
"edt_conf_fw_json_itemtitle": "Destinatario json",
|
||||
"edt_conf_fw_json_services_discovered_expl": "Server Hyperion scoperti che forniscono servizi JSON-API",
|
||||
"edt_conf_fw_json_services_discovered_title": "Target JSON rilevati",
|
||||
"edt_conf_fw_json_title": "Lista dei client json",
|
||||
"edt_conf_fw_remote_service_discovered_none": "Nessun servizio remoto rilevato",
|
||||
"edt_conf_fw_service_name_expl": "Nome del fornitore di servizi",
|
||||
"edt_conf_fw_service_name_title": "Nome di Servizio",
|
||||
"edt_conf_gen_configVersion_title": "Versione di configurazione",
|
||||
"edt_conf_gen_heading_title": "impostazioni Generali",
|
||||
"edt_conf_gen_name_expl": "Un nome definito dall'utente che viene utilizzato per riconoscere Hyperion. (Utile con più di un'istanza di Hyperion)",
|
||||
"edt_conf_gen_name_title": "Nome configurazione",
|
||||
@ -339,10 +395,19 @@
|
||||
"edt_conf_general_port_title": "Porta",
|
||||
"edt_conf_general_priority_expl": "Priorità di questo componente",
|
||||
"edt_conf_general_priority_title": "Priorità canale.",
|
||||
"edt_conf_grabber_discovered_expl": "Seleziona il tuo dispositivo di acquisizione scoperto",
|
||||
"edt_conf_grabber_discovered_none": "Nessun dispositivo di acquisizione rilevato",
|
||||
"edt_conf_grabber_discovered_title": "Dispositivo scoperto",
|
||||
"edt_conf_grabber_discovered_title_info": "Seleziona il tuo dispositivo di acquisizione scoperto",
|
||||
"edt_conf_grabber_discovery_inprogress": "ricerca in corso",
|
||||
"edt_conf_instC_screen_grabber_device_expl": "l dispositivo di cattura dello schermo utilizzato",
|
||||
"edt_conf_instC_screen_grabber_device_title": "Dispositivo di cattura dello schermo",
|
||||
"edt_conf_instC_systemEnable_expl": "Abilita la cattura della piattaforma per questa istanza di hardware led",
|
||||
"edt_conf_instC_systemEnable_title": "Abilita cattura piattaforma",
|
||||
"edt_conf_instC_v4lEnable_expl": "Abilita la cattura USB per questa istanza di hardware led",
|
||||
"edt_conf_instC_v4lEnable_title": "Abilita cattura USB",
|
||||
"edt_conf_instC_video_grabber_device_expl": "Il dispositivo di acquisizione video utilizzato",
|
||||
"edt_conf_instC_video_grabber_device_title": "Dispositivo di acquisizione video",
|
||||
"edt_conf_instCapture_heading_title": "Cattura Istanza",
|
||||
"edt_conf_js_heading_title": "Server JSON",
|
||||
"edt_conf_log_heading_title": "Logging",
|
||||
@ -374,8 +439,6 @@
|
||||
"edt_conf_smooth_heading_title": "Sfumatura",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Velocità di calcolo dei regolari frame intermedi",
|
||||
"edt_conf_smooth_interpolationRate_title": "Tasso di interpolazione",
|
||||
"edt_conf_smooth_outputRate_expl": "Velocità di uscita al tuo controller LED.",
|
||||
"edt_conf_smooth_outputRate_title": "Velocità di uscita",
|
||||
"edt_conf_smooth_time_ms_expl": "Quanto a lungo la sfumatura dovrebbe raggiungere le immagini?",
|
||||
"edt_conf_smooth_time_ms_title": "Durata",
|
||||
"edt_conf_smooth_type_expl": "Tipo di sfumatura.",
|
||||
@ -390,21 +453,41 @@
|
||||
"edt_conf_v4l2_cecDetection_title": "Rilevamento CEC",
|
||||
"edt_conf_v4l2_cropBottom_expl": "Numero di pixels in basso che vengono rimossi dall'immagine.",
|
||||
"edt_conf_v4l2_cropBottom_title": "Ritaglia in basso",
|
||||
"edt_conf_v4l2_cropHeightValidation_error": "Ritaglia in alto + Ritaglia in basso non può essere maggiore di Altezza ($ 1)",
|
||||
"edt_conf_v4l2_cropLeft_expl": "Numero di pixels sulla sinistra che vengono rimossi dall'immagine.",
|
||||
"edt_conf_v4l2_cropLeft_title": "Ritaglia sinistra",
|
||||
"edt_conf_v4l2_cropRight_expl": "Numero di pixels sulla destra che vengono rimossi dall'immagine.",
|
||||
"edt_conf_v4l2_cropRight_title": "Ritaglia destra",
|
||||
"edt_conf_v4l2_cropTop_expl": "Numero di pixels in alto che vengono rimossi dall'immagine.",
|
||||
"edt_conf_v4l2_cropTop_title": "Ritaglia in alto",
|
||||
"edt_conf_v4l2_cropWidthValidation_error": "Ritaglia a sinistra + Ritaglia a destra non può essere maggiore di Larghezza ($1)",
|
||||
"edt_conf_v4l2_device_expl": "Percorso del dispositivo di cattura USB. Imposta su 'Automatico' per riconoscimento automatico. Esempio: '/dev/video0'",
|
||||
"edt_conf_v4l2_device_title": "Dispositivo",
|
||||
"edt_conf_v4l2_encoding_expl": "Forza la codifica video per i grabber multiformato",
|
||||
"edt_conf_v4l2_encoding_title": "Formato di codifica",
|
||||
"edt_conf_v4l2_flip_expl": "Ciò consente di capovolgere l'immagine orizzontalmente, verticalmente o entrambi.",
|
||||
"edt_conf_v4l2_flip_title": "Capovolgimento dell'immagine",
|
||||
"edt_conf_v4l2_fpsSoftwareDecimation_expl": "Per risparmiare risorse verrà elaborato solo ogni n-esimo frame. Per es. se il grabber è impostato a 30fps con questa opzione impostata a 5 il risultato finale sarà di circa 6fps",
|
||||
"edt_conf_v4l2_fpsSoftwareDecimation_title": "Salto del frame del software",
|
||||
"edt_conf_v4l2_framerate_expl": "Frame al secondo supportati per il dispositivo attivo",
|
||||
"edt_conf_v4l2_framerate_title": "Frame al secondo",
|
||||
"edt_conf_v4l2_greenSignalThreshold_expl": "Scurisce valori bassi di verde (riconosciuti come nero)",
|
||||
"edt_conf_v4l2_greenSignalThreshold_title": "Soglia segnale verde",
|
||||
"edt_conf_v4l2_hardware_brightness_expl": "Imposta la luminosità dell'hardware",
|
||||
"edt_conf_v4l2_hardware_brightness_title": "Controllo hardware della luminosità",
|
||||
"edt_conf_v4l2_hardware_contrast_expl": "Imposta il contrasto hardware",
|
||||
"edt_conf_v4l2_hardware_contrast_title": "Controllo hardware del contrasto",
|
||||
"edt_conf_v4l2_hardware_hue_expl": "Imposta la tonalità dell'hardware",
|
||||
"edt_conf_v4l2_hardware_hue_title": "Controllo della tonalità hardware",
|
||||
"edt_conf_v4l2_hardware_saturation_expl": "Imposta la saturazione hardware",
|
||||
"edt_conf_v4l2_hardware_saturation_title": "Controllo della saturazione hardware",
|
||||
"edt_conf_v4l2_hardware_set_defaults": "Controlli hardware predefiniti",
|
||||
"edt_conf_v4l2_hardware_set_defaults_tip": "Imposta i valori predefiniti del dispositivo per luminosità, contrasto, tonalità e saturazione",
|
||||
"edt_conf_v4l2_heading_title": "Cattura USB",
|
||||
"edt_conf_v4l2_input_expl": "Seleziona l'input video per il tuo dispositivo. 'Automatico' mantiene il Valero scelto dall'interfaccia v4l2.",
|
||||
"edt_conf_v4l2_input_title": "Input",
|
||||
"edt_conf_v4l2_noSignalCounterThreshold_expl": "Conteggio dei fotogrammi (controllalo con l'attuale modalità FPS del grabber) dopo il quale viene attivato il segnale di assenza",
|
||||
"edt_conf_v4l2_noSignalCounterThreshold_title": "Soglia del contatore di segnale",
|
||||
"edt_conf_v4l2_redSignalThreshold_expl": "Scurisce valori bassi di rosso (riconosciuti come nero)",
|
||||
"edt_conf_v4l2_redSignalThreshold_title": "Soglia segnale rosso",
|
||||
"edt_conf_v4l2_resolution_expl": "Lista Risoluzioni supportate per il dispositivo attivo",
|
||||
@ -435,12 +518,21 @@
|
||||
"edt_conf_webc_sslport_expl": "Porta del webserver HTTPS",
|
||||
"edt_conf_webc_sslport_title": "Porta HTTPS",
|
||||
"edt_dev_auth_key_title": "Token di autenticazione",
|
||||
"edt_dev_auth_key_title_info": "Token di autenticazione necessario per accedere al dispositivo",
|
||||
"edt_dev_enum_sub_min_cool_adjust": "Regolazione freddo min",
|
||||
"edt_dev_enum_sub_min_warm_adjust": "Regolazione calore min",
|
||||
"edt_dev_enum_subtract_minimum": "Sottrai minimo",
|
||||
"edt_dev_enum_white_off": "Bianco off",
|
||||
"edt_dev_general_autostart_title": "avvio automatico",
|
||||
"edt_dev_general_autostart_title_info": "Il dispositivo LED è acceso durante l'avvio oppure no",
|
||||
"edt_dev_general_colorOrder_title": "Ordine byte RGB",
|
||||
"edt_dev_general_colorOrder_title_info": "L'ordine dei colori del dispositivo",
|
||||
"edt_dev_general_enableAttemptsInterval_title": "Intervallo tra tentativi",
|
||||
"edt_dev_general_enableAttemptsInterval_title_info": "Intervallo tra due tentativi di connessione.",
|
||||
"edt_dev_general_enableAttempts_title": "Tentativi di connessione",
|
||||
"edt_dev_general_enableAttempts_title_info": "Numero di tentativi di connessione di un dispositivo prima che entri in uno stato di errore.",
|
||||
"edt_dev_general_hardwareLedCount_title": "Numero di LED Hardware",
|
||||
"edt_dev_general_hardwareLedCount_title_info": "Il numero di LED fisici disponibili per il dispositivo specificato",
|
||||
"edt_dev_general_heading_title": "Impostazioni Generali",
|
||||
"edt_dev_general_name_title": "Nome configurazione",
|
||||
"edt_dev_general_rewriteTime_title": "Tempo di ricarica",
|
||||
@ -449,21 +541,28 @@
|
||||
"edt_dev_spec_FCsetConfig_title": "Imposta configurazione Fadecandy",
|
||||
"edt_dev_spec_LBap102Mode_title": "Modalità LightBerry APA102",
|
||||
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
|
||||
"edt_dev_spec_ada_mode_title": "Adalight - Standard",
|
||||
"edt_dev_spec_awa_mode_title": "HyperSerial - Alta velocità",
|
||||
"edt_dev_spec_baudrate_title": "Baudrate",
|
||||
"edt_dev_spec_blackLightsTimeout_title": "Timeout rilevamento segnale su nero",
|
||||
"edt_dev_spec_brightnessFactor_title": "Fattore luminosità",
|
||||
"edt_dev_spec_brightnessMax_title": "Luminosità massima",
|
||||
"edt_dev_spec_brightnessMin_title": "Luminosità minima",
|
||||
"edt_dev_spec_brightnessMin_title": "Luminosità minima",
|
||||
"edt_dev_spec_brightnessOverwrite_title": "Sovrascrivi luminosità",
|
||||
"edt_dev_spec_brightnessThreshold_title": "Luminosità minima per rilevamento segnale",
|
||||
"edt_dev_spec_brightness_title": "Luminosità",
|
||||
"edt_dev_spec_candyGamma_title": "Modalità \"Candy\" (doppia correzione gamma)",
|
||||
"edt_dev_spec_chanperfixture_title": "Canali per dispositivo",
|
||||
"edt_dev_spec_cid_title": "CID",
|
||||
"edt_dev_spec_clientKey_title": "Clientkey",
|
||||
"edt_dev_spec_colorComponent_title": "Componente colore",
|
||||
"edt_dev_spec_debugLevel_title": "Livello Debug",
|
||||
"edt_dev_spec_debugStreamer_title": "Debug Streamer",
|
||||
"edt_dev_spec_delayAfterConnect_title": "Ritardo dopo la connessione",
|
||||
"edt_dev_spec_devices_discovered_none": "Nessun dispositivo rilevato",
|
||||
"edt_dev_spec_devices_discovered_title": "Dispositivi trovato",
|
||||
"edt_dev_spec_devices_discovered_title_info": "Seleziona il tuo dispositivo LED trovato",
|
||||
"edt_dev_spec_devices_discovered_title_info_custom": "Seleziona il tuo dispositivo LED trovato o configurane uno personalizzato",
|
||||
"edt_dev_spec_devices_discovery_inprogress": "Ricerca in corso",
|
||||
"edt_dev_spec_dithering_title": "Dithering",
|
||||
"edt_dev_spec_dmaNumber_title": "Canale DMA",
|
||||
"edt_dev_spec_gamma_title": "Gamma",
|
||||
@ -478,6 +577,7 @@
|
||||
"edt_dev_spec_intervall_title": "Intervallo",
|
||||
"edt_dev_spec_invert_title": "Inverti segnale",
|
||||
"edt_dev_spec_latchtime_title": "Tempo di latch",
|
||||
"edt_dev_spec_latchtime_title_info": "Il tempo di latch è l'intervallo di tempo richiesto da un dispositivo fino all'elaborazione del successivo aggiornamento. Durante tale periodo di tempo, tutti gli aggiornamenti effettuati vengono ignorati.",
|
||||
"edt_dev_spec_ledIndex_title": "Indice LED",
|
||||
"edt_dev_spec_ledType_title": "Tipo LED",
|
||||
"edt_dev_spec_lightid_itemtitle": "ID",
|
||||
@ -491,6 +591,8 @@
|
||||
"edt_dev_spec_networkDeviceName_title": "Nome dispositivo di rete",
|
||||
"edt_dev_spec_networkDevicePort_title": "Porta",
|
||||
"edt_dev_spec_numberOfLeds_title": "Numero di LEDs",
|
||||
"edt_dev_spec_onBlackTimeToPowerOff": "È ora di spegnere la lampada se viene attivato il livello di nero",
|
||||
"edt_dev_spec_onBlackTimeToPowerOn": "È ora di accendere la lampada se il segnale viene ripristinato",
|
||||
"edt_dev_spec_orbIds_title": "Orb ID",
|
||||
"edt_dev_spec_order_left_right_title": "2.",
|
||||
"edt_dev_spec_order_top_down_title": "1.",
|
||||
@ -498,18 +600,29 @@
|
||||
"edt_dev_spec_panel_start_position": "Pannello d'inizio [0-max panels]",
|
||||
"edt_dev_spec_panelorganisation_title": "Sequenza numerazione pannelli",
|
||||
"edt_dev_spec_pid_title": "PID",
|
||||
"edt_dev_spec_port_expl": "Porta di servizio [1-65535]",
|
||||
"edt_dev_spec_port_title": "Porta",
|
||||
"edt_dev_spec_printTimeStamp_title": "Aggiungi timestamp",
|
||||
"edt_dev_spec_pwmChannel_title": "Canale PWM",
|
||||
"edt_dev_spec_razer_device_title": "Dispositivo Razer Chroma",
|
||||
"edt_dev_spec_restoreOriginalState_title": "Ripristina lo stato delle luci",
|
||||
"edt_dev_spec_restoreOriginalState_title_info": "Ripristina lo stato originale del dispositivo quando il dispositivo è disabilitato",
|
||||
"edt_dev_spec_rgbw_calibration_blue": "Aspetto del canale blu/bianco",
|
||||
"edt_dev_spec_rgbw_calibration_enable": "Calibrazione del canale del bianco (solo RGBW)",
|
||||
"edt_dev_spec_rgbw_calibration_green": "Aspetto canale verde/bianco",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Limite canale bianco",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Aspetto del canale rosso/bianco",
|
||||
"edt_dev_spec_serial_title": "Numero seriale",
|
||||
"edt_dev_spec_spipath_title": "Percorso SPI",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Timeout massimo handkshake streamer",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Timeout minimo handkshake streamer",
|
||||
"edt_dev_spec_sslReadTimeout_title": "Timeout lettura streamer",
|
||||
"edt_dev_spec_stream_protocol_title": "Protocollo di streaming",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Spegni o accendi il nero",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Spegni, sotto il minimo",
|
||||
"edt_dev_spec_syncOverwrite_title": "Disabilita la sincronizzazione",
|
||||
"edt_dev_spec_targetIpHost_expl": "Nome host (DNS/mDNS) o indirizzo IP (IPv4 o IPv6)",
|
||||
"edt_dev_spec_targetIpHost_title": "IP di destinazione/nome host",
|
||||
"edt_dev_spec_targetIpHost_title_info": "Il nome host o l'indirizzo IP del dispositivo",
|
||||
"edt_dev_spec_targetIp_title": "IP di destinazione",
|
||||
"edt_dev_spec_transeffect_title": "Effetto Transizione",
|
||||
"edt_dev_spec_transistionTimeExtra_title": "Tempo extra di buio",
|
||||
@ -573,9 +686,14 @@
|
||||
"edt_eff_frequency": "Frequenza",
|
||||
"edt_eff_gif_header": "GIF",
|
||||
"edt_eff_gif_header_desc": "Questo effetto riproduce file .gif. Fornisci un semplice loop video come effetto.",
|
||||
"edt_eff_grayscale": "Scala di grigi",
|
||||
"edt_eff_height": "Altezza",
|
||||
"edt_eff_huechange": "Cambiamento colore",
|
||||
"edt_eff_image": "file immagine",
|
||||
"edt_eff_image_source": "Fonte immagine",
|
||||
"edt_eff_image_source_file": "File locale",
|
||||
"edt_eff_image_source_url": "URL",
|
||||
"edt_eff_initial_blink": "Flash per attirare l'attenzione",
|
||||
"edt_eff_interval": "Intervallo",
|
||||
"edt_eff_knightrider_header": "Supercar",
|
||||
"edt_eff_knightrider_header_desc": "K.I.T.T. è tornato! Lo scanner frontale della nota automobile, questa volta non solo in rosso.",
|
||||
@ -613,6 +731,7 @@
|
||||
"edt_eff_reversedirection": "Inverti direzione",
|
||||
"edt_eff_rotationtime": "Tempo di rotazione",
|
||||
"edt_eff_saturation": "Saturazione",
|
||||
"edt_eff_set_post_color": "Imposta il colore del post dopo l'allarme",
|
||||
"edt_eff_showseconds": "Mostra secondi",
|
||||
"edt_eff_sleeptime": "Tempo di riposo",
|
||||
"edt_eff_smooth_custom": "Abilita sfumatura",
|
||||
@ -631,6 +750,7 @@
|
||||
"edt_eff_traces_header_desc": "Necessita redesign",
|
||||
"edt_eff_trails_header": "Scie",
|
||||
"edt_eff_trails_header_desc": "Stelle colorate che cadono dall'alto verso il basso",
|
||||
"edt_eff_url": "Indirizzo dell'immagine",
|
||||
"edt_eff_waves_header": "Onde",
|
||||
"edt_eff_waves_header_desc": "Onde di colore! Scegli i colori, tempo di rotazione, direzione e altro.",
|
||||
"edt_eff_whichleds": "Quali Leds",
|
||||
@ -655,6 +775,10 @@
|
||||
"edt_msg_error_disallow": "Il valore non deve essere di tipo $1",
|
||||
"edt_msg_error_disallow_union": "Il valore non deve essere di uno dei tipi forniti non ammessi",
|
||||
"edt_msg_error_enum": "Il valore deve essere uno tra quelli enumerati",
|
||||
"edt_msg_error_hostname": "Il nome host ha il formato errato",
|
||||
"edt_msg_error_invalid_epoch": "La data deve essere successiva al 1° gennaio 1970",
|
||||
"edt_msg_error_ipv4": "Il valore deve essere un indirizzo IPv4 valido sotto forma di 4 numeri compresi tra 0 e 255, separati da punti",
|
||||
"edt_msg_error_ipv6": "Il valore deve essere un indirizzo IPv6 valido",
|
||||
"edt_msg_error_maxItems": "Il valore deve contenere al massimo $1 elementi",
|
||||
"edt_msg_error_maxLength": "Il valore deve essere al massimo lungo $1 caratteri",
|
||||
"edt_msg_error_maxProperties": "L'oggetto deve avere al massimo $1 proprietà",
|
||||
@ -675,6 +799,8 @@
|
||||
"edt_msg_error_type": "Il valore deve essere di tipo $1",
|
||||
"edt_msg_error_type_union": "Il valore deve essere di uno dei tipi forniti",
|
||||
"edt_msg_error_uniqueItems": "Il vettore deve contenere elementi diversi",
|
||||
"edt_msgcust_error_hostname_ip": "Non è un nome host valido né IPv4 né IPv6",
|
||||
"edt_msgcust_error_hostname_ip4": "Non è un nome host valido né IPv4",
|
||||
"effectsconfigurator_button_conttest": "Test continuo",
|
||||
"effectsconfigurator_button_deleffect": "Cancella Effetto",
|
||||
"effectsconfigurator_button_editeffect": "Carica Effetto",
|
||||
@ -686,7 +812,7 @@
|
||||
"effectsconfigurator_label_effectname": "Nome Effetto",
|
||||
"effectsconfigurator_label_intro": "Crea nuovi effetti calibrati a tuo piacimento a partire dagli effetti base. A seconda dell'effetto sono disponibili opzioni come colore, velocità, direzione e altri.",
|
||||
"general_access_advanced": "Avanzate",
|
||||
"general_access_default": "Default",
|
||||
"general_access_default": "Predefinito",
|
||||
"general_access_expert": "Esperto",
|
||||
"general_btn_back": "Indietro",
|
||||
"general_btn_cancel": "Annulla",
|
||||
@ -699,6 +825,7 @@
|
||||
"general_btn_off": "Off",
|
||||
"general_btn_ok": "OK",
|
||||
"general_btn_on": "On",
|
||||
"general_btn_overwrite": "Sovrascrivi",
|
||||
"general_btn_rename": "Rinomina",
|
||||
"general_btn_restarthyperion": "Riavvia Hyperion",
|
||||
"general_btn_save": "Salva",
|
||||
@ -718,7 +845,7 @@
|
||||
"general_comp_FORWARDER": "Forwarder",
|
||||
"general_comp_GRABBER": "Cattura di Sistema",
|
||||
"general_comp_LEDDEVICE": "Dispositivo LED",
|
||||
"general_comp_PROTOSERVER": "Server Protocol Buffers",
|
||||
"general_comp_PROTOSERVER": "Buffer del protocollo del server",
|
||||
"general_comp_SMOOTHING": "Sfumatura",
|
||||
"general_comp_V4L": "Cattura USB",
|
||||
"general_country_cn": "Cina",
|
||||
@ -730,14 +857,23 @@
|
||||
"general_country_ru": "Russia",
|
||||
"general_country_uk": "Regno Unito",
|
||||
"general_country_us": "Stati Uniti",
|
||||
"general_disabled": "disabilitato",
|
||||
"general_enabled": "abilitato",
|
||||
"general_speech_ca": "Catalano",
|
||||
"general_speech_cs": "Czech",
|
||||
"general_speech_da": "Danimarca",
|
||||
"general_speech_de": "Tedesco",
|
||||
"general_speech_el": "Greco",
|
||||
"general_speech_en": "Inglese",
|
||||
"general_speech_es": "Spagnolo",
|
||||
"general_speech_fr": "Francese",
|
||||
"general_speech_hu": "Ungheria",
|
||||
"general_speech_it": "Italiano",
|
||||
"general_speech_ja": "Giapponese",
|
||||
"general_speech_nb": "Norvegese (Bokmål)",
|
||||
"general_speech_nl": "Olandese",
|
||||
"general_speech_pl": "Polacco",
|
||||
"general_speech_pt": "Portoghese",
|
||||
"general_speech_ro": "Rumeno",
|
||||
"general_speech_ru": "Russo",
|
||||
"general_speech_sv": "Svedese",
|
||||
@ -757,6 +893,10 @@
|
||||
"infoDialog_import_confirm_title": "Conferma import",
|
||||
"infoDialog_import_hyperror_text": "Il file di configurazione selezionato \"$1\" non può essere importato. Non è compatibile con Hyperion 2.0 e superiori!",
|
||||
"infoDialog_import_jsonerror_text": "Il file di configurazione selezionato \"$1\" non è un file .json o è corrotto. Messaggio di errore: ($2)",
|
||||
"infoDialog_password_current_text": "Password attuale",
|
||||
"infoDialog_password_minimum_length": "Le password devono contenere almeno 8 caratteri.",
|
||||
"infoDialog_password_new_text": "Nuova Password",
|
||||
"infoDialog_username_text": "Nome utente",
|
||||
"infoDialog_wizrgb_text": "L'ordine dei Byte RGB è già impostato correttamente.",
|
||||
"infoDialog_writeconf_error_text": "Salvataggio della configurazione fallito.",
|
||||
"infoDialog_writeimage_error_text": "Il file selezionato \"$1\" non è un immagine o è corrotto! Seleziona un altro file.",
|
||||
@ -776,6 +916,7 @@
|
||||
"main_ledsim_btn_togglelednumber": "Numeri LED",
|
||||
"main_ledsim_btn_toggleleds": "Mostra LEDs",
|
||||
"main_ledsim_btn_togglelivevideo": "Video live",
|
||||
"main_ledsim_btn_togglesigdetect": "Area di rilevamento del segnale",
|
||||
"main_ledsim_text": "Visualizzazione live dei colori dei led e, opzionalmente, lo stream video dal dispositivo di cattura.",
|
||||
"main_ledsim_title": "Visualizzazione LED",
|
||||
"main_menu_about_token": "Info su Hyperion",
|
||||
@ -787,6 +928,7 @@
|
||||
"main_menu_general_conf_token": "Generale",
|
||||
"main_menu_grabber_conf_token": "Hardware di cattura",
|
||||
"main_menu_input_selection_token": "Selezione Input",
|
||||
"main_menu_instcapture_conf_token": "Fonti",
|
||||
"main_menu_leds_conf_token": "Hardware LED",
|
||||
"main_menu_logging_token": "Log",
|
||||
"main_menu_network_conf_token": "Servizi di Rete",
|
||||
@ -886,6 +1028,7 @@
|
||||
"wiz_cc_testintrok": "Premi il bottone qui sotto per iniziare un test video.",
|
||||
"wiz_cc_testintrowok": "Guarda il link di seguito per il download dei video di test",
|
||||
"wiz_cc_title": "Assistente calibrazione colore",
|
||||
"wiz_cc_try_connect": "Connessione...",
|
||||
"wiz_cololight_desc2": "Ora scegli quali Cololights dovrebbero essere aggiunti. Per identificare le singole luci, premere il pulsante a destra.",
|
||||
"wiz_cololight_intro1": "Questa procedura guidata configura Hyperion per il sistema Cololight. Le caratteristiche sono il rilevamento automatico di Cololight e la regolazione automatica delle impostazioni di Hyperion! In breve: bastano pochi clic e il gioco è fatto!<br />Nota: in caso di una Strip Cololight, potrebbe essere necessario correggere manualmente il numero e il layout dei LED",
|
||||
"wiz_cololight_noprops": "Impossibile ottenere le proprietà del dispositivo: definire manualmente il numero dei LED",
|
||||
@ -921,6 +1064,7 @@
|
||||
"wiz_hue_username": "ID Utente",
|
||||
"wiz_identify": "Identifica",
|
||||
"wiz_identify_light": "Identifica $1",
|
||||
"wiz_identify_tip": "Identifica il dispositivo configurato accendendolo",
|
||||
"wiz_ids_disabled": "Disattivato",
|
||||
"wiz_ids_entire": "Immagine intera",
|
||||
"wiz_noLights": "Nessun $1 trovato! Per favore connetti le luci alla rete o configurale manualmente",
|
||||
|
@ -10,6 +10,9 @@
|
||||
"InfoDialog_nowrite_foottext": "Interfejs WebUI zostanie odblokowany automatycznie po rozwiązaniu problemu!",
|
||||
"InfoDialog_nowrite_text": "Hyperion nie może zapisać do aktualnie załadowanego pliku konfiguracyjnego. zmień uprawnienia zapisu plików, aby kontynuować.",
|
||||
"InfoDialog_nowrite_title": "Nie masz uprawnień do zapisu!",
|
||||
"InfoDialog_systemRestart_title": "Restart",
|
||||
"InfoDialog_systemResume_title": "Wznów",
|
||||
"InfoDialog_systemSuspend_title": "Zawieś",
|
||||
"about_3rd_party_licenses": "Licencje stron trzecich",
|
||||
"about_3rd_party_licenses_error": "Mieliśmy problem z gromadzeniem informacji o licencjach stron trzecich z sieci. <br /> Kliknij ten link do zasobu GitHub.",
|
||||
"about_build": "Build:",
|
||||
@ -254,6 +257,8 @@
|
||||
"edt_conf_color_blue_title": "Niebieski",
|
||||
"edt_conf_color_brightnessComp_expl": "Kompensuje różnice jasności między czerwonym, zielonym, niebieskim, cyjanowym, purpurowym, żółtym i białym. 100 oznacza pełną kompensację, 0 oznacza brak kompensacji",
|
||||
"edt_conf_color_brightnessComp_title": "Kompensacja jasności",
|
||||
"edt_conf_color_brightnessGain_expl": "Reguluje jasność kolorów. 1.0 oznacza brak zmian, powyżej 1.0 zwiększa jasność, poniżej 1.0 zmniejsza jasność.",
|
||||
"edt_conf_color_brightnessGain_title": "Wzmocnienie jasności",
|
||||
"edt_conf_color_brightness_expl": "Ustaw jasność LEDów",
|
||||
"edt_conf_color_brightness_title": "Jasność",
|
||||
"edt_conf_color_channelAdjustment_header_expl": "Utwórz profile kolorów, które można przypisać do określonego profilu. Dostosuj kolor, gamma, jasność, kompensację i więcej",
|
||||
@ -280,6 +285,8 @@
|
||||
"edt_conf_color_magenta_title": "Magenta",
|
||||
"edt_conf_color_red_expl": "Skalibrowana wartość koloru czerwonego",
|
||||
"edt_conf_color_red_title": "Czerwony",
|
||||
"edt_conf_color_saturationGain_expl": "Reguluje nasycenie kolorów. 1.0 oznacza brak zmian, powyżej 1.0 zwiększa nasycenie, poniżej 1.0 zmniejsza nasycenie.",
|
||||
"edt_conf_color_saturationGain_title": "Wzmocnienie nasycenia",
|
||||
"edt_conf_color_white_expl": "Skalibrowana wartość koloru białego.",
|
||||
"edt_conf_color_white_title": "Biały",
|
||||
"edt_conf_color_yellow_expl": "Skalibrowana wartość koloru żółtego",
|
||||
@ -393,7 +400,7 @@
|
||||
"edt_conf_general_priority_title": "Kanał priorytetowy",
|
||||
"edt_conf_grabber_discovered_expl": "Wybierz wykryte urządzenie do przechwytywania",
|
||||
"edt_conf_grabber_discovered_none": "Nie wykryto urządzenia do przechwytywania",
|
||||
"edt_conf_grabber_discovered_title": "Wykryto urządzenie",
|
||||
"edt_conf_grabber_discovered_title": "Wykryte urządzenia",
|
||||
"edt_conf_grabber_discovered_title_info": "Wybierz wykryte urządzenie do przechwytywania",
|
||||
"edt_conf_grabber_discovery_inprogress": "Wykrywanie w toku",
|
||||
"edt_conf_instC_screen_grabber_device_expl": "Używane urządzenie do przechwytywania ekranu",
|
||||
@ -435,8 +442,6 @@
|
||||
"edt_conf_smooth_heading_title": "Wygładzanie",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Szybkość obliczania wygładzanych ramek pośrednich.",
|
||||
"edt_conf_smooth_interpolationRate_title": "Współczynnik interpolacji",
|
||||
"edt_conf_smooth_outputRate_expl": "Prędkość wyjściowa do twojego kontrolera led.",
|
||||
"edt_conf_smooth_outputRate_title": "Szybkość wyjściowa",
|
||||
"edt_conf_smooth_time_ms_expl": "Jak długo wygładzanie powinno zbierać obrazy?",
|
||||
"edt_conf_smooth_time_ms_title": "Czas",
|
||||
"edt_conf_smooth_type_expl": "Rodzaj wygładzania.",
|
||||
@ -539,9 +544,13 @@
|
||||
"edt_dev_spec_FCsetConfig_title": "Ustaw konfigurację zanikania",
|
||||
"edt_dev_spec_LBap102Mode_title": "Tryb \"LightBerry APA102\"",
|
||||
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
|
||||
"edt_dev_spec_ada_mode_title": "Adalight - Standard",
|
||||
"edt_dev_spec_awa_mode_title": "HyperSerial - High speed",
|
||||
"edt_dev_spec_baudrate_title": "Prędkość (Baudrate)",
|
||||
"edt_dev_spec_blackLightsTimeout_title": "Limit czasu wykrywania sygnału na czarno",
|
||||
"edt_dev_spec_brightnessFactor_title": "Współczynnik jasności",
|
||||
"edt_dev_spec_brightnessMax_title": "Maksymalna jasność",
|
||||
"edt_dev_spec_brightnessMin_title": "Minimalna jasność",
|
||||
"edt_dev_spec_brightnessOverwrite_title": "Nadpisz jasność",
|
||||
"edt_dev_spec_brightnessThreshold_title": "Minimalna jasność wykrywania sygnału",
|
||||
"edt_dev_spec_brightness_title": "Jasność",
|
||||
@ -601,6 +610,11 @@
|
||||
"edt_dev_spec_razer_device_title": "Urządzenie Razer Chroma",
|
||||
"edt_dev_spec_restoreOriginalState_title": "Przywróć oryginalny stan świateł, gdy są wyłączone",
|
||||
"edt_dev_spec_restoreOriginalState_title_info": "Przywróć pierwotny stan urządzenia, gdy jest ono wyłączone",
|
||||
"edt_dev_spec_rgbw_calibration_blue": "Wygląd kanału niebieskiego/białego",
|
||||
"edt_dev_spec_rgbw_calibration_enable": "Kalibracja kanału bieli (tylko RGBW)",
|
||||
"edt_dev_spec_rgbw_calibration_green": "Wygląd kanału zielonego/białego",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Limit kanału białego",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Wygląd kanału czerwonego/białego",
|
||||
"edt_dev_spec_serial_title": "Numer seryjny",
|
||||
"edt_dev_spec_spipath_title": "SPI path",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Maksymalny limit czasu uzgadniania streamera",
|
||||
@ -848,9 +862,11 @@
|
||||
"general_country_us": "Stany Zjednoczone (United States)",
|
||||
"general_disabled": "wyłączony",
|
||||
"general_enabled": "włączony",
|
||||
"general_speech_ca": "Kataloński",
|
||||
"general_speech_cs": "Czeski (Czech)",
|
||||
"general_speech_da": "Duński",
|
||||
"general_speech_de": "Niemiecki (German)",
|
||||
"general_speech_el": "Grecki",
|
||||
"general_speech_en": "Angielski (English)",
|
||||
"general_speech_es": "Hiszpański (Spanish)",
|
||||
"general_speech_fr": "Francuski (French)",
|
||||
|
@ -10,6 +10,9 @@
|
||||
"InfoDialog_nowrite_foottext": "Веб-интерфейс будет разблокирован автоматически после того, как вы решите проблему!",
|
||||
"InfoDialog_nowrite_text": "Hyperion не может выполнять запись в ваш текущий загруженный файл конфигурации. Чтобы продолжить, восстановите права доступа к файлу.",
|
||||
"InfoDialog_nowrite_title": "Ошибка разрешения записи!",
|
||||
"InfoDialog_systemRestart_title": "Перезапустить",
|
||||
"InfoDialog_systemResume_title": "Продолжить",
|
||||
"InfoDialog_systemSuspend_title": "Отключить",
|
||||
"about_3rd_party_licenses": "Сторонние лицензии",
|
||||
"about_3rd_party_licenses_error": "У нас возникли проблемы со сбором информации о сторонних лицензиях из Интернета. <br /> Перейдите по этой ссылке на GitHub.",
|
||||
"about_build": "Сборка",
|
||||
@ -51,6 +54,8 @@
|
||||
"conf_leds_contr_label_contrtype": "Тип контроллера:",
|
||||
"conf_leds_device_info_log": "Если ваши светодиоды не работают, проверьте здесь наличие ошибок:",
|
||||
"conf_leds_device_intro": "Hyperion поддерживает множество контроллеров для передачи данных на целевое устройство. Выберите светодиодный контроллер из списка и настройте его. Мы выбрали лучшие настройки по умолчанию для каждого устройства.",
|
||||
"conf_leds_error_get_properties_text": "Ошибка при работе с устройством. Проверьте настройки.",
|
||||
"conf_leds_error_get_properties_title": "Настройки устройства",
|
||||
"conf_leds_error_hwled_gt_layout": "Количество светодиодов оборудования ($1) больше, чем количество светодиодов, настроенных с помощью макета ($2), <br> $3 {{plural:$3|Светодиод|Светодиоды}} останутся черными, если вы продолжите.",
|
||||
"conf_leds_error_hwled_gt_maxled": "Количество светодиодов оборудования ($1) превышает максимальное количество светодиодов, поддерживаемое устройством ($2). <br> Счетчик аппаратных светодиодов установлен на ($3).",
|
||||
"conf_leds_error_hwled_lt_layout": "Количество светодиодных индикаторов оборудования ($1) меньше, чем количество светодиодов, настроенных с помощью макета ($2). <br> Количество светодиодов, настроенных в макете, не должно превышать количество доступных светодиодов",
|
||||
@ -62,6 +67,7 @@
|
||||
"conf_leds_layout_blacklist_start_title": "Начальный светодиод",
|
||||
"conf_leds_layout_blacklistleds_title": "Светодиоды из черного списка",
|
||||
"conf_leds_layout_btn_checklist": "Показать сверку",
|
||||
"conf_leds_layout_btn_keystone": "Коррекция трапеции",
|
||||
"conf_leds_layout_button_savelay": "Сохранить раскладку",
|
||||
"conf_leds_layout_button_updsim": "Просмотр обновления",
|
||||
"conf_leds_layout_checkp1": "Черный светодиод — это ваш первый светодиод, первый светодиод — это точка, в которую вы вводите сигнал данных.",
|
||||
@ -81,6 +87,16 @@
|
||||
"conf_leds_layout_cl_leftbottom": "Левый 50% - 100% снизу",
|
||||
"conf_leds_layout_cl_leftmiddle": "Левый 25% - 75% посередине",
|
||||
"conf_leds_layout_cl_lefttop": "Слева 0% - 50% сверху",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft11": "Низ: 75 - 100% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft112": "Низ: 0 - 50% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft12": "Низ: 25 - 50% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft121": "Низ: 50 - 100% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft14": "Низ: 0 - 25% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeft34": "Низ: 50 - 75% слева",
|
||||
"conf_leds_layout_cl_lightPosBottomLeftNewMid": "Низ: 25 - 75% слева",
|
||||
"conf_leds_layout_cl_lightPosTopLeft112": "Верх: 0 - 50% слева",
|
||||
"conf_leds_layout_cl_lightPosTopLeft121": "Верх: 50 - 100% слева",
|
||||
"conf_leds_layout_cl_lightPosTopLeftNewMid": "Верх: 25 - 75% слева",
|
||||
"conf_leds_layout_cl_overlap": "Нахлёст",
|
||||
"conf_leds_layout_cl_reversdir": "Обратное направление",
|
||||
"conf_leds_layout_cl_right": "Справа",
|
||||
@ -95,6 +111,7 @@
|
||||
"conf_leds_layout_generatedconf": "Сгенерированная/Текущая Конфигурация LED",
|
||||
"conf_leds_layout_intro": "Вам также нужна LED-раскладка, которая отражает положение ваших светодиодов. Классическая раскладка обычно представляет ТВ-рамку, но поддерживается и LED-матрица (LED-стена). Вид на этой раскладке ВСЕГДА СПЕРЕДИ вашего ТВ.",
|
||||
"conf_leds_layout_ma_cabling": "Подключение",
|
||||
"conf_leds_layout_ma_direction": "Направление",
|
||||
"conf_leds_layout_ma_horiz": "Горизонтально",
|
||||
"conf_leds_layout_ma_optbottomleft": "Низ слева",
|
||||
"conf_leds_layout_ma_optbottomright": "Низ справа",
|
||||
@ -181,6 +198,7 @@
|
||||
"dashboard_infobox_label_instance": "Пример:",
|
||||
"dashboard_infobox_label_latesthyp": "Последняя версия Hyperion:",
|
||||
"dashboard_infobox_label_platform": "Платформа:",
|
||||
"dashboard_infobox_label_port_boblight": "Boblight сервер:",
|
||||
"dashboard_infobox_label_port_flat": "Плоский буфер:",
|
||||
"dashboard_infobox_label_port_json": "JSON-сервер",
|
||||
"dashboard_infobox_label_port_proto": "Протобуфер:",
|
||||
@ -213,6 +231,7 @@
|
||||
"edt_append_percent_v": "% верт.",
|
||||
"edt_append_pixel": "Пиксель",
|
||||
"edt_append_s": "сек",
|
||||
"edt_append_sdegree": "с/градус",
|
||||
"edt_conf_bb_blurRemoveCnt_expl": "Количество пикселей, которые удаляются с обнаруженной границы, чтобы убрать размытие.",
|
||||
"edt_conf_bb_blurRemoveCnt_title": "Размытие пикселя",
|
||||
"edt_conf_bb_borderFrameCnt_expl": "Количество кадров до установки согласованной обнаруженной границы.",
|
||||
@ -238,6 +257,8 @@
|
||||
"edt_conf_color_blue_title": "Синий",
|
||||
"edt_conf_color_brightnessComp_expl": "Компенсирует разницу в яркости между красным, зеленым, синим, голубым, пурпурным, жёлтым и белым. 100 означает полную компенсацию, 0 без компенсации",
|
||||
"edt_conf_color_brightnessComp_title": "Компенсация яркости",
|
||||
"edt_conf_color_brightnessGain_expl": "Настройка яркости. 1.0 - без коррекции, больше 1.0 - повышает, а меньше 1.0 уменьшает яркость.",
|
||||
"edt_conf_color_brightnessGain_title": "Яркость",
|
||||
"edt_conf_color_brightness_expl": "установить общую яркость светодиодов",
|
||||
"edt_conf_color_brightness_title": "Яркость",
|
||||
"edt_conf_color_channelAdjustment_header_expl": "Создавайте цветовые профили, которые можно назначить конкретному компоненту. Отрегулируйте цвет, гамму, яркость, компенсацию и многое другое.",
|
||||
@ -264,6 +285,8 @@
|
||||
"edt_conf_color_magenta_title": "Пурпурный",
|
||||
"edt_conf_color_red_expl": "Откалиброванное значение красного.",
|
||||
"edt_conf_color_red_title": "Красный",
|
||||
"edt_conf_color_saturationGain_expl": "Настройка насыщенности цветов. 1.0 - без коррекции, больше 1.0 - повышает, а меньше 1.0 уменьшает насыщенность.",
|
||||
"edt_conf_color_saturationGain_title": "Насыщенность",
|
||||
"edt_conf_color_white_expl": "Калиброванное значение белого.",
|
||||
"edt_conf_color_white_title": "Белый",
|
||||
"edt_conf_color_yellow_expl": "Откалиброванное значение жёлтого цвета.",
|
||||
@ -319,6 +342,8 @@
|
||||
"edt_conf_enum_top_down": "Сверху вниз",
|
||||
"edt_conf_enum_transeffect_smooth": "Сглаживание",
|
||||
"edt_conf_enum_transeffect_sudden": "Внезапный",
|
||||
"edt_conf_enum_udp_ddp": "DDP",
|
||||
"edt_conf_enum_udp_raw": "RAW",
|
||||
"edt_conf_enum_unicolor_mean": "Одноцветный",
|
||||
"edt_conf_fbs_heading_title": "Сервер Flatbuffers",
|
||||
"edt_conf_fbs_timeout_expl": "Если данные за указанный период не поступают, компонент будет (мягко) отключён.",
|
||||
@ -347,11 +372,18 @@
|
||||
"edt_conf_fge_type_title": "Тип",
|
||||
"edt_conf_fw_flat_expl": "Одна цель плоского буфера на строку. Содержит IP: ПОРТ (Пример: 127.0.0.1:19401)",
|
||||
"edt_conf_fw_flat_itemtitle": "цель плоского буфера",
|
||||
"edt_conf_fw_flat_services_discovered_expl": "Обнаруженные Hyperion сервера с flatbuffer сервисами",
|
||||
"edt_conf_fw_flat_services_discovered_title": "Найденные Flatbuffer цели",
|
||||
"edt_conf_fw_flat_title": "Список целей плоского буфера",
|
||||
"edt_conf_fw_heading_title": "Экспедитор",
|
||||
"edt_conf_fw_json_expl": "Одна json цель на строку. Содержит IP:PORT (Пример: 127.0.0.1:19446)",
|
||||
"edt_conf_fw_json_itemtitle": "Цель Json",
|
||||
"edt_conf_fw_json_itemtitle": "Цель JSON",
|
||||
"edt_conf_fw_json_services_discovered_expl": "Обнаруженные Hyperion сервера с JSON-API сервисами",
|
||||
"edt_conf_fw_json_services_discovered_title": "Найденные JSON цели",
|
||||
"edt_conf_fw_json_title": "Список целей json",
|
||||
"edt_conf_fw_remote_service_discovered_none": "Не найдено никаких сервисов",
|
||||
"edt_conf_fw_service_name_expl": "Название провайдера",
|
||||
"edt_conf_fw_service_name_title": "Название сервиса",
|
||||
"edt_conf_gen_configVersion_title": "Версия конфигурации",
|
||||
"edt_conf_gen_heading_title": "Общие настройки",
|
||||
"edt_conf_gen_name_expl": "Пользовательское имя, которое используется для обнаружения Hyperion. (Полезно с более чем одним экземпляром Hyperion)",
|
||||
@ -410,8 +442,6 @@
|
||||
"edt_conf_smooth_heading_title": "Сглаживание",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Скорость расчета плавных промежуточных кадров.",
|
||||
"edt_conf_smooth_interpolationRate_title": "Скорость интерполяции",
|
||||
"edt_conf_smooth_outputRate_expl": "Скорость вывода на ваш светодиодный контроллер.",
|
||||
"edt_conf_smooth_outputRate_title": "Выходная скорость",
|
||||
"edt_conf_smooth_time_ms_expl": "Как долго сглаживание должно собирать картинки?",
|
||||
"edt_conf_smooth_time_ms_title": "Время",
|
||||
"edt_conf_smooth_type_expl": "Тип сглаживания.",
|
||||
@ -497,8 +527,13 @@
|
||||
"edt_dev_enum_subtract_minimum": "Уменьшить минимум",
|
||||
"edt_dev_enum_white_off": "Выключить белый ",
|
||||
"edt_dev_general_autostart_title": "Автозапуск",
|
||||
"edt_dev_general_autostart_title_info": "Включать LED устройство при загрузке или нет",
|
||||
"edt_dev_general_colorOrder_title": "Порядок байтов RGB",
|
||||
"edt_dev_general_colorOrder_title_info": "Порядок цвета устройства",
|
||||
"edt_dev_general_enableAttemptsInterval_title": "Задержка",
|
||||
"edt_dev_general_enableAttemptsInterval_title_info": "Задержка между попытками подключения",
|
||||
"edt_dev_general_enableAttempts_title": "Количество попыток",
|
||||
"edt_dev_general_enableAttempts_title_info": "Количество попыток подключения к устройству до аварийного состояния.",
|
||||
"edt_dev_general_hardwareLedCount_title": "Количество светодиодных индикаторов оборудования",
|
||||
"edt_dev_general_hardwareLedCount_title_info": "Количество физических светодиодов, доступных для данного устройства",
|
||||
"edt_dev_general_heading_title": "Общие настройки",
|
||||
@ -509,12 +544,17 @@
|
||||
"edt_dev_spec_FCsetConfig_title": "Установить конфигурацию fadecandy",
|
||||
"edt_dev_spec_LBap102Mode_title": "Режим LightBerry APA102",
|
||||
"edt_dev_spec_PBFiFo_title": "Pi-Blaster FiFo",
|
||||
"edt_dev_spec_ada_mode_title": "Adalight - Стандартно",
|
||||
"edt_dev_spec_awa_mode_title": "HyperSerial - Высокая скорость",
|
||||
"edt_dev_spec_baudrate_title": "Скорость",
|
||||
"edt_dev_spec_blackLightsTimeout_title": "Тайм-аут обнаружения сигнала на черном",
|
||||
"edt_dev_spec_brightnessFactor_title": "Фактор яркости",
|
||||
"edt_dev_spec_brightnessMax_title": "Максимальная яркость",
|
||||
"edt_dev_spec_brightnessMin_title": "Минимальная яркость",
|
||||
"edt_dev_spec_brightnessOverwrite_title": "Перезаписать яркость",
|
||||
"edt_dev_spec_brightnessThreshold_title": "Минимальная яркость обнаружения сигнала",
|
||||
"edt_dev_spec_brightness_title": "Яркость",
|
||||
"edt_dev_spec_candyGamma_title": "'Candy' режим (двойная гамма коррекция)",
|
||||
"edt_dev_spec_chanperfixture_title": "Каналов на прибор",
|
||||
"edt_dev_spec_cid_title": "CID",
|
||||
"edt_dev_spec_clientKey_title": "Клиентский ключ",
|
||||
@ -530,6 +570,7 @@
|
||||
"edt_dev_spec_dmaNumber_title": "Канал DMA",
|
||||
"edt_dev_spec_gamma_title": "Гамма",
|
||||
"edt_dev_spec_globalBrightnessControlMaxLevel_title": "Максимальный текущий уровень",
|
||||
"edt_dev_spec_globalBrightnessControlThreshold_title": "Адаптивный контроль тока",
|
||||
"edt_dev_spec_gpioBcm_title": "Вывод GPIO",
|
||||
"edt_dev_spec_gpioMap_title": "Отображение GPIO",
|
||||
"edt_dev_spec_gpioNumber_title": "Номер GPIO",
|
||||
@ -553,6 +594,8 @@
|
||||
"edt_dev_spec_networkDeviceName_title": "Сетевое имя устройства",
|
||||
"edt_dev_spec_networkDevicePort_title": "Порт",
|
||||
"edt_dev_spec_numberOfLeds_title": "Количество светодиодов",
|
||||
"edt_dev_spec_onBlackTimeToPowerOff": "Время до отключения подсветки если сработала проверка уровня черного",
|
||||
"edt_dev_spec_onBlackTimeToPowerOn": "Время до включения подсветки после восстановления сигнала",
|
||||
"edt_dev_spec_orbIds_title": "ID сфер",
|
||||
"edt_dev_spec_order_left_right_title": "2.",
|
||||
"edt_dev_spec_order_top_down_title": "1.",
|
||||
@ -560,19 +603,27 @@
|
||||
"edt_dev_spec_panel_start_position": "Стартовая панель [0-макс панели]",
|
||||
"edt_dev_spec_panelorganisation_title": "Последовательность нумерации панелей",
|
||||
"edt_dev_spec_pid_title": "PID",
|
||||
"edt_dev_spec_port_expl": "Сервисный порт [1-65535]",
|
||||
"edt_dev_spec_port_title": "Порт",
|
||||
"edt_dev_spec_printTimeStamp_title": "Добавить отметку времени",
|
||||
"edt_dev_spec_pwmChannel_title": "Канал ШИМ (PWM)",
|
||||
"edt_dev_spec_razer_device_title": "Устройство Razer Chroma",
|
||||
"edt_dev_spec_restoreOriginalState_title": "Восстановить состояние огней",
|
||||
"edt_dev_spec_restoreOriginalState_title_info": "Восстановить исходное состояние устройства, когда устройство отключено",
|
||||
"edt_dev_spec_rgbw_calibration_blue": "Соотношение Синего/Белого каналов",
|
||||
"edt_dev_spec_rgbw_calibration_enable": "Калибровка белого (только для RGBW)",
|
||||
"edt_dev_spec_rgbw_calibration_green": "Соотношение Зеленого/Белого каналов",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Ограничение белого",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Соотношение Красного/Белого каналов",
|
||||
"edt_dev_spec_serial_title": "Серийный номер",
|
||||
"edt_dev_spec_spipath_title": "Устройство SPI",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Максимальное время ожидания подтверждения стримером",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Минимальное время ожидания подтверждения стримером",
|
||||
"edt_dev_spec_stream_protocol_title": "Протокол",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Выключить черный",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Отключение, ниже минимального",
|
||||
"edt_dev_spec_syncOverwrite_title": "Выключить синхронизацию",
|
||||
"edt_dev_spec_targetIpHost_expl": "Имя хоста (DNS/mDNS) или IP адрес (IPv4 or IPv6)",
|
||||
"edt_dev_spec_targetIpHost_title": "Целевое имя хоста/IP-адрес",
|
||||
"edt_dev_spec_targetIpHost_title_info": "Имя хоста или IP-адрес устройства",
|
||||
"edt_dev_spec_targetIp_title": "Целевой IP-адрес",
|
||||
@ -811,14 +862,17 @@
|
||||
"general_country_us": "Соединённые Штаты Америки",
|
||||
"general_disabled": "отключено",
|
||||
"general_enabled": "включено",
|
||||
"general_speech_ca": "Каталонский",
|
||||
"general_speech_cs": "Чешский",
|
||||
"general_speech_da": "Danish",
|
||||
"general_speech_da": "Датский",
|
||||
"general_speech_de": "Немецкий",
|
||||
"general_speech_el": "Греческий",
|
||||
"general_speech_en": "Английский",
|
||||
"general_speech_es": "Испанский",
|
||||
"general_speech_fr": "французский",
|
||||
"general_speech_hu": "Hungarian",
|
||||
"general_speech_hu": "Венгерский",
|
||||
"general_speech_it": "Итальянский",
|
||||
"general_speech_ja": "Японский",
|
||||
"general_speech_nb": "норвежский",
|
||||
"general_speech_nl": "Dutch",
|
||||
"general_speech_pl": "Polish",
|
||||
|
@ -10,6 +10,9 @@
|
||||
"InfoDialog_nowrite_foottext": "Webbkonfigurationen kommer att släppas igen automatiskt så snart problemet har åtgärdats!",
|
||||
"InfoDialog_nowrite_text": "Hyperion har inte skrivbehörighet till den för närvarande inlästa konfigurationen. Korrigera filbehörigheterna för att fortsätta.",
|
||||
"InfoDialog_nowrite_title": "Skrivåtkomstfel!",
|
||||
"InfoDialog_systemRestart_title": "Starta om",
|
||||
"InfoDialog_systemResume_title": "Återuppta",
|
||||
"InfoDialog_systemSuspend_title": "Stäng av",
|
||||
"about_3rd_party_licenses": "Tredjepartslicenser",
|
||||
"about_3rd_party_licenses_error": "Vi hade problem med att ladda tredjepartslicenserna från internet. <br />Klicka här för att komma åt filen på GitHub.",
|
||||
"about_build": "Bygge",
|
||||
@ -55,7 +58,9 @@
|
||||
"conf_leds_error_get_properties_title": "Enhetsegenskaper",
|
||||
"conf_leds_error_hwled_gt_layout": "Antalet givna hårdvarulysdioder ($1) är större än antalet definierade i LED-layouten ($2), $3 {{plural:$3|LEDs kommer|LEDs kommer att vara}} förblir svarta.",
|
||||
"conf_leds_error_hwled_gt_maxled": "Antalet LED-lampor för hårdvara ($1) är större än det maximala antalet lysdioder som stöds av enheten ($2). <br> Antalet LED-lampor för hårdvara är inställt på ($3).",
|
||||
"conf_leds_error_hwled_gt_maxled_protocol": "Antalet LED-lampor för hårdvara ($1) är större än det maximala antalet lysdioder som stöds av streamingprotokollet ($2). <br> Strömningsprotokollet kommer att ändras till ($3).",
|
||||
"conf_leds_error_hwled_lt_layout": "Antalet givna hårdvarulysdioder ($1) är mindre än antalet definierade i LED-layouten ($2). <br> I LED-layouten får inte fler lysdioder konfigureras än vad som är tillgängligt.",
|
||||
"conf_leds_error_wled_segment_missing": "Det för närvarande konfigurerade segmentet ($1) är inte konfigurerat på din WLED-enhet.<br>Du kan behöva kontrollera WLED-konfigurationen!<br>Konfigurationssidan representerar den aktuella WLED-inställningen.",
|
||||
"conf_leds_info_ws281x": "Hyperion måste köras med 'root'-privilegier för denna styrenhetstyp!",
|
||||
"conf_leds_layout_advanced": "Utökade alternativ",
|
||||
"conf_leds_layout_blacklist_num_title": "Antal lysdioder",
|
||||
@ -439,8 +444,6 @@
|
||||
"edt_conf_smooth_heading_title": "Utjämning",
|
||||
"edt_conf_smooth_interpolationRate_expl": "Frekvens i vilken mellanliggande utjämningssteg beräknas.",
|
||||
"edt_conf_smooth_interpolationRate_title": "Interpolationsfrekvens",
|
||||
"edt_conf_smooth_outputRate_expl": "Utgångsfrekvensen till LED-enheten",
|
||||
"edt_conf_smooth_outputRate_title": "Utfrekvens",
|
||||
"edt_conf_smooth_time_ms_expl": "Hur länge ska utjämningen samla bilder?",
|
||||
"edt_conf_smooth_time_ms_title": "Tid",
|
||||
"edt_conf_smooth_type_expl": "Utjämningsalgoritm.",
|
||||
@ -614,10 +617,17 @@
|
||||
"edt_dev_spec_rgbw_calibration_green": "Grön/Vit kanalförhållande",
|
||||
"edt_dev_spec_rgbw_calibration_limit": "Vit kanalgräns",
|
||||
"edt_dev_spec_rgbw_calibration_red": "Röd/Vit kanalförhållande",
|
||||
"edt_dev_spec_segmentId_title": "Segment-ID",
|
||||
"edt_dev_spec_segmentsOverlapValidation_error": "Korrigera WLED-inställningen! Segmentet får inte överlappa med {{plural:$1| segment|segments}}: \"$2\".",
|
||||
"edt_dev_spec_segmentsSwitchOffOthers_title": "Stäng av övriga segment",
|
||||
"edt_dev_spec_segments_disabled_title": "Segmentströmning inaktiverad vid WLED.",
|
||||
"edt_dev_spec_segments_title": "Streama till segment",
|
||||
"edt_dev_spec_serial_title": "Serienummer",
|
||||
"edt_dev_spec_spipath_title": "SPI Pfad",
|
||||
"edt_dev_spec_sslHSTimeoutMax_title": "Streamer handskakning maximal timeout",
|
||||
"edt_dev_spec_sslHSTimeoutMin_title": "Minsta timeout för Streamerhandslag",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title": "Förbli på efter streaming",
|
||||
"edt_dev_spec_stayOnAfterStreaming_title_info": "Enheten förblir på efter streaming eller återställning.",
|
||||
"edt_dev_spec_stream_protocol_title": "Strömningsprotokoll",
|
||||
"edt_dev_spec_switchOffOnBlack_title": "Av på svart",
|
||||
"edt_dev_spec_switchOffOnbelowMinBrightness_title": "Av vid lägsta",
|
||||
@ -861,9 +871,11 @@
|
||||
"general_country_us": "USA",
|
||||
"general_disabled": "Inaktiverad",
|
||||
"general_enabled": "Aktiverad",
|
||||
"general_speech_ca": "Katalanska",
|
||||
"general_speech_cs": "Tjeckiska",
|
||||
"general_speech_da": "Danska",
|
||||
"general_speech_de": "Tyska",
|
||||
"general_speech_el": "Grekiska",
|
||||
"general_speech_en": "Engelska",
|
||||
"general_speech_es": "Spanska",
|
||||
"general_speech_fr": "Franska",
|
||||
|
@ -24,8 +24,10 @@
|
||||
<script src="js/lib/jquery/jquery-migrate-3.3.2.min.js"></script>
|
||||
|
||||
<!-- jQuery - Dev -->
|
||||
<!script src="js/lib/jquery/dev/jquery-3.6.0.js"></script>
|
||||
<!script src="js/lib/jquery/dev/jquery-migrate-3.3.2.js"></script>
|
||||
<!--
|
||||
<script src="js/lib/jquery/dev/jquery-3.6.0.js"></script>
|
||||
<script src="js/lib/jquery/dev/jquery-migrate-3.3.2.js"></script>
|
||||
-->
|
||||
|
||||
<!-- SemVer -->
|
||||
<script src='js/lib/semver.js'></script>
|
||||
|
@ -58,7 +58,8 @@ $(document).ready(function () {
|
||||
if (components[idx].name != "ALL") {
|
||||
if ((components[idx].name === "FORWARDER" && window.currentHyperionInstance != 0) ||
|
||||
(components[idx].name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
|
||||
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable))
|
||||
(components[idx].name === "V4L" && !window.serverConfig.grabberV4L2.enable) ||
|
||||
(components[idx].name === "AUDIO" && !window.serverConfig.grabberAudio.enable))
|
||||
continue;
|
||||
|
||||
var comp_enabled = components[idx].enabled ? "checked" : "";
|
||||
@ -104,8 +105,9 @@ $(document).ready(function () {
|
||||
|
||||
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
|
||||
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
|
||||
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
|
||||
|
||||
if (screenGrabberAvailable || videoGrabberAvailable) {
|
||||
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
|
||||
|
||||
if (screenGrabberAvailable) {
|
||||
var screenGrabber = window.serverConfig.framegrabber.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
|
||||
@ -120,6 +122,13 @@ $(document).ready(function () {
|
||||
} else {
|
||||
$("#dash_video_grabber_row").hide();
|
||||
}
|
||||
|
||||
if (audioGrabberAvailable) {
|
||||
const audioGrabber = window.serverConfig.grabberAudio.enable ? $.i18n('general_enabled') : $.i18n('general_disabled');
|
||||
$('#dash_audio_grabber').html(audioGrabber);
|
||||
} else {
|
||||
$("#dash_audio_grabber_row").hide();
|
||||
}
|
||||
} else {
|
||||
$("#dash_capture_hw").hide();
|
||||
}
|
||||
|
@ -4,9 +4,11 @@ $(document).ready(function () {
|
||||
|
||||
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
|
||||
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
|
||||
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
|
||||
var CEC_ENABLED = (jQuery.inArray("cec", window.serverInfo.services) !== -1);
|
||||
|
||||
var conf_editor_video = null;
|
||||
var conf_editor_audio = null;
|
||||
var conf_editor_screen = null;
|
||||
|
||||
var configuredDevice = "";
|
||||
@ -38,6 +40,22 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
// Audio-Grabber
|
||||
if (audioGrabberAvailable) {
|
||||
$('#conf_cont').append(createRow('conf_cont_audio'));
|
||||
$('#conf_cont_audio').append(createOptPanel('fa-volume', $.i18n("edt_conf_audio_heading_title"), 'editor_container_audiograbber', 'btn_submit_audiograbber', 'panel-system', 'audiograbberPanelId'));
|
||||
|
||||
if (storedAccess === 'expert') {
|
||||
const conf_cont_audio_footer = document.getElementById("editor_container_audiograbber").nextElementSibling;
|
||||
$(conf_cont_audio_footer).prepend('<button class="btn btn-primary mdi-24px" id="btn_audiograbber_set_effect_defaults" disabled data-toggle="tooltip" data-placement="top" title="' + $.i18n("edt_conf_audio_hardware_set_defaults_tip") + '">'
|
||||
+ '<i class= "fa fa-fw fa-undo" ></i >' + $.i18n("edt_conf_audio_effect_set_defaults") + '</button > ');
|
||||
}
|
||||
|
||||
if (window.showOptHelp) {
|
||||
$('#conf_cont_audio').append(createHelpTable(window.schema.grabberAudio.properties, $.i18n("edt_conf_audio_heading_title"), "audiograbberHelpPanelId"));
|
||||
}
|
||||
}
|
||||
|
||||
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
|
||||
var errors = [];
|
||||
|
||||
@ -694,6 +712,121 @@ $(document).ready(function () {
|
||||
});
|
||||
}
|
||||
|
||||
// External Input Sources (Audio-Grabbers)
|
||||
if (audioGrabberAvailable) {
|
||||
|
||||
conf_editor_audio = createJsonEditor('editor_container_audiograbber', {
|
||||
grabberAudio: window.schema.grabberAudio
|
||||
}, true, true);
|
||||
|
||||
conf_editor_audio.on('ready', () => {
|
||||
// Trigger conf_editor_audio.watch - 'root.grabberAudio.enable'
|
||||
const audioEnable = window.serverConfig.grabberAudio.enable;
|
||||
conf_editor_audio.getEditor("root.grabberAudio.enable").setValue(audioEnable);
|
||||
});
|
||||
|
||||
conf_editor_audio.on('change', () => {
|
||||
|
||||
// Validate the current editor's content
|
||||
if (!conf_editor_audio.validate().length) {
|
||||
const deviceSelected = conf_editor_audio.getEditor("root.grabberAudio.available_devices").getValue();
|
||||
switch (deviceSelected) {
|
||||
case "SELECT":
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
|
||||
break;
|
||||
case "NONE":
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
|
||||
break;
|
||||
default:
|
||||
window.readOnlyMode ? $('#btn_submit_audiograbber').prop('disabled', true) : $('#btn_submit_audiograbber').prop('disabled', false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$('#btn_submit_audiograbber').prop('disabled', true);
|
||||
}
|
||||
});
|
||||
|
||||
// Enable
|
||||
conf_editor_audio.watch('root.grabberAudio.enable', () => {
|
||||
|
||||
const audioEnable = conf_editor_audio.getEditor("root.grabberAudio.enable").getValue();
|
||||
if (audioEnable)
|
||||
{
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", "enable", true);
|
||||
|
||||
$('#btn_audiograbber_set_effect_defaults').show();
|
||||
|
||||
if (window.showOptHelp) {
|
||||
$('#audiograbberHelpPanelId').show();
|
||||
}
|
||||
|
||||
discoverInputSources("audio");
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#btn_submit_audiograbber').prop('disabled', false);
|
||||
$('#btn_audiograbber_set_effect_defaults').hide();
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", "enable", false);
|
||||
$('#audiograbberHelpPanelId').hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Available Devices
|
||||
conf_editor_audio.watch('root.grabberAudio.available_devices', () => {
|
||||
const deviceSelected = conf_editor_audio.getEditor("root.grabberAudio.available_devices").getValue();
|
||||
|
||||
if (deviceSelected === "SELECT" || deviceSelected === "NONE" || deviceSelected === "") {
|
||||
$('#btn_submit_audiograbber').prop('disabled', true);
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], false);
|
||||
}
|
||||
else
|
||||
{
|
||||
showInputOptionsForKey(conf_editor_audio, "grabberAudio", ["enable", "available_devices"], true);
|
||||
|
||||
const deviceProperties = getPropertiesOfDevice("audio", deviceSelected);
|
||||
|
||||
//Update hidden input element
|
||||
conf_editor_audio.getEditor("root.grabberAudio.device").setValue(deviceProperties.device);
|
||||
|
||||
//Enfore configured JSON-editor dependencies
|
||||
conf_editor_audio.notifyWatchers("root.grabberAudio.audioEffect");
|
||||
|
||||
//Enable set defaults button
|
||||
$('#btn_audiograbber_set_effect_defaults').prop('disabled', false);
|
||||
|
||||
if (conf_editor_audio.validate().length && !window.readOnlyMode) {
|
||||
$('#btn_submit_audiograbber').prop('disabled', false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn_submit_audiograbber').off().on('click', function () {
|
||||
const saveOptions = conf_editor_audio.getValue();
|
||||
|
||||
const instCaptOptions = window.serverConfig.instCapture;
|
||||
instCaptOptions.audioEnable = true;
|
||||
saveOptions.instCapture = instCaptOptions;
|
||||
|
||||
requestWriteConfig(saveOptions);
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
$('#btn_audiograbber_set_effect_defaults').off().on('click', function () {
|
||||
const currentEffect = conf_editor_audio.getEditor("root.grabberAudio.audioEffect").getValue();
|
||||
var effectEditor = conf_editor_audio.getEditor("root.grabberAudio." + currentEffect);
|
||||
var defaultProperties = effectEditor.schema.defaultProperties;
|
||||
|
||||
var default_values = {};
|
||||
for (const item of defaultProperties) {
|
||||
|
||||
default_values[item] = effectEditor.schema.properties[item].default;
|
||||
}
|
||||
effectEditor.setValue(default_values);
|
||||
});
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
@ -706,6 +839,9 @@ $(document).ready(function () {
|
||||
if (videoGrabberAvailable) {
|
||||
createHint("intro", $.i18n('conf_grabber_v4l_intro'), "editor_container_videograbber");
|
||||
}
|
||||
if (audioGrabberAvailable) {
|
||||
createHint("intro", $.i18n('conf_grabber_audio_intro'), "editor_container_audiograbber");
|
||||
}
|
||||
}
|
||||
|
||||
removeOverlay();
|
||||
@ -773,6 +909,38 @@ $(document).ready(function () {
|
||||
}
|
||||
};
|
||||
|
||||
// build dynamic audio input enum
|
||||
const updateAudioSourcesList = function (type, discoveryInfo) {
|
||||
const enumVals = [];
|
||||
const enumTitelVals = [];
|
||||
let enumDefaultVal = "";
|
||||
let addSelect = false;
|
||||
|
||||
if (jQuery.isEmptyObject(discoveryInfo)) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_conf_grabber_discovered_none'));
|
||||
}
|
||||
else {
|
||||
for (const device of discoveryInfo) {
|
||||
enumVals.push(device.device_name);
|
||||
}
|
||||
conf_editor_audio.getEditor('root.grabberAudio').enable();
|
||||
configuredDevice = window.serverConfig.grabberAudio.available_devices;
|
||||
|
||||
if ($.inArray(configuredDevice, enumVals) != -1) {
|
||||
enumDefaultVal = configuredDevice;
|
||||
}
|
||||
else {
|
||||
addSelect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (enumVals.length > 0) {
|
||||
updateJsonEditorSelection(conf_editor_audio, 'root.grabberAudio',
|
||||
'available_devices', {}, enumVals, enumTitelVals, enumDefaultVal, addSelect, false);
|
||||
}
|
||||
};
|
||||
|
||||
async function discoverInputSources(type, params) {
|
||||
const result = await requestInputSourcesDiscovery(type, params);
|
||||
|
||||
@ -782,7 +950,8 @@ $(document).ready(function () {
|
||||
}
|
||||
else {
|
||||
discoveryResult = {
|
||||
"video_sources": []
|
||||
"video_sources": [],
|
||||
"audio_soruces": []
|
||||
};
|
||||
}
|
||||
|
||||
@ -799,6 +968,12 @@ $(document).ready(function () {
|
||||
updateVideoSourcesList(type, discoveredInputSources.video);
|
||||
}
|
||||
break;
|
||||
case "audio":
|
||||
discoveredInputSources.audio = discoveryResult.audio_sources;
|
||||
if (audioGrabberAvailable) {
|
||||
updateAudioSourcesList(type, discoveredInputSources.audio);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,8 +278,9 @@ $(document).ready(function () {
|
||||
if (getStorage('lastSelectedInstance'))
|
||||
removeStorage('lastSelectedInstance')
|
||||
|
||||
currentHyperionInstance = 0;
|
||||
currentHyperionInstanceName = getInstanceNameByIndex(0);
|
||||
window.currentHyperionInstance = 0;
|
||||
window.currentHyperionInstanceName = getInstanceNameByIndex(0);
|
||||
|
||||
requestServerConfig();
|
||||
setTimeout(requestServerInfo, 100)
|
||||
setTimeout(requestTokenInfo, 200)
|
||||
@ -293,7 +294,7 @@ $(document).ready(function () {
|
||||
$('#btn_hypinstanceswitch').toggle(false)
|
||||
|
||||
// update listing for button
|
||||
updateUiOnInstance(currentHyperionInstance);
|
||||
updateUiOnInstance(window.currentHyperionInstance);
|
||||
updateHyperionInstanceListing();
|
||||
});
|
||||
|
||||
|
@ -3,6 +3,7 @@ $(document).ready(function () {
|
||||
|
||||
var screenGrabberAvailable = (window.serverInfo.grabbers.screen.available.length !== 0);
|
||||
var videoGrabberAvailable = (window.serverInfo.grabbers.video.available.length !== 0);
|
||||
const audioGrabberAvailable = (window.serverInfo.grabbers.audio.available.length !== 0);
|
||||
|
||||
var BOBLIGHT_ENABLED = (jQuery.inArray("boblight", window.serverInfo.services) !== -1);
|
||||
|
||||
@ -15,7 +16,7 @@ $(document).ready(function () {
|
||||
// Instance Capture
|
||||
|
||||
if (window.showOptHelp) {
|
||||
if (screenGrabberAvailable || videoGrabberAvailable) {
|
||||
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
|
||||
$('#conf_cont').append(createRow('conf_cont_instCapt'));
|
||||
$('#conf_cont_instCapt').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
|
||||
$('#conf_cont_instCapt').append(createHelpTable(window.schema.instCapture.properties, $.i18n("edt_conf_instCapture_heading_title")));
|
||||
@ -29,7 +30,7 @@ $(document).ready(function () {
|
||||
}
|
||||
else {
|
||||
$('#conf_cont').addClass('row');
|
||||
if (screenGrabberAvailable || videoGrabberAvailable) {
|
||||
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
|
||||
$('#conf_cont').append(createOptPanel('fa-camera', $.i18n("edt_conf_instCapture_heading_title"), 'editor_container_instCapt', 'btn_submit_instCapt', ''));
|
||||
}
|
||||
if (BOBLIGHT_ENABLED) {
|
||||
@ -37,7 +38,7 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
if (screenGrabberAvailable || videoGrabberAvailable) {
|
||||
if (screenGrabberAvailable || videoGrabberAvailable || audioGrabberAvailable) {
|
||||
|
||||
// Instance Capture
|
||||
conf_editor_instCapt = createJsonEditor('editor_container_instCapt', {
|
||||
@ -81,12 +82,29 @@ $(document).ready(function () {
|
||||
showInputOptionForItem(conf_editor_instCapt, "instCapture", "v4lPriority", false);
|
||||
}
|
||||
|
||||
if (audioGrabberAvailable) {
|
||||
if (!window.serverConfig.grabberAudio.enable) {
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").setValue(false);
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").disable();
|
||||
}
|
||||
else {
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").setValue(window.serverConfig.instCapture.audioEnable);
|
||||
|
||||
}
|
||||
} else {
|
||||
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioGrabberDevice", false);
|
||||
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioEnable", false);
|
||||
showInputOptionForItem(conf_editor_instCapt, "instCapture", "audioPriority", false);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
conf_editor_instCapt.on('change', function () {
|
||||
|
||||
if (!conf_editor_instCapt.validate().length) {
|
||||
if (!window.serverConfig.framegrabber.enable && !window.serverConfig.grabberV4L2.enable) {
|
||||
if (!window.serverConfig.framegrabber.enable &&
|
||||
!window.serverConfig.grabberV4L2.enable &&
|
||||
!window.serverConfig.grabberAudio.enable) {
|
||||
$('#btn_submit_instCapt').prop('disabled', true);
|
||||
} else {
|
||||
window.readOnlyMode ? $('#btn_submit_instCapt').prop('disabled', true) : $('#btn_submit_instCapt').prop('disabled', false);
|
||||
@ -130,6 +148,23 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
conf_editor_instCapt.watch('root.instCapture.audioEnable', () => {
|
||||
const audioEnable = conf_editor_instCapt.getEditor("root.instCapture.audioEnable").getValue();
|
||||
if (audioEnable) {
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioGrabberDevice").setValue(window.serverConfig.grabberAudio.available_devices);
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioGrabberDevice").disable();
|
||||
showInputOptions("instCapture", ["audioGrabberDevice"], true);
|
||||
showInputOptions("instCapture", ["audioPriority"], true);
|
||||
}
|
||||
else {
|
||||
if (!window.serverConfig.grabberAudio.enable) {
|
||||
conf_editor_instCapt.getEditor("root.instCapture.audioEnable").disable();
|
||||
}
|
||||
showInputOptions("instCapture", ["audioGrabberDevice"], false);
|
||||
showInputOptions("instCapture", ["audioPriority"], false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#btn_submit_instCapt').off().on('click', function () {
|
||||
requestWriteConfig(conf_editor_instCapt.getValue());
|
||||
});
|
||||
|
@ -1002,6 +1002,21 @@ $(document).ready(function () {
|
||||
|
||||
addJsonEditorHostValidation();
|
||||
|
||||
JSONEditor.defaults.custom_validators.push(function (schema, value, path) {
|
||||
var errors = [];
|
||||
|
||||
if (path === "root.specificOptions.segments.segmentList") {
|
||||
var overlapSegNames = validateWledSegmentConfig(value);
|
||||
if (overlapSegNames.length > 0) {
|
||||
errors.push({
|
||||
path: "root.specificOptions.segments",
|
||||
message: $.i18n('edt_dev_spec_segmentsOverlapValidation_error', overlapSegNames.length, overlapSegNames.join(', '))
|
||||
});
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
});
|
||||
|
||||
$("#leddevices").off().on("change", function () {
|
||||
var generalOptions = window.serverSchema.properties.device;
|
||||
|
||||
@ -1080,8 +1095,8 @@ $(document).ready(function () {
|
||||
$('#btn_test_controller').hide();
|
||||
|
||||
switch (ledType) {
|
||||
case "cololight":
|
||||
case "wled":
|
||||
case "cololight":
|
||||
case "nanoleaf":
|
||||
showAllDeviceInputOptions("hostList", false);
|
||||
case "apa102":
|
||||
@ -1107,7 +1122,22 @@ $(document).ready(function () {
|
||||
if (storedAccess === 'expert') {
|
||||
filter.discoverAll = true;
|
||||
}
|
||||
discover_device(ledType, filter);
|
||||
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
|
||||
discover_device(ledType, filter)
|
||||
.then(discoveryResult => {
|
||||
updateOutputSelectList(ledType, discoveryResult);
|
||||
})
|
||||
.then(discoveryResult => {
|
||||
if (ledType === "wled") {
|
||||
updateElementsWled(ledType);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showNotification('danger', "Device discovery for " + ledType + " failed with error:" + error);
|
||||
});
|
||||
|
||||
hwLedCountDefault = 1;
|
||||
colorOrderDefault = "rgb";
|
||||
break;
|
||||
@ -1211,8 +1241,8 @@ $(document).ready(function () {
|
||||
}
|
||||
break;
|
||||
|
||||
case "cololight":
|
||||
case "wled":
|
||||
case "cololight":
|
||||
var hostList = conf_editor.getEditor("root.specificOptions.hostList").getValue();
|
||||
if (hostList !== "SELECT") {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
@ -1339,7 +1369,9 @@ $(document).ready(function () {
|
||||
break;
|
||||
|
||||
case "wled":
|
||||
params = { host: host, filter: "info" };
|
||||
//Ensure that elements are defaulted for new host
|
||||
updateElementsWled(ledType, host);
|
||||
params = { host: host };
|
||||
getProperties_device(ledType, host, params);
|
||||
break;
|
||||
|
||||
@ -1452,6 +1484,10 @@ $(document).ready(function () {
|
||||
}
|
||||
conf_editor.getEditor("root.specificOptions.rateList").setValue(rate);
|
||||
break;
|
||||
case "wled":
|
||||
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
|
||||
validateWledLedCount(hardwareLedCount);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
@ -1547,12 +1583,54 @@ $(document).ready(function () {
|
||||
}
|
||||
});
|
||||
|
||||
//WLED
|
||||
conf_editor.watch('root.specificOptions.segments.segmentList', () => {
|
||||
|
||||
//Update hidden streamSegmentId element
|
||||
var selectedSegment = conf_editor.getEditor("root.specificOptions.segments.segmentList").getValue();
|
||||
var streamSegmentId = parseInt(selectedSegment);
|
||||
conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").setValue(streamSegmentId);
|
||||
|
||||
if (devicesProperties[ledType]) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledDeviceProperties = devicesProperties[ledType][host];
|
||||
|
||||
if (ledDeviceProperties) {
|
||||
var hardwareLedCount = 1;
|
||||
if (streamSegmentId > -1) {
|
||||
// Set hardware LED count to segment length
|
||||
if (ledDeviceProperties.state) {
|
||||
var segments = ledDeviceProperties.state.seg;
|
||||
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
|
||||
hardwareLedCount = segmentConfig.len;
|
||||
}
|
||||
} else {
|
||||
//"Use main segment only" is disabled, i.e. stream to all LEDs
|
||||
if (ledDeviceProperties.info) {
|
||||
hardwareLedCount = ledDeviceProperties.info.leds.count;
|
||||
}
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Handle Hardware Led Count constraint list
|
||||
conf_editor.watch('root.generalOptions.hardwareLedCountList', () => {
|
||||
var hwLedCountSelected = conf_editor.getEditor("root.generalOptions.hardwareLedCountList").getValue();
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(Number(hwLedCountSelected));
|
||||
});
|
||||
|
||||
//Handle Hardware Led update and constraints
|
||||
conf_editor.watch('root.generalOptions.hardwareLedCount', () => {
|
||||
var hardwareLedCount = conf_editor.getEditor("root.generalOptions.hardwareLedCount").getValue();
|
||||
switch (ledType) {
|
||||
case "wled":
|
||||
validateWledLedCount(hardwareLedCount);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
//philipshueentertainment backward fix
|
||||
@ -1798,8 +1876,8 @@ function saveLedConfig(genDefLayout = false) {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
// build dynamic enum
|
||||
var updateSelectList = function (ledType, discoveryInfo) {
|
||||
// build dynamic enum for hosts or output paths
|
||||
var updateOutputSelectList = function (ledType, discoveryInfo) {
|
||||
// Only update, if ledType is equal of selected controller type and discovery info exists
|
||||
if (ledType !== $("#leddevices").val() || !discoveryInfo.devices) {
|
||||
return;
|
||||
@ -1810,7 +1888,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
var key;
|
||||
var enumVals = [];
|
||||
var enumTitelVals = [];
|
||||
var enumTitleVals = [];
|
||||
var enumDefaultVal = "";
|
||||
var addSelect = false;
|
||||
var addCustom = false;
|
||||
@ -1835,7 +1913,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length === 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
}
|
||||
else {
|
||||
var name;
|
||||
@ -1876,7 +1954,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
}
|
||||
|
||||
enumVals.push(host);
|
||||
enumTitelVals.push(name);
|
||||
enumTitleVals.push(name);
|
||||
}
|
||||
|
||||
//Always allow to add custom configuration
|
||||
@ -1904,7 +1982,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
}
|
||||
@ -1922,7 +2000,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
} else {
|
||||
enumVals.push(device.portName);
|
||||
}
|
||||
enumTitelVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
|
||||
enumTitleVals.push(device.portName + " (" + device.vendorIdentifier + "|" + device.productIdentifier + ") - " + device.manufacturer);
|
||||
}
|
||||
|
||||
// Select configured device
|
||||
@ -1951,7 +2029,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
}
|
||||
@ -1970,7 +2048,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
case "piblaster":
|
||||
for (const device of discoveryInfo.devices) {
|
||||
enumVals.push(device.systemLocation);
|
||||
enumTitelVals.push(device.deviceName + " (" + device.systemLocation + ")");
|
||||
enumTitleVals.push(device.deviceName + " (" + device.systemLocation + ")");
|
||||
}
|
||||
|
||||
// Select configured device
|
||||
@ -1993,7 +2071,7 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
|
||||
if (discoveryInfo.devices.length == 0) {
|
||||
enumVals.push("NONE");
|
||||
enumTitelVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
enumTitleVals.push($.i18n('edt_dev_spec_devices_discovered_none'));
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
showAllDeviceInputOptions(key, false);
|
||||
|
||||
@ -2004,18 +2082,19 @@ var updateSelectList = function (ledType, discoveryInfo) {
|
||||
}
|
||||
|
||||
if (enumVals.length > 0) {
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitelVals, enumDefaultVal, addSelect, addCustom);
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions', key, addSchemaElements, enumVals, enumTitleVals, enumDefaultVal, addSelect, addCustom);
|
||||
}
|
||||
};
|
||||
|
||||
async function discover_device(ledType, params) {
|
||||
|
||||
$('#btn_submit_controller').prop('disabled', true);
|
||||
|
||||
const result = await requestLedDeviceDiscovery(ledType, params);
|
||||
|
||||
var discoveryResult;
|
||||
if (result && !result.error) {
|
||||
var discoveryResult = {};
|
||||
if (result) {
|
||||
if (result.error) {
|
||||
throw (result.error);
|
||||
}
|
||||
discoveryResult = result.info;
|
||||
}
|
||||
else {
|
||||
@ -2024,8 +2103,7 @@ async function discover_device(ledType, params) {
|
||||
ledDevicetype: ledType
|
||||
}
|
||||
}
|
||||
|
||||
updateSelectList(ledType, discoveryResult);
|
||||
return discoveryResult;
|
||||
}
|
||||
|
||||
async function getProperties_device(ledType, key, params) {
|
||||
@ -2089,23 +2167,7 @@ function updateElements(ledType, key) {
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
break;
|
||||
case "wled":
|
||||
var ledProperties = devicesProperties[ledType][key];
|
||||
|
||||
if (ledProperties && ledProperties.leds) {
|
||||
hardwareLedCount = ledProperties.leds.count;
|
||||
if (ledProperties.maxLedCount) {
|
||||
var maxLedCount = ledProperties.maxLedCount;
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
|
||||
hardwareLedCount = maxLedCount;
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
|
||||
//Workaround, as value seems to getting updated property when a 'getEditor("root.specificOptions").getValue()' is done during save
|
||||
var editor = conf_editor.getEditor("root.specificOptions");
|
||||
editor.value["streamProtocol"] = "RAW";
|
||||
}
|
||||
}
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
updateElementsWled(ledType, key);
|
||||
break;
|
||||
|
||||
case "nanoleaf":
|
||||
@ -2190,3 +2252,168 @@ function disableAutoResolvedGeneralOptions() {
|
||||
conf_editor.getEditor("root.generalOptions.colorOrder").disable();
|
||||
}
|
||||
|
||||
function validateWledSegmentConfig(streamSegmentId) {
|
||||
var overlapSegNames = [];
|
||||
if (streamSegmentId > -1) {
|
||||
if (!jQuery.isEmptyObject(devicesProperties)) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledProperties = devicesProperties['wled'][host];
|
||||
if (ledProperties && ledProperties.state) {
|
||||
var segments = ledProperties.state.seg;
|
||||
var segmentConfig = segments.filter(seg => seg.id == streamSegmentId)[0];
|
||||
|
||||
var overlappingSegments = segments.filter((seg) => {
|
||||
if (seg.id != streamSegmentId) {
|
||||
if ((segmentConfig.start >= seg.stop) || (segmentConfig.start < seg.start && segmentConfig.stop <= seg.start)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (overlappingSegments.length > 0) {
|
||||
var overlapSegNames = [];
|
||||
for (const segment of overlappingSegments) {
|
||||
if (segment.n) {
|
||||
overlapSegNames.push(segment.n);
|
||||
} else {
|
||||
overlapSegNames.push("Segment " + segment.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return overlapSegNames;
|
||||
}
|
||||
|
||||
function validateWledLedCount(hardwareLedCount) {
|
||||
|
||||
if (!jQuery.isEmptyObject(devicesProperties)) {
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
var ledDeviceProperties = devicesProperties["wled"][host];
|
||||
|
||||
if (ledDeviceProperties) {
|
||||
|
||||
var streamProtocol = conf_editor.getEditor("root.specificOptions.streamProtocol").getValue();
|
||||
if (streamProtocol === "RAW") {
|
||||
var maxLedCount = 490;
|
||||
if (ledDeviceProperties.maxLedCount) {
|
||||
//WLED not DDP ready
|
||||
maxLedCount = ledDeviceProperties.maxLedCount;
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled', hardwareLedCount, maxLedCount, maxLedCount));
|
||||
hardwareLedCount = maxLedCount;
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue("RAW");
|
||||
}
|
||||
} else {
|
||||
//WLED is DDP ready
|
||||
if (hardwareLedCount > maxLedCount) {
|
||||
var newStreamingProtocol = "DDP";
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_hwled_gt_maxled_protocol', hardwareLedCount, maxLedCount, newStreamingProtocol));
|
||||
conf_editor.getEditor("root.specificOptions.streamProtocol").setValue(newStreamingProtocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateElementsWled(ledType, key) {
|
||||
|
||||
// Get configured device's details
|
||||
var configuredDeviceType = window.serverConfig.device.type;
|
||||
var configuredHost = window.serverConfig.device.host;
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
|
||||
//New segment selection list values
|
||||
var enumSegSelectVals = [];
|
||||
var enumSegSelectTitleVals = [];
|
||||
var enumSegSelectDefaultVal = "";
|
||||
|
||||
if (devicesProperties[ledType] && devicesProperties[ledType][key]) {
|
||||
var ledDeviceProperties = devicesProperties[ledType][key];
|
||||
|
||||
if (!jQuery.isEmptyObject(ledDeviceProperties)) {
|
||||
|
||||
if (ledDeviceProperties.info) {
|
||||
if (ledDeviceProperties.info.liveseg && ledDeviceProperties.info.liveseg < 0) {
|
||||
// "Use main segment only" is disabled
|
||||
var defaultSegmentId = "-1";
|
||||
enumSegSelectVals.push(defaultSegmentId);
|
||||
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
|
||||
enumSegSelectDefaultVal = defaultSegmentId;
|
||||
|
||||
} else {
|
||||
if (ledDeviceProperties.state) {
|
||||
//Prepare new segment selection list
|
||||
var segments = ledDeviceProperties.state.seg;
|
||||
for (const segment of segments) {
|
||||
enumSegSelectVals.push(segment.id.toString());
|
||||
if (segment.n) {
|
||||
enumSegSelectTitleVals.push(segment.n);
|
||||
} else {
|
||||
enumSegSelectTitleVals.push("Segment " + segment.id);
|
||||
}
|
||||
}
|
||||
var currentSegmentId = conf_editor.getEditor("root.specificOptions.segments.streamSegmentId").getValue().toString();
|
||||
enumSegSelectDefaultVal = currentSegmentId;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if currently configured segment is available at WLED
|
||||
var configuredDeviceType = window.serverConfig.device.type;
|
||||
var configuredHost = window.serverConfig.device.host;
|
||||
|
||||
var host = conf_editor.getEditor("root.specificOptions.host").getValue();
|
||||
if (configuredDeviceType == ledType && configuredHost == host) {
|
||||
var configuredStreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
|
||||
var segmentIdFound = enumSegSelectVals.filter(segId => segId == configuredStreamSegmentId).length;
|
||||
if (!segmentIdFound) {
|
||||
showInfoDialog('warning', $.i18n("conf_leds_config_warning"), $.i18n('conf_leds_error_wled_segment_missing', configuredStreamSegmentId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//If failed to get properties
|
||||
var hardwareLedCount;
|
||||
var segmentConfig = false;
|
||||
|
||||
if (configuredDeviceType == ledType && configuredHost == host) {
|
||||
// Populate elements from existing configuration
|
||||
if (window.serverConfig.device.segments) {
|
||||
segmentConfig = true;
|
||||
}
|
||||
hardwareLedCount = window.serverConfig.device.hardwareLedCount;
|
||||
} else {
|
||||
// Populate elements with default values
|
||||
hardwareLedCount = 1;
|
||||
}
|
||||
|
||||
if (segmentConfig) {
|
||||
var configuredstreamSegmentId = window.serverConfig.device.segments.streamSegmentId.toString();
|
||||
enumSegSelectVals = [configuredstreamSegmentId];
|
||||
enumSegSelectTitleVals = ["Segment " + configuredstreamSegmentId];
|
||||
enumSegSelectDefaultVal = configuredstreamSegmentId;
|
||||
} else {
|
||||
defaultSegmentId = "-1";
|
||||
enumSegSelectVals.push(defaultSegmentId);
|
||||
enumSegSelectTitleVals.push($.i18n('edt_dev_spec_segments_disabled_title'));
|
||||
enumSegSelectDefaultVal = defaultSegmentId;
|
||||
}
|
||||
conf_editor.getEditor("root.generalOptions.hardwareLedCount").setValue(hardwareLedCount);
|
||||
}
|
||||
|
||||
updateJsonEditorSelection(conf_editor, 'root.specificOptions.segments',
|
||||
'segmentList', {}, enumSegSelectVals, enumSegSelectTitleVals, enumSegSelectDefaultVal, false, false);
|
||||
|
||||
//Show additional configuration options, if more than one segment is available
|
||||
var showAdditionalOptions = false;
|
||||
if (enumSegSelectVals.length > 1) {
|
||||
showAdditionalOptions = true;
|
||||
}
|
||||
showInputOptionForItem(conf_editor, "root.specificOptions.segments", "switchOffOtherSegments", showAdditionalOptions);
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,8 @@ $(document).ready(function () {
|
||||
function infoSummary() {
|
||||
var info = "";
|
||||
|
||||
info += 'Hyperion System Summary Report (' + window.serverConfig.general.name + '), Reported instance: ' + window.currentHyperionInstanceName + '\n';
|
||||
info += 'Hyperion System Summary Report (' + window.serverConfig.general.name + ')\n';
|
||||
info += 'Reported instance: [' + window.currentHyperionInstance + '] - ' + window.currentHyperionInstanceName + '\n';
|
||||
|
||||
info += "\n< ----- System information -------------------- >\n";
|
||||
info += getSystemInfo() + '\n';
|
||||
@ -43,22 +44,36 @@ $(document).ready(function () {
|
||||
info += "\n< ----- Configured Instances ------------------ >\n";
|
||||
var instances = window.serverInfo.instance;
|
||||
for (var i = 0; i < instances.length; i++) {
|
||||
info += instances[i].instance + ': ' + instances[i].friendly_name + ' Running: ' + instances[i].running + '\n';
|
||||
info += instances[i].instance + ': ' + instances[i].friendly_name + ', Running: ' + instances[i].running + '\n';
|
||||
}
|
||||
|
||||
info += "\n< ----- This instance's priorities ------------ >\n";
|
||||
var prios = window.serverInfo.priorities;
|
||||
for (var i = 0; i < prios.length; i++) {
|
||||
info += prios[i].priority + ': ';
|
||||
if (prios[i].visible) {
|
||||
info += ' VISIBLE!';
|
||||
|
||||
if (prios.length > 0) {
|
||||
|
||||
for (var i = 0; i < prios.length; i++) {
|
||||
|
||||
var prio = prios[i].priority.toString().padStart(3, '0');
|
||||
|
||||
info += prio + ': ';
|
||||
if (prios[i].visible) {
|
||||
info += ' VISIBLE -';
|
||||
}
|
||||
else {
|
||||
info += ' INVISIBLE -';
|
||||
}
|
||||
info += ' (' + prios[i].componentId + ')';
|
||||
if (prios[i].owner) {
|
||||
info += ' (Owner: ' + prios[i].owner + ')';
|
||||
}
|
||||
info += '\n';
|
||||
|
||||
}
|
||||
else {
|
||||
info += ' ';
|
||||
}
|
||||
info += ' (' + prios[i].componentId + ') Owner: ' + prios[i].owner + '\n';
|
||||
} else {
|
||||
info += 'The current priority list is empty!\n';
|
||||
}
|
||||
info += 'priorities_autoselect: ' + window.serverInfo.priorities_autoselect + '\n';
|
||||
info += 'Autoselect: ' + window.serverInfo.priorities_autoselect + '\n';
|
||||
|
||||
info += "\n< ----- This instance components' status ------->\n";
|
||||
var comps = window.serverInfo.components;
|
||||
@ -67,7 +82,7 @@ $(document).ready(function () {
|
||||
}
|
||||
|
||||
info += "\n< ----- This instance's configuration --------- >\n";
|
||||
info += JSON.stringify(window.serverConfig) + '\n';
|
||||
info += JSON.stringify(window.serverConfig, null, 2) + '\n';
|
||||
|
||||
info += "\n< ----- Current Log --------------------------- >\n";
|
||||
var logMsgs = document.getElementById("logmessages").textContent;
|
||||
@ -193,18 +208,19 @@ $(document).ready(function () {
|
||||
});
|
||||
|
||||
// toggle fullscreen button in log output
|
||||
$(".fullscreen-btn").mousedown(function(e) {
|
||||
$(".fullscreen-btn").mousedown(function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$(".fullscreen-btn").click(function(e) {
|
||||
$(".fullscreen-btn").click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).children('i')
|
||||
.toggleClass('fa-expand')
|
||||
.toggleClass('fa-compress');
|
||||
$('#conf_cont').toggle();
|
||||
$('#logmessages').css('max-height', $('#logmessages').css('max-height') !== 'none' ? 'none' : '400px' );
|
||||
$('#logmessages').css('max-height', $('#logmessages').css('max-height') !== 'none' ? 'none' : '400px');
|
||||
});
|
||||
|
||||
removeOverlay();
|
||||
});
|
||||
|
||||
|
@ -98,7 +98,7 @@ $(document).ready(function () {
|
||||
}
|
||||
|
||||
function updateInputSelect() {
|
||||
$('.sstbody').html("");
|
||||
$('.sstbody').empty();
|
||||
var prios = window.serverInfo.priorities;
|
||||
var clearAll = false;
|
||||
|
||||
@ -155,6 +155,9 @@ $(document).ready(function () {
|
||||
case "V4L":
|
||||
owner = $.i18n('general_comp_V4L') + ': (' + owner + ')';
|
||||
break;
|
||||
case "AUDIO":
|
||||
owner = $.i18n('general_comp_AUDIO') + ': (' + owner + ')';
|
||||
break;
|
||||
case "BOBLIGHTSERVER":
|
||||
owner = $.i18n('general_comp_BOBLIGHTSERVER');
|
||||
break;
|
||||
@ -220,7 +223,8 @@ $(document).ready(function () {
|
||||
for (const comp of components) {
|
||||
if (comp.name === "ALL" || (comp.name === "FORWARDER" && window.currentHyperionInstance != 0) ||
|
||||
(comp.name === "GRABBER" && !window.serverConfig.framegrabber.enable) ||
|
||||
(comp.name === "V4L" && !window.serverConfig.grabberV4L2.enable))
|
||||
(comp.name === "V4L" && !window.serverConfig.grabberV4L2.enable) ||
|
||||
(comp.name === "AUDIO" && !window.serverConfig.grabberAudio.enable))
|
||||
continue;
|
||||
|
||||
const enable_style = (comp.enabled ? "checked" : "");
|
||||
|
@ -171,6 +171,10 @@ function initLanguageSelection() {
|
||||
}
|
||||
|
||||
function updateUiOnInstance(inst) {
|
||||
|
||||
window.currentHyperionInstance = inst;
|
||||
window.currentHyperionInstanceName = getInstanceNameByIndex(inst);
|
||||
|
||||
$("#active_instance_friendly_name").text(getInstanceNameByIndex(inst));
|
||||
if (window.serverInfo.instance.filter(entry => entry.running).length > 1) {
|
||||
$('#btn_hypinstanceswitch').toggle(true);
|
||||
@ -316,7 +320,7 @@ function showInfoDialog(type, header, message) {
|
||||
|
||||
$(document).on('click', '[data-dismiss-modal]', function () {
|
||||
var target = $(this).attr('data-dismiss-modal');
|
||||
$(target).modal('hide'); // lgtm [js/xss-through-dom]
|
||||
$.find(target).modal('hide');
|
||||
});
|
||||
}
|
||||
|
||||
@ -407,6 +411,32 @@ function isJsonString(str) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const getObjectProperty = (obj, path) => path.split(".").reduce((o, key) => o && typeof o[key] !== 'undefined' ? o[key] : undefined, obj);
|
||||
|
||||
const setObjectProperty = (object, path, value) => {
|
||||
const parts = path.split('.');
|
||||
const limit = parts.length - 1;
|
||||
for (let i = 0; i < limit; ++i) {
|
||||
const key = parts[i];
|
||||
if (key === "__proto__" || key === "constructor") continue;
|
||||
object = object[key] ?? (object[key] = {});
|
||||
}
|
||||
const key = parts[limit];
|
||||
object[key] = value;
|
||||
};
|
||||
|
||||
function getLongPropertiesPath(path) {
|
||||
if (path) {
|
||||
var path = path.replace('root.', '');
|
||||
const parts = path.split('.');
|
||||
parts.forEach(function (part, index) {
|
||||
this[index] += ".properties";
|
||||
}, parts);
|
||||
path = parts.join('.') + '.';
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function createJsonEditor(container, schema, setconfig, usePanel, arrayre) {
|
||||
$('#' + container).off();
|
||||
$('#' + container).html("");
|
||||
@ -527,7 +557,8 @@ function updateJsonEditorSelection(rootEditor, path, key, addElements, newEnumVa
|
||||
|
||||
editor.original_schema.properties[key] = orginalProperties;
|
||||
editor.schema.properties[key] = newSchema[key];
|
||||
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
|
||||
//Update schema properties for validator
|
||||
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
|
||||
|
||||
editor.removeObjectProperty(key);
|
||||
delete editor.cached_editors[key];
|
||||
@ -596,7 +627,8 @@ function updateJsonEditorMultiSelection(rootEditor, path, key, addElements, newE
|
||||
|
||||
editor.original_schema.properties[key] = orginalProperties;
|
||||
editor.schema.properties[key] = newSchema[key];
|
||||
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
|
||||
//Update schema properties for validator
|
||||
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
|
||||
|
||||
editor.removeObjectProperty(key);
|
||||
delete editor.cached_editors[key];
|
||||
@ -644,7 +676,8 @@ function updateJsonEditorRange(rootEditor, path, key, minimum, maximum, defaultV
|
||||
|
||||
editor.original_schema.properties[key] = orginalProperties;
|
||||
editor.schema.properties[key] = newSchema[key];
|
||||
rootEditor.validator.schema.properties[editor.key].properties[key] = newSchema[key];
|
||||
//Update schema properties for validator
|
||||
setObjectProperty(rootEditor.validator.schema.properties, getLongPropertiesPath(path) + key, newSchema[key]);
|
||||
|
||||
editor.removeObjectProperty(key);
|
||||
delete editor.cached_editors[key];
|
||||
@ -1187,6 +1220,7 @@ function getSystemInfo() {
|
||||
//info += '- Log lvl: ' + window.serverConfig.logger.level + '\n';
|
||||
info += '- Avail Screen Cap.: ' + window.serverInfo.grabbers.screen.available + '\n';
|
||||
info += '- Avail Video Cap.: ' + window.serverInfo.grabbers.video.available + '\n';
|
||||
info += '- Avail Audio Cap.: ' + window.serverInfo.grabbers.audio.available + '\n';
|
||||
info += '- Avail Services: ' + window.serverInfo.services + '\n';
|
||||
info += '- Config path: ' + shy.rootPath + '\n';
|
||||
info += '- Database: ' + (shy.readOnlyMode ? "ready-only" : "read/write") + '\n';
|
||||
@ -1246,15 +1280,26 @@ function isAccessLevelCompliant(accessLevel) {
|
||||
}
|
||||
|
||||
function showInputOptions(path, elements, state) {
|
||||
|
||||
if (!path.startsWith("root.")) {
|
||||
path = ["root", path].join('.');
|
||||
}
|
||||
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
$('[data-schemapath="root.' + path + '.' + elements[i] + '"]').toggle(state);
|
||||
$('[data-schemapath="' + path + '.' + elements[i] + '"]').toggle(state);
|
||||
}
|
||||
}
|
||||
|
||||
function showInputOptionForItem(editor, path, item, state) {
|
||||
var accessLevel = editor.schema.properties[path].properties[item].access;
|
||||
//Get access level for full path and item
|
||||
var accessLevel = getObjectProperty(editor.schema.properties, getLongPropertiesPath(path) + item + ".access");
|
||||
// Enable element only, if access level compliant
|
||||
if (!state || isAccessLevelCompliant(accessLevel)) {
|
||||
|
||||
if (!path) {
|
||||
debugger;
|
||||
path = editor.path;
|
||||
}
|
||||
showInputOptions(path, [item], state);
|
||||
}
|
||||
}
|
||||
@ -1269,17 +1314,26 @@ function showInputOptionsForKey(editor, item, showForKeys, state) {
|
||||
if (typeof showForKeys === 'string') {
|
||||
keysToshow.push(showForKeys);
|
||||
} else {
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (var key in editor.schema.properties[item].properties) {
|
||||
for (let key in editor.schema.properties[item].properties) {
|
||||
if ($.inArray(key, keysToshow) === -1) {
|
||||
var accessLevel = editor.schema.properties[item].properties[key].access;
|
||||
const accessLevel = editor.schema.properties[item].properties[key].access;
|
||||
|
||||
var hidden = false;
|
||||
if (editor.schema.properties[item].properties[key].options) {
|
||||
hidden = editor.schema.properties[item].properties[key].options.hidden;
|
||||
if (typeof hidden === 'undefined') {
|
||||
hidden = false;
|
||||
}
|
||||
}
|
||||
//Always disable all elements, but only enable elements, if access level compliant
|
||||
if (!state || isAccessLevelCompliant(accessLevel)) {
|
||||
elements.push(key);
|
||||
if (!hidden) {
|
||||
elements.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1314,7 +1368,7 @@ function isValidIPv6(value) {
|
||||
|
||||
function isValidHostname(value) {
|
||||
if (value.match(
|
||||
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$' //lgtm [js/redos]
|
||||
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$'
|
||||
))
|
||||
return true;
|
||||
else
|
||||
@ -1323,7 +1377,7 @@ function isValidHostname(value) {
|
||||
|
||||
function isValidServicename(value) {
|
||||
if (value.match(
|
||||
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9 -]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$' //lgtm [js/redos]
|
||||
'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9 -]{0,61}[a-zA-Z0-9])(.([a-zA-Z0-9]|[_a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]))*$'
|
||||
))
|
||||
return true;
|
||||
else
|
||||
|
@ -864,6 +864,7 @@ function useGroupId(id) {
|
||||
get_hue_lights();
|
||||
}
|
||||
|
||||
|
||||
async function discover_hue_bridges() {
|
||||
$('#wiz_hue_ipstate').html($.i18n('edt_dev_spec_devices_discovery_inprogress'));
|
||||
$('#wiz_hue_discovered').html("")
|
||||
@ -1021,11 +1022,11 @@ function beginWizardHue() {
|
||||
$('#host').val(host);
|
||||
|
||||
var port = eV("port");
|
||||
if (port > 0) {
|
||||
$('#port').val(port);
|
||||
if (port == 0) {
|
||||
$('#port').val(80);
|
||||
}
|
||||
else {
|
||||
$('#port').val('');
|
||||
$('#port').val(port);
|
||||
}
|
||||
hueIPs.unshift({ host: host, port: port });
|
||||
|
||||
@ -1042,7 +1043,13 @@ function beginWizardHue() {
|
||||
|
||||
hueIPs = [];
|
||||
hueIPsinc = 0;
|
||||
hueIPs.push({ host: $('#host').val(), port: $('#port').val() });
|
||||
|
||||
var port = $('#port').val();
|
||||
if (isNaN(port) || port < 1 || port > 65535) {
|
||||
port = 80;
|
||||
$('#port').val(80);
|
||||
}
|
||||
hueIPs.push({ host: $('#host').val(), port: port });
|
||||
}
|
||||
else {
|
||||
discover_hue_bridges();
|
||||
|
@ -1,4 +1,4 @@
|
||||
# - Find the Windows SDK aka Platform SDK
|
||||
# - Find the Windows SDK aka Platform SDK (from https://github.com/rpavlik/cmake-modules)
|
||||
#
|
||||
# Relevant Wikipedia article: http://en.wikipedia.org/wiki/Microsoft_Windows_SDK
|
||||
#
|
||||
@ -49,10 +49,11 @@
|
||||
# http://academic.cleardefinition.com
|
||||
# Iowa State University HCI Graduate Program/VRAC
|
||||
#
|
||||
# Copyright Iowa State University 2012.
|
||||
# Copyright 2012, Iowa State University
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||
# http://www.boost.org/LICENSE_1_0.txt)
|
||||
# SPDX-License-Identifier: BSL-1.0
|
||||
|
||||
set(_preferred_sdk_dirs) # pre-output
|
||||
set(_win_sdk_dirs) # pre-output
|
||||
@ -76,6 +77,9 @@ endmacro()
|
||||
# although version numbers listed on that page don't necessarily match the directory
|
||||
# used by the installer.
|
||||
set(_winsdk_win10vers
|
||||
10.0.22000.0
|
||||
10.0.20348.0
|
||||
10.0.19041.0
|
||||
10.0.18362.0 # Win10 1903 "19H1"
|
||||
10.0.17763.0 # Win10 1809 "October 2018 Update"
|
||||
10.0.17134.0 # Redstone 4 aka Win10 1803 "April 2018 Update"
|
||||
|
@ -155,6 +155,9 @@ if(ENABLE_FLATBUF_CONNECT)
|
||||
if(ENABLE_V4L2)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_v4l2" )
|
||||
endif()
|
||||
if(ENABLE_AUDIO)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_audio" )
|
||||
endif()
|
||||
if(ENABLE_X11)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_x11" )
|
||||
endif()
|
||||
|
@ -1,240 +1,237 @@
|
||||
{
|
||||
"general" :
|
||||
{
|
||||
"name" : "My Hyperion Config",
|
||||
"configVersion": "configVersionValue",
|
||||
"previousVersion": "previousVersionValue",
|
||||
"watchedVersionBranch" : "Stable",
|
||||
"showOptHelp" : true
|
||||
"general": {
|
||||
"name": "My Hyperion Config",
|
||||
"configVersion": "configVersionValue",
|
||||
"previousVersion": "previousVersionValue",
|
||||
"watchedVersionBranch": "Stable",
|
||||
"showOptHelp": true
|
||||
},
|
||||
"logger" :
|
||||
{
|
||||
"level" : "warn"
|
||||
"logger": {
|
||||
"level": "warn"
|
||||
},
|
||||
|
||||
"device" :
|
||||
{
|
||||
"type" : "file",
|
||||
"hardwareLedCount" : 1,
|
||||
"autoStart" : true,
|
||||
"output" : "/dev/null",
|
||||
"colorOrder" : "rgb",
|
||||
"latchTime" : 0,
|
||||
"device": {
|
||||
"type": "file",
|
||||
"hardwareLedCount": 1,
|
||||
"autoStart": true,
|
||||
"output": "/dev/null",
|
||||
"colorOrder": "rgb",
|
||||
"latchTime": 0,
|
||||
"rewriteTime": 0,
|
||||
"enableAttempts": 6,
|
||||
"enableAttemptsInterval": 15
|
||||
},
|
||||
|
||||
"color" :
|
||||
{
|
||||
"imageToLedMappingType" : "multicolor_mean",
|
||||
"channelAdjustment" :
|
||||
[
|
||||
"color": {
|
||||
"imageToLedMappingType": "multicolor_mean",
|
||||
"channelAdjustment": [
|
||||
{
|
||||
"id" : "default",
|
||||
"leds" : "*",
|
||||
"white" : [255,255,255],
|
||||
"red" : [255,0,0],
|
||||
"green" : [0,255,0],
|
||||
"blue" : [0,0,255],
|
||||
"cyan" : [0,255,255],
|
||||
"magenta" : [255,0,255],
|
||||
"yellow" : [255,255,0],
|
||||
"gammaRed" : 2.2,
|
||||
"gammaGreen" : 2.2,
|
||||
"gammaBlue" : 2.2,
|
||||
"backlightThreshold" : 0,
|
||||
"backlightColored" : false,
|
||||
"brightness" : 100,
|
||||
"brightnessCompensation" : 100,
|
||||
"saturationGain" : 1.0,
|
||||
"brightnessGain" : 1.0,
|
||||
"id": "default",
|
||||
"leds": "*",
|
||||
"white": [ 255, 255, 255 ],
|
||||
"red": [ 255, 0, 0 ],
|
||||
"green": [ 0, 255, 0 ],
|
||||
"blue": [ 0, 0, 255 ],
|
||||
"cyan": [ 0, 255, 255 ],
|
||||
"magenta": [ 255, 0, 255 ],
|
||||
"yellow": [ 255, 255, 0 ],
|
||||
"gammaRed": 2.2,
|
||||
"gammaGreen": 2.2,
|
||||
"gammaBlue": 2.2,
|
||||
"backlightThreshold": 0,
|
||||
"backlightColored": false,
|
||||
"brightness": 100,
|
||||
"brightnessCompensation": 100,
|
||||
"saturationGain": 1.0,
|
||||
"brightnessGain": 1.0,
|
||||
"temperature" : 6600
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
"smoothing" :
|
||||
{
|
||||
"enable" : true,
|
||||
"type" : "linear",
|
||||
"time_ms" : 200,
|
||||
"updateFrequency" : 25.0000,
|
||||
"interpolationRate" : 25.0000,
|
||||
"decay" : 1,
|
||||
"dithering" : false,
|
||||
"updateDelay" : 0
|
||||
"smoothing": {
|
||||
"enable": true,
|
||||
"type": "linear",
|
||||
"time_ms": 150,
|
||||
"updateFrequency": 25.0000,
|
||||
"interpolationRate": 25.0000,
|
||||
"decay": 1,
|
||||
"dithering": false,
|
||||
"updateDelay": 0
|
||||
},
|
||||
|
||||
"grabberV4L2" :
|
||||
{
|
||||
"enable" : false,
|
||||
"device" : "none",
|
||||
"input" : 0,
|
||||
"encoding" : "NO_CHANGE",
|
||||
"width" : 0,
|
||||
"height" : 0,
|
||||
"fps" : 15,
|
||||
"flip" : "NO_CHANGE",
|
||||
"fpsSoftwareDecimation" : 0,
|
||||
"sizeDecimation" : 8,
|
||||
"cropLeft" : 0,
|
||||
"cropRight" : 0,
|
||||
"cropTop" : 0,
|
||||
"cropBottom" : 0,
|
||||
"redSignalThreshold" : 0,
|
||||
"greenSignalThreshold" : 100,
|
||||
"blueSignalThreshold" : 0,
|
||||
"signalDetection" : false,
|
||||
"noSignalCounterThreshold" : 200,
|
||||
"cecDetection" : false,
|
||||
"sDVOffsetMin" : 0.1,
|
||||
"sDVOffsetMax" : 0.9,
|
||||
"sDHOffsetMin" : 0.4,
|
||||
"sDHOffsetMax" : 0.46,
|
||||
"hardware_brightness" : 0,
|
||||
"hardware_contrast" : 0,
|
||||
"hardware_saturation" : 0,
|
||||
"hardware_hue" : 0
|
||||
"grabberV4L2": {
|
||||
"enable": false,
|
||||
"device": "none",
|
||||
"input": 0,
|
||||
"encoding": "NO_CHANGE",
|
||||
"width": 0,
|
||||
"height": 0,
|
||||
"fps": 15,
|
||||
"flip": "NO_CHANGE",
|
||||
"fpsSoftwareDecimation": 0,
|
||||
"sizeDecimation": 8,
|
||||
"cropLeft": 0,
|
||||
"cropRight": 0,
|
||||
"cropTop": 0,
|
||||
"cropBottom": 0,
|
||||
"redSignalThreshold": 0,
|
||||
"greenSignalThreshold": 100,
|
||||
"blueSignalThreshold": 0,
|
||||
"signalDetection": false,
|
||||
"noSignalCounterThreshold": 200,
|
||||
"cecDetection": false,
|
||||
"sDVOffsetMin": 0.1,
|
||||
"sDVOffsetMax": 0.9,
|
||||
"sDHOffsetMin": 0.4,
|
||||
"sDHOffsetMax": 0.46,
|
||||
"hardware_brightness": 0,
|
||||
"hardware_contrast": 0,
|
||||
"hardware_saturation": 0,
|
||||
"hardware_hue": 0
|
||||
},
|
||||
|
||||
"framegrabber" :
|
||||
{
|
||||
"enable" : false,
|
||||
"device" : "auto",
|
||||
"input" : 0,
|
||||
"width" : 80,
|
||||
"height" : 45,
|
||||
"fps" : 10,
|
||||
"pixelDecimation" : 8,
|
||||
"cropLeft" : 0,
|
||||
"cropRight" : 0,
|
||||
"cropTop" : 0,
|
||||
"cropBottom" : 0
|
||||
"grabberAudio": {
|
||||
"enable": false,
|
||||
"device": "auto",
|
||||
"audioEffect": "vuMeter",
|
||||
"vuMeter": {
|
||||
"flip": "NO_CHANGE",
|
||||
"hotColor": [ 255, 0, 0 ],
|
||||
"multiplier": 1,
|
||||
"safeColor": [ 0, 255, 0 ],
|
||||
"safeValue": 45,
|
||||
"tolerance": 5,
|
||||
"warnColor": [ 255, 255, 0 ],
|
||||
"warnValue": 80
|
||||
}
|
||||
},
|
||||
|
||||
"blackborderdetector" :
|
||||
{
|
||||
"enable" : true,
|
||||
"threshold" : 5,
|
||||
"unknownFrameCnt" : 600,
|
||||
"borderFrameCnt" : 50,
|
||||
"maxInconsistentCnt" : 10,
|
||||
"blurRemoveCnt" : 1,
|
||||
"mode" : "default"
|
||||
"framegrabber": {
|
||||
"enable": false,
|
||||
"device": "auto",
|
||||
"input": 0,
|
||||
"width": 80,
|
||||
"height": 45,
|
||||
"fps": 10,
|
||||
"pixelDecimation": 8,
|
||||
"cropLeft": 0,
|
||||
"cropRight": 0,
|
||||
"cropTop": 0,
|
||||
"cropBottom": 0
|
||||
},
|
||||
|
||||
"foregroundEffect" :
|
||||
{
|
||||
"enable" : true,
|
||||
"type" : "effect",
|
||||
"color" : [0,0,255],
|
||||
"effect" : "Rainbow swirl fast",
|
||||
"duration_ms" : 3000
|
||||
"blackborderdetector": {
|
||||
"enable": true,
|
||||
"threshold": 5,
|
||||
"unknownFrameCnt": 600,
|
||||
"borderFrameCnt": 50,
|
||||
"maxInconsistentCnt": 10,
|
||||
"blurRemoveCnt": 1,
|
||||
"mode": "default"
|
||||
},
|
||||
|
||||
"backgroundEffect" :
|
||||
{
|
||||
"enable" : false,
|
||||
"type" : "effect",
|
||||
"color" : [255,138,0],
|
||||
"effect" : "Warm mood blobs"
|
||||
"foregroundEffect": {
|
||||
"enable": true,
|
||||
"type": "effect",
|
||||
"color": [ 0, 0, 255 ],
|
||||
"effect": "Rainbow swirl fast",
|
||||
"duration_ms": 3000
|
||||
},
|
||||
|
||||
"forwarder" :
|
||||
{
|
||||
"enable" : false,
|
||||
"jsonapi" : [],
|
||||
"flatbuffer" : []
|
||||
"backgroundEffect": {
|
||||
"enable": false,
|
||||
"type": "effect",
|
||||
"color": [ 255, 138, 0 ],
|
||||
"effect": "Warm mood blobs"
|
||||
},
|
||||
|
||||
"jsonServer" :
|
||||
{
|
||||
"port" : 19444
|
||||
"forwarder": {
|
||||
"enable": false,
|
||||
"jsonapi": [],
|
||||
"flatbuffer": []
|
||||
},
|
||||
|
||||
"flatbufServer" :
|
||||
{
|
||||
"enable" : true,
|
||||
"port" : 19400,
|
||||
"timeout" : 5
|
||||
"jsonServer": {
|
||||
"port": 19444
|
||||
},
|
||||
|
||||
"protoServer" :
|
||||
{
|
||||
"enable" : true,
|
||||
"port" : 19445,
|
||||
"timeout" : 5
|
||||
"flatbufServer": {
|
||||
"enable": true,
|
||||
"port": 19400,
|
||||
"timeout": 5
|
||||
},
|
||||
|
||||
"boblightServer" :
|
||||
{
|
||||
"enable" : false,
|
||||
"port" : 19333,
|
||||
"priority" : 128
|
||||
"protoServer": {
|
||||
"enable": true,
|
||||
"port": 19445,
|
||||
"timeout": 5
|
||||
},
|
||||
|
||||
"webConfig" :
|
||||
{
|
||||
"document_root" : "",
|
||||
"port" : 8090,
|
||||
"sslPort" : 8092,
|
||||
"crtPath" : "",
|
||||
"keyPath" : "",
|
||||
"keyPassPhrase" : ""
|
||||
"boblightServer": {
|
||||
"enable": false,
|
||||
"port": 19333,
|
||||
"priority": 128
|
||||
},
|
||||
|
||||
"effects" :
|
||||
{
|
||||
"paths" : ["$ROOT/custom-effects"],
|
||||
"disable": [""]
|
||||
"webConfig": {
|
||||
"document_root": "",
|
||||
"port": 8090,
|
||||
"sslPort": 8092,
|
||||
"crtPath": "",
|
||||
"keyPath": "",
|
||||
"keyPassPhrase": ""
|
||||
},
|
||||
|
||||
"instCapture" :
|
||||
{
|
||||
"systemEnable" : false,
|
||||
"systemGrabberDevice" : "NONE",
|
||||
"systemPriority" : 250,
|
||||
"v4lEnable" : false,
|
||||
"v4lGrabberDevice" : "NONE",
|
||||
"v4lPriority" : 240
|
||||
"effects": {
|
||||
"paths": [ "$ROOT/custom-effects" ],
|
||||
"disable": [ "" ]
|
||||
},
|
||||
|
||||
"network" :
|
||||
{
|
||||
"internetAccessAPI" : false,
|
||||
"restirctedInternetAccessAPI" : false,
|
||||
"ipWhitelist" : [],
|
||||
"apiAuth" : true,
|
||||
"localApiAuth" : false,
|
||||
"instCapture": {
|
||||
"systemEnable": false,
|
||||
"systemGrabberDevice": "NONE",
|
||||
"systemPriority": 250,
|
||||
"v4lEnable": false,
|
||||
"v4lGrabberDevice": "NONE",
|
||||
"v4lPriority": 240,
|
||||
"audioEnable": false,
|
||||
"audioGrabberDevice": "NONE",
|
||||
"audioPriority": 230
|
||||
},
|
||||
|
||||
"network": {
|
||||
"internetAccessAPI": false,
|
||||
"restirctedInternetAccessAPI": false,
|
||||
"ipWhitelist": [],
|
||||
"apiAuth": true,
|
||||
"localApiAuth": false,
|
||||
"localAdminAuth": true
|
||||
},
|
||||
|
||||
"ledConfig" :
|
||||
{
|
||||
"classic":
|
||||
{
|
||||
"top" : 1,
|
||||
"bottom" : 0,
|
||||
"left" : 0,
|
||||
"right" : 0,
|
||||
"glength" : 0,
|
||||
"gpos" : 0,
|
||||
"position" : 0,
|
||||
"reverse" : false,
|
||||
"hdepth" : 8,
|
||||
"vdepth" : 5,
|
||||
"overlap" : 0,
|
||||
"edgegap" : 0,
|
||||
"ptlh" : 0,
|
||||
"ptlv" : 0,
|
||||
"ptrh" : 100,
|
||||
"ptrv" : 0,
|
||||
"pblh" : 0,
|
||||
"pblv" : 100,
|
||||
"pbrh" : 100,
|
||||
"pbrv" : 100
|
||||
},
|
||||
"ledConfig": {
|
||||
"classic": {
|
||||
"top": 1,
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"right": 0,
|
||||
"glength": 0,
|
||||
"gpos": 0,
|
||||
"position": 0,
|
||||
"reverse": false,
|
||||
"hdepth": 8,
|
||||
"vdepth": 5,
|
||||
"overlap": 0,
|
||||
"edgegap": 0,
|
||||
"ptlh": 0,
|
||||
"ptlv": 0,
|
||||
"ptrh": 100,
|
||||
"ptrv": 0,
|
||||
"pblh": 0,
|
||||
"pblv": 100,
|
||||
"pbrh": 100,
|
||||
"pbrv": 100
|
||||
},
|
||||
|
||||
"matrix": {
|
||||
"ledshoriz": 1,
|
||||
@ -245,8 +242,7 @@
|
||||
}
|
||||
},
|
||||
|
||||
"leds":
|
||||
[
|
||||
"leds": [
|
||||
{
|
||||
"hmax": 1,
|
||||
"hmin": 0,
|
||||
|
37
dependencies/CMakeLists-mbedtls.txt.in
vendored
37
dependencies/CMakeLists-mbedtls.txt.in
vendored
@ -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}
|
||||
)
|
78
dependencies/CMakeLists.txt
vendored
78
dependencies/CMakeLists.txt
vendored
@ -150,7 +150,7 @@ if(ENABLE_PROTOBUF_SERVER)
|
||||
set(protobuf_MSVC_STATIC_RUNTIME OFF CACHE BOOL "Build protobuf static")
|
||||
endif()
|
||||
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/cmake")
|
||||
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf")
|
||||
|
||||
# define the include for the protobuf library
|
||||
set(PROTOBUF_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/external/protobuf/src")
|
||||
@ -265,15 +265,19 @@ if(ENABLE_DEV_NETWORK)
|
||||
set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF PARENT_SCOPE)
|
||||
set(USE_SYSTEM_MBEDTLS_LIBS OFF)
|
||||
endif (NOT MBEDTLS_FOUND)
|
||||
endif (USE_SYSTEM_MBEDTLS_LIBS)
|
||||
|
||||
if (NOT USE_SYSTEM_MBEDTLS_LIBS)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0071 NEW)
|
||||
|
||||
set(DEFAULT_USE_SYSTEM_MBEDTLS_LIBS OFF CACHE BOOL "system mbedtls libraries not found, disable use system mbedtls libraries")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared mbedtls libraries")
|
||||
|
||||
set(ENABLE_TESTING OFF CACHE BOOL "Disable mbedTLS tests")
|
||||
set(GEN_FILES OFF CACHE BOOL "Disable mbedTLS auto-generated files")
|
||||
set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable mbedTLS programs")
|
||||
#set(LINK_WITH_PTHREAD ON CACHE BOOL "Enable mbedTLS library linked to pthread.")
|
||||
|
||||
set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "Disable mbedTLS shared libraries")
|
||||
set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "Enable mbedTLS static libraries")
|
||||
|
||||
@ -287,69 +291,7 @@ if(ENABLE_DEV_NETWORK)
|
||||
set(MBEDTLS_LOGGING 0)
|
||||
endif ()
|
||||
|
||||
set(MBEDTLS_CMAKE_ARGS
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=${MBEDTLS_INSTALL_DIR}
|
||||
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
|
||||
-DUSE_SHARED_MBEDTLS_LIBRARY:BOOL=OFF
|
||||
-DUSE_STATIC_MBEDTLS_LIBRARY:BOOL=ON
|
||||
-DENABLE_TESTING:BOOL=OFF
|
||||
-DENABLE_PROGRAMS:BOOL=OFF
|
||||
-DLINK_WITH_PTHREAD:BOOL=ON
|
||||
-Wno-dev
|
||||
#-DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=TRUE
|
||||
)
|
||||
|
||||
set(ENABLE_MBEDTLS_FETCH_CONTENT ON)
|
||||
|
||||
if (ENABLE_MBEDTLS_FETCH_CONTENT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.11)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
mbedtls
|
||||
GIT_REPOSITORY https://github.com/ARMmbed/mbedtls.git
|
||||
GIT_TAG "v3.1.0" # Bump versions manually if necessary, do not rely on origin/master to be stable
|
||||
BUILD_ALWAYS OFF
|
||||
GIT_PROGRESS 1
|
||||
DOWNLOAD_DIR "${MBEDTLS_DOWNLOAD_DIR}"
|
||||
SOURCE_DIR "${MBEDTLS_SOURCE_DIR}"
|
||||
BINARY_DIR "${MBEDTLS_BINARY_DIR}"
|
||||
INSTALL_DIR "${MBEDTLS_INSTALL_DIR}"
|
||||
CMAKE_ARGS ${MBEDTLS_CMAKE_ARGS}
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD ${MBEDTLS_LOGGING}
|
||||
LOG_UPDATE ${MBEDTLS_LOGGING}
|
||||
LOG_PATCH ${MBEDTLS_LOGGING}
|
||||
LOG_CONFIGURE ${MBEDTLS_LOGGING}
|
||||
LOG_BUILD ${MBEDTLS_LOGGING}
|
||||
LOG_INSTALL ${MBEDTLS_LOGGING}
|
||||
LOG_TEST ${MBEDTLS_LOGGING}
|
||||
LOG_MERGED_STDOUTERR ${MBEDTLS_LOGGING}
|
||||
LOG_OUTPUT_ON_FAILURE ${MBEDTLS_LOGGING}
|
||||
)
|
||||
|
||||
if (CMAKE_VERSION VERSION_LESS 3.14)
|
||||
macro (FetchContent_MakeAvailable NAME)
|
||||
FetchContent_GetProperties(${NAME})
|
||||
if (NOT ${NAME}_POPULATED)
|
||||
FetchContent_Populate(${NAME})
|
||||
add_subdirectory(${${NAME}_SOURCE_DIR} ${${NAME}_BINARY_DIR})
|
||||
endif ()
|
||||
endmacro ()
|
||||
endif ()
|
||||
|
||||
FetchContent_MakeAvailable(mbedtls)
|
||||
else ()
|
||||
set(ENABLE_MBEDTLS_FETCH_CONTENT OFF PARENT_SCOPE)
|
||||
if(NOT DEFINED BUILD_MBEDTLS_ONCE)
|
||||
set(BUILD_MBEDTLS_ONCE CACHE INTERNAL "Done")
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dependencies/CMakeLists-mbedtls.txt.in ${MBEDTLS_DOWNLOAD_DIR}/CMakeLists.txt @ONLY)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX:PATH=${MBEDTLS_INSTALL_DIR} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${MBEDTLS_DOWNLOAD_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${MBEDTLS_DOWNLOAD_DIR})
|
||||
add_subdirectory(${MBEDTLS_SOURCE_DIR} ${MBEDTLS_BINARY_DIR})
|
||||
endif()
|
||||
endif ()
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/mbedtls)
|
||||
|
||||
set (MBEDTLS_INCLUDE_DIR "${MBEDTLS_SOURCE_DIR}/include")
|
||||
set (MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR} PARENT_SCOPE)
|
||||
@ -387,5 +329,5 @@ if(ENABLE_DEV_NETWORK)
|
||||
|
||||
mark_as_advanced (MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES MBEDTLS_SSL_LIBRARY MBEDTLS_X509_LIBRARY MBEDTLS_CRYPTO_LIBRARY)
|
||||
|
||||
endif (NOT USE_SYSTEM_MBEDTLS_LIBS)
|
||||
endif (USE_SYSTEM_MBEDTLS_LIBS)
|
||||
endif(ENABLE_DEV_NETWORK)
|
||||
|
1
dependencies/external/mbedtls
vendored
Submodule
1
dependencies/external/mbedtls
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 8c89224991adff88d53cd380f42a2baa36f91454
|
2
dependencies/external/protobuf
vendored
2
dependencies/external/protobuf
vendored
@ -1 +1 @@
|
||||
Subproject commit 5ad0697c868235f24559dc07f8a42870d160af76
|
||||
Subproject commit f0dc78d7e6e331b8c6bb2d5283e06aa26883ca7c
|
@ -87,14 +87,14 @@ cd $HYPERION_HOME
|
||||
|
||||
```console
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libturbojpeg0-dev libjpeg-dev libssl-dev
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5svg5-dev libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev
|
||||
```
|
||||
|
||||
**Ubuntu (22.04+) - Qt6 based**
|
||||
|
||||
```console
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config
|
||||
sudo apt-get install git cmake build-essential qt6-base-dev libqt6serialport6-dev libvulkan-dev libgl1-mesa-dev libusb-1.0-0-dev python3-dev libasound2-dev libturbojpeg0-dev libjpeg-dev libssl-dev pkg-config
|
||||
```
|
||||
|
||||
**For Linux X11/XCB grabber support**
|
||||
@ -136,7 +136,7 @@ See [AUR](https://aur.archlinux.org/packages/?O=0&SeB=nd&K=hyperion&outdated=&SB
|
||||
The following dependencies are needed to build hyperion.ng on fedora.
|
||||
```console
|
||||
sudo dnf -y groupinstall "Development Tools"
|
||||
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel
|
||||
sudo dnf install python3-devel qt-devel qt5-qtbase-devel qt5-qtserialport-devel xrandr xcb-util-image-devel qt5-qtx11extras-devel alsa-lib-devel turbojpeg-devel libusb-devel xcb-util-devel dbus-devel openssl-devel fedora-packager rpmdevtools gcc libcec-devel
|
||||
```
|
||||
After installing the dependencies, you can continue with the compile instructions later on this page (the more detailed way..).
|
||||
|
||||
|
42
doc/development/Releasing_Hyperion.md
Normal file
42
doc/development/Releasing_Hyperion.md
Normal 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
|
||||
|
@ -1,54 +1,54 @@
|
||||
import hyperion, time
|
||||
|
||||
# Get the parameters
|
||||
speed = float(hyperion.args.get('speed', 1.0))
|
||||
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
|
||||
color = hyperion.args.get('color', (255,0,0))
|
||||
|
||||
# Check parameters
|
||||
speed = max(0.0001, speed)
|
||||
fadeFactor = max(0.0, min(fadeFactor, 1.0))
|
||||
|
||||
# Initialize the led data
|
||||
width = 25
|
||||
imageData = bytearray(width * (0,0,0))
|
||||
imageData[0] = color[0]
|
||||
imageData[1] = color[1]
|
||||
imageData[2] = color[2]
|
||||
|
||||
# Calculate the sleep time and rotation increment
|
||||
increment = 1
|
||||
sleepTime = 1.0 / (speed * width)
|
||||
while sleepTime < 0.05:
|
||||
increment *= 2
|
||||
sleepTime *= 2
|
||||
|
||||
# Start the write data loop
|
||||
position = 0
|
||||
direction = 1
|
||||
while not hyperion.abort():
|
||||
hyperion.setImage(width, 1, imageData)
|
||||
|
||||
# Move data into next state
|
||||
for i in range(increment):
|
||||
position += direction
|
||||
if position == -1:
|
||||
position = 1
|
||||
direction = 1
|
||||
elif position == width:
|
||||
position = width-2
|
||||
direction = -1
|
||||
|
||||
# Fade the old data
|
||||
for j in range(width):
|
||||
imageData[3*j] = int(fadeFactor * imageData[3*j])
|
||||
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
|
||||
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
|
||||
|
||||
# Insert new data
|
||||
imageData[3*position] = color[0]
|
||||
imageData[3*position+1] = color[1]
|
||||
imageData[3*position+2] = color[2]
|
||||
|
||||
# Sleep for a while
|
||||
time.sleep(sleepTime)
|
||||
import hyperion, time
|
||||
|
||||
# Get the parameters
|
||||
speed = float(hyperion.args.get('speed', 1.0))
|
||||
fadeFactor = float(hyperion.args.get('fadeFactor', 0.7))
|
||||
color = hyperion.args.get('color', (255,0,0))
|
||||
|
||||
# Check parameters
|
||||
speed = max(0.0001, speed)
|
||||
fadeFactor = max(0.0, min(fadeFactor, 1.0))
|
||||
|
||||
# Initialize the led data
|
||||
width = 25
|
||||
imageData = bytearray(width * (0,0,0))
|
||||
imageData[0] = color[0]
|
||||
imageData[1] = color[1]
|
||||
imageData[2] = color[2]
|
||||
|
||||
# Calculate the sleep time and rotation increment
|
||||
increment = 1
|
||||
sleepTime = 1.0 / (speed * width)
|
||||
while sleepTime < 0.05:
|
||||
increment *= 2
|
||||
sleepTime *= 2
|
||||
|
||||
# Start the write data loop
|
||||
position = 0
|
||||
direction = 1
|
||||
while not hyperion.abort():
|
||||
hyperion.setImage(width, 1, imageData)
|
||||
|
||||
# Move data into next state
|
||||
for unused in range(increment):
|
||||
position += direction
|
||||
if position == -1:
|
||||
position = 1
|
||||
direction = 1
|
||||
elif position == width:
|
||||
position = width-2
|
||||
direction = -1
|
||||
|
||||
# Fade the old data
|
||||
for j in range(width):
|
||||
imageData[3*j] = int(fadeFactor * imageData[3*j])
|
||||
imageData[3*j+1] = int(fadeFactor * imageData[3*j+1])
|
||||
imageData[3*j+2] = int(fadeFactor * imageData[3*j+2])
|
||||
|
||||
# Insert new data
|
||||
imageData[3*position] = color[0]
|
||||
imageData[3*position+1] = color[1]
|
||||
imageData[3*position+2] = color[2]
|
||||
|
||||
# Sleep for a while
|
||||
time.sleep(sleepTime)
|
||||
|
@ -1,23 +1,23 @@
|
||||
import hyperion, time, colorsys
|
||||
|
||||
# Get the parameters
|
||||
rotationTime = float(hyperion.args.get('rotation-time', 30.0))
|
||||
brightness = float(hyperion.args.get('brightness', 100))/100.0
|
||||
saturation = float(hyperion.args.get('saturation', 100))/100.0
|
||||
reverse = bool(hyperion.args.get('reverse', False))
|
||||
|
||||
# Calculate the sleep time and hue increment
|
||||
sleepTime = 0.1
|
||||
hueIncrement = sleepTime / rotationTime
|
||||
|
||||
# Switch direction if needed
|
||||
if reverse:
|
||||
increment = -increment
|
||||
|
||||
# Start the write data loop
|
||||
hue = 0.0
|
||||
while not hyperion.abort():
|
||||
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
|
||||
hyperion.setColor(int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))
|
||||
hue = (hue + hueIncrement) % 1.0
|
||||
time.sleep(sleepTime)
|
||||
import hyperion, time, colorsys
|
||||
|
||||
# Get the parameters
|
||||
rotationTime = float(hyperion.args.get('rotation-time', 30.0))
|
||||
brightness = float(hyperion.args.get('brightness', 100))/100.0
|
||||
saturation = float(hyperion.args.get('saturation', 100))/100.0
|
||||
reverse = bool(hyperion.args.get('reverse', False))
|
||||
|
||||
# Calculate the sleep time and hue increment
|
||||
sleepTime = 0.1
|
||||
hueIncrement = sleepTime / rotationTime
|
||||
|
||||
# Switch direction if needed
|
||||
if reverse:
|
||||
hueIncrement = -hueIncrement
|
||||
|
||||
# Start the write data loop
|
||||
hue = 0.0
|
||||
while not hyperion.abort():
|
||||
rgb = colorsys.hsv_to_rgb(hue, saturation, brightness)
|
||||
hyperion.setColor(int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2]))
|
||||
hue = (hue + hueIncrement) % 1.0
|
||||
time.sleep(sleepTime)
|
||||
|
@ -1,43 +1,43 @@
|
||||
import hyperion, time
|
||||
|
||||
# get options from args
|
||||
sleepTime = float(hyperion.args.get('speed', 1.5)) * 0.005
|
||||
whiteLevel = int(hyperion.args.get('whiteLevel', 0))
|
||||
lvl = int(hyperion.args.get('colorLevel', 220))
|
||||
|
||||
# check value
|
||||
whiteLevel = min( whiteLevel, 254 )
|
||||
lvl = min( lvl, 255 )
|
||||
|
||||
if whiteLevel >= lvl:
|
||||
lvl = 255
|
||||
|
||||
# Initialize the led data
|
||||
ledData = bytearray()
|
||||
for i in range(hyperion.ledCount):
|
||||
ledData += bytearray((0,0,0))
|
||||
|
||||
runners = [
|
||||
{ "pos":0, "step": 4, "lvl":lvl},
|
||||
{ "pos":1, "step": 5, "lvl":lvl},
|
||||
{ "pos":2, "step": 6, "lvl":lvl},
|
||||
{ "pos":0, "step": 7, "lvl":lvl},
|
||||
{ "pos":1, "step": 8, "lvl":lvl},
|
||||
{ "pos":2, "step": 9, "lvl":lvl},
|
||||
#{ "pos":0, "step":10, "lvl":lvl},
|
||||
#{ "pos":1, "step":11, "lvl":lvl},
|
||||
#{ "pos":2, "step":12, "lvl":lvl},
|
||||
]
|
||||
|
||||
# Start the write data loop
|
||||
counter = 0
|
||||
while not hyperion.abort():
|
||||
counter += 1
|
||||
for r in runners:
|
||||
if counter % r["step"] == 0:
|
||||
ledData[r["pos"]] = whiteLevel
|
||||
r["pos"] = (r["pos"]+3) % (hyperion.ledCount*3)
|
||||
ledData[r["pos"]] = r["lvl"]
|
||||
|
||||
hyperion.setColor(ledData)
|
||||
time.sleep(sleepTime)
|
||||
import hyperion, time
|
||||
|
||||
# get options from args
|
||||
sleepTime = float(hyperion.args.get('speed', 1.5)) * 0.005
|
||||
whiteLevel = int(hyperion.args.get('whiteLevel', 0))
|
||||
lvl = int(hyperion.args.get('colorLevel', 220))
|
||||
|
||||
# check value
|
||||
whiteLevel = min( whiteLevel, 254 )
|
||||
lvl = min( lvl, 255 )
|
||||
|
||||
if whiteLevel >= lvl:
|
||||
lvl = 255
|
||||
|
||||
# Initialize the led data
|
||||
ledData = bytearray()
|
||||
for unused in range(hyperion.ledCount):
|
||||
ledData += bytearray((0,0,0))
|
||||
|
||||
runners = [
|
||||
{ "pos":0, "step": 4, "lvl":lvl},
|
||||
{ "pos":1, "step": 5, "lvl":lvl},
|
||||
{ "pos":2, "step": 6, "lvl":lvl},
|
||||
{ "pos":0, "step": 7, "lvl":lvl},
|
||||
{ "pos":1, "step": 8, "lvl":lvl},
|
||||
{ "pos":2, "step": 9, "lvl":lvl},
|
||||
#{ "pos":0, "step":10, "lvl":lvl},
|
||||
#{ "pos":1, "step":11, "lvl":lvl},
|
||||
#{ "pos":2, "step":12, "lvl":lvl},
|
||||
]
|
||||
|
||||
# Start the write data loop
|
||||
counter = 0
|
||||
while not hyperion.abort():
|
||||
counter += 1
|
||||
for r in runners:
|
||||
if counter % r["step"] == 0:
|
||||
ledData[r["pos"]] = whiteLevel
|
||||
r["pos"] = (r["pos"]+3) % (hyperion.ledCount*3)
|
||||
ledData[r["pos"]] = r["lvl"]
|
||||
|
||||
hyperion.setColor(ledData)
|
||||
time.sleep(sleepTime)
|
||||
|
@ -42,7 +42,7 @@ def buildGradient(cc, closeCircle = True):
|
||||
pos = 0
|
||||
if len(cc[0]) == 4:
|
||||
withAlpha = True
|
||||
|
||||
|
||||
for c in cc:
|
||||
if withAlpha:
|
||||
alpha = int(c[3]*255)
|
||||
@ -50,7 +50,7 @@ def buildGradient(cc, closeCircle = True):
|
||||
alpha = 255
|
||||
pos += posfac
|
||||
ba += bytearray([pos,c[0],c[1],c[2],alpha])
|
||||
|
||||
|
||||
if closeCircle:
|
||||
# last color as first color
|
||||
lC = cc[-1]
|
||||
@ -61,6 +61,7 @@ def buildGradient(cc, closeCircle = True):
|
||||
ba += bytearray([0,lC[0],lC[1],lC[2],alpha])
|
||||
|
||||
return ba
|
||||
return bytearray()
|
||||
|
||||
def rotateAngle( increment = 1):
|
||||
global angle
|
||||
|
@ -1,98 +1,98 @@
|
||||
import hyperion
|
||||
import time
|
||||
import colorsys
|
||||
import random
|
||||
|
||||
min_len = int(hyperion.args.get('min_len', 3))
|
||||
max_len = int(hyperion.args.get('max_len', 3))
|
||||
#iHeight = int(hyperion.args.get('iHeight', 8))
|
||||
trails = int(hyperion.args.get('int', 8))
|
||||
sleepTime = float(hyperion.args.get('speed', 1)) / 1000.0
|
||||
color = list(hyperion.args.get('color', (255,255,255)))
|
||||
randomise = bool(hyperion.args.get('random', False))
|
||||
iWidth = hyperion.imageWidth()
|
||||
iHeight = hyperion.imageHeight()
|
||||
|
||||
class trail:
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
def start(self, x, y, step, color, _len, _h):
|
||||
self.pos = 0.0
|
||||
self.step = step
|
||||
self.h = _h
|
||||
self.x = x
|
||||
self.data = []
|
||||
brigtness = color[2]
|
||||
step_brigtness = color[2] / _len
|
||||
for i in range(0, _len):
|
||||
rgb = colorsys.hsv_to_rgb(color[0], color[1], brigtness)
|
||||
self.data.insert(0, (int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
|
||||
brigtness -= step_brigtness
|
||||
|
||||
self.data.extend([(0,0,0)]*(_h-y))
|
||||
if len(self.data) < _h:
|
||||
for i in range (_h-len(self.data)):
|
||||
self.data.insert(0, (0,0,0))
|
||||
|
||||
def getdata(self):
|
||||
self.pos += self.step
|
||||
if self.pos > 1.0:
|
||||
self.pos = 0.0
|
||||
self.data.pop()
|
||||
self.data.insert(0, (0,0,0))
|
||||
return self.x, self.data[-self.h:], all(x == self.data[0] for x in self.data)
|
||||
|
||||
tr = []
|
||||
|
||||
for i in range(trails):
|
||||
r = {'exec': trail()}
|
||||
|
||||
if randomise:
|
||||
col = (random.uniform(0.0, 1.0),1,1)
|
||||
else:
|
||||
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||
|
||||
r['exec'].start(
|
||||
random.randint(0, iWidth),
|
||||
random.randint(0, iHeight),
|
||||
random.uniform(0.2, 0.8),
|
||||
col,
|
||||
random.randint(min_len, max_len),
|
||||
iHeight
|
||||
)
|
||||
tr.append(r)
|
||||
|
||||
# Start the write data loop
|
||||
while not hyperion.abort():
|
||||
ledData = bytearray()
|
||||
|
||||
for r in tr:
|
||||
r['x'], r['data'], c = r['exec'].getdata()
|
||||
if c:
|
||||
if randomise:
|
||||
col = (random.uniform(0.0, 1.0),1,1)
|
||||
else:
|
||||
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||
|
||||
r['exec'].start(
|
||||
random.randint(0, iWidth),
|
||||
random.randint(0, iHeight),
|
||||
random.uniform(0.2, 0.8),
|
||||
col,
|
||||
random.randint(min_len, max_len),
|
||||
iHeight
|
||||
)
|
||||
|
||||
for y in range(0, iHeight):
|
||||
for x in range(0, iWidth):
|
||||
for r in tr:
|
||||
if x == r['x']:
|
||||
led = bytearray(r['data'][y])
|
||||
break
|
||||
led = bytearray((0,0,0))
|
||||
ledData += led
|
||||
|
||||
hyperion.setImage(iWidth,iHeight,ledData)
|
||||
time.sleep(sleepTime)
|
||||
|
||||
import hyperion
|
||||
import time
|
||||
import colorsys
|
||||
import random
|
||||
|
||||
min_len = int(hyperion.args.get('min_len', 3))
|
||||
max_len = int(hyperion.args.get('max_len', 3))
|
||||
#iHeight = int(hyperion.args.get('iHeight', 8))
|
||||
trails = int(hyperion.args.get('int', 8))
|
||||
sleepTime = float(hyperion.args.get('speed', 1)) / 1000.0
|
||||
color = list(hyperion.args.get('color', (255,255,255)))
|
||||
randomise = bool(hyperion.args.get('random', False))
|
||||
iWidth = hyperion.imageWidth()
|
||||
iHeight = hyperion.imageHeight()
|
||||
|
||||
class trail:
|
||||
def __init__(self):
|
||||
return
|
||||
|
||||
def start(self, x, y, step, color, _len, _h):
|
||||
self.pos = 0.0
|
||||
self.step = step
|
||||
self.h = _h
|
||||
self.x = x
|
||||
self.data = []
|
||||
brigtness = color[2]
|
||||
step_brigtness = color[2] / _len
|
||||
for i in range(0, _len):
|
||||
rgb = colorsys.hsv_to_rgb(color[0], color[1], brigtness)
|
||||
self.data.insert(0, (int(255*rgb[0]), int(255*rgb[1]), int(255*rgb[2])))
|
||||
brigtness -= step_brigtness
|
||||
|
||||
self.data.extend([(0,0,0)]*(_h-y))
|
||||
if len(self.data) < _h:
|
||||
for i in range (_h-len(self.data)):
|
||||
self.data.insert(0, (0,0,0))
|
||||
|
||||
def getdata(self):
|
||||
self.pos += self.step
|
||||
if self.pos > 1.0:
|
||||
self.pos = 0.0
|
||||
self.data.pop()
|
||||
self.data.insert(0, (0,0,0))
|
||||
return self.x, self.data[-self.h:], all(x == self.data[0] for x in self.data)
|
||||
|
||||
tr = []
|
||||
|
||||
for unused in range(trails):
|
||||
r = {'exec': trail()}
|
||||
|
||||
if randomise:
|
||||
col = (random.uniform(0.0, 1.0),1,1)
|
||||
else:
|
||||
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||
|
||||
r['exec'].start(
|
||||
random.randint(0, iWidth),
|
||||
random.randint(0, iHeight),
|
||||
random.uniform(0.2, 0.8),
|
||||
col,
|
||||
random.randint(min_len, max_len),
|
||||
iHeight
|
||||
)
|
||||
tr.append(r)
|
||||
|
||||
# Start the write data loop
|
||||
while not hyperion.abort():
|
||||
ledData = bytearray()
|
||||
|
||||
for r in tr:
|
||||
r['x'], r['data'], c = r['exec'].getdata()
|
||||
if c:
|
||||
if randomise:
|
||||
col = (random.uniform(0.0, 1.0),1,1)
|
||||
else:
|
||||
col = colorsys.rgb_to_hsv(color[0]/255.0, color[1]/255.0, color[2]/255.0)
|
||||
|
||||
r['exec'].start(
|
||||
random.randint(0, iWidth),
|
||||
random.randint(0, iHeight),
|
||||
random.uniform(0.2, 0.8),
|
||||
col,
|
||||
random.randint(min_len, max_len),
|
||||
iHeight
|
||||
)
|
||||
|
||||
for y in range(0, iHeight):
|
||||
for x in range(0, iWidth):
|
||||
for r in tr:
|
||||
if x == r['x']:
|
||||
led = bytearray(r['data'][y])
|
||||
break
|
||||
led = bytearray((0,0,0))
|
||||
ledData += led
|
||||
|
||||
hyperion.setImage(iWidth,iHeight,ledData)
|
||||
time.sleep(sleepTime)
|
||||
|
||||
|
148
effects/waves.py
148
effects/waves.py
@ -1,77 +1,71 @@
|
||||
import hyperion, time, math, random
|
||||
|
||||
randomCenter = bool(hyperion.args.get('random-center', False))
|
||||
centerX = float(hyperion.args.get('center_x', -0.15))
|
||||
centerY = float(hyperion.args.get('center_y', -0.25))
|
||||
rotationTime = float(hyperion.args.get('rotation_time', 90))
|
||||
colors = hyperion.args.get('colors', ((255,0,0),(255,255,0),(0,255,0),(0,255,255),(0,0,255),(255,0,255)))
|
||||
reverse = bool(hyperion.args.get('reverse', False))
|
||||
reverseTime = int(hyperion.args.get('reverse_time', 0))
|
||||
#rotate = bool(hyperion.args.get('rotate', True))
|
||||
positions = []
|
||||
|
||||
# calc center if random
|
||||
if randomCenter:
|
||||
centerX = random.uniform(0.0, 1.0)
|
||||
centerY = random.uniform(0.0, 1.0)
|
||||
|
||||
rCenterX = int(round(float(hyperion.imageWidth())*centerX))
|
||||
rCenterY = int(round(float(hyperion.imageHeight())*centerY))
|
||||
|
||||
#calc interval
|
||||
sleepTime = max(1/(255/rotationTime), 0.016)
|
||||
|
||||
#calc diagonal
|
||||
if centerX < 0.5:
|
||||
cX = 1.0-centerX
|
||||
else:
|
||||
cX = 0.0+centerX
|
||||
|
||||
if centerY < 0.5:
|
||||
cY = 1.0-centerY
|
||||
else:
|
||||
cY = 0.0+centerY
|
||||
|
||||
diag = int(round(math.sqrt(((cX*hyperion.imageWidth())**2)+((cY*hyperion.imageHeight())**2))))
|
||||
# some diagonal overhead
|
||||
diag = int(diag*1.3)
|
||||
|
||||
# calc positions
|
||||
pos = 0
|
||||
step = int(255/len(colors))
|
||||
for _ in colors:
|
||||
positions.append(pos)
|
||||
pos += step
|
||||
|
||||
# target time
|
||||
targetTime = time.time()+float(reverseTime)
|
||||
|
||||
#hyperion.imageCOffset(int(hyperion.imageWidth()/2), int(hyperion.imageHeight()/2))
|
||||
|
||||
while not hyperion.abort():
|
||||
# verify reverseTime, randomize reverseTime based on reverseTime up to reversedTime*2
|
||||
if reverseTime >= 1:
|
||||
now = time.time()
|
||||
if now > targetTime:
|
||||
reverse = not reverse
|
||||
targetTime = time.time()+random.uniform(float(reverseTime), float(reverseTime*2.0))
|
||||
# apply rotate
|
||||
#if rotate:
|
||||
# hyperion.imageCRotate(1)
|
||||
# prepare bytearray with colors and positions
|
||||
gradientBa = bytearray()
|
||||
it = 0
|
||||
for color in colors:
|
||||
gradientBa += bytearray((positions[it],color[0],color[1],color[2]))
|
||||
it += 1
|
||||
|
||||
hyperion.imageRadialGradient(rCenterX,rCenterY, diag, gradientBa,0)
|
||||
|
||||
# increment positions
|
||||
for i, pos in enumerate(positions):
|
||||
if reverse:
|
||||
positions[i] = pos - 1 if pos >= 1 else 255
|
||||
else:
|
||||
positions[i] = pos + 1 if pos <= 254 else 0
|
||||
hyperion.imageShow()
|
||||
time.sleep(sleepTime)
|
||||
import hyperion, time, math, random
|
||||
|
||||
randomCenter = bool(hyperion.args.get('random-center', False))
|
||||
centerX = float(hyperion.args.get('center_x', -0.15))
|
||||
centerY = float(hyperion.args.get('center_y', -0.25))
|
||||
rotationTime = float(hyperion.args.get('rotation_time', 90))
|
||||
colors = hyperion.args.get('colors', ((255,0,0),(255,255,0),(0,255,0),(0,255,255),(0,0,255),(255,0,255)))
|
||||
reverse = bool(hyperion.args.get('reverse', False))
|
||||
reverseTime = int(hyperion.args.get('reverse_time', 0))
|
||||
positions = []
|
||||
|
||||
# calc center if random
|
||||
if randomCenter:
|
||||
centerX = random.uniform(0.0, 1.0)
|
||||
centerY = random.uniform(0.0, 1.0)
|
||||
|
||||
rCenterX = int(round(float(hyperion.imageWidth())*centerX))
|
||||
rCenterY = int(round(float(hyperion.imageHeight())*centerY))
|
||||
|
||||
#calc interval
|
||||
sleepTime = max(1/(255/rotationTime), 0.016)
|
||||
|
||||
#calc diagonal
|
||||
if centerX < 0.5:
|
||||
cX = 1.0-centerX
|
||||
else:
|
||||
cX = 0.0+centerX
|
||||
|
||||
if centerY < 0.5:
|
||||
cY = 1.0-centerY
|
||||
else:
|
||||
cY = 0.0+centerY
|
||||
|
||||
diag = int(round(math.hypot(cX*hyperion.imageWidth(),cY*hyperion.imageHeight())))
|
||||
# some diagonal overhead
|
||||
diag = int(diag*1.3)
|
||||
|
||||
# calc positions
|
||||
pos = 0
|
||||
step = int(255/len(colors))
|
||||
for _ in colors:
|
||||
positions.append(pos)
|
||||
pos += step
|
||||
|
||||
# target time
|
||||
targetTime = time.time()+float(reverseTime)
|
||||
while not hyperion.abort():
|
||||
# verify reverseTime, randomize reverseTime based on reverseTime up to reversedTime*2
|
||||
if reverseTime >= 1:
|
||||
now = time.time()
|
||||
if now > targetTime:
|
||||
reverse = not reverse
|
||||
targetTime = time.time()+random.uniform(float(reverseTime), float(reverseTime*2.0))
|
||||
|
||||
# prepare bytearray with colors and positions
|
||||
gradientBa = bytearray()
|
||||
it = 0
|
||||
for color in colors:
|
||||
gradientBa += bytearray((positions[it],color[0],color[1],color[2]))
|
||||
it += 1
|
||||
|
||||
hyperion.imageRadialGradient(rCenterX,rCenterY, diag, gradientBa,0)
|
||||
|
||||
# increment positions
|
||||
for i, pos in enumerate(positions):
|
||||
if reverse:
|
||||
positions[i] = pos - 1 if pos >= 1 else 255
|
||||
else:
|
||||
positions[i] = pos + 1 if pos <= 254 else 0
|
||||
hyperion.imageShow()
|
||||
time.sleep(sleepTime)
|
||||
|
@ -1,30 +1,30 @@
|
||||
import hyperion, time
|
||||
|
||||
# Get the parameters
|
||||
sleepTime = float(hyperion.args.get('sleepTime', 1000))/1000.0
|
||||
length = hyperion.args.get('length', 1)
|
||||
color1 = hyperion.args.get('color1', (255,255,255))
|
||||
color2 = hyperion.args.get('color2', (255,0,0))
|
||||
|
||||
# Initialize the led data
|
||||
i = 0
|
||||
ledDataOdd = bytearray()
|
||||
while i < hyperion.ledCount:
|
||||
for l in range(length):
|
||||
if i<hyperion.ledCount:
|
||||
ledDataOdd += bytearray((int(color1[0]), int(color1[1]), int(color1[2])))
|
||||
i += 1
|
||||
|
||||
for l in range(length):
|
||||
if i<hyperion.ledCount:
|
||||
ledDataOdd += bytearray((int(color2[0]), int(color2[1]), int(color2[2])))
|
||||
i += 1
|
||||
|
||||
ledDataEven = ledDataOdd[3*length:] + ledDataOdd[0:3*length]
|
||||
|
||||
# Start the write data loop
|
||||
while not hyperion.abort():
|
||||
hyperion.setColor(ledDataOdd)
|
||||
time.sleep(sleepTime)
|
||||
hyperion.setColor(ledDataEven)
|
||||
time.sleep(sleepTime)
|
||||
import hyperion, time
|
||||
|
||||
# Get the parameters
|
||||
sleepTime = float(hyperion.args.get('sleepTime', 1000))/1000.0
|
||||
length = hyperion.args.get('length', 1)
|
||||
color1 = hyperion.args.get('color1', (255,255,255))
|
||||
color2 = hyperion.args.get('color2', (255,0,0))
|
||||
|
||||
# Initialize the led data
|
||||
i = 0
|
||||
ledDataOdd = bytearray()
|
||||
while i < hyperion.ledCount:
|
||||
for unused in range(length):
|
||||
if i<hyperion.ledCount:
|
||||
ledDataOdd += bytearray((int(color1[0]), int(color1[1]), int(color1[2])))
|
||||
i += 1
|
||||
|
||||
for unused in range(length):
|
||||
if i<hyperion.ledCount:
|
||||
ledDataOdd += bytearray((int(color2[0]), int(color2[1]), int(color2[2])))
|
||||
i += 1
|
||||
|
||||
ledDataEven = ledDataOdd[3*length:] + ledDataOdd[0:3*length]
|
||||
|
||||
# Start the write data loop
|
||||
while not hyperion.abort():
|
||||
hyperion.setColor(ledDataOdd)
|
||||
time.sleep(sleepTime)
|
||||
hyperion.setColor(ledDataEven)
|
||||
time.sleep(sleepTime)
|
||||
|
@ -157,12 +157,7 @@ protected:
|
||||
///
|
||||
bool setHyperionInstance(quint8 inst);
|
||||
|
||||
///
|
||||
/// @brief Get all contrable components and their state
|
||||
///
|
||||
std::map<hyperion::Components, bool> getAllComponents();
|
||||
|
||||
///
|
||||
///
|
||||
/// @brief Check if Hyperion ist enabled
|
||||
/// @return True when enabled else false
|
||||
///
|
||||
|
@ -1,4 +1,3 @@
|
||||
//#include <iostream>
|
||||
#pragma once
|
||||
|
||||
// Utils includes
|
||||
@ -219,7 +218,6 @@ namespace hyperion
|
||||
|| !isBlack(image((width - x), y))
|
||||
|| !isBlack(image((width - x), (height - y))))
|
||||
{
|
||||
// std::cout << "y " << y << " lt " << int(isBlack(color1)) << " lb " << int(isBlack(color2)) << " rt " << int(isBlack(color3)) << " rb " << int(isBlack(color4)) << std::endl;
|
||||
firstNonBlackYPixelIndex = y;
|
||||
break;
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public:
|
||||
// server port services
|
||||
list << "jsonServer" << "protoServer" << "flatbufServer" << "forwarder" << "webConfig" << "network"
|
||||
// capture
|
||||
<< "framegrabber" << "grabberV4L2"
|
||||
<< "framegrabber" << "grabberV4L2" << "grabberAudio"
|
||||
// other
|
||||
<< "logger" << "general";
|
||||
|
||||
|
196
include/grabber/AudioGrabber.h
Normal file
196
include/grabber/AudioGrabber.h
Normal 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
|
91
include/grabber/AudioGrabberLinux.h
Normal file
91
include/grabber/AudioGrabberLinux.h
Normal 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
|
81
include/grabber/AudioGrabberWindows.h
Normal file
81
include/grabber/AudioGrabberWindows.h
Normal 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
|
69
include/grabber/AudioWrapper.h
Normal file
69
include/grabber/AudioWrapper.h
Normal 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
|
||||
|
||||
};
|
@ -4,12 +4,14 @@
|
||||
enum class GrabberType {
|
||||
SCREEN,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
};
|
||||
|
||||
enum class GrabberTypeFilter {
|
||||
ALL,
|
||||
SCREEN,
|
||||
VIDEO,
|
||||
AUDIO,
|
||||
};
|
||||
|
||||
#endif // GRABBERTYPE_H
|
||||
|
@ -20,6 +20,7 @@ public:
|
||||
|
||||
void setSystemCaptureEnable(bool enable);
|
||||
void setV4LCaptureEnable(bool enable);
|
||||
void setAudioCaptureEnable(bool enable);
|
||||
|
||||
private slots:
|
||||
///
|
||||
@ -48,11 +49,22 @@ private slots:
|
||||
///
|
||||
void handleV4lImage(const QString& name, const Image<ColorRgb> & image);
|
||||
|
||||
///
|
||||
/// @brief forward audio image
|
||||
/// @param image The image
|
||||
///
|
||||
void handleAudioImage(const QString& name, const Image<ColorRgb>& image);
|
||||
|
||||
///
|
||||
/// @brief Is called from _v4lInactiveTimer to set source after specific time to inactive
|
||||
///
|
||||
void setV4lInactive();
|
||||
|
||||
///
|
||||
/// @brief Is called from _audioInactiveTimer to set source after specific time to inactive
|
||||
///
|
||||
void setAudioInactive();
|
||||
|
||||
///
|
||||
/// @brief Is called from _systemInactiveTimer to set source after specific time to inactive
|
||||
///
|
||||
@ -73,4 +85,10 @@ private:
|
||||
quint8 _v4lCaptPrio;
|
||||
QString _v4lCaptName;
|
||||
QTimer* _v4lInactiveTimer;
|
||||
|
||||
/// Reflect state of audio capture and prio
|
||||
bool _audioCaptEnabled;
|
||||
quint8 _audioCaptPrio;
|
||||
QString _audioCaptName;
|
||||
QTimer* _audioInactiveTimer;
|
||||
};
|
||||
|
@ -43,8 +43,10 @@ public:
|
||||
|
||||
static QMap<int, QString> GRABBER_SYS_CLIENTS;
|
||||
static QMap<int, QString> GRABBER_V4L_CLIENTS;
|
||||
static QMap<int, QString> GRABBER_AUDIO_CLIENTS;
|
||||
static bool GLOBAL_GRABBER_SYS_ENABLE;
|
||||
static bool GLOBAL_GRABBER_V4L_ENABLE;
|
||||
static bool GLOBAL_GRABBER_AUDIO_ENABLE;
|
||||
|
||||
///
|
||||
/// Starts the grabber which produces led values with the specified update rate
|
||||
@ -78,6 +80,8 @@ public:
|
||||
void setSysGrabberState(bool sysGrabberState){ GLOBAL_GRABBER_SYS_ENABLE = sysGrabberState; }
|
||||
bool getV4lGrabberState() const { return GLOBAL_GRABBER_V4L_ENABLE; }
|
||||
void setV4lGrabberState(bool v4lGrabberState){ GLOBAL_GRABBER_V4L_ENABLE = v4lGrabberState; }
|
||||
bool getAudioGrabberState() const { return GLOBAL_GRABBER_AUDIO_ENABLE; }
|
||||
void setAudioGrabberState(bool audioGrabberState) { GLOBAL_GRABBER_AUDIO_ENABLE = audioGrabberState; }
|
||||
|
||||
static QStringList availableGrabbers(GrabberTypeFilter type = GrabberTypeFilter::ALL);
|
||||
|
||||
@ -147,10 +151,7 @@ private slots:
|
||||
void handleSourceRequest(hyperion::Components component, int hyperionInd, bool listen);
|
||||
|
||||
///
|
||||
/// @brief Update Update capture rate
|
||||
/// @param type interval between frames in milliseconds
|
||||
///
|
||||
void updateTimer(int interval);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
@ -168,6 +169,11 @@ protected:
|
||||
///
|
||||
virtual bool close() { return true; }
|
||||
|
||||
/// @brief Update Update capture rate
|
||||
/// @param type interval between frames in milliseconds
|
||||
///
|
||||
void updateTimer(int interval);
|
||||
|
||||
|
||||
QString _grabberName;
|
||||
|
||||
|
@ -471,6 +471,9 @@ signals:
|
||||
/// Signal which is emitted, when a new V4l proto image should be forwarded
|
||||
void forwardV4lProtoMessage(const QString&, const Image<ColorRgb>&);
|
||||
|
||||
/// Signal which is emitted, when a new Audio proto image should be forwarded
|
||||
void forwardAudioProtoMessage(const QString&, const Image<ColorRgb>&);
|
||||
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
/// Signal which is emitted, when a new Flat-/Proto- Buffer image should be forwarded
|
||||
void forwardBufferMessage(const QString&, const Image<ColorRgb>&);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QSharedPointer>
|
||||
|
||||
// Utils includes
|
||||
#include <utils/Image.h>
|
||||
@ -46,7 +47,7 @@ public:
|
||||
/// @param[in] width The new width of the buffer-image
|
||||
/// @param[in] height The new height of the buffer-image
|
||||
///
|
||||
void setSize(unsigned width, unsigned height);
|
||||
void setSize(int width, int height);
|
||||
|
||||
///
|
||||
/// @brief Update the led string (eg on settings change)
|
||||
@ -56,6 +57,19 @@ public:
|
||||
/// Returns state of black border detector
|
||||
bool blackBorderDetectorEnabled() const;
|
||||
|
||||
///
|
||||
/// Factor to reduce the number of pixels evaluated during processing
|
||||
///
|
||||
/// @param[in] count Use every "count" pixel
|
||||
void setReducedPixelSetFactorFactor(int count);
|
||||
|
||||
///
|
||||
/// Set the accuracy used during processing
|
||||
/// (only for selected types)
|
||||
///
|
||||
/// @param[in] level The accuracy level (0-4)
|
||||
void setAccuracyLevel(int level);
|
||||
|
||||
/// Returns the current _userMappingType, this may not be the current applied type!
|
||||
int getUserLedMappingType() const { return _userMappingType; }
|
||||
|
||||
@ -98,30 +112,45 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// Processes the image to a list of led colors. This will update the size of the buffer-image
|
||||
/// if required and call the image-to-leds mapping to determine the mean color per led.
|
||||
/// Processes the image to a list of LED colors. This will update the size of the buffer-image
|
||||
/// if required and call the image-to-LEDs mapping to determine the color per LED.
|
||||
///
|
||||
/// @param[in] image The image to translate to led values
|
||||
/// @param[in] image The image to translate to LED values
|
||||
///
|
||||
/// @return The color value per led
|
||||
/// @return The color value per LED
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> process(const Image<Pixel_T>& image)
|
||||
{
|
||||
std::vector<ColorRgb> colors;
|
||||
|
||||
if (image.width()>0 && image.height()>0)
|
||||
{
|
||||
// Ensure that the buffer-image is the proper size
|
||||
setSize(image);
|
||||
|
||||
assert(!_imageToLedColors.isNull());
|
||||
|
||||
// Check black border detection
|
||||
verifyBorder(image);
|
||||
|
||||
// Create a result vector and call the 'in place' function
|
||||
switch (_mappingType)
|
||||
{
|
||||
case 1: colors = _imageToLeds->getUniLedColor(image); break;
|
||||
default: colors = _imageToLeds->getMeanLedColor(image);
|
||||
case 1:
|
||||
colors = _imageToLedColors->getUniLedColor(image);
|
||||
break;
|
||||
case 2:
|
||||
colors = _imageToLedColors->getMeanLedColorSqrt(image);
|
||||
break;
|
||||
case 3:
|
||||
colors = _imageToLedColors->getDominantLedColor(image);
|
||||
break;
|
||||
case 4:
|
||||
colors = _imageToLedColors->getDominantLedColorAdv(image);
|
||||
break;
|
||||
default:
|
||||
colors = _imageToLedColors->getMeanLedColor(image);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -136,8 +165,8 @@ public:
|
||||
///
|
||||
/// Determines the led colors of the image in the buffer.
|
||||
///
|
||||
/// @param[in] image The image to translate to led values
|
||||
/// @param[out] ledColors The color value per led
|
||||
/// @param[in] image The image to translate to LED values
|
||||
/// @param[out] ledColors The color value per LED
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void process(const Image<Pixel_T>& image, std::vector<ColorRgb>& ledColors)
|
||||
@ -153,8 +182,20 @@ public:
|
||||
// Determine the mean or uni colors of each led (using the existing mapping)
|
||||
switch (_mappingType)
|
||||
{
|
||||
case 1: _imageToLeds->getUniLedColor(image, ledColors); break;
|
||||
default: _imageToLeds->getMeanLedColor(image, ledColors);
|
||||
case 1:
|
||||
_imageToLedColors->getUniLedColor(image, ledColors);
|
||||
break;
|
||||
case 2:
|
||||
_imageToLedColors->getMeanLedColorSqrt(image, ledColors);
|
||||
break;
|
||||
case 3:
|
||||
_imageToLedColors->getDominantLedColor(image, ledColors);
|
||||
break;
|
||||
case 4:
|
||||
_imageToLedColors->getDominantLedColorAdv(image, ledColors);
|
||||
break;
|
||||
default:
|
||||
_imageToLedColors->getMeanLedColor(image, ledColors);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -164,9 +205,9 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// Get the hscan and vscan parameters for a single led
|
||||
/// Get the hscan and vscan parameters for a single LED
|
||||
///
|
||||
/// @param[in] led Index of the led
|
||||
/// @param[in] led Index of the LED
|
||||
/// @param[out] hscanBegin begin of the hscan
|
||||
/// @param[out] hscanEnd end of the hscan
|
||||
/// @param[out] vscanBegin begin of the hscan
|
||||
@ -175,6 +216,13 @@ public:
|
||||
bool getScanParameters(size_t led, double & hscanBegin, double & hscanEnd, double & vscanBegin, double & vscanEnd) const;
|
||||
|
||||
private:
|
||||
|
||||
void registerProcessingUnit(
|
||||
int width,
|
||||
int height,
|
||||
int horizontalBorder,
|
||||
int verticalBorder);
|
||||
|
||||
///
|
||||
/// Performs black-border detection (if enabled) on the given image
|
||||
///
|
||||
@ -183,34 +231,25 @@ private:
|
||||
template <typename Pixel_T>
|
||||
void verifyBorder(const Image<Pixel_T> & image)
|
||||
{
|
||||
if (!_borderProcessor->enabled() && ( _imageToLeds->horizontalBorder()!=0 || _imageToLeds->verticalBorder()!=0 ))
|
||||
if (!_borderProcessor->enabled() && ( _imageToLedColors->horizontalBorder()!=0 || _imageToLedColors->verticalBorder()!=0 ))
|
||||
{
|
||||
Debug(_log, "Reset border");
|
||||
_borderProcessor->process(image);
|
||||
delete _imageToLeds;
|
||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
|
||||
registerProcessingUnit(image.width(), image.height(), 0, 0);
|
||||
}
|
||||
|
||||
if(_borderProcessor->enabled() && _borderProcessor->process(image))
|
||||
{
|
||||
const hyperion::BlackBorder border = _borderProcessor->getCurrentBorder();
|
||||
|
||||
// Clean up the old mapping
|
||||
delete _imageToLeds;
|
||||
|
||||
if (border.unknown)
|
||||
{
|
||||
// Construct a new buffer and mapping
|
||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), 0, 0, _ledString.leds());
|
||||
registerProcessingUnit(image.width(), image.height(), 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct a new buffer and mapping
|
||||
_imageToLeds = new hyperion::ImageToLedsMap(image.width(), image.height(), border.horizontalSize, border.verticalSize, _ledString.leds());
|
||||
registerProcessingUnit(image.width(), image.height(), border.horizontalSize, border.verticalSize);
|
||||
}
|
||||
|
||||
//Debug(Logger::getInstance("BLACKBORDER"), "CURRENT BORDER TYPE: unknown=%d hor.size=%d vert.size=%d",
|
||||
// border.unknown, border.horizontalSize, border.verticalSize );
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,6 +257,7 @@ private slots:
|
||||
void handleSettingsUpdate(settings::type type, const QJsonDocument& config);
|
||||
|
||||
private:
|
||||
|
||||
Logger * _log;
|
||||
/// The Led-string specification
|
||||
LedString _ledString;
|
||||
@ -226,15 +266,18 @@ private:
|
||||
hyperion::BlackBorderProcessor * _borderProcessor;
|
||||
|
||||
/// The mapping of image-pixels to LEDs
|
||||
hyperion::ImageToLedsMap* _imageToLeds;
|
||||
QSharedPointer<hyperion::ImageToLedsMap> _imageToLedColors;
|
||||
|
||||
/// Type of image 2 led mapping
|
||||
/// Type of image to LED mapping
|
||||
int _mappingType;
|
||||
/// Type of last requested user type
|
||||
int _userMappingType;
|
||||
/// Type of last requested hard type
|
||||
int _hardMappingType;
|
||||
|
||||
int _accuraryLevel;
|
||||
int _reducedPixelSetFactorFactor;
|
||||
|
||||
/// Hyperion instance pointer
|
||||
Hyperion* _hyperion;
|
||||
};
|
||||
|
@ -1,72 +1,90 @@
|
||||
|
||||
#pragma once
|
||||
#ifndef IMAGETOLEDSMAP_H
|
||||
#define IMAGETOLEDSMAP_H
|
||||
|
||||
// STL includes
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <cmath>
|
||||
|
||||
// hyperion-utils includes
|
||||
#include <utils/Image.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/ColorRgbScalar.h>
|
||||
#include <utils/ColorSys.h>
|
||||
|
||||
// hyperion includes
|
||||
#include <hyperion/LedString.h>
|
||||
|
||||
namespace hyperion
|
||||
{
|
||||
|
||||
///
|
||||
/// The ImageToLedsMap holds a mapping of indices into an image to leds. It can be used to
|
||||
/// calculate the average (or mean) color per led for a specific region.
|
||||
/// The ImageToLedsMap holds a mapping of indices into an image to LEDs. It can be used to
|
||||
/// calculate the average (aka mean) or dominant color per LED for a given region.
|
||||
///
|
||||
class ImageToLedsMap
|
||||
class ImageToLedsMap : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
///
|
||||
/// Constructs an mapping from the absolute indices in an image to each led based on the border
|
||||
/// definition given in the list of leds. The map holds absolute indices to any given image,
|
||||
/// Constructs an mapping from the absolute indices in an image to each LED based on the border
|
||||
/// definition given in the list of LEDs. The map holds absolute indices to any given image,
|
||||
/// provided that it is row-oriented.
|
||||
/// The mapping is created purely on size (width and height). The given borders are excluded
|
||||
/// from indexing.
|
||||
///
|
||||
/// @param[in] log Logger
|
||||
/// @param[in] width The width of the indexed image
|
||||
/// @param[in] height The width of the indexed image
|
||||
/// @param[in] horizontalBorder The size of the horizontal border (0=no border)
|
||||
/// @param[in] verticalBorder The size of the vertical border (0=no border)
|
||||
/// @param[in] leds The list with led specifications
|
||||
/// @param[in] reducedProcessingFactor Factor to reduce the number of pixels evaluated during processing
|
||||
/// @param[in] accuraryLevel The accuracy used during processing (only for selected types)
|
||||
///
|
||||
ImageToLedsMap(
|
||||
const unsigned width,
|
||||
const unsigned height,
|
||||
const unsigned horizontalBorder,
|
||||
const unsigned verticalBorder,
|
||||
const std::vector<Led> & leds);
|
||||
Logger* log,
|
||||
int width,
|
||||
int height,
|
||||
int horizontalBorder,
|
||||
int verticalBorder,
|
||||
const std::vector<Led> & leds,
|
||||
int reducedProcessingFactor = 0,
|
||||
int accuraryLevel = 0);
|
||||
|
||||
///
|
||||
/// Returns the width of the indexed image
|
||||
///
|
||||
/// @return The width of the indexed image [pixels]
|
||||
///
|
||||
unsigned width() const;
|
||||
int width() const;
|
||||
|
||||
///
|
||||
/// Returns the height of the indexed image
|
||||
///
|
||||
/// @return The height of the indexed image [pixels]
|
||||
///
|
||||
unsigned height() const;
|
||||
int height() const;
|
||||
|
||||
unsigned horizontalBorder() const { return _horizontalBorder; }
|
||||
unsigned verticalBorder() const { return _verticalBorder; }
|
||||
int horizontalBorder() const { return _horizontalBorder; }
|
||||
int verticalBorder() const { return _verticalBorder; }
|
||||
|
||||
///
|
||||
/// Determines the mean color for each led using the mapping the image given
|
||||
/// Set the accuracy used during processing
|
||||
/// (only for selected types)
|
||||
///
|
||||
/// @param[in] level The accuracy level (0-4)
|
||||
void setAccuracyLevel (int level);
|
||||
|
||||
///
|
||||
/// Determines the mean color for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the led colors
|
||||
///
|
||||
/// @return ledColors The vector containing the output
|
||||
/// @return The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> getMeanLedColor(const Image<Pixel_T> & image) const
|
||||
@ -77,20 +95,18 @@ namespace hyperion
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the mean color for each led using the mapping the image given
|
||||
/// Determines the mean color for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the led colors
|
||||
/// @param[in] image The image from which to extract the LED colors
|
||||
/// @param[out] ledColors The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void getMeanLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
|
||||
{
|
||||
// Sanity check for the number of leds
|
||||
//assert(_colorsMap.size() == ledColors.size());
|
||||
if(_colorsMap.size() != ledColors.size())
|
||||
{
|
||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -104,12 +120,52 @@ namespace hyperion
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the uni color for each led using the mapping the image given
|
||||
/// Determines the mean color squared for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the led colors
|
||||
///
|
||||
/// @return ledColors The vector containing the output
|
||||
/// @return The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> getMeanLedColorSqrt(const Image<Pixel_T> & image) const
|
||||
{
|
||||
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
|
||||
getMeanLedColorSqrt(image, colors);
|
||||
return colors;
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the mean color squared for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the LED colors
|
||||
/// @param[out] ledColors The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void getMeanLedColorSqrt(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
|
||||
{
|
||||
if(_colorsMap.size() != ledColors.size())
|
||||
{
|
||||
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate each led and compute the mean
|
||||
auto led = ledColors.begin();
|
||||
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
|
||||
{
|
||||
const ColorRgb color = calcMeanColorSqrt(image, *colors);
|
||||
*led = color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the mean color of the image and assigns it to all LEDs
|
||||
///
|
||||
/// @param[in] image The image from which to extract the led color
|
||||
///
|
||||
/// @return The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> getUniLedColor(const Image<Pixel_T> & image) const
|
||||
@ -120,57 +176,145 @@ namespace hyperion
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the uni color for each led using the mapping the image given
|
||||
/// at construction.
|
||||
/// Determines the mean color of the image and assigns it to all LEDs
|
||||
///
|
||||
/// @param[in] image The image from which to extract the led colors
|
||||
/// @param[in] image The image from which to extract the LED colors
|
||||
/// @param[out] ledColors The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void getUniLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
|
||||
{
|
||||
// Sanity check for the number of leds
|
||||
// assert(_colorsMap.size() == ledColors.size());
|
||||
if(_colorsMap.size() != ledColors.size())
|
||||
{
|
||||
Debug(Logger::getInstance("HYPERION"), "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// calculate uni color
|
||||
const ColorRgb color = calcMeanColor(image);
|
||||
//Update all LEDs with same color
|
||||
std::fill(ledColors.begin(),ledColors.end(), color);
|
||||
}
|
||||
|
||||
private:
|
||||
/// The width of the indexed image
|
||||
const unsigned _width;
|
||||
/// The height of the indexed image
|
||||
const unsigned _height;
|
||||
|
||||
const unsigned _horizontalBorder;
|
||||
|
||||
const unsigned _verticalBorder;
|
||||
|
||||
/// The absolute indices into the image for each led
|
||||
std::vector<std::vector<int32_t>> _colorsMap;
|
||||
///
|
||||
/// Determines the dominant color for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the LED color
|
||||
///
|
||||
/// @return The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> getDominantLedColor(const Image<Pixel_T> & image) const
|
||||
{
|
||||
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
|
||||
getDominantLedColor(image, colors);
|
||||
return colors;
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'mean color' of the given list. This is the mean over each color-channel
|
||||
/// Determines the dominant color for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the LED colors
|
||||
/// @param[out] ledColors The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void getDominantLedColor(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
|
||||
{
|
||||
// Sanity check for the number of LEDs
|
||||
if(_colorsMap.size() != ledColors.size())
|
||||
{
|
||||
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate each led and compute the dominant color
|
||||
auto led = ledColors.begin();
|
||||
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
|
||||
{
|
||||
const ColorRgb color = calculateDominantColor(image, *colors);
|
||||
*led = color;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the dominant color using a k-means algorithm for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the LED color
|
||||
///
|
||||
/// @return The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
std::vector<ColorRgb> getDominantLedColorAdv(const Image<Pixel_T> & image) const
|
||||
{
|
||||
std::vector<ColorRgb> colors(_colorsMap.size(), ColorRgb{0,0,0});
|
||||
getDominantLedColorAdv(image, colors);
|
||||
return colors;
|
||||
}
|
||||
|
||||
///
|
||||
/// Determines the dominant color using a k-means algorithm for each LED using the LED area mapping given
|
||||
/// at construction.
|
||||
///
|
||||
/// @param[in] image The image from which to extract the LED colors
|
||||
/// @param[out] ledColors The vector containing the output
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
void getDominantLedColorAdv(const Image<Pixel_T> & image, std::vector<ColorRgb> & ledColors) const
|
||||
{
|
||||
// Sanity check for the number of LEDs
|
||||
if(_colorsMap.size() != ledColors.size())
|
||||
{
|
||||
Debug(_log, "ImageToLedsMap: colorsMap.size != ledColors.size -> %d != %d", _colorsMap.size(), ledColors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate each led and compute the dominant color
|
||||
auto led = ledColors.begin();
|
||||
for (auto colors = _colorsMap.begin(); colors != _colorsMap.end(); ++colors, ++led)
|
||||
{
|
||||
const ColorRgb color = calculateDominantColorAdv(image, *colors);
|
||||
*led = color;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Logger* _log;
|
||||
|
||||
/// The width of the indexed image
|
||||
const int _width;
|
||||
/// The height of the indexed image
|
||||
const int _height;
|
||||
|
||||
const int _horizontalBorder;
|
||||
const int _verticalBorder;
|
||||
|
||||
/// Evaluate every "count" pixel
|
||||
int _nextPixelCount;
|
||||
|
||||
/// Number of clusters used during dominant color advanced processing (k-means)
|
||||
int _clusterCount;
|
||||
|
||||
/// The absolute indices into the image for each led
|
||||
std::vector<std::vector<int>> _colorsMap;
|
||||
|
||||
///
|
||||
/// Calculates the 'mean color' over the given image. This is the mean over each color-channel
|
||||
/// (red, green, blue)
|
||||
///
|
||||
/// @param[in] image The image a section from which an average color must be computed
|
||||
/// @param[in] colors The list with colors
|
||||
/// @param[in] pixels The list of pixel indices for the given image to be evaluated///
|
||||
///
|
||||
/// @return The mean of the given list of colors (or black when empty)
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calcMeanColor(const Image<Pixel_T> & image, const std::vector<int32_t> & colors) const
|
||||
ColorRgb calcMeanColor(const Image<Pixel_T> & image, const std::vector<int32_t> & pixels) const
|
||||
{
|
||||
const auto colorVecSize = colors.size();
|
||||
|
||||
if (colorVecSize == 0)
|
||||
const auto pixelNum = pixels.size();
|
||||
if (pixelNum == 0)
|
||||
{
|
||||
return ColorRgb::BLACK;
|
||||
}
|
||||
@ -179,20 +323,20 @@ namespace hyperion
|
||||
uint_fast32_t cummRed = 0;
|
||||
uint_fast32_t cummGreen = 0;
|
||||
uint_fast32_t cummBlue = 0;
|
||||
const auto& imgData = image.memptr();
|
||||
|
||||
for (const unsigned colorOffset : colors)
|
||||
const auto& imgData = image.memptr();
|
||||
for (const int pixelOffset : pixels)
|
||||
{
|
||||
const auto& pixel = imgData[colorOffset];
|
||||
const auto& pixel = imgData[pixelOffset];
|
||||
cummRed += pixel.red;
|
||||
cummGreen += pixel.green;
|
||||
cummBlue += pixel.blue;
|
||||
}
|
||||
|
||||
// Compute the average of each color channel
|
||||
const uint8_t avgRed = uint8_t(cummRed/colorVecSize);
|
||||
const uint8_t avgGreen = uint8_t(cummGreen/colorVecSize);
|
||||
const uint8_t avgBlue = uint8_t(cummBlue/colorVecSize);
|
||||
const uint8_t avgRed = uint8_t(cummRed/pixelNum);
|
||||
const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
|
||||
const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
|
||||
|
||||
// Return the computed color
|
||||
return {avgRed, avgGreen, avgBlue};
|
||||
@ -213,11 +357,11 @@ namespace hyperion
|
||||
uint_fast32_t cummRed = 0;
|
||||
uint_fast32_t cummGreen = 0;
|
||||
uint_fast32_t cummBlue = 0;
|
||||
const unsigned imageSize = image.width() * image.height();
|
||||
|
||||
const unsigned pixelNum = image.width() * image.height();
|
||||
const auto& imgData = image.memptr();
|
||||
|
||||
for (unsigned idx=0; idx<imageSize; idx++)
|
||||
for (unsigned idx=0; idx<pixelNum; idx++)
|
||||
{
|
||||
const auto& pixel = imgData[idx];
|
||||
cummRed += pixel.red;
|
||||
@ -226,13 +370,289 @@ namespace hyperion
|
||||
}
|
||||
|
||||
// Compute the average of each color channel
|
||||
const uint8_t avgRed = uint8_t(cummRed/imageSize);
|
||||
const uint8_t avgGreen = uint8_t(cummGreen/imageSize);
|
||||
const uint8_t avgBlue = uint8_t(cummBlue/imageSize);
|
||||
const uint8_t avgRed = uint8_t(cummRed/pixelNum);
|
||||
const uint8_t avgGreen = uint8_t(cummGreen/pixelNum);
|
||||
const uint8_t avgBlue = uint8_t(cummBlue/pixelNum);
|
||||
|
||||
// Return the computed color
|
||||
return {avgRed, avgGreen, avgBlue};
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'mean color' squared over the given image. This is the mean over each color-channel
|
||||
/// (red, green, blue)
|
||||
///
|
||||
/// @param[in] image The image a section from which an average color must be computed
|
||||
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
|
||||
///
|
||||
/// @return The mean of the given list of colors (or black when empty)
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calcMeanColorSqrt(const Image<Pixel_T> & image, const std::vector<int32_t> & pixels) const
|
||||
{
|
||||
const auto pixelNum = pixels.size();
|
||||
if (pixelNum == 0)
|
||||
{
|
||||
return ColorRgb::BLACK;
|
||||
}
|
||||
|
||||
// Accumulate the squared sum of each separate color channel
|
||||
uint_fast32_t cummRed = 0;
|
||||
uint_fast32_t cummGreen = 0;
|
||||
uint_fast32_t cummBlue = 0;
|
||||
|
||||
const auto& imgData = image.memptr();
|
||||
|
||||
for (const int colorOffset : pixels)
|
||||
{
|
||||
const auto& pixel = imgData[colorOffset];
|
||||
|
||||
cummRed += pixel.red * pixel.red;
|
||||
cummGreen += pixel.green * pixel.green;
|
||||
cummBlue += pixel.blue * pixel.blue;
|
||||
}
|
||||
|
||||
// Compute the average of each color channel
|
||||
const uint8_t avgRed = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummRed/pixelNum))), 255L));
|
||||
const uint8_t avgGreen = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummGreen/pixelNum))), 255L));
|
||||
const uint8_t avgBlue = uint8_t(std::min(std::lround(sqrt(static_cast<double>(cummBlue/pixelNum))), 255L));
|
||||
|
||||
// Return the computed color
|
||||
return {avgRed, avgGreen, avgBlue};
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'mean color' squared over the given image. This is the mean over each color-channel
|
||||
/// (red, green, blue)
|
||||
///
|
||||
/// @param[in] image The image a section from which an average color must be computed
|
||||
///
|
||||
/// @return The mean of the given list of colors (or black when empty)
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calcMeanColorSqrt(const Image<Pixel_T> & image) const
|
||||
{
|
||||
// Accumulate the squared sum of each separate color channel
|
||||
uint_fast32_t cummRed = 0;
|
||||
uint_fast32_t cummGreen = 0;
|
||||
uint_fast32_t cummBlue = 0;
|
||||
|
||||
const unsigned pixelNum = image.width() * image.height();
|
||||
const auto& imgData = image.memptr();
|
||||
|
||||
for (int idx=0; idx<pixelNum; ++idx)
|
||||
{
|
||||
const auto& pixel = imgData[idx];
|
||||
cummRed += pixel.red * pixel.red;
|
||||
cummGreen += pixel.green * pixel.green;
|
||||
cummBlue += pixel.blue * pixel.blue;
|
||||
}
|
||||
|
||||
// Compute the average of each color channel
|
||||
const uint8_t avgRed = uint8_t(std::lround(sqrt(static_cast<double>(cummRed/pixelNum))));
|
||||
const uint8_t avgGreen = uint8_t(std::lround(sqrt(static_cast<double>(cummGreen/pixelNum))));
|
||||
const uint8_t avgBlue = uint8_t(std::lround(sqrt(static_cast<double>(cummBlue/pixelNum))));
|
||||
|
||||
// Return the computed color
|
||||
return {avgRed, avgGreen, avgBlue};
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
|
||||
///
|
||||
/// @param[in] image The image for which a dominant color is to be computed
|
||||
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
|
||||
///
|
||||
/// @return The image area's dominant color or black, if no pixel indices provided
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calculateDominantColor(const Image<Pixel_T> & image, const std::vector<int> & pixels) const
|
||||
{
|
||||
ColorRgb dominantColor {ColorRgb::BLACK};
|
||||
|
||||
const auto pixelNum = pixels.size();
|
||||
if (pixelNum > 0)
|
||||
{
|
||||
const auto& imgData = image.memptr();
|
||||
|
||||
QMap<QRgb,int> colorDistributionMap;
|
||||
int count = 0;
|
||||
for (const int pixelOffset : pixels)
|
||||
{
|
||||
QRgb color = imgData[pixelOffset].rgb();
|
||||
if (colorDistributionMap.contains(color)) {
|
||||
colorDistributionMap[color] = colorDistributionMap[color] + 1;
|
||||
}
|
||||
else {
|
||||
colorDistributionMap[color] = 1;
|
||||
}
|
||||
|
||||
int colorsFound = colorDistributionMap[color];
|
||||
if (colorsFound > count) {
|
||||
dominantColor.setRgb(color);
|
||||
count = colorsFound;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dominantColor;
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'dominant color' of an image
|
||||
///
|
||||
/// @param[in] image The image for which a dominant color is to be computed
|
||||
///
|
||||
/// @return The image's dominant color
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calculateDominantColor(const Image<Pixel_T> & image) const
|
||||
{
|
||||
const unsigned pixelNum = image.width() * image.height();
|
||||
|
||||
std::vector<int> pixels(pixelNum);
|
||||
std::iota(pixels.begin(), pixels.end(), 0);
|
||||
|
||||
return calculateDominantColor(image, pixels);
|
||||
}
|
||||
|
||||
template <typename Pixel_T>
|
||||
struct ColorCluster {
|
||||
|
||||
ColorCluster():count(0) {}
|
||||
ColorCluster(Pixel_T color):count(0),color(color) {}
|
||||
|
||||
Pixel_T color;
|
||||
Pixel_T newColor;
|
||||
int count;
|
||||
};
|
||||
|
||||
const ColorRgb DEFAULT_CLUSTER_COLORS[5] {
|
||||
{ColorRgb::BLACK},
|
||||
{ColorRgb::GREEN},
|
||||
{ColorRgb::WHITE},
|
||||
{ColorRgb::RED},
|
||||
{ColorRgb::YELLOW}
|
||||
};
|
||||
|
||||
///
|
||||
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
|
||||
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
|
||||
///
|
||||
/// @param[in] image The image for which a dominant color is to be computed
|
||||
/// @param[in] pixels The list of pixel indices for the given image to be evaluated
|
||||
///
|
||||
/// @return The image area's dominant color or black, if no pixel indices provided
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calculateDominantColorAdv(const Image<Pixel_T> & image, const std::vector<int> & pixels) const
|
||||
{
|
||||
ColorRgb dominantColor {ColorRgb::BLACK};
|
||||
const auto pixelNum = pixels.size();
|
||||
if (pixelNum > 0)
|
||||
{
|
||||
// initial cluster with different colors
|
||||
auto clusters = std::unique_ptr< ColorCluster<ColorRgbScalar> >(new ColorCluster<ColorRgbScalar>[_clusterCount]);
|
||||
for(int k = 0; k < _clusterCount; ++k)
|
||||
{
|
||||
clusters.get()[k].newColor = DEFAULT_CLUSTER_COLORS[k];
|
||||
}
|
||||
|
||||
// k-means
|
||||
double min_rgb_euclidean {0};
|
||||
double old_rgb_euclidean {0};
|
||||
|
||||
while(1)
|
||||
{
|
||||
for(int k = 0; k < _clusterCount; ++k)
|
||||
{
|
||||
clusters.get()[k].count = 0;
|
||||
clusters.get()[k].color = clusters.get()[k].newColor;
|
||||
clusters.get()[k].newColor.setRgb(ColorRgb::BLACK);
|
||||
}
|
||||
|
||||
const auto& imgData = image.memptr();
|
||||
for (const int pixelOffset : pixels)
|
||||
{
|
||||
const auto& pixel = imgData[pixelOffset];
|
||||
|
||||
min_rgb_euclidean = 255 * 255 * 255;
|
||||
int clusterIndex = -1;
|
||||
for(int k = 0; k < _clusterCount; ++k)
|
||||
{
|
||||
double euclid = ColorSys::rgb_euclidean(ColorRgbScalar(pixel), clusters.get()[k].color);
|
||||
|
||||
if( euclid < min_rgb_euclidean ) {
|
||||
min_rgb_euclidean = euclid;
|
||||
clusterIndex = k;
|
||||
}
|
||||
}
|
||||
|
||||
clusters.get()[clusterIndex].count++;
|
||||
clusters.get()[clusterIndex].newColor += ColorRgbScalar(pixel);
|
||||
}
|
||||
|
||||
min_rgb_euclidean = 0;
|
||||
for(int k = 0; k < _clusterCount; ++k)
|
||||
{
|
||||
if (clusters.get()[k].count > 0)
|
||||
{
|
||||
// new color
|
||||
clusters.get()[k].newColor /= clusters.get()[k].count;
|
||||
double ecli = ColorSys::rgb_euclidean(clusters.get()[k].newColor, clusters.get()[k].color);
|
||||
if(ecli > min_rgb_euclidean)
|
||||
{
|
||||
min_rgb_euclidean = ecli;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( fabs(min_rgb_euclidean - old_rgb_euclidean) < 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
old_rgb_euclidean = min_rgb_euclidean;
|
||||
}
|
||||
|
||||
int colorsFoundMax = 0;
|
||||
int dominantClusterIdx {0};
|
||||
|
||||
for(int clusterIdx=0; clusterIdx < _clusterCount; ++clusterIdx){
|
||||
int colorsFoundinCluster = clusters.get()[clusterIdx].count;
|
||||
if (colorsFoundinCluster > colorsFoundMax) {
|
||||
colorsFoundMax = colorsFoundinCluster;
|
||||
dominantClusterIdx = clusterIdx;
|
||||
}
|
||||
}
|
||||
|
||||
dominantColor.red = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.red);
|
||||
dominantColor.green = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.green);
|
||||
dominantColor.blue = static_cast<uint8_t>(clusters.get()[dominantClusterIdx].newColor.blue);
|
||||
}
|
||||
|
||||
return dominantColor;
|
||||
}
|
||||
|
||||
///
|
||||
/// Calculates the 'dominant color' of an image area defined by a list of pixel indices
|
||||
/// using a k-means algorithm (https://robocraft.ru/computervision/1063)
|
||||
///
|
||||
/// @param[in] image The image for which a dominant color is to be computed
|
||||
///
|
||||
/// @return The image's dominant color
|
||||
///
|
||||
template <typename Pixel_T>
|
||||
ColorRgb calculateDominantColorAdv(const Image<Pixel_T> & image) const
|
||||
{
|
||||
const unsigned pixelNum = image.width() * image.height();
|
||||
|
||||
std::vector<int> pixels(pixelNum);
|
||||
std::iota(pixels.begin(), pixels.end(), 0);
|
||||
|
||||
return calculateDominantColorAdv(image, pixels);
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace hyperion
|
||||
|
||||
#endif // IMAGETOLEDSMAP_H
|
||||
|
@ -289,6 +289,9 @@ private:
|
||||
int _currentConfigId;
|
||||
bool _enabled;
|
||||
|
||||
//The system enable state, to restore smoothing state after effect with smoothing ran
|
||||
bool _enabledSystemCfg;
|
||||
|
||||
/// The type of smoothing to perform
|
||||
SmoothingType _smoothingType;
|
||||
|
||||
|
@ -438,7 +438,10 @@ protected:
|
||||
uint _ledRGBWCount;
|
||||
|
||||
/// Does the device allow restoring the original state?
|
||||
bool _isRestoreOrigState;
|
||||
bool _isRestoreOrigState;
|
||||
|
||||
/// Does the device should be kept on after streaming
|
||||
bool _isStayOnAfterStreaming;
|
||||
|
||||
/// Device, lights state before streaming via hyperion
|
||||
QJsonObject _orignalStateValues;
|
||||
@ -460,6 +463,9 @@ protected:
|
||||
/// Is the device in error state and stopped?
|
||||
bool _isDeviceInError;
|
||||
|
||||
/// Is the device in error state, but is retries might resolve the situation?
|
||||
bool _isDeviceRecoverable;
|
||||
|
||||
/// Timestamp of last write
|
||||
QDateTime _lastWriteTime;
|
||||
|
||||
@ -476,8 +482,9 @@ protected slots:
|
||||
/// @brief Set device in error state
|
||||
///
|
||||
/// @param[in] errorMsg The error message to be logged
|
||||
/// @param[in] isRecoverable If False, no further retries will be done
|
||||
///
|
||||
virtual void setInError( const QString& errorMsg);
|
||||
virtual void setInError( const QString& errorMsg, bool isRecoverable=true);
|
||||
|
||||
private:
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
// Utility includes
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/WeakConnect.h>
|
||||
|
||||
namespace {
|
||||
constexpr std::chrono::milliseconds DEFAULT_DISCOVER_TIMEOUT{ 500 };
|
||||
@ -103,61 +104,6 @@ private slots:
|
||||
void onServiceRemoved(const QMdnsEngine::Service& service);
|
||||
|
||||
private:
|
||||
|
||||
// template <typename Func1, typename Func2, typename std::enable_if_t<std::is_member_pointer<Func2>::value, int> = 0>
|
||||
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
|
||||
// Func1 signal,
|
||||
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
|
||||
// Func2 slot)
|
||||
// {
|
||||
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, receiver, slot);
|
||||
|
||||
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
|
||||
|
||||
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
|
||||
// QObject::disconnect(conn_normal);
|
||||
// QObject::disconnect(*conn_delete);
|
||||
// delete conn_delete;
|
||||
// });
|
||||
// return conn_normal;
|
||||
// }
|
||||
|
||||
template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
|
||||
static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
|
||||
Func1 signal,
|
||||
Func2 slot)
|
||||
{
|
||||
QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
|
||||
|
||||
QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
|
||||
|
||||
*conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
|
||||
QObject::disconnect(conn_normal);
|
||||
QObject::disconnect(*conn_delete);
|
||||
delete conn_delete;
|
||||
});
|
||||
return conn_normal;
|
||||
}
|
||||
|
||||
// template <typename Func1, typename Func2, typename std::enable_if_t<!std::is_member_pointer<Func2>::value, int> = 0>
|
||||
// static inline QMetaObject::Connection weakConnect(typename QtPrivate::FunctionPointer<Func1>::Object* sender,
|
||||
// Func1 signal,
|
||||
// typename QtPrivate::FunctionPointer<Func2>::Object* receiver,
|
||||
// Func2 slot)
|
||||
// {
|
||||
// Q_UNUSED(receiver);
|
||||
// QMetaObject::Connection conn_normal = QObject::connect(sender, signal, slot);
|
||||
|
||||
// QMetaObject::Connection* conn_delete = new QMetaObject::Connection();
|
||||
|
||||
// *conn_delete = QObject::connect(sender, signal, [conn_normal, conn_delete]() {
|
||||
// QObject::disconnect(conn_normal);
|
||||
// QObject::disconnect(*conn_delete);
|
||||
// delete conn_delete;
|
||||
// });
|
||||
// return conn_normal;
|
||||
// }
|
||||
|
||||
/// The logger instance for mDNS-Service
|
||||
Logger* _log;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
#include <QRgb>
|
||||
|
||||
///
|
||||
/// Plain-Old-Data structure containing the red-green-blue color specification. Size of the
|
||||
@ -52,6 +53,18 @@ struct ColorRgb
|
||||
return a;
|
||||
}
|
||||
|
||||
QRgb rgb() const
|
||||
{
|
||||
return qRgb(red,green,blue);
|
||||
}
|
||||
|
||||
void setRgb(QRgb rgb)
|
||||
{
|
||||
red = static_cast<uint8_t>(qRed(rgb));
|
||||
green = static_cast<uint8_t>(qGreen(rgb));
|
||||
blue = static_cast<uint8_t>(qBlue(rgb));
|
||||
}
|
||||
|
||||
QString toQString() const
|
||||
{
|
||||
return QString("(%1,%2,%3)").arg(red).arg(green).arg(blue);
|
||||
|
203
include/utils/ColorRgbScalar.h
Normal file
203
include/utils/ColorRgbScalar.h
Normal 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
|
@ -30,11 +30,11 @@ struct ColorRgba
|
||||
static const ColorRgba WHITE;
|
||||
};
|
||||
|
||||
/// Assert to ensure that the size of the structure is 'only' 3 bytes
|
||||
/// Assert to ensure that the size of the structure is 'only' 4 bytes
|
||||
static_assert(sizeof(ColorRgba) == 4, "Incorrect size of ColorARGB");
|
||||
|
||||
///
|
||||
/// Stream operator to write ColorRgb to an outputstream (format "'{'[alpha]', '[red]','[green]','[blue]'}'")
|
||||
/// Stream operator to write ColorRgba to an outputstream (format "'{'[alpha]', '[red]','[green]','[blue]'}'")
|
||||
///
|
||||
/// @param os The output stream
|
||||
/// @param color The color to write
|
||||
|
@ -105,6 +105,19 @@ public:
|
||||
/// @note See https://bottosson.github.io/posts/colorpicker/#okhsv
|
||||
///
|
||||
static void okhsv2rgb(double hue, double saturation, double value, uint8_t & red, uint8_t & green, uint8_t & blue);
|
||||
|
||||
template <typename Pixel_T>
|
||||
static double rgb_euclidean(Pixel_T p1, Pixel_T p2)
|
||||
{
|
||||
double val = sqrt(
|
||||
(p1.red - p2.red) * (p1.red - p2.red) +
|
||||
(p1.green - p2.green) * (p1.green - p2.green) +
|
||||
(p1.blue - p2.blue) * (p1.blue - p2.blue)
|
||||
);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // COLORSYS_H
|
||||
|
@ -23,6 +23,7 @@ enum Components
|
||||
#endif
|
||||
COMP_GRABBER,
|
||||
COMP_V4L,
|
||||
COMP_AUDIO,
|
||||
COMP_COLOR,
|
||||
COMP_IMAGE,
|
||||
COMP_EFFECT,
|
||||
@ -50,6 +51,7 @@ inline const char* componentToString(Components c)
|
||||
#endif
|
||||
case COMP_GRABBER: return "Framegrabber";
|
||||
case COMP_V4L: return "V4L capture device";
|
||||
case COMP_AUDIO: return "Audio capture device";
|
||||
case COMP_COLOR: return "Solid color";
|
||||
case COMP_EFFECT: return "Effect";
|
||||
case COMP_IMAGE: return "Image";
|
||||
@ -79,6 +81,7 @@ inline const char* componentToIdString(Components c)
|
||||
#endif
|
||||
case COMP_GRABBER: return "GRABBER";
|
||||
case COMP_V4L: return "V4L";
|
||||
case COMP_AUDIO: return "AUDIO";
|
||||
case COMP_COLOR: return "COLOR";
|
||||
case COMP_EFFECT: return "EFFECT";
|
||||
case COMP_IMAGE: return "IMAGE";
|
||||
@ -107,6 +110,7 @@ inline Components stringToComponent(const QString& component)
|
||||
#endif
|
||||
if (cmp == "GRABBER") return COMP_GRABBER;
|
||||
if (cmp == "V4L") return COMP_V4L;
|
||||
if (cmp == "AUDIO") return COMP_AUDIO;
|
||||
if (cmp == "COLOR") return COMP_COLOR;
|
||||
if (cmp == "EFFECT") return COMP_EFFECT;
|
||||
if (cmp == "IMAGE") return COMP_IMAGE;
|
||||
|
@ -56,6 +56,13 @@ signals:
|
||||
void setBufferImage(const QString& name, const Image<ColorRgb>& image);
|
||||
#endif
|
||||
|
||||
///
|
||||
/// @brief PIPE audioCapture images from audioCapture over HyperionDaemon to Hyperion class
|
||||
/// @param name The name of the audio capture (path) that is currently active
|
||||
/// @param image The prepared image
|
||||
///
|
||||
void setAudioImage(const QString& name, const Image<ColorRgb>& image);
|
||||
|
||||
///
|
||||
/// @brief PIPE the register command for a new global input over HyperionDaemon to Hyperion class
|
||||
/// @param[in] priority The priority of the channel
|
||||
|
@ -51,7 +51,7 @@ namespace NetUtils {
|
||||
{
|
||||
if ((port <= 0 || port > MAX_PORT) && port != -1)
|
||||
{
|
||||
Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [0 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
|
||||
Error(log, "Invalid port [%d] for host: (%s)! - Port must be in range [1 - %d]", port, QSTRING_CSTR(host), MAX_PORT);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -122,7 +122,7 @@ namespace NetUtils {
|
||||
{
|
||||
if (hostAddress.setAddress(hostname))
|
||||
{
|
||||
//Debug(log, "IP-address (%s) not required to be resolved.", QSTRING_CSTR(hostAddress.toString()));
|
||||
// An IP-address is not required to be resolved
|
||||
isHostAddressOK = true;
|
||||
}
|
||||
else
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
int getBacklightThreshold() const;
|
||||
|
||||
/// @param backlightThreshold New lower brightness
|
||||
void setBacklightThreshold(int backlightThreshold);
|
||||
void setBacklightThreshold(double backlightThreshold);
|
||||
|
||||
/// @return The current state
|
||||
bool getBacklightColored() const;
|
||||
|
63
include/utils/WeakConnect.h
Normal file
63
include/utils/WeakConnect.h
Normal 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
|
@ -16,14 +16,12 @@
|
||||
#include <effectengine/Effect.h>
|
||||
#endif
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
///
|
||||
/// @brief Provide utility methods for Hyperion class
|
||||
///
|
||||
namespace hyperion {
|
||||
|
||||
void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
|
||||
static void handleInitialEffect(Hyperion* hyperion, const QJsonObject& FGEffectConfig)
|
||||
{
|
||||
#define FGCONFIG_ARRAY fgColorConfig.toArray()
|
||||
|
||||
@ -67,12 +65,12 @@ namespace hyperion {
|
||||
#undef FGCONFIG_ARRAY
|
||||
}
|
||||
|
||||
ColorOrder createColorOrder(const QJsonObject &deviceConfig)
|
||||
static ColorOrder createColorOrder(const QJsonObject &deviceConfig)
|
||||
{
|
||||
return stringToColorOrder(deviceConfig["colorOrder"].toString("rgb"));
|
||||
}
|
||||
|
||||
RgbTransform createRgbTransform(const QJsonObject& colorConfig)
|
||||
static RgbTransform createRgbTransform(const QJsonObject& colorConfig)
|
||||
{
|
||||
const double backlightThreshold = colorConfig["backlightThreshold"].toDouble(0.0);
|
||||
const bool backlightColored = colorConfig["backlightColored"].toBool(false);
|
||||
@ -85,7 +83,7 @@ namespace hyperion {
|
||||
return RgbTransform(gammaR, gammaG, gammaB, backlightThreshold, backlightColored, static_cast<uint8_t>(brightness), static_cast<uint8_t>(brightnessComp));
|
||||
}
|
||||
|
||||
OkhsvTransform createOkhsvTransform(const QJsonObject& colorConfig)
|
||||
static OkhsvTransform createOkhsvTransform(const QJsonObject& colorConfig)
|
||||
{
|
||||
const double saturationGain = colorConfig["saturationGain"].toDouble(1.0);
|
||||
const double brightnessGain = colorConfig["brightnessGain"].toDouble(1.0);
|
||||
@ -93,7 +91,7 @@ namespace hyperion {
|
||||
return OkhsvTransform(saturationGain, brightnessGain);
|
||||
}
|
||||
|
||||
RgbChannelAdjustment createRgbChannelAdjustment(const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB)
|
||||
static RgbChannelAdjustment createRgbChannelAdjustment(const QJsonObject& colorConfig, const QString& channelName, int defaultR, int defaultG, int defaultB)
|
||||
{
|
||||
const QJsonArray& channelConfig = colorConfig[channelName].toArray();
|
||||
return RgbChannelAdjustment(
|
||||
@ -104,7 +102,7 @@ namespace hyperion {
|
||||
);
|
||||
}
|
||||
|
||||
RgbChannelCorrection* createRgbChannelCorrection(const QJsonObject& colorConfig)
|
||||
static RgbChannelCorrection* createRgbChannelCorrection(const QJsonObject& colorConfig)
|
||||
{
|
||||
int varR = colorConfig["red"].toInt(255);
|
||||
int varG = colorConfig["green"].toInt(255);
|
||||
@ -114,7 +112,7 @@ namespace hyperion {
|
||||
return correction;
|
||||
}
|
||||
|
||||
ColorCorrection * createColorCorrection(const QJsonObject& correctionConfig)
|
||||
static ColorCorrection * createColorCorrection(const QJsonObject& correctionConfig)
|
||||
{
|
||||
const QString id = correctionConfig["id"].toString("default");
|
||||
|
||||
@ -130,7 +128,7 @@ namespace hyperion {
|
||||
return correction;
|
||||
}
|
||||
|
||||
ColorAdjustment* createColorAdjustment(const QJsonObject & adjustmentConfig)
|
||||
static ColorAdjustment* createColorAdjustment(const QJsonObject & adjustmentConfig)
|
||||
{
|
||||
const QString id = adjustmentConfig["id"].toString("default");
|
||||
|
||||
@ -150,7 +148,7 @@ namespace hyperion {
|
||||
return adjustment;
|
||||
}
|
||||
|
||||
MultiColorAdjustment * createLedColorsAdjustment(int ledCnt, const QJsonObject & colorConfig)
|
||||
static MultiColorAdjustment * createLedColorsAdjustment(int ledCnt, const QJsonObject & colorConfig)
|
||||
{
|
||||
// Create the result, the transforms are added to this
|
||||
MultiColorAdjustment * adjustment = new MultiColorAdjustment(ledCnt);
|
||||
@ -170,13 +168,12 @@ namespace hyperion {
|
||||
{
|
||||
// Special case for indices '*' => all leds
|
||||
adjustment->setAdjustmentForLed(colorAdjustment->_id, 0, ledCnt-1);
|
||||
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [0-%d]", QSTRING_CSTR(colorAdjustment->_id), ledCnt-1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!overallExp.match(ledIndicesStr).hasMatch())
|
||||
{
|
||||
//Error(Logger::getInstance("HYPERION"), "Given led indices %d not correct format: %s", i, QSTRING_CSTR(ledIndicesStr));
|
||||
// Given LED indices are not correctly formatted
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -203,13 +200,12 @@ namespace hyperion {
|
||||
ss << index;
|
||||
}
|
||||
}
|
||||
//Info(Logger::getInstance("HYPERION"), "ColorAdjustment '%s' => [%s]", QSTRING_CSTR(colorAdjustment->_id), ss.str().c_str());
|
||||
}
|
||||
|
||||
return adjustment;
|
||||
}
|
||||
|
||||
MultiColorCorrection * createLedColorsTemperature(int ledCnt, const QJsonObject & colorConfig)
|
||||
static MultiColorCorrection * createLedColorsTemperature(int ledCnt, const QJsonObject & colorConfig)
|
||||
{
|
||||
// Create the result, the corrections are added to this
|
||||
MultiColorCorrection * correction = new MultiColorCorrection(ledCnt);
|
||||
@ -226,9 +222,6 @@ namespace hyperion {
|
||||
int temperature = config["temperature"].toInt();
|
||||
|
||||
ColorRgb rgb = getRgbFromTemperature(temperature);
|
||||
|
||||
qDebug() << "createLedColorsTemperature: adjustment[temperture]: " << temperature << "-> " << rgb.toQString();
|
||||
|
||||
QJsonObject correctionConfig {
|
||||
{"red", rgb.red},
|
||||
{"green", rgb.green},
|
||||
@ -287,7 +280,7 @@ namespace hyperion {
|
||||
* @param deviceOrder The default RGB channel ordering
|
||||
* @return The constructed ledstring
|
||||
*/
|
||||
LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder)
|
||||
static LedString createLedString(const QJsonArray& ledConfigArray, const ColorOrder deviceOrder)
|
||||
{
|
||||
LedString ledString;
|
||||
const QString deviceOrderStr = colorOrderToString(deviceOrder);
|
||||
@ -318,7 +311,7 @@ namespace hyperion {
|
||||
return ledString;
|
||||
}
|
||||
|
||||
QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray)
|
||||
static QSize getLedLayoutGridSize(const QJsonArray& ledConfigArray)
|
||||
{
|
||||
std::vector<int> midPointsX;
|
||||
std::vector<int> midPointsY;
|
||||
|
@ -57,7 +57,9 @@ public:
|
||||
break;
|
||||
}
|
||||
case QJsonValue::Object:
|
||||
ret = getDefaultValue(value.toObject().find("default").value());
|
||||
{
|
||||
ret = getDefaultValue(value.toObject().value("default"));
|
||||
}
|
||||
break;
|
||||
case QJsonValue::Bool:
|
||||
return value.toBool() ? "True" : "False";
|
||||
@ -174,9 +176,9 @@ private:
|
||||
|
||||
if (!path.isEmpty())
|
||||
{
|
||||
QJsonObject obj;
|
||||
modifyValue(subValue, obj, path, newValue, property);
|
||||
subValue = obj;
|
||||
QJsonObject tempObj;
|
||||
modifyValue(subValue, tempObj, path, newValue, property);
|
||||
subValue = tempObj;
|
||||
}
|
||||
else if (newValue != QJsonValue::Null)
|
||||
subValue = newValue;
|
||||
|
@ -19,6 +19,7 @@ namespace settings {
|
||||
SYSTEMCAPTURE,
|
||||
GENERAL,
|
||||
V4L2,
|
||||
AUDIO,
|
||||
JSONSERVER,
|
||||
LEDCONFIG,
|
||||
LEDS,
|
||||
@ -52,6 +53,7 @@ namespace settings {
|
||||
case SYSTEMCAPTURE: return "framegrabber";
|
||||
case GENERAL: return "general";
|
||||
case V4L2: return "grabberV4L2";
|
||||
case AUDIO: return "grabberAudio";
|
||||
case JSONSERVER: return "jsonServer";
|
||||
case LEDCONFIG: return "ledConfig";
|
||||
case LEDS: return "leds";
|
||||
@ -84,6 +86,7 @@ namespace settings {
|
||||
else if (type == "framegrabber") return SYSTEMCAPTURE;
|
||||
else if (type == "general") return GENERAL;
|
||||
else if (type == "grabberV4L2") return V4L2;
|
||||
else if (type == "grabberAudio") return AUDIO;
|
||||
else if (type == "jsonServer") return JSONSERVER;
|
||||
else if (type == "ledConfig") return LEDCONFIG;
|
||||
else if (type == "leds") return LEDS;
|
||||
|
@ -294,13 +294,6 @@ bool API::setHyperionInstance(quint8 inst)
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<hyperion::Components, bool> API::getAllComponents()
|
||||
{
|
||||
std::map<hyperion::Components, bool> comps;
|
||||
//QMetaObject::invokeMethod(_hyperion, "getAllComponents", Qt::BlockingQueuedConnection, Q_RETURN_ARG(std::map<hyperion::Components, bool>, comps));
|
||||
return comps;
|
||||
}
|
||||
|
||||
bool API::isHyperionEnabled()
|
||||
{
|
||||
int res;
|
||||
|
@ -21,7 +21,7 @@
|
||||
"component":
|
||||
{
|
||||
"type" : "string",
|
||||
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "BOBLIGHTSERVER", "GRABBER", "V4L", "LEDDEVICE"],
|
||||
"enum" : ["ALL", "SMOOTHING", "BLACKBORDER", "FORWARDER", "BOBLIGHTSERVER", "GRABBER", "V4L", "AUDIO", "LEDDEVICE"],
|
||||
"required": true
|
||||
},
|
||||
"state":
|
||||
|
@ -12,7 +12,7 @@
|
||||
},
|
||||
"mappingType": {
|
||||
"type" : "string",
|
||||
"enum" : ["multicolor_mean", "unicolor_mean"]
|
||||
"enum" : ["multicolor_mean", "unicolor_mean", "multicolor_mean_squared", "dominant_color", "dominant_color_advanced"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
@ -21,12 +21,26 @@
|
||||
#include <hyperion/GrabberWrapper.h>
|
||||
#include <grabber/QtGrabber.h>
|
||||
|
||||
#include <utils/WeakConnect.h>
|
||||
|
||||
#if defined(ENABLE_MF)
|
||||
#include <grabber/MFGrabber.h>
|
||||
#elif defined(ENABLE_V4L2)
|
||||
#include <grabber/V4L2Grabber.h>
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AUDIO)
|
||||
#include <grabber/AudioGrabber.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <grabber/AudioGrabberWindows.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <grabber/AudioGrabberLinux.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_X11)
|
||||
#include <grabber/X11Grabber.h>
|
||||
#endif
|
||||
@ -145,7 +159,6 @@ void JsonAPI::handleMessage(const QString &messageString, const QString &httpAut
|
||||
{
|
||||
const QString ident = "JsonRpc@" + _peerAddress;
|
||||
QJsonObject message;
|
||||
//std::cout << "JsonAPI::handleMessage | [" << static_cast<int>(_hyperion->getInstanceIndex()) << "] Received: ["<< messageString.toStdString() << "]" << std::endl;
|
||||
|
||||
// parse the message
|
||||
if (!JsonUtils::parse(ident, messageString, message, _log))
|
||||
@ -556,27 +569,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
info["ledDevices"] = ledDevices;
|
||||
|
||||
QJsonObject grabbers;
|
||||
|
||||
// *** Deprecated ***
|
||||
//QJsonArray availableGrabbers;
|
||||
//if ( GrabberWrapper::getInstance() != nullptr )
|
||||
//{
|
||||
// QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex());
|
||||
// QJsonArray activeGrabberNames;
|
||||
// for (auto grabberName : activeGrabbers)
|
||||
// {
|
||||
// activeGrabberNames.append(grabberName);
|
||||
// }
|
||||
|
||||
// grabbers["active"] = activeGrabberNames;
|
||||
//}
|
||||
//for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::ALL))
|
||||
//{
|
||||
// availableGrabbers.append(grabber);
|
||||
//}
|
||||
|
||||
//grabbers["available"] = availableGrabbers;
|
||||
|
||||
// SCREEN
|
||||
QJsonObject screenGrabbers;
|
||||
if (GrabberWrapper::getInstance() != nullptr)
|
||||
{
|
||||
@ -596,6 +589,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
}
|
||||
screenGrabbers["available"] = availableScreenGrabbers;
|
||||
|
||||
// VIDEO
|
||||
QJsonObject videoGrabbers;
|
||||
if (GrabberWrapper::getInstance() != nullptr)
|
||||
{
|
||||
@ -615,8 +609,31 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
}
|
||||
videoGrabbers["available"] = availableVideoGrabbers;
|
||||
|
||||
// AUDIO
|
||||
QJsonObject audioGrabbers;
|
||||
if (GrabberWrapper::getInstance() != nullptr)
|
||||
{
|
||||
QStringList activeGrabbers = GrabberWrapper::getInstance()->getActive(_hyperion->getInstanceIndex(), GrabberTypeFilter::AUDIO);
|
||||
|
||||
QJsonArray activeGrabberNames;
|
||||
for (auto grabberName : activeGrabbers)
|
||||
{
|
||||
activeGrabberNames.append(grabberName);
|
||||
}
|
||||
|
||||
audioGrabbers["active"] = activeGrabberNames;
|
||||
}
|
||||
QJsonArray availableAudioGrabbers;
|
||||
for (auto grabber : GrabberWrapper::availableGrabbers(GrabberTypeFilter::AUDIO))
|
||||
{
|
||||
availableAudioGrabbers.append(grabber);
|
||||
}
|
||||
audioGrabbers["available"] = availableAudioGrabbers;
|
||||
|
||||
grabbers.insert("screen", screenGrabbers);
|
||||
grabbers.insert("video", videoGrabbers);
|
||||
grabbers.insert("audio", audioGrabbers);
|
||||
|
||||
info["grabbers"] = grabbers;
|
||||
|
||||
info["videomode"] = QString(videoMode2String(_hyperion->getCurrentVideoMode()));
|
||||
@ -690,7 +707,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
QJsonObject obj;
|
||||
obj.insert("friendly_name", entry["friendly_name"].toString());
|
||||
obj.insert("instance", entry["instance"].toInt());
|
||||
//obj.insert("last_use", entry["last_use"].toString());
|
||||
obj.insert("running", entry["running"].toBool());
|
||||
instanceInfo.append(obj);
|
||||
}
|
||||
@ -699,7 +715,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
// add leds configs
|
||||
info["leds"] = _hyperion->getSetting(settings::LEDS).array();
|
||||
|
||||
// BEGIN | The following entries are derecated but used to ensure backward compatibility with hyperion Classic remote control
|
||||
// BEGIN | The following entries are deprecated but used to ensure backward compatibility with hyperion Classic remote control
|
||||
// TODO Output the real transformation information instead of default
|
||||
|
||||
// HOST NAME
|
||||
@ -760,7 +776,6 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
const Hyperion::InputInfo &priorityInfo = _hyperion->getPriorityInfo(_hyperion->getCurrentPriority());
|
||||
if (priorityInfo.componentId == hyperion::COMP_COLOR && !priorityInfo.ledColors.empty())
|
||||
{
|
||||
QJsonObject LEDcolor;
|
||||
// check if LED Color not Black (0,0,0)
|
||||
if ((priorityInfo.ledColors.begin()->red +
|
||||
priorityInfo.ledColors.begin()->green +
|
||||
@ -1323,8 +1338,8 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
// use comment
|
||||
// for user authorized sessions
|
||||
AuthManager::AuthDefinition def;
|
||||
const QString res = API::createToken(comment, def);
|
||||
if (res.isEmpty())
|
||||
const QString createTokenResult = API::createToken(comment, def);
|
||||
if (createTokenResult.isEmpty())
|
||||
{
|
||||
QJsonObject newTok;
|
||||
newTok["comment"] = def.comment;
|
||||
@ -1334,7 +1349,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
sendSuccessDataReply(QJsonDocument(newTok), command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply(res, command + "-" + subc, tan);
|
||||
sendErrorReply(createTokenResult, command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1342,13 +1357,13 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
if (subc == "renameToken")
|
||||
{
|
||||
// use id/comment
|
||||
const QString res = API::renameToken(id, comment);
|
||||
if (res.isEmpty())
|
||||
const QString renameTokenResult = API::renameToken(id, comment);
|
||||
if (renameTokenResult.isEmpty())
|
||||
{
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply(res, command + "-" + subc, tan);
|
||||
sendErrorReply(renameTokenResult, command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1356,13 +1371,13 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
if (subc == "deleteToken")
|
||||
{
|
||||
// use id
|
||||
const QString res = API::deleteToken(id);
|
||||
if (res.isEmpty())
|
||||
const QString deleteTokenResult = API::deleteToken(id);
|
||||
if (deleteTokenResult.isEmpty())
|
||||
{
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
sendErrorReply(res, command + "-" + subc, tan);
|
||||
sendErrorReply(deleteTokenResult, command + "-" + subc, tan);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1370,7 +1385,6 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
if (subc == "requestToken")
|
||||
{
|
||||
// use id/comment
|
||||
const QString &comment = message["comment"].toString().trimmed();
|
||||
const bool &acc = message["accept"].toBool(true);
|
||||
if (acc)
|
||||
API::setNewTokenRequest(comment, id, tan);
|
||||
@ -1387,7 +1401,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
if (API::getPendingTokenRequests(vec))
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (const auto &entry : vec)
|
||||
for (const auto &entry : qAsConst(vec))
|
||||
{
|
||||
QJsonObject obj;
|
||||
obj["comment"] = entry.comment;
|
||||
@ -1492,7 +1506,7 @@ void JsonAPI::handleAuthorizeCommand(const QJsonObject &message, const QString &
|
||||
void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &command, int tan)
|
||||
{
|
||||
const QString &subc = message["subcommand"].toString();
|
||||
const quint8 &inst = message["instance"].toInt();
|
||||
const quint8 &inst = static_cast<quint8>(message["instance"].toInt());
|
||||
const QString &name = message["name"].toString();
|
||||
|
||||
if (subc == "switchTo")
|
||||
@ -1510,7 +1524,12 @@ void JsonAPI::handleInstanceCommand(const QJsonObject &message, const QString &c
|
||||
|
||||
if (subc == "startInstance")
|
||||
{
|
||||
connect(this, &API::onStartInstanceResponse, [=] (const int &tan) { sendSuccessReply(command + "-" + subc, tan); });
|
||||
//Only send update once
|
||||
weakConnect(this, &API::onStartInstanceResponse, [this, command, subc] (int tan)
|
||||
{
|
||||
sendSuccessReply(command + "-" + subc, tan);
|
||||
});
|
||||
|
||||
if (!API::startInstance(inst, tan))
|
||||
sendErrorReply("Can't start Hyperion instance index " + QString::number(inst), command + "-" + subc, tan);
|
||||
|
||||
@ -1570,12 +1589,8 @@ void JsonAPI::handleLedDeviceCommand(const QJsonObject &message, const QString &
|
||||
QString full_command = command + "-" + subc;
|
||||
|
||||
// TODO: Validate that device type is a valid one
|
||||
/* if ( ! valid type )
|
||||
|
||||
{
|
||||
sendErrorReply("Unknown device", full_command, tan);
|
||||
}
|
||||
else
|
||||
*/ {
|
||||
QJsonObject config;
|
||||
config.insert("type", devType);
|
||||
LedDevice* ledDevice = nullptr;
|
||||
@ -1637,17 +1652,13 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
|
||||
QString full_command = command + "-" + subc;
|
||||
|
||||
// TODO: Validate that source type is a valid one
|
||||
/* if ( ! valid type )
|
||||
{
|
||||
sendErrorReply("Unknown device", full_command, tan);
|
||||
}
|
||||
else
|
||||
*/ {
|
||||
if (subc == "discover")
|
||||
{
|
||||
QJsonObject inputSourcesDiscovered;
|
||||
inputSourcesDiscovered.insert("sourceType", sourceType);
|
||||
QJsonArray videoInputs;
|
||||
QJsonArray audioInputs;
|
||||
|
||||
#if defined(ENABLE_V4L2) || defined(ENABLE_MF)
|
||||
|
||||
@ -1664,6 +1675,24 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
#if defined(ENABLE_AUDIO)
|
||||
if (sourceType == "audio")
|
||||
{
|
||||
AudioGrabber* grabber;
|
||||
#ifdef WIN32
|
||||
grabber = new AudioGrabberWindows();
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
grabber = new AudioGrabberLinux();
|
||||
#endif
|
||||
QJsonObject params;
|
||||
audioInputs = grabber->discover(params);
|
||||
delete grabber;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
DebugIf(verbose, _log, "sourceType: [%s]", QSTRING_CSTR(sourceType));
|
||||
|
||||
@ -1760,6 +1789,7 @@ void JsonAPI::handleInputSourceCommand(const QJsonObject& message, const QString
|
||||
|
||||
}
|
||||
inputSourcesDiscovered["video_sources"] = videoInputs;
|
||||
inputSourcesDiscovered["audio_sources"] = audioInputs;
|
||||
|
||||
DebugIf(verbose, _log, "response: [%s]", QString(QJsonDocument(inputSourcesDiscovered).toJson(QJsonDocument::Compact)).toUtf8().constData());
|
||||
|
||||
@ -1873,6 +1903,7 @@ void JsonAPI::sendSuccessReply(const QString &command, int tan)
|
||||
{
|
||||
// create reply
|
||||
QJsonObject reply;
|
||||
reply["instance"] = _hyperion->getInstanceIndex();
|
||||
reply["success"] = true;
|
||||
reply["command"] = command;
|
||||
reply["tan"] = tan;
|
||||
@ -1884,6 +1915,7 @@ void JsonAPI::sendSuccessReply(const QString &command, int tan)
|
||||
void JsonAPI::sendSuccessDataReply(const QJsonDocument &doc, const QString &command, int tan)
|
||||
{
|
||||
QJsonObject reply;
|
||||
reply["instance"] = _hyperion->getInstanceIndex();
|
||||
reply["success"] = true;
|
||||
reply["command"] = command;
|
||||
reply["tan"] = tan;
|
||||
@ -1899,6 +1931,7 @@ void JsonAPI::sendErrorReply(const QString &error, const QString &command, int t
|
||||
{
|
||||
// create reply
|
||||
QJsonObject reply;
|
||||
reply["instance"] = _hyperion->getInstanceIndex();
|
||||
reply["success"] = false;
|
||||
reply["error"] = error;
|
||||
reply["command"] = command;
|
||||
@ -2021,6 +2054,11 @@ void JsonAPI::handleInstanceStateChange(InstanceState state, quint8 instance, co
|
||||
handleInstanceSwitch();
|
||||
}
|
||||
break;
|
||||
|
||||
case InstanceState::H_STARTED:
|
||||
case InstanceState::H_STOPPED:
|
||||
case InstanceState::H_CREATED:
|
||||
case InstanceState::H_DELETED:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -148,7 +148,6 @@ void JsonCB::resetSubscriptions()
|
||||
void JsonCB::setSubscriptionsTo(Hyperion* hyperion)
|
||||
{
|
||||
assert(hyperion);
|
||||
//std::cout << "JsonCB::setSubscriptions for instance [" << static_cast<int>(hyperion->getInstanceIndex()) << "] " << std::endl;
|
||||
|
||||
// get current subs
|
||||
QStringList currSubs(getSubscribedCommands());
|
||||
@ -179,8 +178,6 @@ void JsonCB::doCallback(const QString& cmd, const QVariant& data)
|
||||
else
|
||||
obj["data"] = data.toJsonObject();
|
||||
|
||||
//std::cout << "JsonCB::doCallback | [" << static_cast<int>(_hyperion->getInstanceIndex()) << "] Send: [" << QJsonDocument(obj).toJson(QJsonDocument::Compact).toStdString() << "]" << std::endl;
|
||||
|
||||
emit newCallback(obj);
|
||||
}
|
||||
|
||||
@ -398,7 +395,6 @@ void JsonCB::handleInstanceChange()
|
||||
QJsonObject obj;
|
||||
obj.insert("friendly_name", entry["friendly_name"].toString());
|
||||
obj.insert("instance", entry["instance"].toInt());
|
||||
//obj.insert("last_use", entry["last_use"].toString());
|
||||
obj.insert("running", entry["running"].toBool());
|
||||
arr.append(obj);
|
||||
}
|
||||
|
@ -23,7 +23,5 @@ uint8_t BlackBorderDetector::calculateThreshold(double threshold) const
|
||||
|
||||
uint8_t blackborderThreshold = uint8_t(rgbThreshold);
|
||||
|
||||
//Debug(Logger::getInstance("BLACKBORDER"), "threshold set to %f (%d)", threshold , int(blackborderThreshold));
|
||||
|
||||
return blackborderThreshold;
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
|
||||
#include <hyperion/Hyperion.h>
|
||||
|
||||
@ -33,6 +34,8 @@ BlackBorderProcessor::BlackBorderProcessor(Hyperion* hyperion, QObject* parent)
|
||||
|
||||
// listen for component state changes
|
||||
connect(_hyperion, &Hyperion::compStateChangeRequest, this, &BlackBorderProcessor::handleCompStateChangeRequest);
|
||||
|
||||
_detector = new BlackBorderDetector(_oldThreshold);
|
||||
}
|
||||
|
||||
BlackBorderProcessor::~BlackBorderProcessor()
|
||||
@ -60,7 +63,7 @@ void BlackBorderProcessor::handleSettingsUpdate(settings::type type, const QJson
|
||||
_detectionMode = obj["mode"].toString("default");
|
||||
const double newThreshold = obj["threshold"].toDouble(5.0) / 100.0;
|
||||
|
||||
if (_oldThreshold != newThreshold)
|
||||
if (fabs(_oldThreshold - newThreshold) > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
_oldThreshold = newThreshold;
|
||||
|
||||
@ -140,8 +143,6 @@ bool BlackBorderProcessor::updateBorder(const BlackBorder & newDetectedBorder)
|
||||
// makes it look like the border detectionn is not working - since the new 3 line detection algorithm is more precise this became a problem specialy in dark scenes
|
||||
// wisc
|
||||
|
||||
// std::cout << "c: " << setw(2) << _currentBorder.verticalSize << " " << setw(2) << _currentBorder.horizontalSize << " p: " << setw(2) << _previousDetectedBorder.verticalSize << " " << setw(2) << _previousDetectedBorder.horizontalSize << " n: " << setw(2) << newDetectedBorder.verticalSize << " " << setw(2) << newDetectedBorder.horizontalSize << " c:i " << setw(2) << _consistentCnt << ":" << setw(2) << _inconsistentCnt << std::endl;
|
||||
|
||||
// set the consistency counter
|
||||
if (newDetectedBorder == _previousDetectedBorder)
|
||||
{
|
||||
|
@ -106,8 +106,6 @@ QString BoblightClientConnection::readMessage(const char* data, const size_t siz
|
||||
const int len = end - data + 1;
|
||||
const QString message = QString::fromLatin1(data, len);
|
||||
|
||||
//std::cout << bytes << ": \"" << message.toUtf8().constData() << "\"" << std::endl;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
@ -124,7 +122,6 @@ void BoblightClientConnection::socketClosed()
|
||||
|
||||
void BoblightClientConnection::handleMessage(const QString& message)
|
||||
{
|
||||
//std::cout << "boblight message: " << message.toStdString() << std::endl;
|
||||
QStringList messageParts = QStringUtils::split(message, ' ', QStringUtils::SplitBehavior::SkipEmptyParts);
|
||||
if (!messageParts.isEmpty())
|
||||
{
|
||||
@ -340,7 +337,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
|
||||
{
|
||||
if (ok)
|
||||
{
|
||||
//std::cout << "FAIL L " << q << ": " << s.toUtf8().constData() << std::endl;
|
||||
*ok = false;
|
||||
}
|
||||
return 0;
|
||||
@ -348,7 +344,6 @@ float BoblightClientConnection::parseFloat(const QString& s, bool *ok) const
|
||||
|
||||
if (ok)
|
||||
{
|
||||
//std::cout << "OK " << d << ": " << s.toUtf8().constData() << std::endl;
|
||||
*ok = true;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <QFile>
|
||||
|
||||
/* Enable to turn on detailed CEC logs */
|
||||
// #define VERBOSE_CEC
|
||||
#define NO_VERBOSE_CEC
|
||||
|
||||
CECHandler::CECHandler()
|
||||
{
|
||||
@ -138,9 +138,9 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||
|
||||
if(!_cecAdapter->Open(descriptor.strComName))
|
||||
{
|
||||
Error(_logger, QString("Failed to open the CEC adaper on port %1")
|
||||
.arg(descriptor.strComName)
|
||||
.toLocal8Bit());
|
||||
Error(_logger, "%s", QSTRING_CSTR(QString("Failed to open the CEC adaper on port %1")
|
||||
.arg(descriptor.strComName))
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -149,9 +149,9 @@ bool CECHandler::openAdapter(const CECAdapterDescriptor & descriptor)
|
||||
|
||||
void CECHandler::printAdapter(const CECAdapterDescriptor & descriptor) const
|
||||
{
|
||||
Info(_logger, QString("CEC Adapter:").toLocal8Bit());
|
||||
Info(_logger, QString("\tName : %1").arg(descriptor.strComName).toLocal8Bit());
|
||||
Info(_logger, QString("\tPath : %1").arg(descriptor.strComPath).toLocal8Bit());
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("CEC Adapter:")));
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tName : %1").arg(descriptor.strComName)));
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tPath : %1").arg(descriptor.strComPath)));
|
||||
}
|
||||
|
||||
QString CECHandler::scan() const
|
||||
@ -180,12 +180,12 @@ QString CECHandler::scan() const
|
||||
|
||||
devices << device;
|
||||
|
||||
Info(_logger, QString("\tCECDevice: %1 / %2 / %3 / %4")
|
||||
.arg(device["name"].toString())
|
||||
.arg(device["vendor"].toString())
|
||||
.arg(device["address"].toString())
|
||||
.arg(device["power"].toString())
|
||||
.toLocal8Bit());
|
||||
Info(_logger, "%s", QSTRING_CSTR(QString("\tCECDevice: %1 / %2 / %3 / %4")
|
||||
.arg(device["name"].toString(),
|
||||
device["vendor"].toString(),
|
||||
device["address"].toString(),
|
||||
device["power"].toString()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,16 +305,16 @@ void CECHandler::onCecCommandReceived(void * context, const CECCommand * command
|
||||
{
|
||||
if (command->opcode == CEC::CEC_OPCODE_SET_STREAM_PATH)
|
||||
{
|
||||
Info(handler->_logger, QString("CEC source activated: %1")
|
||||
.arg(adapter->ToString(command->initiator))
|
||||
.toLocal8Bit());
|
||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source activated: %1")
|
||||
.arg(adapter->ToString(command->initiator)))
|
||||
);
|
||||
emit handler->cecEvent(CECEvent::On);
|
||||
}
|
||||
if (command->opcode == CEC::CEC_OPCODE_STANDBY)
|
||||
{
|
||||
Info(handler->_logger, QString("CEC source deactivated: %1")
|
||||
.arg(adapter->ToString(command->initiator))
|
||||
.toLocal8Bit());
|
||||
Info(handler->_logger, "%s", QSTRING_CSTR(QString("CEC source deactivated: %1")
|
||||
.arg(adapter->ToString(command->initiator)))
|
||||
);
|
||||
emit handler->cecEvent(CECEvent::Off);
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ QSqlDatabase DBManager::getDB() const
|
||||
db.setDatabaseName(_rootPath+"/db/"+_dbn+".db");
|
||||
if(!db.open())
|
||||
{
|
||||
Error(_log, QSTRING_CSTR(db.lastError().text()));
|
||||
Error(_log, "%s", QSTRING_CSTR(db.lastError().text()));
|
||||
throw std::runtime_error("Failed to open database connection!");
|
||||
}
|
||||
return db;
|
||||
|
@ -121,17 +121,21 @@ void EffectEngine::handleUpdatedEffectList()
|
||||
// add smoothing configurations to Hyperion
|
||||
if (def.args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
int settlingTime_ms = def.args["smoothing-time_ms"].toInt();
|
||||
double ledUpdateFrequency_hz = def.args["smoothing-updateFrequency"].toDouble();
|
||||
unsigned updateDelay {0};
|
||||
|
||||
Debug(_log, "Effect \"%s\": Add custom smoothing settings [%d]. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(def.name), specificId, settlingTime_ms, ledUpdateFrequency_hz);
|
||||
|
||||
def.smoothCfg = _hyperion->updateSmoothingConfig(
|
||||
++specificId,
|
||||
def.args["smoothing-time_ms"].toInt(),
|
||||
def.args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
//Debug( _log, "Customs Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
|
||||
++specificId,
|
||||
settlingTime_ms,
|
||||
ledUpdateFrequency_hz,
|
||||
updateDelay );
|
||||
}
|
||||
else
|
||||
{
|
||||
def.smoothCfg = SmoothingConfigID::SYSTEM;
|
||||
//Debug( _log, "Default Settings: Update effect %s, script %s, file %s, smoothCfg [%u]", QSTRING_CSTR(def.name), QSTRING_CSTR(def.script), QSTRING_CSTR(def.file), def.smoothCfg);
|
||||
}
|
||||
_availableEffects.push_back(def);
|
||||
}
|
||||
@ -157,11 +161,18 @@ int EffectEngine::runEffect(const QString &effectName, const QJsonObject &args,
|
||||
//In case smoothing information is provided dynamically use temp smoothing config item (2)
|
||||
if (smoothCfg == SmoothingConfigID::SYSTEM && args["smoothing-custom-settings"].toBool())
|
||||
{
|
||||
int settlingTime_ms = args["smoothing-time_ms"].toInt();
|
||||
double ledUpdateFrequency_hz = args["smoothing-updateFrequency"].toDouble();
|
||||
unsigned updateDelay {0};
|
||||
|
||||
Debug(_log, "Effect \"%s\": Apply dynamic smoothing settings, if smoothing. Type: Linear, Settling time: %dms, Interval: %.fHz ", QSTRING_CSTR(effectName), settlingTime_ms, ledUpdateFrequency_hz);
|
||||
|
||||
smoothCfg = _hyperion->updateSmoothingConfig(
|
||||
SmoothingConfigID::EFFECT_DYNAMIC,
|
||||
args["smoothing-time_ms"].toInt(),
|
||||
args["smoothing-updateFrequency"].toDouble(),
|
||||
0 );
|
||||
SmoothingConfigID::EFFECT_DYNAMIC,
|
||||
settlingTime_ms,
|
||||
ledUpdateFrequency_hz,
|
||||
updateDelay
|
||||
);
|
||||
}
|
||||
|
||||
if (pythonScript.isEmpty())
|
||||
|
@ -53,9 +53,12 @@ PyObject *EffectModule::json2python(const QJsonValue &jsonData)
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
case QJsonValue::Double:
|
||||
{
|
||||
if (std::round(jsonData.toDouble()) != jsonData.toDouble())
|
||||
double doubleIntegratlPart;
|
||||
double doubleFractionalPart = std::modf(jsonData.toDouble(), &doubleIntegratlPart);
|
||||
if (doubleFractionalPart > std::numeric_limits<double>::epsilon())
|
||||
{
|
||||
return Py_BuildValue("d", jsonData.toDouble());
|
||||
|
||||
}
|
||||
return Py_BuildValue("i", jsonData.toInt());
|
||||
}
|
||||
case QJsonValue::Bool:
|
||||
@ -184,7 +187,8 @@ PyObject* EffectModule::wrapSetColor(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapSetImage(PyObject *self, PyObject *args)
|
||||
{
|
||||
// bytearray of values
|
||||
int width, height;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
PyObject * bytearray = nullptr;
|
||||
if (PyArg_ParseTuple(args, "iiO", &width, &height, &bytearray))
|
||||
{
|
||||
@ -391,8 +395,10 @@ PyObject* EffectModule::wrapImageLinearGradient(PyObject *self, PyObject *args)
|
||||
int startRY = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int endX, width = getEffect()->_imageSize.width();
|
||||
int endY, height = getEffect()->_imageSize.height();
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int endX {width};
|
||||
int height = getEffect()->_imageSize.height();
|
||||
int endY {height};
|
||||
int spread = 0;
|
||||
|
||||
bool argsOK = false;
|
||||
@ -454,7 +460,9 @@ PyObject* EffectModule::wrapImageConicalGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
PyObject * bytearray = nullptr;
|
||||
int centerX, centerY, angle;
|
||||
int centerX = 0;
|
||||
int centerY = 0;
|
||||
int angle = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int width = getEffect()->_imageSize.width();
|
||||
@ -520,7 +528,13 @@ PyObject* EffectModule::wrapImageRadialGradient(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
PyObject * bytearray = nullptr;
|
||||
int centerX, centerY, radius, focalX, focalY, focalRadius, spread;
|
||||
int centerX = 0;
|
||||
int centerY = 0;
|
||||
int radius = 0;
|
||||
int focalX = 0;
|
||||
int focalY = 0;
|
||||
int focalRadius =0;
|
||||
int spread = 0;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int width = getEffect()->_imageSize.width();
|
||||
@ -599,7 +613,9 @@ PyObject* EffectModule::wrapImageDrawPolygon(PyObject *self, PyObject *args)
|
||||
PyObject * bytearray = nullptr;
|
||||
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int a = 255;
|
||||
|
||||
bool argsOK = false;
|
||||
@ -658,7 +674,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
|
||||
QString brush;
|
||||
int argCount = PyTuple_Size(args);
|
||||
int radius, centerX, centerY;
|
||||
int radius = 0;
|
||||
int centerX = 0;
|
||||
int centerY = 0;
|
||||
int startAngle = 0;
|
||||
int spanAngle = 360;
|
||||
int r = 0;
|
||||
@ -749,7 +767,9 @@ PyObject* EffectModule::wrapImageDrawPie(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int a = 255;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
@ -788,8 +808,10 @@ PyObject* EffectModule::wrapImageSolidFill(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
int a = 255;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int a = 255;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int thick = 1;
|
||||
@ -826,8 +848,12 @@ PyObject* EffectModule::wrapImageDrawLine(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b, x, y;
|
||||
int a = 255;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int a = 255;
|
||||
int thick = 1;
|
||||
|
||||
bool argsOK = false;
|
||||
@ -859,8 +885,10 @@ PyObject* EffectModule::wrapImageDrawPoint(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b;
|
||||
int a = 255;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int a = 255;
|
||||
int startX = 0;
|
||||
int startY = 0;
|
||||
int thick = 1;
|
||||
@ -898,7 +926,11 @@ PyObject* EffectModule::wrapImageDrawRect(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int r, g, b, x, y;
|
||||
int r = 0;
|
||||
int g = 0;
|
||||
int b = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
if ( argCount == 5 && PyArg_ParseTuple(args, "iiiii", &x, &y, &r, &g, &b ) )
|
||||
{
|
||||
@ -913,7 +945,8 @@ PyObject* EffectModule::wrapImageSetPixel(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageGetPixel(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int x, y;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &x, &y) )
|
||||
{
|
||||
@ -934,7 +967,8 @@ PyObject* EffectModule::wrapImageSave(PyObject *self, PyObject *args)
|
||||
PyObject* EffectModule::wrapImageMinSize(PyObject *self, PyObject *args)
|
||||
{
|
||||
int argCount = PyTuple_Size(args);
|
||||
int w, h;
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int width = getEffect()->_imageSize.width();
|
||||
int height = getEffect()->_imageSize.height();
|
||||
|
||||
@ -994,7 +1028,8 @@ PyObject* EffectModule::wrapImageCOffset(PyObject *self, PyObject *args)
|
||||
|
||||
PyObject* EffectModule::wrapImageCShear(PyObject *self, PyObject *args)
|
||||
{
|
||||
int sh,sv;
|
||||
int sh = 0;
|
||||
int sv = 0;
|
||||
int argCount = PyTuple_Size(args);
|
||||
|
||||
if ( argCount == 2 && PyArg_ParseTuple(args, "ii", &sh, &sv ))
|
||||
|
@ -115,6 +115,9 @@ void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
|
||||
case hyperion::COMP_V4L:
|
||||
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
|
||||
break;
|
||||
case hyperion::COMP_AUDIO:
|
||||
connect(_hyperion, &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
|
||||
break;
|
||||
#if defined(ENABLE_FLATBUF_SERVER)
|
||||
case hyperion::COMP_FLATBUFSERVER:
|
||||
#endif
|
||||
@ -138,7 +141,7 @@ void MessageForwarder::enableTargets(bool enable, const QJsonObject& config)
|
||||
else
|
||||
{
|
||||
_forwarder_enabled = false;
|
||||
Warning(_log,"No JSON- nor Flatbuffer-Forwarder configured -> Forwarding disabled", _forwarder_enabled);
|
||||
Warning(_log,"No JSON- nor Flatbuffer-Forwarder configured -> Forwarding disabled");
|
||||
}
|
||||
}
|
||||
_hyperion->setNewComponentState(hyperion::COMP_FORWARDER, _forwarder_enabled);
|
||||
@ -153,6 +156,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
|
||||
switch (activeCompId) {
|
||||
case hyperion::COMP_GRABBER:
|
||||
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
|
||||
#endif
|
||||
@ -160,11 +164,20 @@ void MessageForwarder::handlePriorityChanges(int priority)
|
||||
break;
|
||||
case hyperion::COMP_V4L:
|
||||
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
|
||||
#endif
|
||||
connect(_hyperion, &Hyperion::forwardV4lProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
|
||||
break;
|
||||
case hyperion::COMP_AUDIO:
|
||||
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
|
||||
#endif
|
||||
connect(_hyperion, &Hyperion::forwardAudioProtoMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
|
||||
break;
|
||||
#if defined(ENABLE_FLATBUF_SERVER)
|
||||
case hyperion::COMP_FLATBUFSERVER:
|
||||
#endif
|
||||
@ -172,6 +185,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
|
||||
case hyperion::COMP_PROTOSERVER:
|
||||
#endif
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
|
||||
connect(_hyperion, &Hyperion::forwardBufferMessage, this, &MessageForwarder::forwardFlatbufferMessage, Qt::UniqueConnection);
|
||||
@ -180,6 +194,7 @@ void MessageForwarder::handlePriorityChanges(int priority)
|
||||
default:
|
||||
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
|
||||
#endif
|
||||
@ -373,6 +388,7 @@ void MessageForwarder::stopFlatbufferTargets()
|
||||
{
|
||||
disconnect(_hyperion, &Hyperion::forwardSystemProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardV4lProtoMessage, nullptr, nullptr);
|
||||
disconnect(_hyperion, &Hyperion::forwardAudioProtoMessage, nullptr, nullptr);
|
||||
#if defined(ENABLE_FLATBUF_SERVER) || defined(ENABLE_PROTOBUF_SERVER)
|
||||
disconnect(_hyperion, &Hyperion::forwardBufferMessage, nullptr, nullptr);
|
||||
#endif
|
||||
|
@ -33,3 +33,7 @@ endif(ENABLE_QT)
|
||||
if (ENABLE_DX)
|
||||
add_subdirectory(directx)
|
||||
endif(ENABLE_DX)
|
||||
|
||||
if (ENABLE_AUDIO)
|
||||
add_subdirectory(audio)
|
||||
endif()
|
||||
|
201
libsrc/grabber/audio/AudioGrabber.cpp
Normal file
201
libsrc/grabber/audio/AudioGrabber.cpp
Normal 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;
|
||||
}
|
317
libsrc/grabber/audio/AudioGrabberLinux.cpp
Normal file
317
libsrc/grabber/audio/AudioGrabberLinux.cpp
Normal 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;
|
||||
}
|
354
libsrc/grabber/audio/AudioGrabberWindows.cpp
Normal file
354
libsrc/grabber/audio/AudioGrabberWindows.cpp
Normal 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 *) ¬ify)))
|
||||
{
|
||||
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;
|
||||
}
|
63
libsrc/grabber/audio/AudioWrapper.cpp
Normal file
63
libsrc/grabber/audio/AudioWrapper.cpp
Normal 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
Loading…
x
Reference in New Issue
Block a user