ruck 0.1.2 → 0.2.0

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.
data/lib/ruck/ugen/wav.rb DELETED
@@ -1,185 +0,0 @@
1
-
2
- require File.join(File.dirname(__FILE__), "..", "misc", "riff")
3
-
4
- module Ruck
5
- module Generators
6
-
7
- # saves all incoming samples in memory to export to disk later
8
- # outputs 0.0 samples
9
- class WavOut
10
- include UGen
11
- include Source
12
- include MultiChannelTarget
13
-
14
- attr_reader :filename
15
-
16
- def initialize(attrs = {})
17
- require_attrs attrs, [:filename]
18
- @filename = attrs.delete(:filename)
19
- @num_channels = attrs.delete(:num_channels) || 1
20
- @bits_per_sample = attrs.delete(:bits_per_sample) || 16
21
- parse_attrs attrs
22
-
23
- @in_channels = (1..@num_channels).map { InChannel.new }
24
-
25
- @sample_rate = SAMPLE_RATE
26
- @samples = (1..@num_channels).map { [] }
27
- @ins = []
28
- @last = 0.0
29
-
30
- # TODO: this is necessary, but if UGen graph were explicitly
31
- # destructed, that would be nice.
32
- at_exit { save }
33
- end
34
-
35
- def next(now)
36
- return @last if @now == now
37
- @now = now
38
- @samples << @in_channels.map { |chan| chan.next now }
39
- @last
40
- end
41
-
42
- def save
43
- LOG.info "Saving WAV to #{@filename}..."
44
- File.open(@filename, "wb") { |f| f.write encode }
45
- end
46
-
47
- def attr_names
48
- [:filename]
49
- end
50
-
51
- private
52
-
53
- def encode
54
- chunk("RIFF") do |riff|
55
- riff << ascii("WAVE")
56
- riff << chunk("fmt ") do |fmt|
57
- fmt << short(1) # format = 1: PCM (no compression)
58
- fmt << short(@num_channels)
59
- fmt << int(@sample_rate)
60
- fmt << int((@sample_rate * @num_channels * (@bits_per_sample / 8))) # byte-rate
61
- fmt << short((@num_channels * @bits_per_sample/8)) # block align
62
- fmt << short(@bits_per_sample) # bits/sample
63
- end
64
- riff << chunk("data") do |data|
65
- range = 2 ** (@bits_per_sample - 1)
66
- @samples.each do |sample_list|
67
- sample_list.each { |sample| data << [sample * range].pack("s1") }
68
- end
69
- end
70
- end
71
- end
72
-
73
- def int(i)
74
- [i].pack("i1")
75
- end
76
-
77
- def short(s)
78
- [s].pack("s1")
79
- end
80
-
81
- def ascii(str)
82
- str.split("").pack("A1" * str.length)
83
- end
84
-
85
- def chunk(type, &block)
86
- buf = ""
87
- block.call(buf)
88
- ascii(type) + int(buf.length) + buf
89
- end
90
-
91
- end
92
-
93
- # plays sound stored in a RIFF WAV file
94
- class WavIn
95
- include UGen
96
- include MultiChannelSource
97
-
98
- linkable_attr :rate
99
- linkable_attr :gain
100
- attr_reader :filename
101
-
102
- def initialize(attrs = {})
103
- require_attrs attrs, [:filename]
104
- @rate = 1.0
105
- @gain = 1.0
106
- @filename = attrs.delete(:filename)
107
- parse_attrs attrs
108
-
109
- @loaded = false
110
- @playing = true
111
-
112
- init_wav
113
- end
114
-
115
- def init_wav
116
- riff = Riff::RiffReader.new(@filename).chunks.first
117
- unless riff.type == "RIFF"
118
- LOG.error "#{@filename}: Not RIFF!"
119
- return
120
- end
121
- unless riff[0..3] == "WAVE"
122
- LOG.error "#{@filename}: Not WAVE!"
123
- return
124
- end
125
-
126
- riff.data_skip = 4 # skip "WAVE"
127
- fmt = riff.chunks.first
128
- @wav = riff.chunks.find { |c| c.type == "data" }
129
- unless fmt[0..1].unpack("s1").first == 1
130
- LOG.error "#{@filename}: Not PCM!"
131
- return
132
- end
133
-
134
- @num_channels, @sample_rate, @byte_rate,
135
- @block_align, @bits_per_sample =
136
- fmt[2..15].unpack("s1i1i1s1s1")
137
- @range = (2 ** (@bits_per_sample - 1)).to_f
138
-
139
- @out_channels = (0..@num_channels-1).map { |chan| OutChannel.new self, chan }
140
- @sample = [0.0] * @num_channels
141
- @last = [0.0] * @num_channels
142
- @now = [nil] * @num_channels
143
- @rate_adjust = @sample_rate / SAMPLE_RATE
144
-
145
- @loaded = true
146
- end
147
-
148
- def duration
149
- @loaded ? @wav.size / @block_align / @rate_adjust : 0
150
- end
151
-
152
- def attr_names
153
- [:filename, :rate]
154
- end
155
-
156
- def next(now, chan = 0)
157
- return @last[chan] if @now[chan] == now
158
- @now[chan] = now
159
-
160
- return @last[chan] unless @loaded && @playing
161
-
162
- offset = @sample[chan].to_i * @block_align
163
- chan_offset = (chan * @bits_per_sample) / 8
164
-
165
- if offset + @block_align > @wav.size
166
- @playing = false
167
- return @last[chan]
168
- end
169
-
170
- @last[chan] = @wav[offset + chan_offset, @bits_per_sample].unpack("s1").first / @range * gain
171
- @sample[chan] += rate * @rate_adjust
172
- @last[chan]
173
- end
174
-
175
- def play; @playing = true; end
176
- def stop; @playing = false; end
177
-
178
- def reset
179
- @offset = 0
180
- end
181
-
182
- end
183
-
184
- end
185
- end