cysox 0.1.5__cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
cysox/__init__.py ADDED
@@ -0,0 +1,61 @@
1
+ """cysox - A Cython wrapper for libsox.
2
+
3
+ High-level API (recommended):
4
+ >>> import cysox
5
+ >>> from cysox import fx
6
+ >>>
7
+ >>> # Get file info
8
+ >>> info = cysox.info('audio.wav')
9
+ >>>
10
+ >>> # Convert with effects
11
+ >>> cysox.convert('input.wav', 'output.mp3', effects=[
12
+ ... fx.Volume(db=3),
13
+ ... fx.Reverb(),
14
+ ... ])
15
+ >>>
16
+ >>> # Stream samples
17
+ >>> for chunk in cysox.stream('audio.wav'):
18
+ ... process(chunk)
19
+ >>>
20
+ >>> # Play audio
21
+ >>> cysox.play('audio.wav')
22
+
23
+ Low-level API (power users):
24
+ >>> from cysox import sox
25
+ >>> sox.init()
26
+ >>> f = sox.Format('audio.wav')
27
+ >>> # ... low-level operations ...
28
+ >>> sox.quit()
29
+ """
30
+
31
+ # High-level API (auto-initializing)
32
+ from .audio import (
33
+ info,
34
+ convert,
35
+ stream,
36
+ play,
37
+ concat,
38
+ )
39
+
40
+ # Effects module
41
+ from . import fx
42
+
43
+ # Low-level API (explicit init/quit required)
44
+ from . import sox
45
+
46
+ # get libsox version via cysox.sox.version()
47
+ __version__ = "0.1.5"
48
+
49
+
50
+ __all__ = [
51
+ # High-level functions
52
+ "info",
53
+ "convert",
54
+ "stream",
55
+ "play",
56
+ # Modules
57
+ "fx",
58
+ "sox",
59
+ # Version
60
+ "__version__",
61
+ ]
cysox/__init__.pyi ADDED
@@ -0,0 +1,223 @@
1
+ # Type stubs for cysox
2
+ # This file provides type hints for IDEs and type checkers
3
+
4
+ from typing import Optional, List, Union, Tuple
5
+ import array
6
+
7
+ # Exception classes
8
+ class SoxError(Exception): ...
9
+ class SoxInitError(SoxError): ...
10
+ class SoxFormatError(SoxError): ...
11
+ class SoxEffectError(SoxError): ...
12
+ class SoxIOError(SoxError): ...
13
+ class SoxMemoryError(SoxError): ...
14
+
15
+ # Core classes
16
+ class SignalInfo:
17
+ rate: float
18
+ channels: int
19
+ precision: int
20
+ length: int
21
+ mult: Optional[float]
22
+
23
+ def __init__(
24
+ self,
25
+ rate: float = 0.0,
26
+ channels: int = 0,
27
+ precision: int = 0,
28
+ length: int = 0,
29
+ mult: float = 0.0
30
+ ) -> None: ...
31
+
32
+ class EncodingInfo:
33
+ encoding: int
34
+ bits_per_sample: int
35
+ compression: float
36
+ reverse_bytes: int
37
+ reverse_nibbles: int
38
+ reverse_bits: int
39
+ opposite_endian: bool
40
+
41
+ def __init__(
42
+ self,
43
+ encoding: int = 0,
44
+ bits_per_sample: int = 0,
45
+ compression: float = 0.0,
46
+ reverse_bytes: int = 0,
47
+ reverse_nibbles: int = 0,
48
+ reverse_bits: int = 0,
49
+ opposite_endian: bool = False
50
+ ) -> None: ...
51
+
52
+ class Format:
53
+ filename: str
54
+ signal: SignalInfo
55
+ encoding: EncodingInfo
56
+ filetype: str
57
+ seekable: bool
58
+ mode: str
59
+ olength: int
60
+ clips: int
61
+ sox_errno: int
62
+ sox_errstr: str
63
+
64
+ def __init__(
65
+ self,
66
+ filename: str,
67
+ signal: Optional[SignalInfo] = None,
68
+ encoding: Optional[EncodingInfo] = None,
69
+ filetype: Optional[str] = None,
70
+ mode: str = 'r'
71
+ ) -> None: ...
72
+
73
+ def __enter__(self) -> Format: ...
74
+ def __exit__(self, exc_type, exc_val, exc_tb) -> bool: ...
75
+
76
+ def read(self, length: int) -> List[int]: ...
77
+ def read_buffer(self, length: int) -> memoryview: ...
78
+ def read_into(self, buffer) -> int: ...
79
+ def write(self, samples: Union[List[int], array.array, memoryview]) -> int: ...
80
+ def seek(self, offset: int, whence: int = 0) -> int: ...
81
+ def close(self) -> int: ...
82
+
83
+ class EffectHandler:
84
+ name: str
85
+ usage: str
86
+ flags: int
87
+ priv_size: int
88
+
89
+ @staticmethod
90
+ def find(name: str, ignore_devices: bool = False) -> Optional[EffectHandler]: ...
91
+
92
+ class Effect:
93
+ handler: EffectHandler
94
+ in_signal: SignalInfo
95
+ out_signal: SignalInfo
96
+ in_encoding: Optional[EncodingInfo]
97
+ out_encoding: Optional[EncodingInfo]
98
+ clips: int
99
+ flows: int
100
+ flow: int
101
+
102
+ def __init__(self, handler: EffectHandler) -> None: ...
103
+ def set_options(self, options: List[str]) -> int: ...
104
+ def stop(self) -> int: ...
105
+ def trim_get_start(self) -> int: ...
106
+ def trim_clear_start(self) -> None: ...
107
+
108
+ class EffectsChain:
109
+ effects: List[Effect]
110
+ length: int
111
+ table_size: int
112
+
113
+ def __init__(
114
+ self,
115
+ in_encoding: Optional[EncodingInfo] = None,
116
+ out_encoding: Optional[EncodingInfo] = None
117
+ ) -> None: ...
118
+
119
+ def add_effect(
120
+ self,
121
+ effect: Effect,
122
+ in_signal: SignalInfo,
123
+ out_signal: SignalInfo
124
+ ) -> int: ...
125
+
126
+ def flow_effects(self, callback=None, client_data=None) -> int: ...
127
+ def get_clips(self) -> int: ...
128
+ def push_effect_last(self, effect: Effect) -> None: ...
129
+ def pop_effect_last(self) -> Optional[Effect]: ...
130
+ def delete_effect_last(self) -> None: ...
131
+ def delete_effects(self) -> None: ...
132
+
133
+ class FormatHandler:
134
+ sox_lib_version_code: int
135
+ description: str
136
+ names: List[str]
137
+ flags: int
138
+ priv_size: int
139
+
140
+ def __init__(self, path: str) -> None: ...
141
+
142
+ @staticmethod
143
+ def find(name: str, ignore_devices: bool = False) -> Optional[FormatHandler]: ...
144
+
145
+ class Globals:
146
+ verbosity: int
147
+ repeatable: bool
148
+ bufsiz: int
149
+ input_bufsiz: int
150
+ ranqd1: int
151
+ stdin_in_use_by: Optional[str]
152
+ stdout_in_use_by: Optional[str]
153
+ subsystem: Optional[str]
154
+ tmp_path: Optional[str]
155
+ use_magic: bool
156
+ use_threads: bool
157
+ log2_dft_min_size: int
158
+
159
+ def as_dict(self) -> dict: ...
160
+
161
+ # Functions
162
+ def init() -> None: ...
163
+ def quit() -> None: ...
164
+ def format_init() -> None: ...
165
+ def format_quit() -> None: ...
166
+ def version() -> str: ...
167
+ def version_info() -> dict: ...
168
+ def strerror(sox_errno: int) -> str: ...
169
+ def find_effect(name: str) -> Optional[EffectHandler]: ...
170
+ def find_format(name: str, ignore_devices: bool = False) -> Optional[FormatHandler]: ...
171
+ def is_playlist(filename: str) -> bool: ...
172
+ def basename(filename: str) -> str: ...
173
+ def precision(encoding: int, bits_per_sample: int) -> int: ...
174
+ def format_supports_encoding(path: str, encoding: EncodingInfo) -> bool: ...
175
+ def get_encodings() -> List: ...
176
+ def get_effects_globals() -> dict: ...
177
+ def get_format_fns() -> List[dict]: ...
178
+ def get_effect_fns() -> List[int]: ...
179
+
180
+ # Memory I/O
181
+ def open_mem_read(
182
+ buffer: bytes,
183
+ signal: Optional[SignalInfo] = None,
184
+ encoding: Optional[EncodingInfo] = None,
185
+ filetype: Optional[str] = None
186
+ ) -> Format: ...
187
+
188
+ def open_mem_write(
189
+ buffer: bytearray,
190
+ signal: SignalInfo,
191
+ encoding: EncodingInfo,
192
+ filetype: Optional[str] = None,
193
+ oob = None
194
+ ) -> Format: ...
195
+
196
+ def open_memstream_write(
197
+ signal: SignalInfo,
198
+ encoding: EncodingInfo,
199
+ filetype: Optional[str] = None,
200
+ oob = None
201
+ ) -> Tuple[Format, int, int]: ...
202
+
203
+ # Sample I/O
204
+ def read_samples(format: Format, buffer: List, length: int) -> int: ...
205
+ def write_samples(format: Format, samples: List) -> int: ...
206
+ def seek_samples(format: Format, offset: int, whence: int = 0) -> int: ...
207
+
208
+ # Callback exception handling
209
+ def get_last_callback_exception() -> Optional[Tuple[type, BaseException, object]]: ...
210
+
211
+ # Constants
212
+ SUCCESS: int
213
+ EOF: int
214
+ EHDR: int
215
+ EFMT: int
216
+ ENOMEM: int
217
+ EPERM: int
218
+ ENOTSUP: int
219
+ EINVAL: int
220
+
221
+ SOX_SEEK_SET: int
222
+
223
+ ENCODINGS: List[Tuple[str, str]]
cysox/__main__.py ADDED
@@ -0,0 +1,102 @@
1
+ """Command-line interface for cysox."""
2
+
3
+ import argparse
4
+ import sys
5
+
6
+ import cysox
7
+ from cysox import sox
8
+
9
+
10
+ def main():
11
+ """Main entry point for the cysox CLI."""
12
+ parser = argparse.ArgumentParser(
13
+ prog="cysox",
14
+ description="A Pythonic audio processing library wrapping libsox",
15
+ )
16
+ parser.add_argument(
17
+ "--version", "-V",
18
+ action="store_true",
19
+ help="Show version information",
20
+ )
21
+
22
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
23
+
24
+ # info command
25
+ info_parser = subparsers.add_parser("info", help="Get audio file information")
26
+ info_parser.add_argument("file", help="Path to audio file")
27
+
28
+ # convert command
29
+ convert_parser = subparsers.add_parser("convert", help="Convert audio file")
30
+ convert_parser.add_argument("input", help="Input audio file")
31
+ convert_parser.add_argument("output", help="Output audio file")
32
+ convert_parser.add_argument(
33
+ "--rate", "-r", type=int, help="Target sample rate in Hz"
34
+ )
35
+ convert_parser.add_argument(
36
+ "--channels", "-c", type=int, help="Target number of channels"
37
+ )
38
+ convert_parser.add_argument(
39
+ "--bits", "-b", type=int, help="Target bits per sample"
40
+ )
41
+
42
+ # play command
43
+ play_parser = subparsers.add_parser("play", help="Play audio file")
44
+ play_parser.add_argument("file", help="Path to audio file")
45
+
46
+ # concat command
47
+ concat_parser = subparsers.add_parser("concat", help="Concatenate audio files")
48
+ concat_parser.add_argument("inputs", nargs="+", help="Input audio files")
49
+ concat_parser.add_argument("-o", "--output", required=True, help="Output file")
50
+
51
+ args = parser.parse_args()
52
+
53
+ if args.version:
54
+ sox.init()
55
+ print(f"cysox {cysox.__version__}")
56
+ print(f"libsox {sox.version()}")
57
+ sox.quit()
58
+ return 0
59
+
60
+ if args.command is None:
61
+ parser.print_help()
62
+ return 0
63
+
64
+ if args.command == "info":
65
+ file_info = cysox.info(args.file)
66
+ print(f"File: {file_info['path']}")
67
+ print(f"Format: {file_info['format']}")
68
+ print(f"Duration: {file_info['duration']:.2f}s")
69
+ print(f"Sample rate: {file_info['sample_rate']} Hz")
70
+ print(f"Channels: {file_info['channels']}")
71
+ print(f"Bits per sample: {file_info['bits_per_sample']}")
72
+ print(f"Encoding: {file_info['encoding']}")
73
+ return 0
74
+
75
+ if args.command == "convert":
76
+ cysox.convert(
77
+ args.input,
78
+ args.output,
79
+ sample_rate=args.rate,
80
+ channels=args.channels,
81
+ bits=args.bits,
82
+ )
83
+ print(f"Converted: {args.input} -> {args.output}")
84
+ return 0
85
+
86
+ if args.command == "play":
87
+ cysox.play(args.file)
88
+ return 0
89
+
90
+ if args.command == "concat":
91
+ if len(args.inputs) < 2:
92
+ print("Error: concat requires at least 2 input files", file=sys.stderr)
93
+ return 1
94
+ cysox.concat(args.inputs, args.output)
95
+ print(f"Concatenated {len(args.inputs)} files -> {args.output}")
96
+ return 0
97
+
98
+ return 0
99
+
100
+
101
+ if __name__ == "__main__":
102
+ sys.exit(main())