File: samplebuffer.rb

package info (click to toggle)
sonic-pi 3.2.2~repack-8
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 71,872 kB
  • sloc: ruby: 30,548; cpp: 8,490; sh: 957; ansic: 461; erlang: 360; lisp: 141; makefile: 44
file content (166 lines) | stat: -rw-r--r-- 4,137 bytes parent folder | download | duplicates (4)
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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#--
# This file is part of Sonic Pi: http://sonic-pi.net
# Full project source: https://github.com/samaaron/sonic-pi
# License: https://github.com/samaaron/sonic-pi/blob/master/LICENSE.md
#
# Copyright 2013, 2014, 2015, 2016 by Sam Aaron (http://sam.aaron.name).
# All rights reserved.
#
# Permission is granted for use, copying, modification, and
# distribution of modified versions of this work as long as this
# notice is included.
#++

require_relative "buffer"
require_relative "util"
require_relative "sox"
require 'aubio'


module SonicPi
  class SampleBuffer < Buffer
    include Util
    def initialize(buffer, path)
      @aubio_onsets = {}
      @buffer = buffer
      @mono_buffer = nil
      @path = path
      @aubio_sem = Mutex.new
      @slices = {}
      @slices_sem = Mutex.new
      @sox_sem = Mutex.new
      @sox_info = nil
    end

    def num_frames
      @buffer.num_frames
    end

    def num_chans
      @buffer.num_chans
    end

    def sample_rate
      @buffer.sample_rate
    end

    def duration
      @buffer.duration
    end

    def buffer
      @buffer
    end

    def state
      @buffer.state
    end

    def path
      @path
    end

    def free
      @buffer.free
    end

    def id
      @buffer.id
    end

    def to_i
      @buffer.to_i
    end

    def mono
      return self if num_chans == 1
      raise "implement me!"
    end

    def info
      return @sox_info if @sox_info
      @sox_sem.synchronize do
        return @sox_info if @sox_info
        @sox_info = Sox.info(@path)
      end
      return @sox_info
    end

    def onset_data
      return @aubio_onset_data if @aubio_onset_data
      @aubio_sem.synchronize do
        return @aubio_onset_data if @aubio_onset_data
        __no_kill_block do
          aubio_file = Aubio.open(@path, {sample_rate: sample_rate})
          native_onsets = aubio_file.onsets.to_a.ring
          aubio_file.close
          @aubio_onset_data = native_onsets
        end
      end
      return @aubio_onset_data
    end

    def onsets(stretch=1)
      return @aubio_onsets[stretch] if @aubio_onsets[stretch]
      data = onset_data
      @aubio_sem.synchronize do
        return @aubio_onsets[stretch] if @aubio_onsets[stretch]
        onset_times = data.map do |el|
          [1, (el[:s].to_f / duration)].min * stretch
        end
        @aubio_onsets[stretch] = onset_times
      end
      return @aubio_onsets[stretch]
    end

    def onset_slices
      return @aubio_slices if @aubio_slices
      ons = onsets
      @aubio_sem.synchronize do
        return @aubio_slices if @aubio_slices
        res = []
        ons[0...-1].each_with_index do |onset, idx|
          res << {:start => onset, :finish => ons[idx + 1], index: idx}
        end
        @aubio_slices = res.ring
      end
      return @aubio_slices
    end

    def slices(num=16, start=0, finish=1)
      return @slices[[num, start, finish]] if @slices[[num, start, finish]]
      res = []
      @slices_sem.synchronize do
        return @slices[[num, start, finish]] if @slices[[num, start, finish]]

        raise "start arg must be a number, got: #{start.inspect}" unless start.is_a?(Numeric)
        raise "finish arg must be a number, got: #{finish.inspect}" unless finish.is_a?(Numeric)

        slice_size = (finish - start) / num.to_f
        prev = start
        val = start + slice_size
        num = num.to_i
        num.times do |n|
          res << {:start => prev, :finish => val, index: n}
          prev = val
          val += slice_size
        end
        res = res.ring
        @slices[[num, start, finish]] = res
      end
      return res
    end

    def inspect
      to_s
    end

    def to_s
      if @buffer.path
        "#<SampleBuffer @id=#{@buffer.id}, @num_chans=#{@buffer.num_chans}, @num_frames=#{@buffer.num_frames}, @sample_rate=#{@buffer.sample_rate}, @duration=#{@buffer.duration}, @path=#{@buffer.path}>"
      else
        "#<SampleBuffer @id=#{@buffer.id}, @num_chans=#{@buffer.num_chans.inspect}, @num_frames=#{@buffer.num_frames}, @sample_rate=#{@buffer.sample_rate}, @duration=#{@buffer.duration}>"
      end
    end
  end
end