Skip to content

Commit 63bab82

Browse files
Merge pull request #29 from gituser12981u2/feature/lua-config
Added lua-config file for key bindings and audio-visualizer parameters
2 parents 33666f7 + 96fa77a commit 63bab82

5 files changed

Lines changed: 152 additions & 34 deletions

File tree

README.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,25 @@ audio-visualizer --mode horizontal-ltr
9393

9494
**Note**: there are two horizontal modes. One that draws bars from left to right (ltr) and one that draws bars from right to left (rtl)
9595

96-
### Hotkey mode switcher
96+
### Configuration File
97+
98+
Modify `config.lua` to change default settings and key bindings. This file controls various aspects of the Audio Visualizer's behavior, including the visual mode, hotkeys, and audio processing parameters.
9799
98-
Switch to a different view--mode--while already in a visualization.
100+
#### Config File Location
99101
100-
While running a mode, press **'ctrl+l'** to switch to horizontal ltr mode, **'ctrl+r'** to switch to horizontal rtl mode, or **'ctrl+v'** to switch to vertical mode.
102+
- **Linux/macOS**: Place your `config.lua` in `~/.config/audio_visualizer/`. This is teh recommended location as it follwos the standard configuration directory structure on Unix-like systems.
103+
- **Windows**: Place your `config.lua` in `%APPDATA%\audio-visualizer\`. This location is recommended for Windows users as it aligns with the typical application data storage.
101104
102-
-'ctrl+l': horizontal ltr mode
103-
-'ctrl+r': horizontal rtl mode
104-
-'ctrl+v': vertical mode
105+
If a `config.lua` file is not found in these locations, the program will attempt to load it from the directory where the `audio-visualizer` command is executed.
106+
107+
### Hotkey mode switcher
108+
109+
Switch visualization modes dynamically with configured hotkeys.
110+
For example, the default keybindings are:
111+
112+
-'ctrl+h': horizontal ltr mode
113+
-'ctrl+l': horizontal rtl mode
114+
-'ctrl+j': vertical mode
105115
106116
### Command Line Options
107117
@@ -127,7 +137,6 @@ On Windows, audio routing can be tricky. If you want to visualize audio from you
127137

128138
This will allow for the program to render the audio that is being outputted on the device as well as continue to have the user be able to hear the same audio.
129139

130-
131140
## License
132141

133142
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.

audio_visualizer/__init__.py

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import argparse
33
import logging
44
import time
5+
import os
6+
from sys import platform
7+
from lupa import LuaRuntime
8+
59

610
# Configure logging
711
logging.basicConfig(level=logging.DEBUG,
@@ -11,32 +15,87 @@
1115
])
1216

1317

18+
def load_config():
19+
"""
20+
Dynamically loads configuration settings from a Lua file
21+
based on the operating system.
22+
The function checks common configuration directories on
23+
Linux, macOS, and Windows.
24+
25+
Returns:
26+
dict: A dictionary containing configuration settings loaded
27+
from the Lua file.
28+
"""
29+
config_filename = "config.lua"
30+
paths = []
31+
if platform == "linux" or platform == "linux2" or platform == "darwin":
32+
# Unix-like systems: look in the user's home directory config folder
33+
paths.append(os.path.join(os.getenv("HOME"), ".config",
34+
"audio_visualizer", config_filename))
35+
elif platform == "win32":
36+
# Windows: look in AppData folder
37+
paths.append(os.path.join(os.getenv("APPDATA"),
38+
"audio_visualizer", config_filename))
39+
40+
# Check the current directory last
41+
paths.append(os.path.join(os.getcwd(), config_filename))
42+
43+
# Try each path in sequence
44+
for path in paths:
45+
if os.path.exists(path):
46+
with open(path, 'r') as file:
47+
lua = LuaRuntime(unpack_returned_tuples=True)
48+
config_script = file.read()
49+
return lua.execute(config_script)
50+
51+
# If no config file is found, log the error and return default settings
52+
logging.warning(f"No configuration file found in expected locations: {
53+
paths}. Using default settings.")
54+
return {
55+
'key_binds': {
56+
'modifier_key': 'ctrl',
57+
'keys': {
58+
'v': 'vertical',
59+
'l': 'horizontal-ltr',
60+
'r': 'horizontal-rtl'
61+
}
62+
},
63+
'settings': {
64+
'default_mode': 'vertical',
65+
'alpha': 0.4,
66+
'chunk_size': 2048,
67+
'sample_rate': 44100
68+
}
69+
}
70+
71+
1472
def main():
1573
"""Entry point for the audio visualizer command line interface."""
74+
config = load_config()
1675

1776
parser = argparse.ArgumentParser(description="Terminal Audio Visualizer")
1877
parser.add_argument(
1978
"--mode",
2079
choices=["vertical", "horizontal-ltr", "horizontal-rtl"],
21-
default="vertical",
80+
default=config['settings']['default_mode'],
2281
help="Choose visualization mode: vertical or horizontal",
2382
)
2483
parser.add_argument(
2584
"--alpha",
2685
type=float,
27-
default=0.4,
86+
default=config['settings']['alpha'],
2887
help="Smoothing factor for FFT; default is 0.4.",
2988
)
3089
parser.add_argument(
3190
"--chunk",
3291
type=int,
33-
default=2048,
92+
default=config['settings']['chunk_size'],
3493
help="Number of frames per buffer; default is 2048",
3594
)
3695
parser.add_argument(
3796
"--rate",
3897
type=int,
39-
default=44100,
98+
default=config['settings']['sample_rate'],
4099
help="Sampling rate; default is 44100",
41100
)
42101
args = parser.parse_args()
@@ -46,6 +105,7 @@ def main():
46105
alpha=args.alpha,
47106
chunk=args.chunk,
48107
rate=args.rate,
108+
config=config['key_binds']
49109
)
50110
visualizer.start()
51111

audio_visualizer/visualizer.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class AudioVisualizer:
5454
stop_event (Event): Event to signal the thread to stop.
5555
"""
5656

57-
def __init__(self, mode='vertical', alpha=0.4, chunk=2048, rate=44100):
57+
def __init__(self, mode, alpha, chunk, rate, config=None):
5858
"""
5959
Initializes the AudioVisualizer object with default settings
6060
for audio streaming.
@@ -64,60 +64,81 @@ def __init__(self, mode='vertical', alpha=0.4, chunk=2048, rate=44100):
6464
alpha (float, optional): Smoothing factor for visualization.
6565
chunk (int, optional): Number of audio samples per buffer.
6666
rate (int, optional): Sampling rate of the audio in Hz.
67+
config (dict): Configuration for key bindings and other settings.
6768
"""
6869
self.mode = mode
6970
self.alpha = alpha
7071
self.chunk = chunk
7172
self.rate = rate
73+
self.config = config or {
74+
'modifier_key': 'ctrl', # Default modifier key and default keys
75+
'keys': {'j': 'vertical', 'h': 'horizontal-ltr',
76+
'l': 'horizontal-rtl'}
77+
}
7278
self.stream = AudioCapture(
7379
chunk=self.chunk, rate=self.rate, channels=2)
7480
self.stream.start_stream()
7581
self.thread = None
7682
self.stop_event = Event()
83+
self.modifier_pressed = False # State flag for modifier key
7784
self.setup_hotkeys()
78-
logging.info("Audio Visualizer initialized")
85+
logging.info(
86+
f"Audio Visualizer initialized with mode: {self.mode}, alpha: {
87+
self.alpha}, chunk: {self.chunk}, rate: {self.rate}"
88+
)
7989

8090
def setup_hotkeys(self):
8191
"""
8292
Sets up the keyboard listener
8393
for hotkeys to change visualization modes.
8494
"""
85-
listener = keyboard.Listener(on_press=self.on_key_press)
95+
listener = keyboard.Listener(
96+
on_press=self.on_key_press, on_release=self.on_key_release)
8697
listener.start()
8798
logging.info("Keyboard listener started")
8899

89100
def on_key_press(self, key):
90101
"""
91102
Handles key press events to change visualization modes based
92-
on Ctrl combinations.
103+
on on configured keybindings.
93104
94105
Args:
95106
key (Key): The key that was pressed.
96107
"""
97-
# Check if the 'Ctrl' key is pressed
98-
if isinstance(key, keyboard.Key):
99-
if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
100-
self.ctrl_pressed = True
101-
102-
# Check for the specific character keys while 'Ctrl' is pressed
103-
if isinstance(key, keyboard.KeyCode):
104-
char = key.char
105-
if self.ctrl_pressed and char in ['v', 'l', 'r']:
106-
new_mode = {'v': 'vertical', 'l': 'horizontal-ltr',
107-
'r': 'horizontal-rtl'}.get(char)
108-
if new_mode and new_mode != self.mode:
109-
self.change_mode(new_mode)
108+
try:
109+
if isinstance(key, keyboard.Key):
110+
modifier_keys = [getattr(keyboard.Key,
111+
f'{self.config["modifier_key"]}_l'),
112+
getattr(keyboard.Key,
113+
f'{self.config["modifier_key"]}_r')]
114+
if key in modifier_keys:
115+
self.modifier_pressed = True
116+
117+
if isinstance(key, keyboard.KeyCode) and self.modifier_pressed:
118+
if key.char in self.config['keys']:
119+
logging.debug(f"{key.char} is pressed")
120+
new_mode = self.config['keys'][key.char]
121+
if new_mode and new_mode != self.mode:
122+
self.change_mode(new_mode)
123+
except Exception as e:
124+
logging.error(f"Error handling key press: {e}")
110125

111126
def on_key_release(self, key):
112127
"""
113-
Handles key release events to manage the state of the Ctrl key.
128+
Manages the state of the Ctrl key upon its release.
114129
115130
Args:
116131
key (Key): The key that was released.
117132
"""
118-
# Check if the 'Ctrl' key is released
119-
if key == keyboard.Key.ctrl_l or key == keyboard.Key.ctrl_r:
120-
self.ctrl_pressed = False
133+
try:
134+
modifier_keys = [getattr(keyboard.Key,
135+
f'{self.config["modifier_key"]}_l'),
136+
getattr(keyboard.Key,
137+
f'{self.config["modifier_key"]}_r')]
138+
if key in modifier_keys:
139+
self.modifier_pressed = False
140+
except Exception as e:
141+
logging.error(f"Error handling key release: {e}")
121142

122143
def change_mode(self, new_mode):
123144
"""
@@ -126,7 +147,7 @@ def change_mode(self, new_mode):
126147
Args:
127148
new_mode (str): The new visualization mode to set.
128149
"""
129-
logging.debug(f"Attempting to change mode from {
150+
logging.debug(f"Changing mode from {
130151
self.mode} to {new_mode}")
131152
self.mode = new_mode
132153
self.restart_visualization()

example_config/config.lua

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
return {
2+
key_binds = {
3+
-- The modifier key used for hotkeys. Possible values are 'ctrl', 'shift', 'alt'.
4+
-- Note: On macOS, using 'shift or 'alt' can alter teh character keys.
5+
-- For example, 'shift' + 'l' becomes 'L', and 'alt' + 'l' might result in a non-alphanumeric character '¬'.
6+
-- Users should adjust the 'keys' bindings accordingly if using 'shift' or 'alt'.
7+
modifier_key = 'ctrl',
8+
9+
-- Defines the hotkeys for switching visualization modes.
10+
-- Ensure these keys match the output when combined with the modifier key, especially on macOS.
11+
keys = { -- Hotkeys for mode-switcher (all are inherently prefaced with the 'ctrl' modifier key)
12+
j = 'vertical', -- Hotkey for vertical visualization mode.
13+
h = 'horizontal-ltr', -- Hotkey for horizonta left-to-right mode.
14+
l = 'horizontal-rtl' -- Hotkey for horizontal right-to-left mode.
15+
},
16+
},
17+
settings = {
18+
default_mode = 'vertical', -- The default visualization mode on startup.
19+
alpha = 0.4, -- Smoothing factor for the Fast Fourier Transform (FFT).
20+
chunk_size = 2048, -- Number of audio samples per buffer.
21+
sample_rate = 44100 -- Audio sampling rate in Hertz (samples per second).
22+
}
23+
-- Future settings for theming
24+
-- themes = {
25+
-- background_color = 'black',
26+
-- bar_color = 'white'
27+
-- }
28+
}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
setup(
1212
name="audio_visualizer",
13-
version="0.1.0",
13+
version="0.4.0",
1414
packages=find_packages(),
1515
# List of dependencies installed via pip install -e .[dev]
1616
install_requires=[

0 commit comments

Comments
 (0)