source: python/lib/aubio/slicing.py @ 97a8bef

feature/autosinkfeature/constantqfeature/pitchshiftfeature/pydocstringsfeature/timestretch
Last change on this file since 97a8bef was 97a8bef, checked in by Paul Brossier <piem@piem.org>, 2 years ago

[py] add a note about missing last timestamp to slice_source_at_stamps docs

  • Property mode set to 100644
File size: 6.0 KB
Line 
1"""utility routines to slice sound files at given timestamps"""
2
3import os
4from aubio import source, sink
5
6_max_timestamp = 1e120
7
8def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
9                           output_dir=None, samplerate=0, hopsize=256,
10                           create_first=False):
11    """Slice a sound file at given timestamps.
12
13    This function reads `source_file` and creates slices, new smaller
14    files each starting at `t` in `timestamps`, a list of integer
15    corresponding to time locations in `source_file`, in samples.
16
17    If `timestamps_end` is unspecified, the slices will end at
18    `timestamps_end[n] = timestamps[n+1]-1`, or the end of file.
19    Otherwise, `timestamps_end` should be a list with the same length
20    as `timestamps` containing the locations of the end of each slice.
21
22    If `output_dir` is unspecified, the new slices will be written in
23    the current directory. If `output_dir` is a string, new slices
24    will be written in `output_dir`, after creating the directory if
25    required.
26
27    The default `samplerate` is 0, meaning the original sampling rate
28    of `source_file` will be used. When using a sampling rate
29    different to the one of the original files, `timestamps` and
30    `timestamps_end` should be expressed in the re-sampled signal.
31
32    The `hopsize` parameter simply tells :class:`source` to use this
33    hopsize and does not change the output slices.
34
35    If `create_first` is True and `timestamps` does not start with `0`, the
36    first slice from `0` to `timestamps[0] - 1` will be automatically added.
37
38    Parameters
39    ----------
40    source_file : str
41        path of the resource to slice
42    timestamps : :obj:`list` of :obj:`int`
43        time stamps at which to slice, in samples
44    timestamps_end : :obj:`list` of :obj:`int` (optional)
45        time stamps at which to end the slices
46    output_dir : str (optional)
47        output directory to write the slices to
48    samplerate : int (optional)
49        samplerate to read the file at
50    hopsize : int (optional)
51        number of samples read from source per iteration
52    create_first : bool (optional)
53        always create the slice at the start of the file
54
55    Examples
56    --------
57    Create two slices, the first second of a file and the rest of it:
58
59    >>> aubio.slice_source_at_stamps('loop.wav', [0, 44100])
60
61    Create one slice, from 1 second to 2 seconds:
62
63    >>> aubio.slice_source_at_stamps('loop.wav', [44100], [44100*2])
64
65    Notes
66    -----
67    Slices may be overlapping. If `timestamps_end` is `1` element
68    shorter than `timestamps`, the last slice will end at the end of
69    the file.
70    """
71
72    if timestamps is None or len(timestamps) == 0:
73        raise ValueError("no timestamps given")
74
75    if timestamps[0] != 0 and create_first:
76        timestamps = [0] + timestamps
77        if timestamps_end is not None:
78            timestamps_end = [timestamps[1] - 1] + timestamps_end
79
80    if timestamps_end is not None:
81        if len(timestamps_end) == len(timestamps) - 1:
82            timestamps_end = timestamps_end + [_max_timestamp]
83        elif len(timestamps_end) != len(timestamps):
84            raise ValueError("len(timestamps_end) != len(timestamps)")
85    else:
86        timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]
87
88    regions = list(zip(timestamps, timestamps_end))
89    #print regions
90
91    source_base_name, _ = os.path.splitext(os.path.basename(source_file))
92    if output_dir is not None:
93        if not os.path.isdir(output_dir):
94            os.makedirs(output_dir)
95        source_base_name = os.path.join(output_dir, source_base_name)
96
97    def new_sink_name(source_base_name, timestamp, samplerate):
98        """ create a sink based on a timestamp in samples, converted in seconds """
99        timestamp_seconds = timestamp / float(samplerate)
100        return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'
101
102    # open source file
103    _source = source(source_file, samplerate, hopsize)
104    samplerate = _source.samplerate
105
106    total_frames = 0
107    slices = []
108
109    while True:
110        # get hopsize new samples from source
111        vec, read = _source.do_multi()
112        # if the total number of frames read will exceed the next region start
113        while len(regions) and total_frames + read >= regions[0][0]:
114            #print "getting", regions[0], "at", total_frames
115            # get next region
116            start_stamp, end_stamp = regions.pop(0)
117            # create a name for the sink
118            new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)
119            # create its sink
120            _sink = sink(new_sink_path, samplerate, _source.channels)
121            # create a dictionary containing all this
122            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}
123            # append the dictionary to the current list of slices
124            slices.append(new_slice)
125
126        for current_slice in slices:
127            start_stamp = current_slice['start_stamp']
128            end_stamp = current_slice['end_stamp']
129            _sink = current_slice['sink']
130            # sample index to start writing from new source vector
131            start = max(start_stamp - total_frames, 0)
132            # number of samples yet to written be until end of region
133            remaining = end_stamp - total_frames + 1
134            #print current_slice, remaining, start
135            # not enough frames remaining, time to split
136            if remaining < read:
137                if remaining > start:
138                    # write remaining samples from current region
139                    _sink.do_multi(vec[:, start:remaining], remaining - start)
140                    #print("closing region", "remaining", remaining)
141                    # close this file
142                    _sink.close()
143            elif read > start:
144                # write all the samples
145                _sink.do_multi(vec[:, start:read], read - start)
146        total_frames += read
147        # remove old slices
148        slices = list(filter(lambda s: s['end_stamp'] > total_frames,
149            slices))
150        if read < hopsize:
151            break
Note: See TracBrowser for help on using the repository browser.