Coverage for src/nendo/utils.py: 66%

44 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-21 14:33 +0100

1"""Utility functions used by Nendo.""" 

2import hashlib 

3import logging 

4import uuid 

5from abc import ABC 

6from typing import Callable, ClassVar, List, Optional, Union 

7 

8import numpy as np 

9import sounddevice as sd 

10 

11logger = logging.getLogger("nendo") 

12 

13 

14def get_wrapped_methods(plugin_class: ABC) -> List[Callable]: 

15 """Get all wrapped methods of the given plugin class.""" 

16 return [ 

17 f 

18 for f in type(plugin_class).__dict__.values() 

19 if hasattr(f, "__wrapped__") and "pydantic" not in f.__name__ 

20 ] 

21 

22 

23def md5sum(file_path): 

24 """Compute md5 checksum of file found under the given file_path.""" 

25 hash_md5 = hashlib.md5() # noqa: S324 

26 with open(file_path, "rb") as f: 

27 for chunk in iter(lambda: f.read(4096), b""): 

28 hash_md5.update(chunk) 

29 return hash_md5.hexdigest() 

30 

31 

32def play_signal(signal: np.ndarray, sr: int, loop: bool = False): 

33 """Play the signal given as numpy array using `sounddevice`.""" 

34 logger.info("Playing signal with sample rate %d...", sr) 

35 

36 # sounddevice wants the signal to be in the shape (n_samples, n_channels) 

37 sd.play(signal.T, samplerate=sr, loop=loop, blocking=True) 

38 sd.wait() 

39 

40 

41def ensure_uuid(target_id: Optional[Union[str, uuid.UUID]] = None) -> uuid.UUID: 

42 """Take a string or a UUID and return the same value as a guaranteed UUID. 

43 

44 Args: 

45 target_id (Union[str, uuid.UUID]): The target ID to convert to UUID type. 

46 

47 Returns: 

48 uuid.UUID: The given target_id, converted to UUID. None if None was passed. 

49 """ 

50 if target_id is not None and isinstance(target_id, str) and target_id != "": 

51 return uuid.UUID(target_id) 

52 if isinstance(target_id, uuid.UUID): 

53 return target_id 

54 return None 

55 

56 

57class AudioFileUtils: 

58 """Utility class for handling audio files.""" 

59 

60 supported_filetypes: ClassVar[List[str]] = [ 

61 "wav", 

62 "mp3", 

63 "aiff", 

64 "flac", 

65 "ogg", 

66 ] 

67 

68 def is_supported_filetype(self, filepath: str) -> bool: 

69 """Check if the filetype of the given file is supported by Nendo.""" 

70 return filepath.lower().split(".")[-1] in self.supported_filetypes 

71 

72 

73def pretty_print(data, indent=0): 

74 """Helper function for pretty printing.""" 

75 result = "" 

76 if isinstance(data, dict): 

77 for key, value in data.items(): 

78 result += "\t" * indent + str(key) + ": " 

79 if isinstance(value, (dict, list)): 

80 result += "\n" + pretty_print(value, indent + 1) 

81 else: 

82 result += str(value) + "\n" 

83 elif isinstance(data, list): 

84 for item in data: 

85 if isinstance(item, (dict, list)): 

86 result += pretty_print(item, indent + 1) 

87 else: 

88 result += "\t" * indent + str(item) + "\n" 

89 return result