picotune 0.0.3 → 0.0.7
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.
- checksums.yaml +4 -4
- data/lib/picotune.rb +69 -28
- metadata +8 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e57f6a22a29ff8a1c1351c5d605eb5a1b39bf25b69989dfff852819bcf4dc181
|
4
|
+
data.tar.gz: 634ad7d42ddcdd5943e714a42bc7d93a51c1bf2f2522ccdc0e415b3239e0afbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d59d66810aca618e911c54ec1fae977f2e52b4a6962dd3e22baf3abaeb70586ac27d40160d03e9119a7bae031b2c5e8460d030be5411ba29eae44373afaed5d
|
7
|
+
data.tar.gz: c8d003ca2a3c684bfeebce155639273197f1726069c5e7ce159e95425885fc7a803a23602e80bfdd81726836464efdd17c515cbff55f7c2359308c6315c3622e
|
data/lib/picotune.rb
CHANGED
@@ -49,17 +49,18 @@ class PicoTune::Sample
|
|
49
49
|
@left += sample.left
|
50
50
|
@right += sample.right
|
51
51
|
|
52
|
-
#
|
52
|
+
# hard clip
|
53
|
+
|
53
54
|
if @left > 1.0
|
54
|
-
@left = 1.0
|
55
|
+
@left = 1.0
|
55
56
|
elsif @left < -1.0
|
56
|
-
@left = -1.0
|
57
|
+
@left = -1.0
|
57
58
|
end
|
58
59
|
|
59
60
|
if @right > 1.0
|
60
|
-
@right = 1.0
|
61
|
+
@right = 1.0
|
61
62
|
elsif @right < -1.0
|
62
|
-
@right = -1.0
|
63
|
+
@right = -1.0
|
63
64
|
end
|
64
65
|
|
65
66
|
self # return self to chain ops EX: sample.add(sample).add(sample) etc
|
@@ -75,10 +76,9 @@ class PicoTune::Sample
|
|
75
76
|
end
|
76
77
|
|
77
78
|
class PicoTune::WaveSample
|
78
|
-
def initialize tone, samples_per_wave
|
79
|
+
def initialize tone, samples_per_wave
|
79
80
|
@tone = tone
|
80
81
|
@samples_per_wave = samples_per_wave
|
81
|
-
@multiplier = multiplier
|
82
82
|
end
|
83
83
|
|
84
84
|
def sample index
|
@@ -101,43 +101,42 @@ class PicoTune::WaveSample
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def sine index
|
104
|
-
Math.sin(index / (@samples_per_wave / (Math::PI * 2)))
|
104
|
+
Math.sin(index / (@samples_per_wave / (Math::PI * 2)))
|
105
105
|
end
|
106
106
|
|
107
107
|
def saw index
|
108
108
|
interval = @samples_per_wave / 2
|
109
109
|
half_interval = interval / 2
|
110
110
|
percent = ((index + half_interval) % interval) / interval.to_f
|
111
|
-
((0.6 * percent) - 0.3)
|
111
|
+
((0.6 * percent) - 0.3)
|
112
112
|
end
|
113
113
|
|
114
114
|
def square index
|
115
|
-
(index <= @samples_per_wave / 2 ? 1.0 : -1.0)
|
115
|
+
(index <= @samples_per_wave / 2 ? 1.0 : -1.0)
|
116
116
|
end
|
117
117
|
|
118
118
|
def noise index
|
119
119
|
value = sine index
|
120
120
|
rand = Random.rand - 0.5
|
121
|
-
value * rand
|
121
|
+
value * rand
|
122
122
|
end
|
123
123
|
|
124
124
|
def triangle index
|
125
125
|
half = @samples_per_wave / 2
|
126
126
|
quarter = @samples_per_wave / 4
|
127
127
|
ramp = 1.0 / quarter
|
128
|
-
m = @multiplier || 0.5
|
129
128
|
|
130
129
|
if index <= half
|
131
130
|
if index <= quarter
|
132
|
-
index * ramp
|
131
|
+
index * ramp
|
133
132
|
else
|
134
|
-
(half - index) * ramp
|
133
|
+
(half - index) * ramp
|
135
134
|
end
|
136
135
|
else
|
137
136
|
if index <= half + quarter
|
138
|
-
-((index - half) * ramp)
|
137
|
+
-((index - half) * ramp)
|
139
138
|
else
|
140
|
-
-((@samples_per_wave - index) * ramp)
|
139
|
+
-((@samples_per_wave - index) * ramp)
|
141
140
|
end
|
142
141
|
end
|
143
142
|
end
|
@@ -150,6 +149,22 @@ class PicoTune::Tune
|
|
150
149
|
@name = name
|
151
150
|
@sequence = sequence
|
152
151
|
@phrases = phrases
|
152
|
+
|
153
|
+
@phrases.each do |p|
|
154
|
+
p.tune = self
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def volume_factor_for_simultaneous_melodies
|
159
|
+
max_melodies_in_phrases = 0
|
160
|
+
|
161
|
+
@phrases.each do |phrase|
|
162
|
+
if phrase.simultaneous_melodies > max_melodies_in_phrases
|
163
|
+
max_melodies_in_phrases = phrase.simultaneous_melodies
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
1.0 / max_melodies_in_phrases if max_melodies_in_phrases > 0
|
153
168
|
end
|
154
169
|
|
155
170
|
def buffer
|
@@ -193,7 +208,8 @@ class PicoTune::Tune
|
|
193
208
|
end
|
194
209
|
|
195
210
|
class PicoTune::Phrase
|
196
|
-
attr_reader :name, :tempo, :beats, :subbeats, :melodies
|
211
|
+
attr_reader :name, :tempo, :beats, :subbeats, :melodies, :simultaneous_melodies
|
212
|
+
attr_accessor :tune
|
197
213
|
|
198
214
|
def initialize name, tempo, beats, subbeats, melodies
|
199
215
|
@name = name
|
@@ -201,6 +217,11 @@ class PicoTune::Phrase
|
|
201
217
|
@beats = beats.to_i
|
202
218
|
@subbeats = subbeats.to_i
|
203
219
|
@melodies = melodies
|
220
|
+
@simultaneous_melodies = @melodies.count
|
221
|
+
|
222
|
+
@melodies.each do |m|
|
223
|
+
m.instrument.phrase = self
|
224
|
+
end
|
204
225
|
end
|
205
226
|
|
206
227
|
def seconds_per_beat
|
@@ -225,6 +246,10 @@ class PicoTune::Phrase
|
|
225
246
|
last_step_number = -1
|
226
247
|
carry_over = 0
|
227
248
|
|
249
|
+
if melody.pattern.steps.count != @beats * @subbeats
|
250
|
+
raise "Mismatch between Pattern \"#{melody.pattern.name}\", which has #{melody.pattern.steps.count} steps, and Phrase \"#{@name}\", which has #{@beats} beats and #{subbeats} subbeats (meaning any pattern it uses should have #{@beats * @subbeats} steps). Please check your pattern and phrase definitions to find the discrepancy!"
|
251
|
+
end
|
252
|
+
|
228
253
|
melody.pattern.steps.each_with_index do |note, step_number|
|
229
254
|
unless note == '.'
|
230
255
|
buffer_pointer = step_number * sub_buffer_size
|
@@ -281,6 +306,7 @@ end
|
|
281
306
|
|
282
307
|
class PicoTune::Instrument
|
283
308
|
attr_reader :name, :tone, :length, :volume, :pan, :reverb
|
309
|
+
attr_accessor :phrase
|
284
310
|
|
285
311
|
def initialize name, tone = 0, length = 'full', volume = 'full', pan = 'center', reverb = 'none'
|
286
312
|
@name = name
|
@@ -337,7 +363,7 @@ class PicoTune::Instrument
|
|
337
363
|
end
|
338
364
|
|
339
365
|
def delay
|
340
|
-
@reverb == 'none' ? 0.0 : 0.
|
366
|
+
@reverb == 'none' ? 0.0 : 0.08
|
341
367
|
end
|
342
368
|
|
343
369
|
def decay
|
@@ -345,11 +371,11 @@ class PicoTune::Instrument
|
|
345
371
|
when 'none'
|
346
372
|
0.0
|
347
373
|
when 'some'
|
348
|
-
0.
|
374
|
+
0.1
|
349
375
|
when 'more'
|
350
|
-
0.
|
376
|
+
0.3
|
351
377
|
when 'lots'
|
352
|
-
0.
|
378
|
+
0.5
|
353
379
|
else
|
354
380
|
0.0
|
355
381
|
end
|
@@ -365,16 +391,23 @@ class PicoTune::Instrument
|
|
365
391
|
|
366
392
|
def wave wave_index, note
|
367
393
|
frequency = frequency_for_note note
|
368
|
-
samples_per_wave = (PicoTune::SAMPLE_RATE / frequency).
|
394
|
+
samples_per_wave = (PicoTune::SAMPLE_RATE / frequency).to_i
|
369
395
|
sample = PicoTune::WaveSample.new(@tone, samples_per_wave).sample wave_index
|
370
396
|
sample.modify_left :*, volume_value * (1 - pan_value / 4.0)
|
371
397
|
sample.modify_right :*, volume_value * (pan_value / 4.0)
|
398
|
+
|
399
|
+
if v = phrase&.tune&.volume_factor_for_simultaneous_melodies
|
400
|
+
sample.modify_left :*, v
|
401
|
+
sample.modify_right :*, v
|
402
|
+
end
|
403
|
+
|
372
404
|
sample
|
373
405
|
end
|
374
406
|
|
375
407
|
def samples_per_wave note
|
376
408
|
frequency = frequency_for_note note
|
377
|
-
(PicoTune::SAMPLE_RATE / frequency).
|
409
|
+
(PicoTune::SAMPLE_RATE / frequency).to_i
|
410
|
+
end
|
378
411
|
|
379
412
|
def frequency_for_note note
|
380
413
|
parts = note.split ''
|
@@ -445,8 +478,13 @@ class PicoTune::Assembler
|
|
445
478
|
phrases = list.select { |item| item['type'] == 'phrase' }.map do |item|
|
446
479
|
melodies = item['melodies'].map do |m|
|
447
480
|
instrument = instruments.find { |i| i.name == m[0] }
|
481
|
+
|
482
|
+
raise "Instrument named \"#{m[0]}\" doesn't exist!" unless instrument
|
483
|
+
|
448
484
|
pattern = patterns.find { |p| p.name == m[1] }
|
449
485
|
|
486
|
+
raise "Pattern named \"#{m[1]}\" doesn't exist!" unless pattern
|
487
|
+
|
450
488
|
PicoTune::Melody.new instrument, pattern
|
451
489
|
end
|
452
490
|
|
@@ -460,13 +498,16 @@ class PicoTune::Assembler
|
|
460
498
|
end
|
461
499
|
|
462
500
|
sequence = list.find { |item| item['type'] == 'sequence' }
|
501
|
+
|
502
|
+
raise "Please define a sequence in your txt file with \"sequence s1 s2 s3...\" where s1/s2/s3/etc are names of phrases" unless sequence
|
503
|
+
|
463
504
|
sequence['list'].each do |phrase_name|
|
464
505
|
raise "undefined phrase \"#{phrase_name}\" in sequence" unless phrases.find { |p| p.name == phrase_name }
|
465
506
|
end
|
466
507
|
|
467
|
-
|
468
508
|
tune = list.find { |item| item['type'] == 'tune' }
|
469
|
-
|
509
|
+
|
510
|
+
raise "Please define your tune's name in your txt file with \"tune <tune name>\"" unless tune && tune['name']
|
470
511
|
|
471
512
|
PicoTune::Tune.new tune['name'], sequence['list'], phrases
|
472
513
|
end
|
@@ -497,9 +538,9 @@ class PicoTune::Parser
|
|
497
538
|
item['list'] = parts[1..-1]
|
498
539
|
elsif parts[0] == 'pattern'
|
499
540
|
item['name'] = parts[1]
|
500
|
-
item['list'] = pattern_steps
|
541
|
+
item['list'] = pattern_steps parts[2..-1].join
|
501
542
|
else
|
502
|
-
item['name'] = parts[1]
|
543
|
+
item['name'] = parts[1..-1].join
|
503
544
|
end
|
504
545
|
|
505
546
|
bag << item
|
@@ -512,7 +553,7 @@ class PicoTune::Parser
|
|
512
553
|
elsif collecting_melodies
|
513
554
|
bag.last['melodies'].push parts
|
514
555
|
else
|
515
|
-
bag.last[parts[0]] = parts[1]
|
556
|
+
bag.last[parts[0]] = parts[1..-1].join
|
516
557
|
end
|
517
558
|
end
|
518
559
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: picotune
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zachary Schroeder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: wavefile
|
@@ -25,6 +25,7 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.1.1
|
27
27
|
description: Use a text file with a simple DSL to generate a musical (maybe) wav file.
|
28
|
+
See https://github.com/robobluebird/picotune for DSL documentation!
|
28
29
|
email: schroza@gmail.com
|
29
30
|
executables:
|
30
31
|
- picotune
|
@@ -33,10 +34,13 @@ extra_rdoc_files: []
|
|
33
34
|
files:
|
34
35
|
- bin/picotune
|
35
36
|
- lib/picotune.rb
|
36
|
-
homepage: https://
|
37
|
+
homepage: https://github.com/robobluebird/picotune
|
37
38
|
licenses:
|
38
39
|
- MIT
|
39
|
-
metadata:
|
40
|
+
metadata:
|
41
|
+
documentation_uri: https://github.com/robobluebird/picotune
|
42
|
+
homepage_uri: https://github.com/robobluebird/picotune
|
43
|
+
source_code_uri: https://github.com/robobluebird/picotune
|
40
44
|
post_install_message:
|
41
45
|
rdoc_options: []
|
42
46
|
require_paths:
|