juicy 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|