summaryrefslogtreecommitdiff
path: root/python/lib/aubio/slicing.py
blob: fa9d2e347466b63b8c31fcaef707073812843036 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
"""utility routines to slice sound files at given timestamps"""

import os
from aubio import source, sink

_max_timestamp = 1e120

def slice_source_at_stamps(source_file, timestamps, timestamps_end=None,
                           output_dir=None, samplerate=0, hopsize=256):
    """ slice a sound file at given timestamps """

    if timestamps is None or len(timestamps) == 0:
        raise ValueError("no timestamps given")

    if timestamps[0] != 0:
        timestamps = [0] + timestamps
        if timestamps_end is not None:
            timestamps_end = [timestamps[1] - 1] + timestamps_end

    if timestamps_end is not None:
        if len(timestamps_end) != len(timestamps):
            raise ValueError("len(timestamps_end) != len(timestamps)")
    else:
        timestamps_end = [t - 1 for t in timestamps[1:]] + [_max_timestamp]

    regions = list(zip(timestamps, timestamps_end))
    #print regions

    source_base_name, _ = os.path.splitext(os.path.basename(source_file))
    if output_dir is not None:
        if not os.path.isdir(output_dir):
            os.makedirs(output_dir)
        source_base_name = os.path.join(output_dir, source_base_name)

    def new_sink_name(source_base_name, timestamp, samplerate):
        """ create a sink based on a timestamp in samples, converted in seconds """
        timestamp_seconds = timestamp / float(samplerate)
        return source_base_name + "_%011.6f" % timestamp_seconds + '.wav'

    # open source file
    _source = source(source_file, samplerate, hopsize)
    samplerate = _source.samplerate

    total_frames = 0
    slices = []

    while True:
        # get hopsize new samples from source
        vec, read = _source.do_multi()
        # if the total number of frames read will exceed the next region start
        if len(regions) and total_frames + read >= regions[0][0]:
            #print "getting", regions[0], "at", total_frames
            # get next region
            start_stamp, end_stamp = regions.pop(0)
            # create a name for the sink
            new_sink_path = new_sink_name(source_base_name, start_stamp, samplerate)
            # create its sink
            _sink = sink(new_sink_path, samplerate, _source.channels)
            # create a dictionary containing all this
            new_slice = {'start_stamp': start_stamp, 'end_stamp': end_stamp, 'sink': _sink}
            # append the dictionary to the current list of slices
            slices.append(new_slice)

        for current_slice in slices:
            start_stamp = current_slice['start_stamp']
            end_stamp = current_slice['end_stamp']
            _sink = current_slice['sink']
            # sample index to start writing from new source vector
            start = max(start_stamp - total_frames, 0)
            # number of samples yet to written be until end of region
            remaining = end_stamp - total_frames + 1
            #print current_slice, remaining, start
            # not enough frames remaining, time to split
            if remaining < read:
                if remaining > start:
                    # write remaining samples from current region
                    _sink.do_multi(vec[:, start:remaining], remaining - start)
                    #print "closing region", "remaining", remaining
                    # close this file
                    _sink.close()
            elif read > start:
                # write all the samples
                _sink.do_multi(vec[:, start:read], read - start)
        total_frames += read
        if read < hopsize:
            break