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 +7 -0
- rapidly/bin/Windows_x86/RapidlyEngine.dll +0 -0
- rapidly/models/speech-denoise-11ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-21ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-32ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-96ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-dereverb-11ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-dereverb-21ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-dereverb-32ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-dereverb-96ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-dereverb-micro-32ms.v1.0.rapidly +0 -0
- rapidly/models/speech-denoise-micro-32ms.v1.0.rapidly +0 -0
- rapidly/rapidly.py +482 -0
- rapidly/rapidly_file.py +87 -0
- rapidly/version.py +1 -0
- rapidly-1.0.3.dist-info/METADATA +92 -0
- rapidly-1.0.3.dist-info/RECORD +19 -0
- rapidly-1.0.3.dist-info/WHEEL +5 -0
- rapidly-1.0.3.dist-info/top_level.txt +1 -0
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
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
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
|
rapidly/rapidly_file.py
ADDED
|
@@ -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 @@
|
|
|
1
|
+
rapidly
|