spcore 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/ChangeLog.rdoc +5 -1
  2. data/lib/spcore.rb +9 -6
  3. data/lib/spcore/analysis/calculus.rb +38 -0
  4. data/lib/spcore/analysis/features.rb +186 -0
  5. data/lib/spcore/analysis/frequency_domain.rb +191 -0
  6. data/lib/spcore/analysis/{correlation.rb → statistics.rb} +41 -18
  7. data/lib/spcore/core/delay_line.rb +1 -1
  8. data/lib/spcore/filters/fir/dual_sinc_filter.rb +1 -1
  9. data/lib/spcore/filters/fir/sinc_filter.rb +1 -1
  10. data/lib/spcore/generation/comb_filter.rb +65 -0
  11. data/lib/spcore/{core → generation}/oscillator.rb +1 -1
  12. data/lib/spcore/{util → generation}/signal_generator.rb +1 -1
  13. data/lib/spcore/util/envelope_detector.rb +1 -1
  14. data/lib/spcore/util/gain.rb +9 -167
  15. data/lib/spcore/util/plotter.rb +1 -1
  16. data/lib/spcore/{analysis → util}/signal.rb +116 -127
  17. data/lib/spcore/version.rb +1 -1
  18. data/spcore.gemspec +2 -0
  19. data/spec/analysis/calculus_spec.rb +54 -0
  20. data/spec/analysis/features_spec.rb +106 -0
  21. data/spec/analysis/frequency_domain_spec.rb +147 -0
  22. data/spec/analysis/piano_C4.wav +0 -0
  23. data/spec/analysis/statistics_spec.rb +61 -0
  24. data/spec/analysis/trumpet_B4.wav +0 -0
  25. data/spec/generation/comb_filter_spec.rb +37 -0
  26. data/spec/{core → generation}/oscillator_spec.rb +0 -0
  27. data/spec/{util → generation}/signal_generator_spec.rb +0 -0
  28. data/spec/interpolation/interpolation_spec.rb +0 -2
  29. data/spec/{analysis → util}/signal_spec.rb +1 -35
  30. metadata +64 -22
  31. data/lib/spcore/analysis/envelope.rb +0 -76
  32. data/lib/spcore/analysis/extrema.rb +0 -55
  33. data/spec/analysis/correlation_spec.rb +0 -28
  34. data/spec/analysis/envelope_spec.rb +0 -50
  35. data/spec/analysis/extrema_spec.rb +0 -42
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spcore
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-21 00:00:00.000000000 Z
12
+ date: 2013-07-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: hashmake
@@ -123,6 +123,38 @@ dependencies:
123
123
  - - ! '>='
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry-nav
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: wavefile
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
126
158
  description: ! 'A library of signal processing methods and classes for resampling,
127
159
  frequency domain transform (FFT and DFT), windowing (Blackman, Hamming, etc.), FIR
128
160
  filtering (windowed sinc), interpolation (linear and polynomial), plotting data,
@@ -144,14 +176,13 @@ files:
144
176
  - README.rdoc
145
177
  - Rakefile
146
178
  - lib/spcore.rb
147
- - lib/spcore/analysis/correlation.rb
148
- - lib/spcore/analysis/envelope.rb
149
- - lib/spcore/analysis/extrema.rb
150
- - lib/spcore/analysis/signal.rb
179
+ - lib/spcore/analysis/calculus.rb
180
+ - lib/spcore/analysis/features.rb
181
+ - lib/spcore/analysis/frequency_domain.rb
182
+ - lib/spcore/analysis/statistics.rb
151
183
  - lib/spcore/core/circular_buffer.rb
152
184
  - lib/spcore/core/constants.rb
153
185
  - lib/spcore/core/delay_line.rb
154
- - lib/spcore/core/oscillator.rb
155
186
  - lib/spcore/filters/fir/dual_sinc_filter.rb
156
187
  - lib/spcore/filters/fir/fir.rb
157
188
  - lib/spcore/filters/fir/sinc_filter.rb
@@ -161,6 +192,9 @@ files:
161
192
  - lib/spcore/filters/iir/cookbook_highpass_filter.rb
162
193
  - lib/spcore/filters/iir/cookbook_lowpass_filter.rb
163
194
  - lib/spcore/filters/iir/cookbook_notch_filter.rb
195
+ - lib/spcore/generation/comb_filter.rb
196
+ - lib/spcore/generation/oscillator.rb
197
+ - lib/spcore/generation/signal_generator.rb
164
198
  - lib/spcore/interpolation/interpolation.rb
165
199
  - lib/spcore/resampling/discrete_resampling.rb
166
200
  - lib/spcore/resampling/hybrid_resampling.rb
@@ -173,7 +207,7 @@ files:
173
207
  - lib/spcore/util/plotter.rb
174
208
  - lib/spcore/util/saturation.rb
175
209
  - lib/spcore/util/scale.rb
176
- - lib/spcore/util/signal_generator.rb
210
+ - lib/spcore/util/signal.rb
177
211
  - lib/spcore/version.rb
178
212
  - lib/spcore/windows/bartlett_hann_window.rb
179
213
  - lib/spcore/windows/bartlett_window.rb
@@ -191,16 +225,20 @@ files:
191
225
  - lib/spcore/windows/triangular_window.rb
192
226
  - lib/spcore/windows/tukey_window.rb
193
227
  - spcore.gemspec
194
- - spec/analysis/correlation_spec.rb
195
- - spec/analysis/envelope_spec.rb
196
- - spec/analysis/extrema_spec.rb
197
- - spec/analysis/signal_spec.rb
228
+ - spec/analysis/calculus_spec.rb
229
+ - spec/analysis/features_spec.rb
230
+ - spec/analysis/frequency_domain_spec.rb
231
+ - spec/analysis/piano_C4.wav
232
+ - spec/analysis/statistics_spec.rb
233
+ - spec/analysis/trumpet_B4.wav
198
234
  - spec/core/circular_buffer_spec.rb
199
235
  - spec/core/delay_line_spec.rb
200
- - spec/core/oscillator_spec.rb
201
236
  - spec/filters/fir/dual_sinc_filter_spec.rb
202
237
  - spec/filters/fir/sinc_filter_spec.rb
203
238
  - spec/filters/iir/cookbook_filter_spec.rb
239
+ - spec/generation/comb_filter_spec.rb
240
+ - spec/generation/oscillator_spec.rb
241
+ - spec/generation/signal_generator_spec.rb
204
242
  - spec/interpolation/interpolation_spec.rb
205
243
  - spec/resampling/discrete_resampling_spec.rb
206
244
  - spec/resampling/hybrid_resampling_spec.rb
@@ -213,7 +251,7 @@ files:
213
251
  - spec/util/gain_spec.rb
214
252
  - spec/util/limiters_spec.rb
215
253
  - spec/util/saturate_spec.rb
216
- - spec/util/signal_generator_spec.rb
254
+ - spec/util/signal_spec.rb
217
255
  - spec/windows/window_spec.rb
218
256
  homepage: https://github.com/jamestunnell/spcore
219
257
  licenses:
@@ -230,7 +268,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
230
268
  version: '0'
231
269
  segments:
232
270
  - 0
233
- hash: 688071795
271
+ hash: 2316548233979027039
234
272
  required_rubygems_version: !ruby/object:Gem::Requirement
235
273
  none: false
236
274
  requirements:
@@ -239,7 +277,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
277
  version: '0'
240
278
  segments:
241
279
  - 0
242
- hash: 688071795
280
+ hash: 2316548233979027039
243
281
  requirements: []
244
282
  rubyforge_project:
245
283
  rubygems_version: 1.8.23
@@ -247,16 +285,20 @@ signing_key:
247
285
  specification_version: 3
248
286
  summary: A library of signal processing methods and classes.
249
287
  test_files:
250
- - spec/analysis/correlation_spec.rb
251
- - spec/analysis/envelope_spec.rb
252
- - spec/analysis/extrema_spec.rb
253
- - spec/analysis/signal_spec.rb
288
+ - spec/analysis/calculus_spec.rb
289
+ - spec/analysis/features_spec.rb
290
+ - spec/analysis/frequency_domain_spec.rb
291
+ - spec/analysis/piano_C4.wav
292
+ - spec/analysis/statistics_spec.rb
293
+ - spec/analysis/trumpet_B4.wav
254
294
  - spec/core/circular_buffer_spec.rb
255
295
  - spec/core/delay_line_spec.rb
256
- - spec/core/oscillator_spec.rb
257
296
  - spec/filters/fir/dual_sinc_filter_spec.rb
258
297
  - spec/filters/fir/sinc_filter_spec.rb
259
298
  - spec/filters/iir/cookbook_filter_spec.rb
299
+ - spec/generation/comb_filter_spec.rb
300
+ - spec/generation/oscillator_spec.rb
301
+ - spec/generation/signal_generator_spec.rb
260
302
  - spec/interpolation/interpolation_spec.rb
261
303
  - spec/resampling/discrete_resampling_spec.rb
262
304
  - spec/resampling/hybrid_resampling_spec.rb
@@ -269,6 +311,6 @@ test_files:
269
311
  - spec/util/gain_spec.rb
270
312
  - spec/util/limiters_spec.rb
271
313
  - spec/util/saturate_spec.rb
272
- - spec/util/signal_generator_spec.rb
314
+ - spec/util/signal_spec.rb
273
315
  - spec/windows/window_spec.rb
274
316
  has_rdoc:
@@ -1,76 +0,0 @@
1
- module SPCore
2
- # Determines the envelope of given samples. Unlike the EnvelopeDetector, this
3
- # operates on a signal (time-series data) after it is recieved, not
4
- # sample-by-sample. As a result, it provides the envelope as an entire signal.
5
- #
6
- # @author James Tunnell
7
- class Envelope
8
-
9
- attr_reader :data
10
-
11
- def initialize samples
12
- # combine absolute values of positive maxima and negative minima
13
- extrema = Extrema.new(samples)
14
- starting_outline = {}
15
- extrema.minima.each do |idx,val|
16
- if val <= 0.0
17
- starting_outline[idx] =val.abs
18
- end
19
- end
20
-
21
- extrema.maxima.each do |idx,val|
22
- if val >= 0.0
23
- starting_outline[idx] = val.abs
24
- end
25
- end
26
-
27
- # add in first and last samples so the envelope follows entire signal
28
- starting_outline[0] = samples[0].abs
29
- starting_outline[samples.count - 1] = samples[samples.count - 1].abs
30
-
31
- # the extrema we have now are probably not spaced evenly. Upsampling at
32
- # this point would lead to a time-distorted signal. So the next step is to
33
- # interpolate between all the extrema to make a crude but properly sampled
34
- # envelope.
35
-
36
- proper_outline = Array.new(samples.count, 0)
37
- indices = starting_outline.keys.sort
38
-
39
- for i in 1...indices.count
40
- l_idx = indices[i-1]
41
- r_idx = indices[i]
42
-
43
- l_val = starting_outline[l_idx]
44
- r_val = starting_outline[r_idx]
45
-
46
- proper_outline[l_idx] = l_val
47
- proper_outline[r_idx] = r_val
48
-
49
- idx_span = r_idx - l_idx
50
-
51
- for j in (l_idx + 1)...(r_idx)
52
- x = (j - l_idx).to_f / idx_span
53
- y = Interpolation.linear l_val, r_val, x
54
- proper_outline[j] = y
55
- end
56
- end
57
-
58
- # Now downsample by dropping samples, back to the number of starting_outline we had
59
- # with just the extrema, but this time with samples properly spaced so as
60
- # to avoid time distortion after upsampling.
61
-
62
- downsample_factor = (samples.count / starting_outline.count).to_i
63
- downsampled_outline = []
64
-
65
- (0...proper_outline.count).step(downsample_factor) do |n|
66
- downsampled_outline.push proper_outline[n]
67
- end
68
-
69
- # finally, use polynomial interpolation to upsample to the original sample rate.
70
-
71
- upsample_factor = samples.count / downsampled_outline.count.to_f
72
- @data = PolynomialResampling.upsample(downsampled_outline, upsample_factor)
73
- end
74
-
75
- end
76
- end
@@ -1,55 +0,0 @@
1
- module SPCore
2
- # Finds extrema (minima and maxima).
3
- class Extrema
4
-
5
- attr_reader :minima, :maxima, :extrema
6
-
7
- def initialize samples
8
- @minima = {}
9
- @maxima = {}
10
-
11
- global_min_idx = 0
12
- global_min_val = samples[0]
13
- global_max_idx = 0
14
- global_max_val = samples[0]
15
-
16
- diffs = []
17
- for i in (1...samples.count)
18
- diffs.push(samples[i] - samples[i-1])
19
-
20
- if samples[i] < global_min_val
21
- global_min_idx = i
22
- global_min_val = samples[i]
23
- end
24
-
25
- if samples[i] > global_max_val
26
- global_max_idx = i
27
- global_max_val = samples[i]
28
- end
29
- end
30
- @minima[global_min_idx] = global_min_val
31
- @maxima[global_max_idx] = global_max_val
32
-
33
- is_positive = diffs.first > 0.0 # starting off with positive difference?
34
-
35
- # at zero crossings there is a local maxima/minima
36
- for i in (1...diffs.count)
37
- if is_positive
38
- # at positive-to-negative transition there is a local maxima
39
- if diffs[i] <= 0.0
40
- @maxima[i] = samples[i]
41
- is_positive = false
42
- end
43
- else
44
- # at negative-to-positive transition there is a local minima
45
- if diffs[i] > 0.0
46
- @minima[i] = samples[i]
47
- is_positive = true
48
- end
49
- end
50
- end
51
-
52
- @extrema = @minima.merge(@maxima)
53
- end
54
- end
55
- end
@@ -1,28 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe SPCore::Correlation do
4
- context 'image => triangular window' do
5
- before :all do
6
- @size = size = 48
7
- @triangle = TriangularWindow.new(size * 2).data
8
- end
9
-
10
- context 'feature => rising ramp (half size of triangular window)' do
11
- it 'should have maximum correlation at beginning' do
12
- rising_ramp = Array.new(@size){|i| i / @size.to_f }
13
- correlation = Correlation.new(@triangle, rising_ramp)
14
- correlation.data.first.should eq(correlation.data.max)
15
- end
16
- end
17
-
18
- context 'feature => falling ramp (half size of triangular window)' do
19
- it 'should have maximum correlation at end' do
20
- falling_ramp = Array.new(@size){|i| (@size - i) / @size.to_f }
21
- correlation = Correlation.new(@triangle, falling_ramp)
22
- correlation.data.last.should eq(correlation.data.max)
23
-
24
- #Plotter.plot_1d "correlate falling ramp" => correlation.data
25
- end
26
- end
27
- end
28
- end
@@ -1,50 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe SPCore::Envelope do
4
- before :all do
5
- sample_rate = 1000
6
- sample_count = 512 * 2
7
- generator = SignalGenerator.new(:size => sample_count, :sample_rate => sample_rate)
8
-
9
- @modulation_signal = generator.make_signal [4.0], :amplitude => 0.1
10
- @modulation_signal.multiply! BlackmanWindow.new(sample_count).data
11
-
12
- @base_signal = generator.make_signal [64.0]
13
- @base_signal.multiply! @modulation_signal
14
- end
15
-
16
- it 'should produce an output that follows the amplitude of the input' do
17
- envelope = @base_signal.envelope
18
- check_envelope(envelope)
19
- end
20
-
21
- def check_envelope envelope
22
- #signals = {
23
- # "signal" => @base_signal,
24
- # "modulation (abs)" => @modulation_signal.abs,
25
- # "envelope" => envelope,
26
- #}
27
- #
28
- #Plotter.new(
29
- # :title => "signal and envelope",
30
- # :xlabel => "sample",
31
- # :ylabel => "values",
32
- #).plot_signals(signals)
33
-
34
- #Plotter.new().plot_2d("envelop freq magnitudes" => envelope.freq_magnitudes)
35
-
36
- begin
37
- ideal = @modulation_signal.energy
38
- actual = envelope.energy
39
- error = (ideal - actual).abs / ideal
40
- error.should be_within(0.1).of(0.0)
41
- end
42
-
43
- begin
44
- ideal = @modulation_signal.rms
45
- actual = envelope.rms
46
- error = (ideal - actual).abs / ideal
47
- error.should be_within(0.1).of(0.0)
48
- end
49
- end
50
- end
@@ -1,42 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
-
3
- describe SPCore::Extrema do
4
- context '#minima' do
5
- it 'should return points where local and global minima occur' do
6
- cases = {
7
- [3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 2 => 2.9, 9 => 2.0 },
8
- [3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 3 => 2.7, 7 => 2.2, 10 => 2.0 },
9
- }
10
-
11
- cases.each do |samples, minima|
12
- Extrema.new(samples).minima.should eq minima
13
- end
14
- end
15
- end
16
-
17
- context '#maxima' do
18
- it 'should return points where local and global maxima occur' do
19
- cases = {
20
- [3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 0 => 3.8, 4 => 3.6 },
21
- [3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 1 => 3.5, 4 => 2.8, 8 => 2.4},
22
- }
23
-
24
- cases.each do |samples, maxima|
25
- Extrema.new(samples).maxima.should eq maxima
26
- end
27
- end
28
- end
29
-
30
- context '#extrema' do
31
- it 'should return points where local and global extrema occur' do
32
- cases = {
33
- [3.8, 3.0, 2.9, 2.95, 3.6, 3.4, 2.8, 2.3, 2.1, 2.0, 2.5] => { 0 => 3.8, 2 => 2.9, 4 => 3.6, 9 => 2.0},
34
- [3.2, 3.5, 2.9, 2.7, 2.8, 2.7, 2.5, 2.2, 2.4, 2.3, 2.0] => { 1 => 3.5, 3 => 2.7, 4 => 2.8, 7 => 2.2, 8 => 2.4, 10 => 2.0},
35
- }
36
-
37
- cases.each do |samples, extrema|
38
- Extrema.new(samples).extrema.should eq extrema
39
- end
40
- end
41
- end
42
- end