@@ -248,51 +248,83 @@ def _process_file(
248248 ) -> typing .Tuple [
249249 typing .List [typing .Any ],
250250 typing .List [str ],
251- typing .List [pd .Timedelta ],
252- typing .List [pd .Timedelta ],
251+ typing .Optional [ typing . List [pd .Timedelta ] ],
252+ typing .Optional [ typing . List [pd .Timedelta ] ],
253253 ]:
254+ r"""Process a file.
255+
256+ Args:
257+ file: file path
258+ root: optional root path of file
259+ start: start time to read media file
260+ end: end time to read media file
261+ process_func_args: arguments to pass to process function
262+
263+ Returns:
264+ result of processing function, files, starts, ends
265+
266+ """
254267 if start is not None :
255268 start = utils .to_timedelta (start , self .sampling_rate )
256269 if end is not None :
257270 end = utils .to_timedelta (end , self .sampling_rate )
258271
259- signal , sampling_rate = utils .read_audio (
260- file ,
261- start = start ,
262- end = end ,
263- root = root ,
264- )
272+ ext = audeer .file_extension (file ).lower ()
265273
266- y , files , starts , ends = self ._process_signal (
267- signal ,
268- sampling_rate ,
269- idx = idx ,
270- root = root ,
271- file = file ,
272- process_func_args = process_func_args ,
273- )
274-
275- def precision_offset (duration , sampling_rate ):
276- # Ensure we get the same precision
277- # by storing what is lost due to rounding
278- # when reading the file
279- duration_at_sample = utils .to_timedelta (
280- audmath .samples (duration .total_seconds (), sampling_rate ) / sampling_rate
274+ # Text files
275+ if ext in ["json" , "txt" ]:
276+ data = utils .read_text (file , root = root )
277+ y = self ._call_data (
278+ data ,
279+ idx = idx ,
280+ root = root ,
281+ file = file ,
282+ process_func_args = process_func_args ,
281283 )
282- return duration - duration_at_sample
284+ files = [file ]
285+ starts = None
286+ ends = None
283287
284- if self .win_dur is not None :
285- if start is not None :
286- starts = starts + start
287- ends = ends + start
288+ # Audio/video files
288289 else :
289- if start is not None and not pd .isna (start ):
290- starts [0 ] += start
291- ends [0 ] += start - precision_offset (start , sampling_rate )
292- if self .keep_nat and (end is None or pd .isna (end )):
293- ends [0 ] = pd .NaT
294- if end is not None and not pd .isna (end ):
295- ends [- 1 ] += precision_offset (end , sampling_rate )
290+ signal , sampling_rate = utils .read_audio (
291+ file ,
292+ start = start ,
293+ end = end ,
294+ root = root ,
295+ )
296+
297+ y , files , starts , ends = self ._process_signal (
298+ signal ,
299+ sampling_rate ,
300+ idx = idx ,
301+ root = root ,
302+ file = file ,
303+ process_func_args = process_func_args ,
304+ )
305+
306+ def precision_offset (duration , sampling_rate ):
307+ # Ensure we get the same precision
308+ # by storing what is lost due to rounding
309+ # when reading the file
310+ duration_at_sample = utils .to_timedelta (
311+ audmath .samples (duration .total_seconds (), sampling_rate )
312+ / sampling_rate
313+ )
314+ return duration - duration_at_sample
315+
316+ if self .win_dur is not None :
317+ if start is not None :
318+ starts = starts + start
319+ ends = ends + start
320+ else :
321+ if start is not None and not pd .isna (start ):
322+ starts [0 ] += start
323+ ends [0 ] += start - precision_offset (start , sampling_rate )
324+ if self .keep_nat and (end is None or pd .isna (end )):
325+ ends [0 ] = pd .NaT
326+ if end is not None and not pd .isna (end ):
327+ ends [- 1 ] += precision_offset (end , sampling_rate )
296328
297329 return y , files , starts , ends
298330
@@ -348,7 +380,6 @@ def process_file(
348380 end = end ,
349381 process_func_args = process_func_args ,
350382 )
351-
352383 index = audformat .segmented_index (files , starts , ends )
353384
354385 if len (y ) == 0 :
@@ -714,7 +745,7 @@ def _process_signal(
714745 def process_signal (
715746 self ,
716747 signal : np .ndarray ,
717- sampling_rate : int ,
748+ sampling_rate : int = None ,
718749 * ,
719750 file : str = None ,
720751 start : Timestamp = None ,
@@ -768,24 +799,31 @@ def process_signal(
768799 process_func_args = process_func_args ,
769800 )
770801 else :
771- if start is not None :
772- start = utils .to_timedelta (start , sampling_rate )
773- if end is not None :
774- end = utils .to_timedelta (end , sampling_rate )
775-
776- y , files , starts , ends = self ._process_signal (
777- signal ,
778- sampling_rate ,
779- file = file ,
780- start = start ,
781- end = end ,
782- process_func_args = process_func_args ,
783- )
802+ # Text files
803+ if sampling_rate is None :
804+ pass
805+ # Implement
784806
785- if file is not None :
786- index = audformat .segmented_index (files , starts , ends )
807+ # Audio/video files
787808 else :
788- index = utils .signal_index (starts , ends )
809+ if start is not None :
810+ start = utils .to_timedelta (start , sampling_rate )
811+ if end is not None :
812+ end = utils .to_timedelta (end , sampling_rate )
813+
814+ y , files , starts , ends = self ._process_signal (
815+ signal ,
816+ sampling_rate ,
817+ file = file ,
818+ start = start ,
819+ end = end ,
820+ process_func_args = process_func_args ,
821+ )
822+
823+ if file is not None :
824+ index = audformat .segmented_index (files , starts , ends )
825+ else :
826+ index = utils .signal_index (starts , ends )
789827
790828 if len (y ) == 0 :
791829 return pd .Series ([], index , dtype = object )
@@ -920,7 +958,28 @@ def _call(
920958 file : str = None ,
921959 process_func_args : typing .Dict [str , typing .Any ] = None ,
922960 ) -> typing .Any :
923- r"""Call processing function, possibly pass special args."""
961+ r"""Call processing function on audio/video files.
962+
963+ Assumes a ``numpy`` array as signal,
964+ with channels and samples as dimensions.
965+ The signal is resampled and/or remixed,
966+ if required.
967+
968+ Special arguments are extracted,
969+ and passed to the processing function.
970+
971+ Args:
972+ signal: signal values
973+ sampling_rate: sampling rate in Hz
974+ idx: index
975+ root: root path
976+ file: file path
977+ process_func_args: processing function arguments
978+
979+ Returns:
980+ result of processing function
981+
982+ """
924983 signal , sampling_rate = utils .preprocess_signal (
925984 signal ,
926985 sampling_rate ,
@@ -931,14 +990,7 @@ def _call(
931990 )
932991
933992 process_func_args = process_func_args or self .process_func_args
934- special_args = {}
935- for key , value in [
936- ("idx" , idx ),
937- ("root" , root ),
938- ("file" , file ),
939- ]:
940- if key in self ._process_func_signature and key not in process_func_args :
941- special_args [key ] = value
993+ special_args = self ._special_args (idx , root , file , process_func_args )
942994
943995 def _helper (x ):
944996 if self .process_func_is_mono :
@@ -973,18 +1025,66 @@ def _helper(x):
9731025
9741026 return y
9751027
1028+ def _call_data (
1029+ self ,
1030+ data : typing .Any ,
1031+ * ,
1032+ idx : int = 0 ,
1033+ root : str = None ,
1034+ file : str = None ,
1035+ process_func_args : typing .Dict [str , typing .Any ] = None ,
1036+ ) -> typing .Any :
1037+ r"""Call processing function on general data."""
1038+ process_func_args = process_func_args or self .process_func_args
1039+ special_args = self ._special_args (idx , root , file , process_func_args )
1040+ y = self .process_func (data , ** special_args , ** process_func_args )
1041+ return y
1042+
1043+ def _special_args (
1044+ self ,
1045+ idx : int ,
1046+ root : typing .Optional [str ],
1047+ file : typing .Optional [str ],
1048+ process_func_args : typing .Dict [str , typing .Any ] = None ,
1049+ ) -> typing .Dict [str , typing .Union [int , str ]]:
1050+ r"""Identify special arguments in processing function.
1051+
1052+ If one of the arguments of the processing function is named
1053+ ``"idx"``, ``"root"``, or ``"file"``,
1054+ and not provided in ``process_func_args``,
1055+ it is identified as a special argument.
1056+
1057+ Args:
1058+ idx: index
1059+ root: root path
1060+ file: file path
1061+ process_func_args: processing function arguments
1062+
1063+ Returns:
1064+ special arguments dictionary
1065+
1066+ """
1067+ special_args = {}
1068+ for key , value in [("idx" , idx ), ("root" , root ), ("file" , file )]:
1069+ if key in self ._process_func_signature and key not in process_func_args :
1070+ special_args [key ] = value
1071+ return special_args
1072+
9761073 def __call__ (
9771074 self ,
9781075 signal : np .ndarray ,
979- sampling_rate : int ,
1076+ sampling_rate : int = None ,
9801077 ) -> typing .Any :
9811078 r"""Apply processing to signal.
9821079
983- This function processes the signal **without** transforming the output
984- into a :class:`pd.Series`. Instead, it will return the raw processed
985- signal. However, if channel selection, mixdown and/or resampling
986- is enabled, the signal will be first remixed and resampled if the
987- input sampling rate does not fit the expected sampling rate.
1080+ This function processes the signal
1081+ **without** transforming the output into a :class:`pd.Series`.
1082+ Instead, it will return the raw processed signal.
1083+ However,
1084+ if channel selection, mixdown and/or resampling is enabled,
1085+ and ``sampling_rate`` is not ``None``,
1086+ the signal will be first remixed and resampled
1087+ if the input sampling rate does not fit the expected sampling rate.
9881088
9891089 Args:
9901090 signal: signal values
@@ -998,4 +1098,7 @@ def __call__(
9981098 RuntimeError: if channel selection is invalid
9991099
10001100 """
1001- return self ._call (signal , sampling_rate )
1101+ if sampling_rate is not None :
1102+ return self ._call (signal , sampling_rate )
1103+ else :
1104+ return self ._call_data (signal )
0 commit comments