Skip to content

Commit bbe9e14

Browse files
committed
refactored into library
1 parent 9a21dd6 commit bbe9e14

14 files changed

Lines changed: 363 additions & 493 deletions

app.py

Lines changed: 0 additions & 159 deletions
This file was deleted.

example-config.yaml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
microphone:
22
# A microphone configuration should match the call signature of
3-
# `sounddevice.InputStream` as it will be used on the call, except for two
4-
# optional parameters: `name` and `samplewidth`
3+
# `sounddevice.InputStream` as it will be used on the call, except for three
4+
# additional parameters: `name`, `index` and `samplewidth`
55
# https://python-sounddevice.readthedocs.io/en/0.4.4/api/streams.html#sounddevice.InputStream
6-
17: # device_id
6+
7+
0: # Device index
78
name: Realtek HD Audio Mic input # Optional, human-readable device_id
9+
index: 17 # device_id
810
channels: 2
911
samplerate: 44100.0
1012
samplewidth: 2 # Sample width in bytes when stored in the `wav` file
11-
camera: {}
13+
realsense: {}

recorder/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .recorder import Recorder
2+
from .config import Config

recorder/config.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from recorder.io import yaml_load
2+
from recorder.device import Device, DEVICE_CLASSES
3+
4+
from typing import List
5+
from pathlib import Path
6+
from datetime import datetime
7+
8+
DEFAULT_CONFIG_PATH = Path.cwd() / 'vao-recorder.yaml'
9+
10+
DEFAULT_OUTPUT_FOLDER = Path.cwd() / 'output'
11+
12+
13+
class Config(dict):
14+
15+
@classmethod
16+
def from_yaml(cls, path: Path = DEFAULT_CONFIG_PATH) -> 'Config':
17+
if not path.exists():
18+
raise AttributeError(f'Configuration file {path} does not exist')
19+
return cls(yaml_load(path))
20+
21+
def devices(
22+
self,
23+
output_folder: Path = DEFAULT_OUTPUT_FOLDER,
24+
) -> List[Device]:
25+
"""Initializes all devices present in the dictionary with the provided
26+
output folder.
27+
28+
Args:
29+
output_folder (Path, optional): Devices output_folder. Defaults to
30+
DEFAULT_OUTPUT_FOLDER.
31+
32+
Raises:
33+
RuntimeError: _description_
34+
35+
Returns:
36+
List[Device]: List of initialized devices.
37+
38+
"""
39+
devices = []
40+
for _device_class, _devices in self.items():
41+
try:
42+
device_class = DEVICE_CLASSES[_device_class]
43+
except KeyError:
44+
raise RuntimeError(
45+
f'Invalid configuration file, {_device_class} is not a '
46+
'valid device class. Available options: '
47+
f'{list(DEVICE_CLASSES.keys())}'
48+
)
49+
for index, device_config in _devices.items():
50+
devices.append(
51+
device_class(
52+
index=index,
53+
output_folder=output_folder,
54+
config=device_config,
55+
)
56+
)
57+
return devices

recorder/device/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
from .device import Device
22
from .microphone import Microphone
33
from .realsense import RealSense
4+
5+
DEVICE_CLASSES = {repr(cls): cls for cls in Device.__subclasses__()}

recorder/device/device.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
1+
from recorder.io import yaml_dump
2+
13
from typing import Dict
24
from pathlib import Path
5+
from warnings import warn
6+
from datetime import datetime
37
from abc import ABC, ABCMeta, abstractmethod
48

59

610
class MetaDevice(ABCMeta):
711

8-
def __str__(cls):
12+
def __str__(cls) -> str:
913
return cls.__name__
1014

15+
def __repr__(cls) -> str:
16+
return cls.__name__.lower()
17+
1118

1219
class Device(ABC, metaclass=MetaDevice):
1320

14-
def __init__(self, _index: int, output_folder: Path, *args, **kwargs):
15-
self.name = kwargs.get('name', 'unknown')
16-
self.output_file = output_folder / f'{self.__name__.lower()}{_index}'
21+
def __init__(self, index: int, output_folder: Path, config: dict):
22+
self.config = config
23+
assert 'start_timestamp' not in self.config
24+
assert 'end_timestamp' not in self.config
25+
self.index = index
26+
self.name = self.config.get('name', 'unknown')
27+
self.output_file = output_folder / f'{repr(self)}'
1728

1829
def __str__(self) -> str:
1930
return self.name
2031

32+
def __repr__(self) -> str:
33+
return f'{repr(type(self))}{self.index}'
34+
35+
def __del__(self):
36+
yaml_dump(self.config, to_file=self.output_file.with_suffix('.yaml'))
37+
2138
@classmethod
2239
@abstractmethod
2340
def find(cls) -> Dict[int, dict]:
@@ -29,20 +46,27 @@ def find(cls) -> Dict[int, dict]:
2946
"""
3047
pass
3148

32-
@abstractmethod
3349
def start(self) -> None:
34-
"""Starts recording.
50+
# Starts recording storing the start timestamp in configuration
51+
self.config['start_timestamp'] = datetime.now().timestamp()
52+
self._start()
3553

36-
Returns:
37-
None
38-
"""
54+
@abstractmethod
55+
def _start(self) -> None:
56+
# Starts recording.
3957
pass
4058

41-
@abstractmethod
42-
def stop(self) -> None:
43-
"""Stops recording.
59+
def stop(self):
60+
# Stops recording storing the end timestamp in configuration
61+
self._stop()
62+
self.config['end_timestamp'] = datetime.now().timestamp()
4463

45-
Returns:
46-
None
47-
"""
64+
@abstractmethod
65+
def _stop(self):
66+
# Stops recording
4867
pass
68+
69+
@classmethod
70+
@abstractmethod
71+
def show_results(cls, from_folder):
72+
pass

0 commit comments

Comments
 (0)