Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next30 #62

Draft
wants to merge 37 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
17eddba
Filter steps work on multiple channels, read wav data for wavfile cap…
HEnquist Mar 29, 2024
a6e0d1a
Add support for the aux faders
HEnquist Apr 9, 2024
ab3ba9e
Update test for pycamilladsp changes
HEnquist Apr 13, 2024
a8cf5ff
Merge pull request #60 from HEnquist/auxfaders
HEnquist Apr 13, 2024
00a0711
Include file sizes
HEnquist Apr 14, 2024
a25252b
Merge pull request #61 from HEnquist/datatable
HEnquist Apr 17, 2024
76c12e4
update github actions
HEnquist Apr 17, 2024
1ade180
update github actions
HEnquist Apr 17, 2024
5fb96fe
update github actions
HEnquist Apr 17, 2024
db96850
update github actions
HEnquist Apr 17, 2024
f5f890b
Support plotting loudness filters
HEnquist Jun 11, 2024
15a966d
Merge pull request #64 from HEnquist/loudness_eval
HEnquist Jun 17, 2024
46a0b6d
Add some logging
HEnquist Jun 25, 2024
5d5466f
Merge pull request #65 from HEnquist/noisegate
HEnquist Jun 28, 2024
1dcf56c
Add proper argument parsing, add option for config file path
HEnquist Jul 18, 2024
ebb5b0f
Include title and desc for config file listing
HEnquist Aug 13, 2024
99e5664
Catch general Exception when extracting title
HEnquist Aug 13, 2024
64d98a7
Shortcuts may affect multiple config elements
HEnquist Aug 14, 2024
f28865f
Support boolean shortcuts
HEnquist Aug 14, 2024
c376573
Improve gui config schema
HEnquist Aug 15, 2024
7e3dca4
Complete gui config schema
HEnquist Aug 16, 2024
90dace1
Improve shortcut descriptions
HEnquist Aug 17, 2024
ac7b1a4
Update readme for new shortcuts
HEnquist Aug 17, 2024
592a96a
Improve readme on shortcuts
HEnquist Aug 17, 2024
5110b4c
Merge pull request #67 from HEnquist/fancy_shortcuts
HEnquist Aug 17, 2024
35eb429
Add setting for volume slider range
HEnquist Aug 29, 2024
21d7248
Add multithreading options
HEnquist Sep 13, 2024
b5c38bf
Use preview versions of deps
HEnquist Sep 21, 2024
db78d86
Update tests for new pycamilladsp
HEnquist Sep 21, 2024
df27db9
Add missing gui config file for tests
HEnquist Sep 21, 2024
309b468
Dont include .hidden files, indicate config file errors in listing
HEnquist Sep 27, 2024
9584d94
Remove unwanted .
HEnquist Sep 28, 2024
03486ad
Bump version
HEnquist Sep 28, 2024
80e58ee
Check if ramp_time exists before trying to remove it
HEnquist Oct 10, 2024
4a4446c
Add more details to file listings, fix import/migration bugs
HEnquist Oct 13, 2024
915568c
Typo in error message
HEnquist Oct 13, 2024
c6d8037
Include version of pycamilladsp-plot
HEnquist Oct 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
outputs:
fe_tag: ${{ steps.fe_tag_step.outputs.fe_tag }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Get CamillaGUI tag from versions.yml
id: fe_tag_step
run: |
Expand All @@ -21,19 +21,19 @@ jobs:
runs-on: ubuntu-latest
needs: read_fe_tag
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
name: Check out frontend ${{ needs.read_fe_tag.outputs.fe_tag }}
with:
repository: HEnquist/camillagui
ref: ${{ needs.read_fe_tag.outputs.fe_tag }}
- name: Build and publish
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '16'
node-version: '20'
- run: npm install
- run: npm run build
- name: Upload build
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: build
path: build
Expand All @@ -42,9 +42,9 @@ jobs:
runs-on: ubuntu-latest
needs: build_fe
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.12'

Expand All @@ -71,13 +71,13 @@ jobs:
rm -rf tests

- name: Download frontend
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4

- name: Create zip
run: zip -r camillagui.zip *

- name: Upload all as artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: camillagui-backend
path: |
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
*.pyc
build/*
.venv
88 changes: 72 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ for more information.

## Configuration

The backend configuration is stored in `config/camillagui.yml`
The backend configuration is stored in `config/camillagui.yml` by default.

```yaml
---
Expand All @@ -45,6 +45,7 @@ bind_address: "0.0.0.0"
port: 5005
ssl_certificate: null (*)
ssl_private_key: null (*)
gui_config_file: null (*)
config_dir: "~/camilladsp/configs"
coeff_dir: "~/camilladsp/coeffs"
default_config: "~/camilladsp/default_config.yml"
Expand All @@ -62,6 +63,8 @@ The web interface will be served on port 5005 using plain HTTP.
It is possible to run the gui and CamillaDSP on different machines,
just point the `camilla_host` to the right address.

The optional `gui_config_file` can be used to override the default path to the gui config file.

**Warning**: By default the backend will bind to all network interfaces.
This makes the gui available on all networks the system is connected to, which may be insecure.
Make sure to change the `bind_address` if you want it to be reachable only on specific
Expand Down Expand Up @@ -149,32 +152,51 @@ The styling can be customized by editing `build/css-variables.css`.

### Adding custom shortcut settings
It is possible to configure custom shortcuts for the `Shortcuts` section and the compact view.
The included config file contains the default Bass and Treble filters.
The included config file contains the default Bass and Treble filters,
as well as a few commented out examples.

To add more, edit the file `config/gui-config.yml` to add
the new shortcuts to the list under `custom_shortcuts`.

Here is an example config to set the gain of a filter called `MyFilter`
within the range from 0 to 10 db in steps of 0.1 dB.
Here is an example config to set the gain of the filters called `MyFilter` and `MyOtherFilter`.
within the range from -10 to 0 db in steps of 0.1 dB.
For `MyOtherFilter`, the scale is reversed, such that moving the slider from -10 to -9 dB
changes the gain of `MyOtherFilter` fom 0 to -1 dB.
The `type` property is set to `number`.
This creates a slider control, used to control numerical values.
It can also be set to `boolean` which creates a checkbox.
For `number`, the `range_from`, `range_to` and `step` properties are required.
They are not used by `boolean` controls and may be left out.

```yaml
custom_shortcuts:
- section: "My custom section"
description: "Optional description for the section. Omit the attribute, if unwanted"
description: |
Optional description for the section.
Omit this attribute, if unwanted.
The text will be shown in the gui with line breaks.
shortcuts:
- name: "My filter gain"
description: "Optional description for the setting. Omit the attribute, if unwanted"
path_in_config: ["filters", "MyFilter", "parameters", "gain"]
range_from: 0
range_to: 10
description: |
Optional description for the setting.
Omit this attribute, if unwanted.
config_elements:
- path: ["filters", "MyFilter", "parameters", "gain"]
reverse: false
- path: ["filters", "MyOtherFilter", "parameters", "gain"]
reverse: true
range_from: -10
range_to: 0
step: 0.1
- name: "The next setting"
...
type: "number"
```
When letting a shortcut control more than one element in the config,
the first one is considered the main one, that controls the slider position.
The first element must be present in the config in order for the shortcut to function.

The gui config is checked when the backend starts, and any problems are logged.
For example, `range_from` must be a number. If it is not, this results in a message such as this:
```
ERROR:root:Parameter 'custom_shortcuts/0/shortcuts/1/range_from': 'hello' is not of type 'number'
```
If any of the others is not at the expected value, the GUI will show a warning.
The same happens if any of the others is missing in the config.
The control can then still be used, but may not give the wanted result.

### Hiding GUI Options
Options can be hidden from your users by editing `config/gui-config.yml`.
Expand All @@ -186,6 +208,7 @@ hide_silence: false
hide_capture_device: false
hide_playback_device: false
hide_rate_monitoring: false
hide_multithreading: false
```

### Styling the GUI
Expand All @@ -199,6 +222,14 @@ To enable it by default, in `config/gui-config.yml` set `apply_config_automatica
The update rate of the level meters can be adjusted by changing the `status_update_interval` setting.
The value is in milliseconds, and the default value is 100 ms.

### Gui config syntax check
The gui config is checked when the backend starts, and any problems are logged.
For example, the `range_from` property of a config shortcut must be a number.
If it is not, this results in a message such as this:
```
ERROR:root:Parameter 'custom_shortcuts/0/shortcuts/1/range_from': 'hello' is not of type 'number'
```

## Running
Start the server with:
```sh
Expand All @@ -210,6 +241,31 @@ The gui should now be available at: http://localhost:5005/gui/index.html
If accessing the gui from a different machine, replace "localhost" by the IP
or hostname of the machine running the gui server.

### Command line options
The logging level for the backend itself as well as the AIOHTTP framework are set to `WARNING` by default.
These can both be changed with command line arguments, which may be useful when debugging some problem.

The backend norally reads its configuration from a default location.
This can be changed by providing a different path as a command line argument.

Use the `-h` or `--help` argument to view the built-in help:
```
> python main.py --help
usage: python main.py [-h] [-c CONFIG] [-l {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}]
[-a {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}]

Backend for the CamillaDSP web GUI

options:
-h, --help show this help message and exit
-c CONFIG, --config CONFIG
Provide a path to a backend config file to use instead of the default
-l {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}, --log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}
Logging level
-a {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}, --aiohttp-log-level {CRITICAL,ERROR,WARNING,INFO,DEBUG,NOTSET}
AIOHTTP logging level
```


## Development
### Render the environment files
Expand Down
8 changes: 4 additions & 4 deletions backend/convolver_config_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ def channels_factors_and_inversions_as_list(
]


def make_filter_step(channel: int, names: List[str]) -> dict:
def make_filter_step(channels: List[int], names: List[str]) -> dict:
return {
"type": "Filter",
"channel": channel,
"channels": channels,
"names": names,
"bypassed": None,
"description": None,
Expand Down Expand Up @@ -157,7 +157,7 @@ def _input_delay_pipeline_steps(self) -> List[dict]:

def _delay_pipeline_steps(self, delays: List[int]) -> List[dict]:
return [
make_filter_step(channel, [self._delay_name(delay)])
make_filter_step([channel], [self._delay_name(delay)])
for channel, delay in enumerate(delays)
if delay != 0
]
Expand Down Expand Up @@ -210,4 +210,4 @@ def _mixer_out_pipeline_step() -> List[dict]:
return [{"type": "Mixer", "name": "Mixer out", "description": None}]

def _filter_pipeline_steps(self) -> List[dict]:
return [make_filter_step(f.channel, [f.name()]) for f in self._filters]
return [make_filter_step([f.channel], [f.name()]) for f in self._filters]
15 changes: 0 additions & 15 deletions backend/eqapo_config_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,21 +292,6 @@ def postprocess(self):
for idx, dest in enumerate(list(mixer["mapping"])):
if len(dest["sources"]) == 0:
mixer["mapping"].pop(idx)
# Expand filter steps to all channels
pipeline = []
for step in self.pipeline:
if step["type"] != "Filter":
pipeline.append(step)
else:
channels = step["channels"]
if channels is None:
channels = range(self.nbr_channels)
for channel in channels:
new_step = deepcopy(step)
new_step["channel"] = channel
del new_step["channels"]
pipeline.append(new_step)
self.pipeline = pipeline

def build_config(self):
config = {
Expand Down
75 changes: 61 additions & 14 deletions backend/filemanagement.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
isabs,
commonpath,
getmtime,
getsize,
)
import logging
import traceback
Expand All @@ -22,6 +23,8 @@

from camilladsp import CamillaError

from .legacy_config_import import identify_version

DEFAULT_STATEFILE = {
"config_path": None,
"mute": [False, False, False, False, False],
Expand Down Expand Up @@ -62,28 +65,72 @@ async def store_files(folder, request):
return web.Response(text="Saved {} file(s)".format(i))


def list_of_files_in_directory(folder):
def list_of_files_in_directory(folder, file_stats=True, title_and_desc=False, validator=None):
"""
Return a list of files (name and modification date) in a folder.
"""
files = [
file_in_folder(folder, file)
for file in os.listdir(folder)
if isfile(file_in_folder(folder, file))
]
files_list = map(
lambda file: {
"name": (os.path.basename(file)),
"lastModified": (getmtime(file)),
},
files,
)

files_list = []
for file in os.listdir(folder):
filepath = file_in_folder(folder, file)
if not isfile(filepath) or file.startswith("."):
# skip directories and hidden files
continue

file_data = {
"name": file,
}
if file_stats:
file_data["lastModified"] = getmtime(filepath)
file_data["size"] = getsize(filepath)

if title_and_desc:
valid = False
version = None
errors = None
title = None
desc = None
with open(filepath) as f:
try:
parsed = yaml.safe_load(f)
title = parsed.get("title")
desc = parsed.get("description")
version = identify_version(parsed)
if version == 3 and validator is not None:
parsed_abs = make_config_filter_paths_absolute(parsed, folder)
validator.validate_config(parsed_abs)
error_list = validator.get_errors()
if len(error_list) > 0:
errors = error_list
else:
valid = True
elif version < 3:
valid = False
errors = [([], f"This config is made for the previous version {version} of CamillaDSP.")]
except yaml.YAMLError as e:
if hasattr(e, 'problem_mark'):
mark = e.problem_mark
errordesc = f"This file has a YAML syntax error on line: {mark.line + 1}, column: {mark.column + 1}"
else:
errordesc = "This config file has a YAML syntax error."
errors = [([], errordesc)]
except (AttributeError, UnicodeDecodeError) as e:
errors = [([], "This does not appear to be a YAML file.")]
except Exception as e:
errors = [([], f"Error: {e}")]
file_data["title"] = title
file_data["description"] = desc
file_data["version"] = version
file_data["valid"] = valid
file_data["errors"] = errors
files_list.append(file_data)

sorted_files = sorted(files_list, key=lambda x: x["name"].lower())
return sorted_files


def list_of_filenames_in_directory(folder):
return map(lambda file: file["name"], list_of_files_in_directory(folder))
return [file["name"] for file in list_of_files_in_directory(folder, file_stats=False)]


def delete_files(folder, files):
Expand Down
Loading