juicy 0.1.2 → 0.1.3
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 +8 -8
- data/{bin → examples}/juicy.rb +5 -4
- data/lib/juicy.rb +15 -16
- data/lib/juicy/chord.rb +1 -2
- data/lib/juicy/mode.rb +24 -16
- data/lib/juicy/note.rb +17 -28
- data/lib/juicy/pitch.rb +15 -7
- data/lib/juicy/scale.rb +58 -8
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YjIwNTk4OWI5YjJhM2M1N2Q2MjEwMGRiN2Q5ZjVlNjk0YzgzMzQzNg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MGNjOWNlMDBkMzVmMWUzNjE5YTQxY2RiZmVjMTJjOWQxOWExZWQ2ZA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NWYyOTgxNzQ5ZjViZDIxNzA3YjFiMTdlZjA3ODRjMWZmOGUwODAxZTk3YWFk
|
10
|
+
ZTA1M2JjMTMzMTk3YzllZjhkZGU3MGY2ZGEwN2RlM2U2M2U3NzExODYxZmU4
|
11
|
+
NTVmODFiODE0YmY4ZGNlNWNhN2I2OWIzYzZhOGJlODY2ZjU2YmU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZTA5N2E4NDA1NWRlZmJkMTdhOWQ5M2VlN2Y0ZjM5ZDhkZDI1MmNmZmZlY2Fm
|
14
|
+
NDlkYzA0ZDI2Nzk5ODlhMTQ2MDlhMDQ3Nzg5MWM3NGE5Y2Y5ZTE4YzA1YjY4
|
15
|
+
MmNlMzIzODNjMGZmNTkyMWE1MjRkMjFiYTY5NWQ1Yzc5MDUzNzQ=
|
data/{bin → examples}/juicy.rb
RENAMED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'juicy'
|
2
2
|
include Juicy
|
3
3
|
play = true
|
4
4
|
# To make a pitch, give it a frequency.
|
@@ -45,7 +45,8 @@ sleep 0.3
|
|
45
45
|
puts "----------"
|
46
46
|
# Of course, this is cumbersome to do all on our own,
|
47
47
|
# so you have a Scale available to you.
|
48
|
-
|
48
|
+
root = Note.new(name: "D", octave_change: -1)
|
49
|
+
scale = Scale.new(mode: :major, root: root)
|
49
50
|
puts scale
|
50
51
|
scale.each_note do |note|
|
51
52
|
note.play if play
|
@@ -63,13 +64,13 @@ end
|
|
63
64
|
# and a beat sequencer to bring it all together.
|
64
65
|
|
65
66
|
sleep 0.3
|
66
|
-
|
67
|
+
root = Note.new(name: "G", octave_change: -1)
|
68
|
+
scale = Scale.new(mode: :major, root: root)
|
67
69
|
puts scale
|
68
70
|
scale.each_note do |note|
|
69
71
|
note.play if play
|
70
72
|
end
|
71
73
|
|
72
|
-
puts
|
73
74
|
|
74
75
|
# threads = []
|
75
76
|
#
|
data/lib/juicy.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
#require 'pry'
|
3
3
|
require 'sound'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
require_relative 'juicy/song' #
|
5
|
+
require 'juicy/pitch' #toned (or not) frequency in a chosen pitch space (temperament/intonation)
|
6
|
+
require 'juicy/note' #named pitch
|
7
|
+
require 'juicy/duration' #length of note in musical time (quarter note, eighth note, etc.)
|
8
|
+
require 'juicy/chord' #collection of notes
|
9
|
+
require 'juicy/chord_progression' #collection of chords in sequence
|
10
|
+
require 'juicy/melody' #collection of chords in sequence
|
11
|
+
require 'juicy/scale' #sequence of relative pitch changes ex. chromatic, diatonic, whole-note, pentatonic
|
12
|
+
require 'juicy/mode' #'flavor' of diatonic scale
|
13
|
+
require 'juicy/scale_degree' #index of given scale relative to root/tonic
|
14
|
+
require 'juicy/key' #scale at a given root note, i.e. Juicy::Note
|
15
|
+
require 'juicy/voice' #an instrument
|
16
|
+
require 'juicy/measure' #a measure of beats
|
17
|
+
require 'juicy/track' #a track of notes
|
18
|
+
require 'juicy/song' #
|
data/lib/juicy/chord.rb
CHANGED
data/lib/juicy/mode.rb
CHANGED
@@ -5,23 +5,31 @@ module Juicy
|
|
5
5
|
|
6
6
|
class Mode
|
7
7
|
|
8
|
-
attr_reader :rotate
|
8
|
+
attr_reader :rotate, :type
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
def initialize(type = :ionian)
|
11
|
+
@type = type
|
12
|
+
@rotate = case @type
|
13
|
+
when :major
|
14
|
+
0
|
15
|
+
when :minor
|
16
|
+
-2
|
17
|
+
else
|
18
|
+
0
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"#{@type}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other_mode)
|
27
|
+
if other_mode.kind_of? Mode
|
28
|
+
type == other_mode.type
|
29
|
+
elsif other_mode.kind_of? Symbol
|
30
|
+
type == other_mode
|
31
|
+
end
|
32
|
+
end
|
25
33
|
|
26
34
|
end
|
27
35
|
|
data/lib/juicy/note.rb
CHANGED
@@ -4,7 +4,10 @@ module Juicy
|
|
4
4
|
|
5
5
|
include Comparable
|
6
6
|
|
7
|
-
|
7
|
+
class << self
|
8
|
+
attr_reader :default_octave
|
9
|
+
end
|
10
|
+
@default_octave = 5
|
8
11
|
attr_reader :name, :pitch, :duration, :octave, :occupying_beat
|
9
12
|
attr_accessor :sum_of_queued_note_durations, :how_far_into_the_song_you_are
|
10
13
|
attr_accessor :distance_from_beat_in_milliseconds
|
@@ -16,7 +19,7 @@ module Juicy
|
|
16
19
|
@name = parse_note_name(options[:name])
|
17
20
|
@pitch = Pitch.new(@name)
|
18
21
|
@duration = Duration.new(options[:duration])
|
19
|
-
|
22
|
+
@octave = Note.default_octave + options[:octave_change]
|
20
23
|
end
|
21
24
|
|
22
25
|
def to_s
|
@@ -25,46 +28,38 @@ module Juicy
|
|
25
28
|
name += "b" if @name=~/flat/
|
26
29
|
"#{name}#{@octave}"
|
27
30
|
end
|
28
|
-
|
31
|
+
|
29
32
|
def inspect
|
30
|
-
"#{@name}"
|
33
|
+
"#{@name}#{@octave}"
|
31
34
|
end
|
32
35
|
|
33
|
-
def play(options = {duration: 200, octave: (@octave
|
36
|
+
def play(options = {duration: 200, octave: (@octave-Note.default_octave)})
|
34
37
|
if @name == :_
|
35
38
|
options[:volume] = 0
|
36
39
|
end
|
37
40
|
@pitch.play(options)
|
38
41
|
end
|
39
42
|
|
40
|
-
def prepare(options = {duration: 200, octave: (@octave
|
43
|
+
def prepare(options = {duration: 200, octave: (@octave-Note.default_octave)})
|
41
44
|
options[:duration] = options[:duration] || 200
|
42
|
-
options[:octave] = options[:octave] || (@octave
|
45
|
+
options[:octave] = options[:octave] || (@octave-Note.default_octave)
|
43
46
|
if @name == :_
|
44
47
|
options[:volume] = 0
|
45
48
|
end
|
46
49
|
Thread.pass
|
47
50
|
@prepared_note = @pitch.prepare(options)
|
48
51
|
@prepared_note[:sleep_time] = @distance_from_beat_in_milliseconds/1000.0
|
49
|
-
#puts @prepared_note.status
|
50
52
|
until @prepared_note.status.eql? "sleep"
|
51
53
|
sleep 0.001
|
52
|
-
#puts @prepared_note.status
|
53
54
|
end
|
54
55
|
@prepared_note
|
55
56
|
self
|
56
57
|
end
|
57
58
|
|
58
59
|
def play_prepared
|
59
|
-
#puts @prepared_note.status
|
60
|
-
#puts "playing"
|
61
60
|
until @prepared_note.status.eql? "sleep"
|
62
61
|
sleep 0.001
|
63
|
-
#puts @prepared_note.status
|
64
|
-
#Thread.pass
|
65
62
|
end
|
66
|
-
#Thread.pass
|
67
|
-
#puts "waking up"
|
68
63
|
@prepared_note.wakeup
|
69
64
|
end
|
70
65
|
|
@@ -114,10 +109,6 @@ module Juicy
|
|
114
109
|
@duration.duration_in_milliseconds(tempo)
|
115
110
|
end
|
116
111
|
|
117
|
-
def self.default_octave
|
118
|
-
@@default_octave
|
119
|
-
end
|
120
|
-
|
121
112
|
def plays_during(beat)
|
122
113
|
@occupying_beat = beat
|
123
114
|
end
|
@@ -133,7 +124,7 @@ module Juicy
|
|
133
124
|
private
|
134
125
|
|
135
126
|
def octave_change
|
136
|
-
@octave -
|
127
|
+
@octave - Note.default_octave + @step/12
|
137
128
|
end
|
138
129
|
|
139
130
|
def step(interval)
|
@@ -147,21 +138,19 @@ module Juicy
|
|
147
138
|
def parse_note_name(name)
|
148
139
|
# parses note name input
|
149
140
|
# user should be able to say "A#" or "a#" or "a sharp" or "A_sharp" or "a_s"
|
150
|
-
groups = name.to_s.match(/([a-gA-G])( |_)?(
|
151
|
-
|
152
|
-
puts "name: #{name}" if groups.nil?
|
153
|
-
if name.to_s.match "rest"
|
141
|
+
groups = name.to_s.match(/(?<name>[a-gA-G])(?<space> |_)?(?<accidental>.*)/)
|
142
|
+
if name.to_s.match "rest" || name.to_s.match(/^_$/)
|
154
143
|
note_name = "_"
|
155
144
|
else
|
156
|
-
note_name = groups[
|
157
|
-
unless groups[
|
158
|
-
note_name += case groups[
|
145
|
+
note_name = groups[:name].upcase
|
146
|
+
unless groups[:accidental].nil? || groups[:accidental].empty?
|
147
|
+
note_name += case groups[:accidental]
|
159
148
|
when /^(s|#)/
|
160
149
|
"_sharp"
|
161
150
|
when /^(f|b)/
|
162
151
|
"_flat"
|
163
152
|
else
|
164
|
-
puts "Unknown note modifier: '#{groups[
|
153
|
+
puts "Unknown note modifier: '#{groups[:accidental]}'"
|
165
154
|
""
|
166
155
|
end
|
167
156
|
end
|
data/lib/juicy/pitch.rb
CHANGED
@@ -30,20 +30,25 @@ module Juicy
|
|
30
30
|
class Pitch
|
31
31
|
|
32
32
|
include Comparable
|
33
|
-
|
34
|
-
|
33
|
+
@temperament = :equal
|
34
|
+
@pitch_standard = 440.0
|
35
|
+
|
36
|
+
class << self
|
37
|
+
attr_reader :temperament, :pitch_standard
|
38
|
+
end
|
35
39
|
|
36
40
|
attr_reader :frequency, :confidence
|
37
41
|
|
38
|
-
def initialize(pitch =
|
42
|
+
def initialize(pitch = Pitch.pitch_standard, tune_now = true)
|
39
43
|
|
40
44
|
if pitch.kind_of? Numeric
|
41
45
|
@frequency = pitch
|
42
46
|
@tuned = false
|
43
47
|
tune if tune_now
|
44
48
|
else
|
49
|
+
raise ArgumentError unless pitch.kind_of? Symbol
|
45
50
|
step = PITCHES[pitch.to_sym]
|
46
|
-
@frequency =
|
51
|
+
@frequency = Pitch.pitch_standard*2**(step/12.0)
|
47
52
|
@tuned = true
|
48
53
|
end
|
49
54
|
|
@@ -57,7 +62,7 @@ module Juicy
|
|
57
62
|
if out_of_tune
|
58
63
|
step = Math.log(@frequency/440.0,2)*12
|
59
64
|
@confidence = (1.0-2*(step - step.round).abs)*100.0
|
60
|
-
@frequency =
|
65
|
+
@frequency = Pitch.pitch_standard*2**((step.round)/12.0)
|
61
66
|
@tuned = true
|
62
67
|
end
|
63
68
|
self
|
@@ -80,7 +85,10 @@ module Juicy
|
|
80
85
|
options[:duration] ||= 200
|
81
86
|
options[:octave] ||= 0
|
82
87
|
options[:volume] ||= 1
|
83
|
-
|
88
|
+
device = Sound::Device.new
|
89
|
+
data = Sound::Data.new(device.format)
|
90
|
+
data.generate_sine_wave(@frequency*2**(options[:octave]), options[:duration], options[:volume])
|
91
|
+
device.play data
|
84
92
|
end
|
85
93
|
|
86
94
|
def prepare(options = {duration: 200, octave: 0, volume: 1})
|
@@ -102,7 +110,7 @@ module Juicy
|
|
102
110
|
end
|
103
111
|
|
104
112
|
def change_by (interval)
|
105
|
-
if
|
113
|
+
if Pitch.temperament.eql? :equal
|
106
114
|
Pitch.new(@frequency*2**(interval/12.0))
|
107
115
|
end
|
108
116
|
end
|
data/lib/juicy/scale.rb
CHANGED
@@ -24,9 +24,13 @@ module Juicy
|
|
24
24
|
|
25
25
|
class Scale
|
26
26
|
include Enumerable
|
27
|
+
|
28
|
+
attr_reader :root, :mode
|
27
29
|
|
28
|
-
def initialize(
|
29
|
-
|
30
|
+
def initialize(options = {mode: :major, root: Note.new})
|
31
|
+
options[:mode] ||= :major
|
32
|
+
options[:root] ||= Note.new
|
33
|
+
case options[:mode]
|
30
34
|
when :major
|
31
35
|
@type = :diatonic
|
32
36
|
when :minor
|
@@ -34,8 +38,8 @@ module Juicy
|
|
34
38
|
else
|
35
39
|
@type = type
|
36
40
|
end
|
37
|
-
@mode = Mode.new(
|
38
|
-
@root = root
|
41
|
+
@mode = Mode.new(options[:mode])
|
42
|
+
@root = options[:root]
|
39
43
|
generate_notes
|
40
44
|
|
41
45
|
end
|
@@ -49,7 +53,7 @@ module Juicy
|
|
49
53
|
end
|
50
54
|
|
51
55
|
def play
|
52
|
-
|
56
|
+
each_note &:play
|
53
57
|
end
|
54
58
|
|
55
59
|
def mode=(type)
|
@@ -72,9 +76,13 @@ module Juicy
|
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
75
|
-
def each_note
|
76
|
-
|
77
|
-
|
79
|
+
def each_note &block
|
80
|
+
if block_given?
|
81
|
+
(SCALE_TYPES[@type].size+1).times do
|
82
|
+
yield @notes.next
|
83
|
+
end
|
84
|
+
else
|
85
|
+
@notes
|
78
86
|
end
|
79
87
|
end
|
80
88
|
|
@@ -98,6 +106,48 @@ module Juicy
|
|
98
106
|
half_steps
|
99
107
|
end
|
100
108
|
|
109
|
+
def do
|
110
|
+
if @mode == :major
|
111
|
+
@root
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def re
|
116
|
+
if @mode == :major
|
117
|
+
@root + 2
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def mi
|
122
|
+
if @mode == :major
|
123
|
+
@root + 4
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def fa
|
128
|
+
if @mode == :major
|
129
|
+
@root + 5
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def so
|
134
|
+
if @mode == :major
|
135
|
+
@root + 7
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def la
|
140
|
+
if @mode == :major
|
141
|
+
@root + 9
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def ti
|
146
|
+
if @mode == :major
|
147
|
+
@root + 11
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
101
151
|
private
|
102
152
|
|
103
153
|
def generate_notes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: juicy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominic Muller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sound
|
@@ -19,7 +19,7 @@ dependencies:
|
|
19
19
|
version: '0.0'
|
20
20
|
- - ! '>='
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.0.
|
22
|
+
version: 0.0.6
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,14 +29,28 @@ dependencies:
|
|
29
29
|
version: '0.0'
|
30
30
|
- - ! '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.0.
|
32
|
+
version: 0.0.6
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: rspec
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
33
47
|
description: Generates songs
|
34
48
|
email: nicklink483@gmail.com
|
35
49
|
executables: []
|
36
50
|
extensions: []
|
37
51
|
extra_rdoc_files: []
|
38
52
|
files:
|
39
|
-
-
|
53
|
+
- examples/juicy.rb
|
40
54
|
- lib/juicy.rb
|
41
55
|
- lib/juicy/chord.rb
|
42
56
|
- lib/juicy/chord_progression.rb
|