rapidly 1.0.3__py3-none-win32.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.
rapidly/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ from .rapidly import (
2
+ RapidlyEngine, # Makes the Rapidly class available when importing the module
3
+ list_models,
4
+ update_models,
5
+ )
6
+ from .rapidly_file import process_file # Makes process_file available when importing the module
7
+ from .version import __version__ # Makes __version__ available when importing the module
rapidly/rapidly.py ADDED
@@ -0,0 +1,482 @@
1
+ import ctypes
2
+ import os
3
+ import platform
4
+ import re
5
+
6
+ # Check for required modules and prompt to install if missing
7
+ try:
8
+ import numpy as np
9
+ except ImportError:
10
+ print("NumPy is not installed. Please install it using the command:")
11
+ print("pip install numpy")
12
+ exit()
13
+
14
+
15
+ MODULE_PATH = os.path.dirname(os.path.abspath(__file__))
16
+ MODEL_PATH = os.path.join(MODULE_PATH, "models")
17
+ BIN_PATH = os.path.join(MODULE_PATH, "bin")
18
+
19
+ if not os.path.exists(MODEL_PATH):
20
+ # We run from the source folder in the github repo
21
+ up_one_folder = os.path.split(MODULE_PATH)[0]
22
+ two_up_folder = os.path.split(up_one_folder)[0]
23
+ MODEL_PATH = os.path.join(two_up_folder, "Models")
24
+
25
+ if not os.path.exists(BIN_PATH):
26
+ # We run from the source folder in the github repo
27
+ up_one_folder = os.path.split(MODULE_PATH)[0]
28
+ two_up_folder = os.path.split(up_one_folder)[0]
29
+ BIN_PATH = os.path.join(two_up_folder, "Bin")
30
+
31
+ # Create a data structure to hold the pointer generated by rapidlyGetModelInfo...
32
+ RAPIDLY_PARAM_MAXATTENUATION = 0x0001
33
+ RAPIDLY_PARAM_SENSITIVITY = 0x0002
34
+ RAPIDLY_PARAM_MASKEXTRAPOLATION = 0x0003
35
+ RAPIDLY_PARAM_BUS_GAINS = 0x0100
36
+ RAPIDLY_PARAM_BUS_SENSITIVITIES = 0x0200
37
+
38
+ class RapidlyEngine:
39
+ """
40
+ The RapidlyEngine is a machine learning audio enhancement module
41
+ developed by Rapidly Labs
42
+ """
43
+
44
+ class Processor:
45
+ def __init__(
46
+ self,
47
+ rapidly_engine: ctypes.CDLL,
48
+ model_path: str,
49
+ num_of_channels: int,
50
+ sample_rate: float,
51
+ ):
52
+ self.rapidly_engine = rapidly_engine
53
+
54
+ self.handle = self.rapidly_engine.rapidlyCreateProcessor(
55
+ model_path.encode(), num_of_channels, sample_rate
56
+ )
57
+ if not self.handle:
58
+ raise Exception("Unable to load Rapidly model file.")
59
+ self.num_of_channels = num_of_channels
60
+
61
+ def __del__(self):
62
+ self.rapidly_engine.rapidlyDeleteProcessor(self.handle)
63
+
64
+ def process(self, audio_signal: np.array) -> bool:
65
+ """
66
+ Processes audio from a numpy array with the format [samples, channels]. The length of the processed audio
67
+ will normally differ from the input lengt due to latency and block based processing.
68
+ """
69
+ num_of_input_samples = audio_signal.shape[0]
70
+ interleaved_audio = audio_signal.astype(np.float32).flatten()
71
+ self.rapidly_engine.rapidlyAddAudioInterleaved(
72
+ self.handle,
73
+ interleaved_audio.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
74
+ num_of_input_samples,
75
+ )
76
+ num_of_output_samples = self.rapidly_engine.rapidlyGetNumOfPendingSamples(self.handle)
77
+ if num_of_output_samples > 0:
78
+ num_of_output_elements = self.num_of_channels * num_of_output_samples
79
+ PCMArray = ctypes.c_float * num_of_output_elements
80
+ pcm_out = PCMArray(*range(num_of_output_elements))
81
+ self.rapidly_engine.rapidlyGetAudioInterleaved(
82
+ self.handle,
83
+ ctypes.cast(pcm_out, ctypes.POINTER(ctypes.c_float)),
84
+ num_of_output_samples,
85
+ )
86
+ return np.array(pcm_out).reshape(num_of_output_samples, self.num_of_channels)
87
+ else:
88
+ return np.empty((0, 0))
89
+
90
+ def reset(self):
91
+ self.rapidly_engine.rapidlyResetProcessor()
92
+
93
+ def get_info(self):
94
+ """
95
+ Returns a processor_info dict with information about the processor.
96
+ """
97
+
98
+ processor_info = ProcessorInfo()
99
+ self.rapidly_engine.rapidlyGetProcessorInfo(self.handle, ctypes.byref(processor_info))
100
+
101
+ processor_info_parsed = {}
102
+ for field in processor_info._fields_:
103
+ processor_info_parsed[field[0]] = getattr(processor_info, field[0])
104
+ return processor_info_parsed
105
+
106
+ def get_max_attenuation(self) -> float:
107
+ """
108
+ Returns the current maximum attenuation setting in dB
109
+ The valid value range is <-inf, 0]
110
+ """
111
+ return self.rapidly_engine.rapidlyGetParameterValue(self.handle, RAPIDLY_PARAM_MAXATTENUATION)
112
+
113
+ def set_max_attenuation(self, max_attenuation: float):
114
+ """
115
+ Sets the current maximum attenuation setting in dB
116
+ The valid value range is <-inf, 0]
117
+ """
118
+ return self.rapidly_engine.rapidlySetParameterValue(
119
+ self.handle, RAPIDLY_PARAM_MAXATTENUATION, max_attenuation
120
+ )
121
+
122
+ def get_sensitivity(self) -> float:
123
+ """
124
+ Returns the current sensitivity in percent. 0% is neutral and positive
125
+ values will increase the amount of reduction.
126
+ The valid value range is [-100, 100]
127
+ """
128
+ return self.rapidly_engine.rapidlyGetParameterValue(self.handle, RAPIDLY_PARAM_SENSITIVITY)
129
+
130
+ def set_sensitivity(self, sensitivity: float):
131
+ """
132
+ Sets the current sensitivity in percent. 0% is neutral and positive
133
+ values will increase the amount of reduction.
134
+ The valid value range is [-100, 100]
135
+ """
136
+ return self.rapidly_engine.rapidlySetParameterValue(
137
+ self.handle, RAPIDLY_PARAM_SENSITIVITY, sensitivity
138
+ )
139
+
140
+ def get_output_bus_name(self, output_bus_index, max_length=256):
141
+ """
142
+ Returns the name of the specified output bus as a string.
143
+ """
144
+ # Create a buffer for the output bus name
145
+ output_bus_name = (ctypes.c_char * max_length)()
146
+ # Call the C function
147
+ self.rapidly_engine.rapidlyGetOutputBusName(self.handle, output_bus_index, output_bus_name, max_length)
148
+ # Convert the result to a Python string and return it
149
+ return output_bus_name.value.decode('utf-8')
150
+
151
+ def get_parameter_range(self, parameter_index):
152
+ """
153
+ Returns a dictionary with the min and max values for the specified parameter.
154
+ """
155
+ min_value = ctypes.c_float()
156
+ max_value = ctypes.c_float()
157
+ # Call the C function
158
+ self.rapidly_engine.rapidlyGetParameterRange(self.handle, parameter_index, ctypes.byref(min_value), ctypes.byref(max_value))
159
+ # Return a dictionary with the min and max values
160
+ return {"minValue": min_value.value, "maxValue": max_value.value}
161
+
162
+ def get_number_of_output_buses(self):
163
+ """
164
+ Returns the number of output buses for this processor.
165
+ """
166
+ return self.rapidly_engine.rapidlyGetNumOfOutputBusses(self.handle)
167
+
168
+ def set_output_bus_volume(self, bus_index: int, volume: float):
169
+ """
170
+ Sets the volume for a specific output bus.
171
+
172
+ Parameters:
173
+ bus_index (int): The index of the output bus.
174
+ volume (float): The volume to set for the output bus (usually 0 or above).
175
+ """
176
+ # Calculate the parameter index for the specific bus
177
+ parameter_index = RAPIDLY_PARAM_BUS_GAINS + bus_index
178
+ # Set the volume using rapidlySetParameterValue
179
+ self.rapidly_engine.rapidlySetParameterValue(self.handle, parameter_index, volume)
180
+
181
+ def get_output_bus_volume(self, bus_index: int) -> float:
182
+ """
183
+ Gets the volume for a specific output bus.
184
+
185
+ Parameters:
186
+ bus_index (int): The index of the output bus.
187
+
188
+ Returns:
189
+ float: The current volume level of the specified output bus.
190
+ """
191
+ # Calculate the parameter index for the specific bus
192
+ parameter_index = RAPIDLY_PARAM_BUS_GAINS + bus_index
193
+ # Get the volume using rapidlyGetParameterValue
194
+ return self.rapidly_engine.rapidlyGetParameterValue(self.handle, parameter_index)
195
+
196
+ def set_output_bus_sensitivity(self, bus_index: int, sensitivity: float):
197
+ """
198
+ Sets the sensitivity for a specific output bus.
199
+
200
+ Parameters:
201
+ bus_index (int): The index of the output bus.
202
+ sensitivity (float): The sensitivity to set for the output bus, typically within a range like [-100, 100].
203
+ """
204
+ # Calculate the parameter index for the specific bus
205
+ parameter_index = RAPIDLY_PARAM_BUS_SENSITIVITIES + bus_index
206
+ # Set the sensitivity using rapidlySetParameterValue
207
+ self.rapidly_engine.rapidlySetParameterValue(self.handle, parameter_index, sensitivity)
208
+
209
+
210
+
211
+
212
+ def __init__(self):
213
+ path_to_binary = self.find_binary()
214
+
215
+ if not path_to_binary:
216
+ raise Exception("Could not find the Rapidly Engine binary.")
217
+
218
+ self.rapidly_engine = ctypes.cdll.LoadLibrary(path_to_binary)
219
+
220
+ # RAPIDLY_API bool rapidlyAddLicense (const char* licenseString);
221
+ self.rapidly_engine.rapidlyAddLicense.argtypes = [ctypes.c_char_p]
222
+ self.rapidly_engine.rapidlyAddLicense.restype = ctypes.c_bool
223
+
224
+ # RAPIDLY_API RapidlyProcessorHandle rapidlyCreateProcessor (const char* modelFilepath, int32_t numOfChannels, double sampleRate);
225
+ self.rapidly_engine.rapidlyCreateProcessor.argtypes = [
226
+ ctypes.c_char_p,
227
+ ctypes.c_int32,
228
+ ctypes.c_double,
229
+ ]
230
+ self.rapidly_engine.rapidlyCreateProcessor.restype = ctypes.c_void_p
231
+
232
+ # RAPIDLY_API RapidlyProcessorHandle rapidlyCreateStemSeparator (int32_t numOfModels, const char** modelFilepaths, int32_t numOfChannels, double sampleRate);
233
+ # self.rapidly_engine.rapidlyCreateStemSeparator.argtypes = [ctypes.c_int32, ctypes.c_char_p, ctypes.c_int32, ctypes.c_double]
234
+ self.rapidly_engine.rapidlyCreateStemSeparator.argtypes = [
235
+ ctypes.c_int32,
236
+ ctypes.POINTER(ctypes.POINTER(ctypes.c_char)),
237
+ ctypes.c_int32,
238
+ ctypes.c_double,
239
+ ]
240
+ self.rapidly_engine.rapidlyCreateStemSeparator.restype = ctypes.c_void_p
241
+
242
+ # RAPIDLY_API RapidlyProcessorHandle rapidlyCreateProcessor (const char* modelFilepath, int32_t numOfChannels, double sampleRate);
243
+ self.rapidly_engine.rapidlyCreateProcessor.argtypes = [
244
+ ctypes.c_char_p,
245
+ ctypes.c_int32,
246
+ ctypes.c_double,
247
+ ]
248
+ self.rapidly_engine.rapidlyCreateProcessor.restype = ctypes.c_void_p
249
+
250
+ # RAPIDLY_API void rapidlyDeleteProcessor (RapidlyProcessorHandle processorHandle);
251
+ self.rapidly_engine.rapidlyDeleteProcessor.argtypes = [ctypes.c_void_p]
252
+ self.rapidly_engine.rapidlyDeleteProcessor.restype = None
253
+
254
+ # RAPIDLY_API void rapidlyAddAudioInterleaved (RapidlyProcessorHandle processorHandle, const float* interleavedPCM, int32_t numOfSamples);
255
+ self.rapidly_engine.rapidlyAddAudioInterleaved.argtypes = [
256
+ ctypes.c_void_p,
257
+ ctypes.POINTER(ctypes.c_float),
258
+ ctypes.c_int32,
259
+ ]
260
+ self.rapidly_engine.rapidlyAddAudioInterleaved.restype = None
261
+
262
+ # RAPIDLY_API int32_t rapidlyGetNumOfPendingSamples (RapidlyProcessorHandle processorHandle);
263
+ self.rapidly_engine.rapidlyGetNumOfPendingSamples.argtypes = [ctypes.c_void_p]
264
+ self.rapidly_engine.rapidlyGetNumOfPendingSamples.restype = ctypes.c_int32
265
+
266
+ # RAPIDLY_API bool rapidlyGetAudioInterleaved (RapidlyProcessorHandle processorHandle, float* interleavedPCM, int32_t numOfSamples);
267
+ self.rapidly_engine.rapidlyGetAudioInterleaved.argtypes = [
268
+ ctypes.c_void_p,
269
+ ctypes.POINTER(ctypes.c_float),
270
+ ctypes.c_int32,
271
+ ]
272
+ self.rapidly_engine.rapidlyGetAudioInterleaved.restype = ctypes.c_bool
273
+
274
+ # RAPIDLY_API void rapidlyResetProcessorState (RapidlyProcessorHandle modelHandle);
275
+ self.rapidly_engine.rapidlyResetProcessorState.argtypes = [ctypes.c_void_p]
276
+ self.rapidly_engine.rapidlyResetProcessorState.restype = None
277
+
278
+ # RAPIDLY_API void rapidlyGetProcessorInfo (RapidlyProcessorHandle modelHandle, RapidlyProcessorInfo* modelInfo);
279
+ self.rapidly_engine.rapidlyGetProcessorInfo.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
280
+ self.rapidly_engine.rapidlyGetProcessorInfo.restype = None
281
+
282
+ # RAPIDLY_API float rapidlyGetParameterValue (RapidlyProcessorHandle processorHandle, int32_t parameterIndex);
283
+ self.rapidly_engine.rapidlyGetParameterValue.argtypes = [ctypes.c_void_p, ctypes.c_int32]
284
+ self.rapidly_engine.rapidlyGetParameterValue.restype = ctypes.c_float
285
+
286
+ # RAPIDLY_API void rapidlySetParameterValue (RapidlyProcessorHandle processorHandle, int32_t parameterIndex, float parameterValue);
287
+ self.rapidly_engine.rapidlySetParameterValue.argtypes = [
288
+ ctypes.c_void_p,
289
+ ctypes.c_int32,
290
+ ctypes.c_float,
291
+ ]
292
+ self.rapidly_engine.rapidlySetParameterValue.restype = None
293
+
294
+ # RAPIDLY_API c_int32 rapidlyGetNumOfOutputBusses (RapidlyProcessorHandle modelHandle);
295
+ self.rapidly_engine.rapidlyGetNumOfOutputBusses.argtypes = [ctypes.c_void_p]
296
+ self.rapidly_engine.rapidlyGetNumOfOutputBusses.restype = ctypes.c_int32
297
+
298
+ # Define the function's argument types and return type
299
+ self.rapidly_engine.rapidlyGetOutputBusName.argtypes = [
300
+ ctypes.c_void_p, # RapidlyProcessorHandle processorHandle
301
+ ctypes.c_int32, # int32_t outputBusIndex
302
+ ctypes.POINTER(ctypes.c_char), # char* outputBusName
303
+ ctypes.c_int32 # int32_t maxLength
304
+ ]
305
+ self.rapidly_engine.rapidlyGetOutputBusName.restype = None # void function
306
+
307
+ # Define the function's argument types and return type
308
+ self.rapidly_engine.rapidlyGetParameterRange.argtypes = [
309
+ ctypes.c_void_p, # RapidlyProcessorHandle processorHandle
310
+ ctypes.c_int32, # int32_t parameterIndex
311
+ ctypes.POINTER(ctypes.c_float), # float* minimumValue
312
+ ctypes.POINTER(ctypes.c_float) # float* maximumValue
313
+ ]
314
+ self.rapidly_engine.rapidlyGetParameterRange.restype = None # void function
315
+
316
+
317
+ self.prev_samplerate = None
318
+ self.prev_channels = None
319
+
320
+ def add_license(self, license_string: str) -> bool:
321
+ return self.rapidly_engine.rapidlyAddLicense(license_string.encode())
322
+
323
+ def create_processor(
324
+ self, model_file_path: str, num_of_channels: int, sample_rate: int
325
+ ) -> Processor:
326
+ """
327
+ Creates an audio processor from a Rapidly model file.
328
+ """
329
+ model_file_abs_path = get_model_file_abs_path(model_file_path)
330
+ return self.Processor(self.rapidly_engine, model_file_abs_path, num_of_channels, sample_rate)
331
+
332
+ def find_binary(self) -> str:
333
+ """
334
+ Returns the path to a binary in the bin folder.
335
+ """
336
+
337
+ relative_path = BIN_PATH
338
+
339
+ path_to_binary = None
340
+ if platform.system() == "Windows":
341
+ if platform.architecture()[0] == "32bit":
342
+ path_to_binary = os.path.join(relative_path, "Windows_x86/RapidlyEngine.dll")
343
+ if platform.architecture()[0] == "64bit":
344
+ path_to_binary = os.path.join(relative_path, "Windows_x64/RapidlyEngine.dll")
345
+ elif platform.system() == "Darwin":
346
+ fn = self.find_preferred_dylib(os.path.join(relative_path, "macOS"), "libRapidlyEngine")
347
+ path_to_binary = os.path.join(relative_path, "macOS", fn)
348
+ elif platform.system() == "Linux":
349
+ machine = platform.machine()
350
+ if machine in ("aarch64", "arm64"):
351
+ path_to_binary = os.path.join(relative_path, "Linux_arm64/libRapidlyEngine.so")
352
+ else:
353
+ path_to_binary = os.path.join(relative_path, "Linux_x64/libRapidlyEngine.so")
354
+
355
+ return path_to_binary
356
+
357
+
358
+ def find_preferred_dylib(self, directory, base_name):
359
+ """
360
+ Finds the preferred .dylib file in the directory.
361
+ If a file with a version number exists, it returns that file.
362
+ Otherwise, it returns the one without a version number.
363
+
364
+ :param directory: The directory to search for .dylib files.
365
+ :param base_name: The base name of the .dylib file (e.g., "libRapidlyEngine").
366
+ :return: The path to the preferred .dylib file, or None if no matches are found.
367
+ """
368
+ versioned_pattern = re.compile(rf"{base_name}\.\d+\.\d+\.\d+\.dylib$")
369
+ non_versioned_name = f"{base_name}.dylib"
370
+
371
+ versioned_file = None
372
+ non_versioned_file = None
373
+
374
+ for file_name in os.listdir(directory):
375
+ if versioned_pattern.match(file_name):
376
+ versioned_file = os.path.join(directory, file_name)
377
+ elif file_name == non_versioned_name:
378
+ non_versioned_file = os.path.join(directory, file_name)
379
+
380
+ return versioned_file or non_versioned_file
381
+
382
+
383
+
384
+ class ProcessorInfo(ctypes.Structure):
385
+ _fields_ = [
386
+ ("sampleRate", ctypes.c_double),
387
+ ("numOfModelChannels", ctypes.c_int32),
388
+ ("latencyInSamples", ctypes.c_int32),
389
+ ("blockSize", ctypes.c_int32),
390
+ ("hopSize", ctypes.c_int32),
391
+ ]
392
+
393
+
394
+ class ProcessorSettings(ctypes.Structure):
395
+ """creates a struct to match rapidlyProcessorSettings"""
396
+
397
+ _fields_ = [
398
+ ("sensitivity", ctypes.c_float),
399
+ ("maximumAttenuation", ctypes.c_float),
400
+ ]
401
+
402
+
403
+ def list_models():
404
+ """
405
+ Returns a list of available models.
406
+ """
407
+ model_list = []
408
+
409
+ for fn in os.listdir(MODEL_PATH):
410
+ if fn.endswith(".rapidly"):
411
+ model_list.append(fn)
412
+ return model_list
413
+
414
+
415
+ def update_models():
416
+ """
417
+ Updates the local models directory by downloading the latest public branch archive
418
+ from the Rapidly models GitHub repository using urllib.
419
+ """
420
+ import urllib.request
421
+ import zipfile
422
+ import io
423
+ import os
424
+
425
+ repo_url = "https://github.com/rapidly-labs/rapidly-sdk/archive/refs/heads/main.zip"
426
+ models_dir = os.path.abspath(MODEL_PATH) # Absolute path to the local Models directory
427
+
428
+ print("Downloading the latest models archive...")
429
+ try:
430
+ # Download the repository archive
431
+ with urllib.request.urlopen(repo_url) as response:
432
+ if response.status != 200:
433
+ raise Exception(f"Failed to download archive: HTTP {response.status}")
434
+
435
+ # Read the archive into memory
436
+ archive_data = response.read()
437
+
438
+ # Extract the downloaded archive
439
+ with zipfile.ZipFile(io.BytesIO(archive_data)) as z:
440
+ extracted_base = z.namelist()[0] # First directory in the archive
441
+ extracted_models_prefix = os.path.join(extracted_base, "Models")
442
+
443
+ if not os.path.exists(models_dir):
444
+ os.makedirs(models_dir)
445
+
446
+ # Extract only the contents of the Models directory
447
+ for file in z.namelist():
448
+ if file.startswith(extracted_models_prefix):
449
+ # Compute the relative path inside the Models folder
450
+ relative_path = os.path.relpath(file, extracted_models_prefix)
451
+ if relative_path == ".": # Skip the root directory itself
452
+ continue
453
+
454
+ # Compute the target path
455
+ target_path = os.path.join(models_dir, relative_path)
456
+
457
+ # Create directories or write files as needed
458
+ if file.endswith('/'):
459
+ os.makedirs(target_path, exist_ok=True) # Create directories
460
+ else:
461
+ with open(target_path, "wb") as f:
462
+ f.write(z.read(file))
463
+
464
+ print(f"Models updated successfully at {models_dir}.")
465
+ except Exception as e:
466
+ print(f"Error updating models: {e}")
467
+ raise
468
+
469
+ def get_model_file_abs_path(model_file_path: str):
470
+ model_file_abs_path = model_file_path
471
+ if not os.path.exists(model_file_abs_path):
472
+ # possible a relative path from list_models
473
+ found_model = False
474
+
475
+ model_file_abs_path = os.path.join(MODEL_PATH, model_file_path)
476
+ if os.path.exists(model_file_abs_path):
477
+ found_model = True
478
+
479
+ if not found_model:
480
+ raise Exception("Model file does not exist.")
481
+
482
+ return model_file_abs_path
@@ -0,0 +1,87 @@
1
+ import os
2
+ import shutil
3
+ import subprocess
4
+ import numpy as np
5
+ import rapidly
6
+
7
+ try:
8
+ import soundfile as sf
9
+ except ImportError:
10
+ print("Soundfile is not installed. Please install it to process files using the command:")
11
+ print("pip install soundfile")
12
+ exit()
13
+
14
+ def process_file(
15
+ model_file_path: str, input_file_path: str, output_file_path: str, license_string: str = "", selected_output_bus: int = 0
16
+ ):
17
+ """
18
+ Processes a full wave file using the preloaded model
19
+ """
20
+ rapidly_engine = rapidly.RapidlyEngine()
21
+ if license_string:
22
+ if not rapidly_engine.add_license(license_string):
23
+ raise Exception("License key not accepted")
24
+
25
+ created_temp_file = False
26
+ try:
27
+ in_file_info = sf.SoundFile(input_file_path)
28
+ except sf.LibsndfileError as e:
29
+ # Try converting using ffmpeg
30
+ if not shutil.which("ffmpeg"):
31
+ raise Exception(
32
+ f"Could not open input file: {e}\nSupported formats are WAV, FLAC, OGG, and MAT. Please install ffmpeg to support more formats."
33
+ )
34
+ print("Converting input file to WAV using ffmpeg...")
35
+ temp_input = "temp_input.wav"
36
+ subprocess.run(["ffmpeg", "-i", input_file_path, temp_input], check=True)
37
+ input_file_path = temp_input
38
+ in_file_info = sf.SoundFile(input_file_path)
39
+ created_temp_file = True
40
+
41
+ processor = rapidly_engine.create_processor(
42
+ model_file_path, in_file_info.channels, in_file_info.samplerate
43
+ )
44
+
45
+ for i in range(processor.get_number_of_output_buses()):
46
+ processor.set_output_bus_sensitivity(i, 0.0)
47
+ if i == selected_output_bus:
48
+ processor.set_output_bus_volume(i, 1.0)
49
+ else:
50
+ processor.set_output_bus_volume(i, 0.0)
51
+
52
+
53
+ output_file = sf.SoundFile(
54
+ output_file_path,
55
+ mode="w",
56
+ samplerate=in_file_info.samplerate,
57
+ channels=in_file_info.channels,
58
+ subtype=in_file_info.subtype,
59
+ endian=in_file_info.endian,
60
+ format=in_file_info.format,
61
+ )
62
+
63
+ block_size = 65536
64
+ num_of_samples_written = 0
65
+ num_of_samples = len(in_file_info) // in_file_info.channels
66
+
67
+ for audio_block in sf.blocks(
68
+ input_file_path, dtype="float32", blocksize=block_size, always_2d=True
69
+ ):
70
+ audio_out = processor.process(audio_block)
71
+ if audio_out.size != 0:
72
+ output_file.write(audio_out)
73
+ num_of_samples_written += audio_out.shape[0]
74
+
75
+ # Process remaining samples by feeding silence to the processor
76
+ silent_audio = np.zeros([block_size, in_file_info.channels], dtype="float32")
77
+ while num_of_samples_written < num_of_samples:
78
+ remaining_samples = num_of_samples - num_of_samples_written
79
+ samples_to_process = min(block_size, remaining_samples)
80
+ audio_out = processor.process(silent_audio[:samples_to_process])
81
+ if audio_out.size != 0:
82
+ output_file.write(audio_out)
83
+ num_of_samples_written += audio_out.shape[0]
84
+
85
+ output_file.close()
86
+ if created_temp_file:
87
+ os.remove(input_file_path)
rapidly/version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "1.0.3"
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: rapidly
3
+ Version: 1.0.3
4
+ Summary: The Python API to the Rapidly engine — realtime audio enhancement powered by Rapidly Labs AS.
5
+ Home-page: https://rapidly.io
6
+ Author: Rapidly Labs AS
7
+ Author-email: Rapidly Labs AS <support@rapidly.io>
8
+ License: Proprietary — see LICENSE
9
+ Project-URL: Homepage, https://rapidly.io
10
+ Keywords: rapidly,audio enhancement,noise reduction,speech denoise
11
+ Classifier: License :: Other/Proprietary License
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: !=2.*,>=3.0
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: setuptools>=41.2.0
17
+ Requires-Dist: numpy
18
+ Requires-Dist: soundfile
19
+ Dynamic: author
20
+ Dynamic: home-page
21
+ Dynamic: requires-python
22
+
23
+ # Audio enhancement in Python with the Rapidly engine
24
+
25
+ The **Rapidly** Engine is a model inference library built with audio in mind. Pre-trained models for speech noise suppression and de-reverberation are available out of the box, and we can train models for any use case — get in touch if you have specific requirements.
26
+
27
+ Our models are trained specifically for real-time usage, achieving low latencies down to 11 milliseconds in speech enhancement applications. The models are designed to be small and resource-efficient, with model file sizes down to 242 KB for the smallest noise suppression model.
28
+
29
+ The Rapidly engine is built in C++ and is compatible across various architectures. With our Python wrapper, you can easily integrate Rapidly into your Python projects and quickly process files and benchmark our models.
30
+
31
+ To learn more about Rapidly, visit [Rapidly](https://rapidly.io).
32
+
33
+
34
+ ## Installation
35
+
36
+ To install the Python wrapper for Rapidly, use `pip`:
37
+
38
+ python -m pip install rapidly
39
+
40
+ Rapidly requires Python 3.
41
+
42
+ ## How to Use
43
+
44
+ To use the API, import it and list the available models:
45
+
46
+ import rapidly
47
+ models = rapidly.list_models()
48
+ print(models)
49
+
50
+ To download and update to the latest models, simply call:
51
+
52
+ rapidly.update_models()
53
+
54
+ ## Process a File
55
+
56
+ To process a file with Rapidly, use the `process_file` function:
57
+
58
+ import rapidly
59
+
60
+ # List the available models in the models folder.
61
+ models = rapidly.list_models()
62
+
63
+ # Process a file using the first model in the list.
64
+ rapidly.process_file(
65
+ model_file_path=models[0],
66
+ input_file_path="path/to/input.wav",
67
+ output_file_path="path/to/output.wav",
68
+ selected_output_bus=0 # Bus 0 is the processed result in most models.
69
+ )
70
+
71
+ Some models support multiple output buses — for example, separate buses for noise reduction and de-reverberation. You can inspect available buses like this:
72
+
73
+ rapidly_engine = rapidly.RapidlyEngine()
74
+ processor = rapidly_engine.create_processor(models[0], 2, 44100)
75
+
76
+ num_buses = processor.get_number_of_output_buses()
77
+ for i in range(num_buses):
78
+ print(i, processor.get_output_bus_name(i))
79
+
80
+ Pass the desired bus index as `selected_output_bus` in `process_file`.
81
+
82
+ ### Models
83
+
84
+ The models in the **models** folder use clear, descriptive names. For example, `speech-denoise-32ms.v1.0.rapidly` indicates a model designed to _denoise_ speech with a latency of 32 ms. The `micro` size variant (e.g. `speech-denoise-micro-32ms.v1.0.rapidly`) is a compact build of the same model for CPU-constrained scenarios.
85
+
86
+ All models within a family (for example, the **speech-denoise** family) share similar characteristics. For general denoising, we recommend starting with `speech-denoise-96ms.v1.0.rapidly` to check if it meets your needs, and moving down to shorter latency variants if needed.
87
+
88
+ If you have specific requirements or challenging audio conditions, we can build customised models optimised for your use case — feel free to [contact us](https://rapidly.io).
89
+
90
+ `process_file` uses [PySoundFile](https://pysoundfile.readthedocs.io/) to read and write audio files. WAV, FLAC, OGG, and MAT formats are supported natively; other formats require [ffmpeg](https://ffmpeg.org/) to be installed.
91
+
92
+ For more information, see the [Rapidly documentation](https://rapidly.io/docs/welcome).
@@ -0,0 +1,19 @@
1
+ rapidly/__init__.py,sha256=o31N6lgMA5Q2WBE61ZuB-GCHFecGZrNSUeJTn7vmFeQ,330
2
+ rapidly/rapidly.py,sha256=t1fyt0yQnzA5dIPq8TPlRmOr2jp7EPqqY6E-hmKfjsM,20570
3
+ rapidly/rapidly_file.py,sha256=OGsZ0kfvkO16D6IX0zAR_dsfbSgsVpJAtmtS2P8pkjE,3100
4
+ rapidly/version.py,sha256=-PYTKPEkq2aGJHOdzkgu5oKZIPKSq6MCqGrFyy4QQXc,21
5
+ rapidly/bin/Windows_x86/RapidlyEngine.dll,sha256=0YGJ5It93_PScPYGcgaSyIvKwBPvyx6ayh8_zOf9Fp8,2808320
6
+ rapidly/models/speech-denoise-11ms.v1.0.rapidly,sha256=-veVKH0njoxXjCasvA03cMx3g8ELBPHuKOivq_lKQsg,628894
7
+ rapidly/models/speech-denoise-21ms.v1.0.rapidly,sha256=sHAeUGk6fREj1u1HANl1AB-C_bwgNyWAJ2mSKImyZYo,870942
8
+ rapidly/models/speech-denoise-32ms.v1.0.rapidly,sha256=o95vaJP3QLv34AINY-RVkMAmmbOQei18NPRcN7jGV7U,874014
9
+ rapidly/models/speech-denoise-96ms.v1.0.rapidly,sha256=y2IiuQklOLS0WPhwOpyOI607yhRji-vlkVQRuZspudk,946929
10
+ rapidly/models/speech-denoise-dereverb-11ms.v1.0.rapidly,sha256=KDonJAnIcub63p-IYP1hX2o5zC6f7-2TDXxAj1jHGh4,629288
11
+ rapidly/models/speech-denoise-dereverb-21ms.v1.0.rapidly,sha256=nBpLpvXfK771-Uji84s87nA_E-d16w5RcGck4wN6t40,871336
12
+ rapidly/models/speech-denoise-dereverb-32ms.v1.0.rapidly,sha256=XCK1899bmrr6m9Oyzv2rIoR8VGhP7Ak3DtzcPEbYb5Q,874408
13
+ rapidly/models/speech-denoise-dereverb-96ms.v1.0.rapidly,sha256=9JxRrk9UvRqDUgPDUnfgBnJWGMsxk0eIqZNza0whJ78,947515
14
+ rapidly/models/speech-denoise-dereverb-micro-32ms.v1.0.rapidly,sha256=tE4NjDqVTkc7vypUuJegTdzRjfifuR1txF6I0SNp8-o,246249
15
+ rapidly/models/speech-denoise-micro-32ms.v1.0.rapidly,sha256=sDDmmqgjR1uXfi-wF8pu7UKJydFnfUoRN_-oV6uGDdY,246205
16
+ rapidly-1.0.3.dist-info/METADATA,sha256=F6dImjo2O8XJ6Lcq1N16E_pbIS2OxMn-ArvlEjzG0XU,4107
17
+ rapidly-1.0.3.dist-info/WHEEL,sha256=rQmUio2x5FDl3aNlsqS1bbvOHEhGOtoGexXubYhGACc,93
18
+ rapidly-1.0.3.dist-info/top_level.txt,sha256=52ZM3YK3lUDEBV2_84U5bo-vy4yVIQ8LxmggFlraF5I,8
19
+ rapidly-1.0.3.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-win32
5
+
@@ -0,0 +1 @@
1
+ rapidly