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:
		
							
								
								
									
										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
									
								
							 Submodule dependencies/external/mbedtls added at 8c89224991
									
								
							
							
								
								
									
										2
									
								
								dependencies/external/protobuf
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								dependencies/external/protobuf
									
									
									
									
										vendored
									
									
								
							 Submodule dependencies/external/protobuf updated: 5ad0697c86...f0dc78d7e6
									
								
							@@ -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
		Reference in New Issue
	
	Block a user