juicy 0.1.0 → 0.1.2
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/juicy.rb +21 -7
- data/lib/juicy.rb +2 -3
- data/lib/juicy/chord.rb +6 -5
- data/lib/juicy/chord_progression.rb +16 -6
- data/lib/juicy/duration.rb +43 -27
- data/lib/juicy/measure.rb +1 -1
- data/lib/juicy/melody.rb +58 -24
- data/lib/juicy/note.rb +41 -14
- data/lib/juicy/pitch.rb +21 -17
- data/lib/juicy/scale.rb +27 -1
- data/lib/juicy/song.rb +54 -24
- data/lib/juicy/track.rb +39 -9
- metadata +13 -8
- data/lib/win32/wave_out_play_freq.rb +0 -131
- data/lib/win32/win32-mmlib_structs.rb +0 -67
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NDk5YWVkNTIyMzU4OWQyOGE3NmM4MTllYWMwODFhMTJiNmZmMmU1Nw==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
MGRiOTRkYzRiOWQzYmFhNWUyNjZmOWIzNzNlY2JmNzJjYjA5NTBjOQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MzMyOTllNGI2ZmRhODIzYjg4ZjA4ODIwYWMwMGYwNTUyN2Y3MTdiODQ0N2Uw
|
10
|
+
N2I0MjJhN2MwZjk1ZjAxMTM4YTdiOGEzNWMxZWUwYmJjNWJjZTAyZjVkN2Uw
|
11
|
+
YWRmZjM3ZDUyOTMwZGIyNDY4MjMyOGEyM2M5ZTZhMzY5YzVjYTM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjlkMjZmNGNlMmUxMzU3NzUyNmFlNGViNzM0MWU4MTMzNjYwNmIwMzk5Yjdl
|
14
|
+
YjhmYjdlOWUzYjMzZmRlNmZlMzhkOTBmMjQyZGNlMmNkOTBjNzc1MmZkZjlm
|
15
|
+
MzFkYzg4NDU0YjJiNzE3MjYyZGFhYWZkMzQwYTQ1NjMyZTU5MDY=
|
data/bin/juicy.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require_relative '../lib/juicy.rb'
|
2
2
|
include Juicy
|
3
|
-
play =
|
3
|
+
play = true
|
4
4
|
# To make a pitch, give it a frequency.
|
5
5
|
# By default, the frequency will be tuned to the nearest
|
6
6
|
# frequency in equal temperament.
|
@@ -9,14 +9,16 @@ pitch = Pitch.new(445)
|
|
9
9
|
puts pitch
|
10
10
|
pitch.play if play
|
11
11
|
|
12
|
+
sleep 0.3
|
12
13
|
puts "----------"
|
13
14
|
# A Note is a Pitch with a name.
|
14
15
|
# You give it a note name and an octave
|
15
16
|
#
|
16
|
-
note = Note.new("G#", -1)
|
17
|
+
note = Note.new(name: "G#", octave_change: -1)
|
17
18
|
puts note
|
18
19
|
note.play if play
|
19
20
|
|
21
|
+
sleep 0.3
|
20
22
|
puts "----------"
|
21
23
|
# You can also add or subtract from a note to go to the next half step
|
22
24
|
note += 1
|
@@ -26,9 +28,10 @@ note -= 2
|
|
26
28
|
puts note
|
27
29
|
note.play if play
|
28
30
|
|
31
|
+
sleep 0.3
|
29
32
|
puts "----------"
|
30
33
|
# With this much, you can make your own scales!
|
31
|
-
note = Note.new("C")
|
34
|
+
note = Note.new(name: "C")
|
32
35
|
major_scale = [2,2,1,2,2,2,1]
|
33
36
|
major_scale.each do |step|
|
34
37
|
puts note
|
@@ -38,10 +41,11 @@ end
|
|
38
41
|
puts note
|
39
42
|
note.play if play
|
40
43
|
|
44
|
+
sleep 0.3
|
41
45
|
puts "----------"
|
42
46
|
# Of course, this is cumbersome to do all on our own,
|
43
47
|
# so you have a Scale available to you.
|
44
|
-
scale = Scale.new(:major, Note.new("D", -1))
|
48
|
+
scale = Scale.new(:major, Note.new(name: "D", octave_change: -1))
|
45
49
|
puts scale
|
46
50
|
scale.each_note do |note|
|
47
51
|
note.play if play
|
@@ -58,8 +62,18 @@ end
|
|
58
62
|
# current development work is being done on scale objects
|
59
63
|
# and a beat sequencer to bring it all together.
|
60
64
|
|
61
|
-
|
65
|
+
sleep 0.3
|
66
|
+
scale = Scale.new(:major, Note.new(name: "G", octave_change: -1))
|
62
67
|
puts scale
|
63
68
|
scale.each_note do |note|
|
64
|
-
note.play
|
65
|
-
end
|
69
|
+
note.play if play
|
70
|
+
end
|
71
|
+
|
72
|
+
puts
|
73
|
+
|
74
|
+
# threads = []
|
75
|
+
#
|
76
|
+
# [660, 440].each do |tone|
|
77
|
+
# threads << Thread.new {Win32::Sound.play_freq(tone, 200)}
|
78
|
+
# end
|
79
|
+
# threads.each {|t| t.join}
|
data/lib/juicy.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# require 'win32-sound'
|
2
|
-
#
|
3
|
-
|
4
|
-
require_relative 'win32/wave_out_play_freq'
|
2
|
+
#require 'pry'
|
3
|
+
require 'sound'
|
5
4
|
|
6
5
|
require_relative 'juicy/pitch' #toned (or not) frequency in a chosen pitch space (temperament/intonation)
|
7
6
|
require_relative 'juicy/note' #named pitch
|
data/lib/juicy/chord.rb
CHANGED
@@ -10,17 +10,18 @@ module Juicy
|
|
10
10
|
|
11
11
|
}
|
12
12
|
|
13
|
-
attr_reader :duration
|
13
|
+
attr_reader :duration, :notes
|
14
14
|
attr_accessor :sum_of_queued_chord_durations, :how_far_into_the_song_you_are
|
15
15
|
|
16
|
-
def initialize(options = {root: Note.new(:C), quality: :major, inversion: 0, context: :none, duration: Duration.new("quarter")})
|
17
|
-
|
16
|
+
def initialize(options = {root: Note.new(name: :C), quality: :major, inversion: 0, context: :none, duration: Duration.new("quarter")})
|
17
|
+
#binding.pry
|
18
|
+
@root = (options[:root].kind_of?(Note) ? options[:root] : Note.new(name: options[:root])) || Note.new(name: :C)
|
18
19
|
@quality = options[:quality] || :major
|
19
20
|
@inversion = options[:inversion] || 0
|
20
21
|
@context = options[:context] || :none
|
21
22
|
@duration = options[:duration] || Duration.new("quarter")
|
22
23
|
@type = :triad
|
23
|
-
@notes = [@root, @root+
|
24
|
+
@notes = [@root + QUALITIES[@quality][0], @root + QUALITIES[@quality][1], @root + QUALITIES[@quality][2]]
|
24
25
|
end
|
25
26
|
|
26
27
|
def to_s
|
@@ -47,7 +48,7 @@ module Juicy
|
|
47
48
|
end
|
48
49
|
|
49
50
|
pitches.each do |interval|
|
50
|
-
notes << Note.new(PITCHES.key((PITCHES[@root.name]+interval) % 12))
|
51
|
+
notes << Note.new(name: PITCHES.key((PITCHES[@root.name]+interval) % 12))
|
51
52
|
end
|
52
53
|
|
53
54
|
case style
|
@@ -4,12 +4,22 @@ module Juicy
|
|
4
4
|
|
5
5
|
attr_accessor :chords
|
6
6
|
|
7
|
-
def initialize
|
8
|
-
|
9
|
-
@
|
10
|
-
|
11
|
-
|
12
|
-
|
7
|
+
def initialize(key, mode, numerals = [1,4,1,5])
|
8
|
+
|
9
|
+
@numerals = numerals
|
10
|
+
|
11
|
+
#given a key and a mode, a number can tell me what chord.
|
12
|
+
|
13
|
+
@chords = [
|
14
|
+
Chord.new(root: "A", quality: :major),
|
15
|
+
Chord.new(root: "D", quality: :major),
|
16
|
+
Chord.new(root: "A", quality: :major),
|
17
|
+
Chord.new(root: "E", quality: :major)
|
18
|
+
]
|
19
|
+
#@numerals.each do |numeral|
|
20
|
+
# @chords << Chord.new(numeral)
|
21
|
+
#end
|
22
|
+
|
13
23
|
end
|
14
24
|
|
15
25
|
def inspect
|
data/lib/juicy/duration.rb
CHANGED
@@ -2,8 +2,22 @@ module Juicy
|
|
2
2
|
|
3
3
|
class Duration
|
4
4
|
|
5
|
+
DURATIONS = ["whole", "half", "quarter", "eighth", "sixteenth", "thirty-second", "sixty-fourth"]
|
6
|
+
MULTIPLIER = {:"" => 0, :triplet => -1, :dotted => 1}
|
7
|
+
|
8
|
+
attr_reader :duration
|
9
|
+
|
5
10
|
def initialize(duration)
|
6
|
-
@duration
|
11
|
+
# @duration is a Rational number which represents the length of
|
12
|
+
# a note relative to a quarter note
|
13
|
+
# ex. Duration.new("dotted eighth")
|
14
|
+
# @duration = Rational(3,4)
|
15
|
+
#
|
16
|
+
if duration.kind_of? Rational
|
17
|
+
@duration = duration
|
18
|
+
else
|
19
|
+
@duration = parse_duration(duration)
|
20
|
+
end
|
7
21
|
|
8
22
|
end
|
9
23
|
|
@@ -22,39 +36,41 @@ module Juicy
|
|
22
36
|
def to_s
|
23
37
|
@duration.to_s
|
24
38
|
end
|
39
|
+
|
40
|
+
def to_f
|
41
|
+
@duration.to_f
|
42
|
+
end
|
43
|
+
|
44
|
+
def +(other_duration)
|
45
|
+
Duration.new(@duration + other_duration.duration)
|
46
|
+
end
|
47
|
+
|
48
|
+
def *(scalar)
|
49
|
+
Duration.new(@duration*scalar)
|
50
|
+
end
|
25
51
|
|
26
52
|
private
|
27
53
|
|
28
54
|
def beats_of_given_type_per_quarter_note
|
29
|
-
|
30
|
-
when "quarter"
|
31
|
-
1.0
|
32
|
-
when "half"
|
33
|
-
2.0
|
34
|
-
when "whole"
|
35
|
-
4.0
|
36
|
-
when "eighth"
|
37
|
-
0.5
|
38
|
-
when "sixteenth"
|
39
|
-
0.25
|
40
|
-
else
|
41
|
-
1.0
|
42
|
-
end
|
55
|
+
@duration.to_f
|
43
56
|
end
|
44
57
|
|
58
|
+
# takes user input and constructs a Rational number
|
59
|
+
# ex. "dotted eighth"
|
60
|
+
# Rational(3**
|
61
|
+
#
|
45
62
|
def parse_duration(duration)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
63
|
+
|
64
|
+
# parses note name input
|
65
|
+
# user should be able to say "dotted sixteenth" or "quarter" or "triplet eighth"
|
66
|
+
groups = duration.to_s.match(/^((dotted|triplet)( |_))?(.*)$/)
|
67
|
+
puts "duration: #{duration}" if groups.nil?
|
68
|
+
|
69
|
+
d = DURATIONS.index(groups[4])
|
70
|
+
multiplier = MULTIPLIER[groups[2].to_s.to_sym]
|
71
|
+
#binding.pry
|
72
|
+
|
73
|
+
Rational(4*(3**multiplier),(2**(multiplier+d)))
|
58
74
|
end
|
59
75
|
|
60
76
|
end
|
data/lib/juicy/measure.rb
CHANGED
data/lib/juicy/melody.rb
CHANGED
@@ -5,38 +5,72 @@ module Juicy
|
|
5
5
|
|
6
6
|
def initialize(chord_progression = ChordProgression.new, song = Song.new)
|
7
7
|
|
8
|
-
@
|
9
|
-
Note.new("C", "half", 1),Note.new("E", "", 1),Note.new("G", "", 1),
|
10
|
-
Note.new("F", "", 1),Note.new("F", "eighth", 1),Note.new("G", "eighth", 1),Note.new("F", "half", 1),
|
11
|
-
Note.new("C", "half", 1),Note.new("E", "", 1),Note.new("G", "", 1),
|
12
|
-
Note.new("D", "", 1),Note.new("D", "eighth", 1),Note.new("E", "eighth", 1),Note.new("D", "half", 1)
|
13
|
-
]
|
14
|
-
|
8
|
+
@song = song
|
15
9
|
@notes = []
|
16
10
|
|
17
11
|
# given the chord progression, make the first note of every chord change a chord tone within 1 octave of the previous.
|
18
12
|
# then add in notes which over all step to the next, but don't have to be chord tones OR simply jump to the next chord tone
|
19
|
-
song.measures.each do |measure|
|
13
|
+
#song.measures.each do |measure|
|
20
14
|
#until
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
(10).times do
|
29
|
-
@notes << Note.new((["A", "C", "E"]).sample + [""].sample, ["eighth"].sample, [*(-1)..1].sample)
|
30
|
-
end
|
15
|
+
#end
|
16
|
+
|
17
|
+
# given a chord progression, make the first note of every measure a chord tone within 1 octave of the previous
|
18
|
+
|
19
|
+
# while distance_between_notes > 1
|
20
|
+
# insert notes between
|
21
|
+
# end
|
31
22
|
|
23
|
+
scale = Scale.new(@song.mode, Note.new(name: @song.key))
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
#
|
25
|
+
#new_note = 0
|
26
|
+
#old_note = 0
|
27
|
+
@song.measures.each_with_index do |measure, index|
|
28
|
+
@notes << Note.new(name: chord_progression.chords[index].notes[0].name, duration: :whole)
|
29
|
+
#new_note = Note.new(name: chord_progression.chords[index].notes.sample.name, duration: :whole, octave_change: [*(-1)..(-1)].sample)
|
30
|
+
#measure.insert_at(0, Note.new(name: "C"))
|
31
|
+
#old_note = new_note
|
39
32
|
end
|
33
|
+
#binding.pry
|
34
|
+
|
35
|
+
#song.measures.each_with_index do |measure, index|
|
36
|
+
# @notes[index].duration = "half"
|
37
|
+
# note = Note.new(name: chord_progression.chords[measure].notes.sample.name, duration: :half, octave_change: [*(-1)..(-1)].sample)
|
38
|
+
# direction = (@notes[measure] <=> @notes[measure+1])
|
39
|
+
#
|
40
|
+
# insert = false
|
41
|
+
#
|
42
|
+
# if direction == -1
|
43
|
+
# until note >= @notes[measure] && note <= @notes[measure+1]
|
44
|
+
# #binding.pry
|
45
|
+
# note = Note.new(name: [*"A".."G"].sample, duration: :half, octave_change: [*(-1)..(0)].sample)
|
46
|
+
# end
|
47
|
+
# insert = true
|
48
|
+
# elsif direction == 1
|
49
|
+
# until note <= @notes[measure] && note >= @notes[measure+1]
|
50
|
+
# #binding.pry
|
51
|
+
# note = Note.new(name: [*"A".."G"].sample, duration: :half, octave_change: [*(-1)..(0)].sample)
|
52
|
+
# end
|
53
|
+
# insert = true
|
54
|
+
# end
|
55
|
+
# @notes.insert(measure+1, note) if insert
|
56
|
+
#end
|
57
|
+
|
58
|
+
#binding.pry
|
59
|
+
|
60
|
+
#duration = ["half"]
|
61
|
+
#number_of_measures = 1
|
62
|
+
#number_of_measures.times do
|
63
|
+
# chord_progression.chords.each do |chord|
|
64
|
+
# number_of_beats_per_measure = 2
|
65
|
+
# number_of_beats_per_measure.times do
|
66
|
+
# puts chord.notes.inspect
|
67
|
+
# @notes << Note.new(chord.notes.sample.name, [duration].sample, [*(-2)..0].sample)
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
#end
|
71
|
+
|
72
|
+
|
73
|
+
|
40
74
|
|
41
75
|
end
|
42
76
|
|
data/lib/juicy/note.rb
CHANGED
@@ -4,16 +4,19 @@ module Juicy
|
|
4
4
|
|
5
5
|
include Comparable
|
6
6
|
|
7
|
-
@@default_octave =
|
7
|
+
@@default_octave = 5
|
8
8
|
attr_reader :name, :pitch, :duration, :octave, :occupying_beat
|
9
9
|
attr_accessor :sum_of_queued_note_durations, :how_far_into_the_song_you_are
|
10
10
|
attr_accessor :distance_from_beat_in_milliseconds
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
|
12
|
+
def initialize(options = {name: "A", duration: :quarter, octave_change: 0})
|
13
|
+
options[:name] ||= "A"
|
14
|
+
options[:duration] ||= :quarter
|
15
|
+
options[:octave_change] ||= 0
|
16
|
+
@name = parse_note_name(options[:name])
|
14
17
|
@pitch = Pitch.new(@name)
|
15
|
-
@duration = Duration.new(duration)
|
16
|
-
@octave = @@default_octave + octave_change
|
18
|
+
@duration = Duration.new(options[:duration])
|
19
|
+
@octave = @@default_octave + options[:octave_change]
|
17
20
|
end
|
18
21
|
|
19
22
|
def to_s
|
@@ -22,7 +25,7 @@ module Juicy
|
|
22
25
|
name += "b" if @name=~/flat/
|
23
26
|
"#{name}#{@octave}"
|
24
27
|
end
|
25
|
-
|
28
|
+
|
26
29
|
def inspect
|
27
30
|
"#{@name}"
|
28
31
|
end
|
@@ -66,24 +69,31 @@ module Juicy
|
|
66
69
|
end
|
67
70
|
|
68
71
|
def +(interval)
|
69
|
-
step
|
70
|
-
|
71
|
-
name = PITCHES.key((PITCHES[@name]+interval) % 12)
|
72
|
-
Note.new(name, @octave-@@default_octave + octave_change)
|
72
|
+
step(interval)
|
73
|
+
Note.new(name: new_name, octave_change: octave_change)
|
73
74
|
end
|
74
75
|
|
75
76
|
def -(interval)
|
76
|
-
|
77
|
+
step(-1*interval)
|
78
|
+
Note.new(name: new_name, octave_change: octave_change)
|
77
79
|
end
|
78
80
|
|
79
81
|
def <=>(other_note)
|
80
|
-
if same_octave
|
82
|
+
if same_octave(other_note)
|
81
83
|
self.pitch <=> other_note.pitch
|
82
84
|
else
|
83
85
|
self.octave <=> other_note.octave
|
84
86
|
end
|
85
87
|
end
|
86
88
|
|
89
|
+
def succ
|
90
|
+
return (self+1)
|
91
|
+
end
|
92
|
+
|
93
|
+
def prev
|
94
|
+
return (self-1)
|
95
|
+
end
|
96
|
+
|
87
97
|
def length
|
88
98
|
duration
|
89
99
|
end
|
@@ -101,7 +111,6 @@ module Juicy
|
|
101
111
|
end
|
102
112
|
|
103
113
|
def duration_in_milliseconds(tempo)
|
104
|
-
#puts tempo
|
105
114
|
@duration.duration_in_milliseconds(tempo)
|
106
115
|
end
|
107
116
|
|
@@ -117,12 +126,29 @@ module Juicy
|
|
117
126
|
beat == @occupying_beat
|
118
127
|
end
|
119
128
|
|
129
|
+
def duration=(duration)
|
130
|
+
@duration = Duration.new(duration)
|
131
|
+
end
|
132
|
+
|
120
133
|
private
|
121
134
|
|
135
|
+
def octave_change
|
136
|
+
@octave - @@default_octave + @step/12
|
137
|
+
end
|
138
|
+
|
139
|
+
def step(interval)
|
140
|
+
@step = PITCHES[@name]+interval
|
141
|
+
end
|
142
|
+
|
143
|
+
def new_name
|
144
|
+
PITCHES.key((@step) % 12)
|
145
|
+
end
|
146
|
+
|
122
147
|
def parse_note_name(name)
|
123
148
|
# parses note name input
|
124
149
|
# user should be able to say "A#" or "a#" or "a sharp" or "A_sharp" or "a_s"
|
125
150
|
groups = name.to_s.match(/([a-gA-G])( |_)?(.*)/)
|
151
|
+
#binding.pry
|
126
152
|
puts "name: #{name}" if groups.nil?
|
127
153
|
if name.to_s.match "rest"
|
128
154
|
note_name = "_"
|
@@ -136,13 +162,14 @@ module Juicy
|
|
136
162
|
"_flat"
|
137
163
|
else
|
138
164
|
puts "Unknown note modifier: '#{groups[3]}'"
|
165
|
+
""
|
139
166
|
end
|
140
167
|
end
|
141
168
|
end
|
142
169
|
note_name.to_sym
|
143
170
|
end
|
144
171
|
|
145
|
-
def same_octave
|
172
|
+
def same_octave(other_note)
|
146
173
|
(self.octave <=> other_note.octave) == 0
|
147
174
|
end
|
148
175
|
|
data/lib/juicy/pitch.rb
CHANGED
@@ -25,14 +25,16 @@ module Juicy
|
|
25
25
|
A_flat: 11
|
26
26
|
}
|
27
27
|
|
28
|
+
# This class encapsulates all of the pitch mechanics for a given temperament.
|
29
|
+
#
|
28
30
|
class Pitch
|
29
|
-
|
31
|
+
|
30
32
|
include Comparable
|
31
33
|
@@temperament = :equal
|
32
34
|
@@pitch_standard = 440.0
|
33
|
-
|
35
|
+
|
34
36
|
attr_reader :frequency, :confidence
|
35
|
-
|
37
|
+
|
36
38
|
def initialize(pitch = @@pitch_standard, tune_now = true)
|
37
39
|
|
38
40
|
if pitch.kind_of? Numeric
|
@@ -46,11 +48,11 @@ module Juicy
|
|
46
48
|
end
|
47
49
|
|
48
50
|
end
|
49
|
-
|
51
|
+
|
50
52
|
def to_s
|
51
53
|
"#{@frequency}"
|
52
54
|
end
|
53
|
-
|
55
|
+
|
54
56
|
def tune
|
55
57
|
if out_of_tune
|
56
58
|
step = Math.log(@frequency/440.0,2)*12
|
@@ -58,27 +60,29 @@ module Juicy
|
|
58
60
|
@frequency = @@pitch_standard*2**((step.round)/12.0)
|
59
61
|
@tuned = true
|
60
62
|
end
|
63
|
+
self
|
61
64
|
end
|
62
|
-
|
65
|
+
|
63
66
|
def +(interval)
|
64
67
|
change_by (interval)
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
def -(interval)
|
68
71
|
change_by (-interval)
|
69
72
|
end
|
70
|
-
|
73
|
+
|
71
74
|
def self.play(options = {duration: 200})
|
72
|
-
|
75
|
+
binding.pry
|
76
|
+
Sound::Out.play_freq(options[:note].pitch.frequency, options[:note].duration)
|
73
77
|
end
|
74
|
-
|
78
|
+
|
75
79
|
def play(options = {duration: 200, octave: 0, volume: 1})
|
76
80
|
options[:duration] ||= 200
|
77
81
|
options[:octave] ||= 0
|
78
82
|
options[:volume] ||= 1
|
79
|
-
|
83
|
+
::Sound::Out.play_freq(@frequency*2**(options[:octave]), options[:duration], options[:volume])
|
80
84
|
end
|
81
|
-
|
85
|
+
|
82
86
|
def prepare(options = {duration: 200, octave: 0, volume: 1})
|
83
87
|
options[:duration] ||= 200
|
84
88
|
options[:octave] ||= 0
|
@@ -86,23 +90,23 @@ module Juicy
|
|
86
90
|
|
87
91
|
return Thread.new{Win32::Sound.play_freq(@frequency*2**(options[:octave]), options[:duration], options[:volume], true)}
|
88
92
|
end
|
89
|
-
|
93
|
+
|
90
94
|
def <=>(other_pitch)
|
91
95
|
self.frequency <=> other_pitch.frequency
|
92
96
|
end
|
93
|
-
|
97
|
+
|
94
98
|
private
|
95
|
-
|
99
|
+
|
96
100
|
def out_of_tune
|
97
101
|
!@tuned
|
98
102
|
end
|
99
|
-
|
103
|
+
|
100
104
|
def change_by (interval)
|
101
105
|
if @@temperament.eql? :equal
|
102
106
|
Pitch.new(@frequency*2**(interval/12.0))
|
103
107
|
end
|
104
108
|
end
|
105
|
-
|
109
|
+
|
106
110
|
end
|
107
111
|
|
108
112
|
end
|
data/lib/juicy/scale.rb
CHANGED
@@ -62,8 +62,14 @@ module Juicy
|
|
62
62
|
generate_notes
|
63
63
|
end
|
64
64
|
|
65
|
+
#def each
|
66
|
+
# yield SCALE_TYPES[@type]
|
67
|
+
#end
|
68
|
+
|
65
69
|
def each
|
66
|
-
|
70
|
+
(SCALE_TYPES[@type].size+1).times do
|
71
|
+
yield @notes.next.name
|
72
|
+
end
|
67
73
|
end
|
68
74
|
|
69
75
|
def each_note
|
@@ -72,6 +78,26 @@ module Juicy
|
|
72
78
|
end
|
73
79
|
end
|
74
80
|
|
81
|
+
def interval_between(note1, note2)
|
82
|
+
half_steps = 0
|
83
|
+
direction = (note1 <=> note2)
|
84
|
+
if direction == 0
|
85
|
+
elsif direction == -1
|
86
|
+
note = note1.dup
|
87
|
+
until (note <=> note2) == 0
|
88
|
+
note += 1
|
89
|
+
half_steps += 1
|
90
|
+
end
|
91
|
+
elsif direction == 1
|
92
|
+
note = note1.dup
|
93
|
+
until (note <=> note2) == 0
|
94
|
+
note -= 1
|
95
|
+
half_steps -= 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
half_steps
|
99
|
+
end
|
100
|
+
|
75
101
|
private
|
76
102
|
|
77
103
|
def generate_notes
|
data/lib/juicy/song.rb
CHANGED
@@ -3,7 +3,7 @@ module Juicy
|
|
3
3
|
|
4
4
|
class Song
|
5
5
|
|
6
|
-
attr_reader :measures, :tempo
|
6
|
+
attr_reader :measures, :tempo, :key, :mode
|
7
7
|
|
8
8
|
def initialize
|
9
9
|
|
@@ -12,47 +12,77 @@ module Juicy
|
|
12
12
|
@voices << Voice.new
|
13
13
|
@key = :A
|
14
14
|
@mode = :major
|
15
|
-
@tempo =
|
15
|
+
@tempo = 100.0
|
16
16
|
@time_signature = [4,4]
|
17
17
|
@measures = []
|
18
18
|
4.times {@measures << Measure.new(@time_signature)}
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def play
|
23
|
-
|
19
|
+
|
24
20
|
# have musical construct yield notes up to a note manager/beat sequencer for each track
|
25
21
|
# chords will eventually have a play style (various types of arpeggiation and such), but
|
26
22
|
# for now they'll all just play all their notes at once for the given duration
|
27
23
|
|
28
|
-
tracks = []
|
24
|
+
@tracks = []
|
29
25
|
|
30
|
-
|
26
|
+
key = Key.new
|
27
|
+
chord_progression = ChordProgression.new(@key, @mode)
|
31
28
|
|
32
|
-
|
29
|
+
@tracks << Track.new(0, demo_melody, @tempo)
|
30
|
+
number_of_tracks = 1
|
31
|
+
number_of_tracks.times do
|
32
|
+
melody = Melody.new(chord_progression, self)
|
33
|
+
@tracks << Track.new(melody.initial_play_time, melody, @tempo)
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
melody = Melody.new(chord_progression, self)
|
38
|
-
puts melody.inspect
|
39
|
-
tracks << Track.new(melody.initial_play_time, melody.to_a, @tempo)
|
40
|
-
melody = Melody.new(chord_progression, self)
|
41
|
-
puts melody.inspect
|
42
|
-
tracks << Track.new(melody.initial_play_time, melody.to_a, @tempo)
|
43
|
-
melody = Melody.new(chord_progression, self)
|
44
|
-
puts melody.inspect
|
45
|
-
tracks << Track.new(melody.initial_play_time, melody.to_a, @tempo)
|
36
|
+
end
|
37
|
+
|
38
|
+
def play
|
46
39
|
|
47
|
-
Track.play_concurrently(tracks, @tempo)
|
40
|
+
Track.play_concurrently(@tracks, @tempo)
|
48
41
|
|
49
|
-
|
50
42
|
end
|
51
43
|
|
52
44
|
def beat_length_in_milliseconds
|
53
45
|
60_000.0/@tempo
|
54
46
|
end
|
55
47
|
|
48
|
+
def demo_melody
|
49
|
+
@demo_melody ||= [
|
50
|
+
Note.new(name: "A", duration: :eighth, octave_change: -1),
|
51
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
52
|
+
Note.new(name: "C#", duration: :eighth, octave_change: -1),
|
53
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
54
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
55
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
56
|
+
Note.new(name: "C#", duration: :eighth, octave_change: -1),
|
57
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
58
|
+
Note.new(name: "A", duration: :eighth, octave_change: -1),
|
59
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
60
|
+
Note.new(name: "C#", duration: :eighth, octave_change: -1),
|
61
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
62
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
63
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
64
|
+
Note.new(name: "C#", duration: :eighth, octave_change: -1),
|
65
|
+
Note.new(name: "E", duration: :eighth, octave_change: -1),
|
66
|
+
|
67
|
+
Note.new(name: "D", duration: :eighth, octave_change: -1),
|
68
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
69
|
+
Note.new(name: "F#", duration: :eighth, octave_change: -1),
|
70
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
71
|
+
Note.new(name: "D", duration: :eighth, octave_change: 0),
|
72
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
73
|
+
Note.new(name: "F#", duration: :eighth, octave_change: -1),
|
74
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
75
|
+
Note.new(name: "D", duration: :eighth, octave_change: -1),
|
76
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
77
|
+
Note.new(name: "F#", duration: :eighth, octave_change: -1),
|
78
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
79
|
+
Note.new(name: "D", duration: :eighth, octave_change: 0),
|
80
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0),
|
81
|
+
Note.new(name: "F#", duration: :eighth, octave_change: -1),
|
82
|
+
Note.new(name: "A", duration: :eighth, octave_change: 0)
|
83
|
+
]
|
84
|
+
end
|
85
|
+
|
56
86
|
end
|
57
87
|
|
58
88
|
end
|
data/lib/juicy/track.rb
CHANGED
@@ -8,9 +8,24 @@ module Juicy
|
|
8
8
|
def initialize(init_time, notes, tempo)
|
9
9
|
|
10
10
|
@start_time = init_time
|
11
|
-
@notes = notes
|
11
|
+
@notes = notes.to_a
|
12
12
|
@tempo = tempo
|
13
|
+
|
14
|
+
@track_length_in_milliseconds = 0
|
15
|
+
@notes.each do |note|
|
16
|
+
note.distance_from_beat_in_milliseconds = distance_from_beat_in_milliseconds
|
17
|
+
note.plays_during occupying_beat
|
18
|
+
@track_length_in_milliseconds += note.duration_in_milliseconds(@tempo)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
13
22
|
|
23
|
+
def distance_from_beat_in_milliseconds
|
24
|
+
(@track_length_in_milliseconds.round % Duration.duration_of_quarter_note_in_milliseconds(@tempo).round)
|
25
|
+
end
|
26
|
+
|
27
|
+
def occupying_beat
|
28
|
+
@track_length_in_milliseconds.round / Duration.duration_of_quarter_note_in_milliseconds(@tempo).round + 1
|
14
29
|
end
|
15
30
|
|
16
31
|
def play
|
@@ -116,14 +131,14 @@ module Juicy
|
|
116
131
|
end
|
117
132
|
|
118
133
|
def self.play_concurrently(tracks, tempo)
|
119
|
-
|
134
|
+
#@song_start_time = Time.now
|
120
135
|
threads = []
|
121
136
|
# iterate over each track over and over again, preparing notes in the current beat
|
122
137
|
# in each iteration, store the notes you've prepared into an array and store that
|
123
138
|
# array into the prepared_notes array which the playing thread will play notes from
|
124
139
|
# when it has enough to play.
|
125
140
|
# A track is an array of playable musical objects. for now, these are individual
|
126
|
-
# notes or chords. a chord is an array of individual notes
|
141
|
+
# notes or chords. a chord is an array of individual notes to be played simultaneously
|
127
142
|
#
|
128
143
|
prepared_beats = []
|
129
144
|
out_of_notes_to_prepare = false
|
@@ -131,12 +146,25 @@ module Juicy
|
|
131
146
|
Thread.current[:name] = "prepare beats thread"
|
132
147
|
current_beat = 1
|
133
148
|
last_beat = 1
|
149
|
+
# find the final beat of all tracks combined
|
134
150
|
tracks.each do |track|
|
135
|
-
track.notes.each do |
|
136
|
-
|
151
|
+
track.notes.each do |playable_thing|
|
152
|
+
if playable_thing.kind_of?(Note)
|
153
|
+
last_beat = playable_thing.occupying_beat if playable_thing.occupying_beat > last_beat
|
154
|
+
elsif playable_thing.kind_of?(Chord)
|
155
|
+
playable_thing.notes.each do |note|
|
156
|
+
last_beat = note.occupying_beat if note.occupying_beat > last_beat
|
157
|
+
end
|
158
|
+
end
|
137
159
|
end
|
138
160
|
end
|
161
|
+
# until you've prepared all the notes, prepare each note in an array with
|
162
|
+
# other notes who occupy the same beat
|
139
163
|
until current_beat > last_beat
|
164
|
+
# only add/prepare beats if there are fewer than 20 prepared already
|
165
|
+
# so that we don't hit the thread limit. This assumes that a buffer of 20
|
166
|
+
# is sufficent and that each beat has, on average, fewer than 30 notes.
|
167
|
+
# if there are too many notes, weird things start to happen, so don't do that.
|
140
168
|
if prepared_beats.size <= 20
|
141
169
|
this_beats_notes = []
|
142
170
|
tracks.each do |track|
|
@@ -154,6 +182,7 @@ module Juicy
|
|
154
182
|
prepared_beats << this_beats_notes
|
155
183
|
current_beat += 1
|
156
184
|
end
|
185
|
+
# Pass thread execution over to note playing so that there is no delay in playback
|
157
186
|
Thread.pass
|
158
187
|
end
|
159
188
|
out_of_notes_to_prepare = true
|
@@ -165,26 +194,27 @@ module Juicy
|
|
165
194
|
# "start" the song by iterating through each element, waking up each note
|
166
195
|
# and then waiting the remainder of a beat's worth of milliseconds until the
|
167
196
|
# next beat
|
168
|
-
until (prepared_beats.size >=
|
197
|
+
until (prepared_beats.size >= 4) || out_of_notes_to_prepare
|
169
198
|
sleep 0.01
|
170
199
|
end
|
171
200
|
last_note = Thread.new {}
|
172
201
|
time = Time.now
|
173
202
|
until prepared_beats.empty? && out_of_notes_to_prepare
|
174
203
|
time = Time.now
|
204
|
+
# take the next beat's worth of notes
|
175
205
|
beat = prepared_beats.shift
|
206
|
+
# start each note in the beat as its own thread
|
176
207
|
beat.each do |note|
|
177
208
|
last_note = note.play_prepared
|
178
209
|
end
|
179
|
-
sleep
|
210
|
+
# to ensure simultaneity, sleep for however much longer a beat lasts
|
211
|
+
# at the current tempo
|
180
212
|
sleep_amount = Duration.duration_of_quarter_note_in_milliseconds(tempo)/1000.0 - (Time.now - time)
|
181
|
-
puts sleep_amount
|
182
213
|
sleep sleep_amount unless sleep_amount < 0
|
183
214
|
end
|
184
215
|
last_note.join
|
185
216
|
end
|
186
217
|
|
187
|
-
#threads.each {|t| puts t[:name] }
|
188
218
|
threads.each {|t| t.join}
|
189
219
|
end
|
190
220
|
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
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.2
|
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-
|
11
|
+
date: 2014-08-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: sound
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.0'
|
17
20
|
- - ! '>='
|
18
21
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
22
|
+
version: 0.0.2
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.0'
|
24
30
|
- - ! '>='
|
25
31
|
- !ruby/object:Gem::Version
|
26
|
-
version: 0.
|
32
|
+
version: 0.0.2
|
27
33
|
description: Generates songs
|
28
34
|
email: nicklink483@gmail.com
|
29
35
|
executables: []
|
@@ -46,9 +52,7 @@ files:
|
|
46
52
|
- lib/juicy/song.rb
|
47
53
|
- lib/juicy/track.rb
|
48
54
|
- lib/juicy/voice.rb
|
49
|
-
|
50
|
-
- lib/win32/win32-mmlib_structs.rb
|
51
|
-
homepage: https://github.com/nicklink483/juicy
|
55
|
+
homepage: https://github.com/RSMP/juicy
|
52
56
|
licenses:
|
53
57
|
- MIT
|
54
58
|
metadata: {}
|
@@ -73,3 +77,4 @@ signing_key:
|
|
73
77
|
specification_version: 4
|
74
78
|
summary: Song writing tool
|
75
79
|
test_files: []
|
80
|
+
has_rdoc:
|
@@ -1,131 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'win32-mmlib_structs'
|
3
|
-
|
4
|
-
module Win32
|
5
|
-
|
6
|
-
class Sound
|
7
|
-
|
8
|
-
# Plays a frequency for a specified duration at a given volume.
|
9
|
-
# Defaults are 440Hz, 1 second, full volume.
|
10
|
-
# Result is a single channel, 44100Hz sampled, 16 bit sine wave.
|
11
|
-
# If multiple instances are plays in simultaneous threads,
|
12
|
-
# they will be started and played at the same time
|
13
|
-
#
|
14
|
-
# ex.: threads = []
|
15
|
-
# [440, 660].each do |freq|
|
16
|
-
# threads << Thread.new { Win32::Sound.play_freq(freq) }
|
17
|
-
# end
|
18
|
-
# threads.each { |th| th.join }
|
19
|
-
#
|
20
|
-
# the first frequency in this array (440) will wait until the
|
21
|
-
# thread for 660 finished calculating its PCM array and they
|
22
|
-
# will both start streaming at the same time.
|
23
|
-
#
|
24
|
-
def self.play_freq(frequency = 440, duration = 1000, volume = 1, pause_execution = false)
|
25
|
-
|
26
|
-
if frequency > HIGH_FREQUENCY || frequency < LOW_FREQUENCY
|
27
|
-
raise ArgumentError, 'invalid frequency'
|
28
|
-
end
|
29
|
-
|
30
|
-
if duration < 0 || duration > 5000
|
31
|
-
raise ArgumentError, 'invalid duration'
|
32
|
-
end
|
33
|
-
|
34
|
-
stream(pause_execution) { |wfx|
|
35
|
-
data = generate_pcm_integer_array_for_freq(frequency, duration, volume)
|
36
|
-
data_buffer = FFI::MemoryPointer.new(:int, data.size)
|
37
|
-
data_buffer.write_array_of_int data
|
38
|
-
buffer_length = wfx[:nAvgBytesPerSec]*duration/1000
|
39
|
-
WAVEHDR.new(data_buffer, buffer_length)
|
40
|
-
}
|
41
|
-
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
# Sets up a ready-made waveOut stream to push a PCM integer array to.
|
47
|
-
# It expects a block to be associated with the method call to which
|
48
|
-
# it will yield an instance of WAVEFORMATEX that the block uses
|
49
|
-
# to prepare a WAVEHDR to return to the function.
|
50
|
-
# The WAVEHDR can contain either a self-made PCM integer array
|
51
|
-
# or an array from a wav file or some other audio file converted
|
52
|
-
# to PCM.
|
53
|
-
#
|
54
|
-
# This function will take the entire PCM array and create one
|
55
|
-
# giant buffer, so it is not intended for audio streams larger
|
56
|
-
# than 5 seconds.
|
57
|
-
#
|
58
|
-
# In order to play larger audio files, you will have to use the waveOut
|
59
|
-
# functions and structs to set up a double buffer to incrementally
|
60
|
-
# push PCM data to.
|
61
|
-
#
|
62
|
-
def self.stream(pause_execution)
|
63
|
-
|
64
|
-
hWaveOut = HWAVEOUT.new
|
65
|
-
wfx = WAVEFORMATEX.new
|
66
|
-
|
67
|
-
if ((error_code = waveOutOpen(hWaveOut.pointer, WAVE_MAPPER, wfx.pointer, 0, 0, 0)) != 0)
|
68
|
-
raise SystemCallError.new('waveOutOpen', FFI.errno)
|
69
|
-
end
|
70
|
-
|
71
|
-
header = yield(wfx)
|
72
|
-
|
73
|
-
if ((error_code = waveOutPrepareHeader(hWaveOut[:i], header.pointer, header.size)) != 0)
|
74
|
-
raise SystemCallError.new('waveOutPrepareHeader', FFI.errno)
|
75
|
-
end
|
76
|
-
|
77
|
-
if pause_execution
|
78
|
-
Thread.stop
|
79
|
-
Thread.current[:sleep_time] ||= 0
|
80
|
-
sleep Thread.current[:sleep_time]
|
81
|
-
end
|
82
|
-
Thread.pass
|
83
|
-
|
84
|
-
if (waveOutWrite(hWaveOut[:i], header.pointer, header.size) != 0)
|
85
|
-
raise SystemCallError.new('waveOutWrite', FFI.errno)
|
86
|
-
end
|
87
|
-
|
88
|
-
while (waveOutUnprepareHeader(hWaveOut[:i], header.pointer, header.size) == 33)
|
89
|
-
sleep 0.1
|
90
|
-
end
|
91
|
-
|
92
|
-
if ((error_code = waveOutClose(hWaveOut[:i])) != 0)
|
93
|
-
raise SystemCallError.new('waveOutClose', FFI.errno)
|
94
|
-
end
|
95
|
-
|
96
|
-
self
|
97
|
-
end
|
98
|
-
|
99
|
-
# Generates an array of PCM integers to play a particular frequency
|
100
|
-
# It also ramps up and down the volume in the first and last
|
101
|
-
# 200 milliseconds to prevent audio clicking.
|
102
|
-
#
|
103
|
-
def self.generate_pcm_integer_array_for_freq(freq, duration, volume)
|
104
|
-
|
105
|
-
data = []
|
106
|
-
ramp = 200.0
|
107
|
-
samples = (44100/2*duration/1000.0).floor
|
108
|
-
|
109
|
-
samples.times do |sample|
|
110
|
-
|
111
|
-
angle = (2.0*Math::PI*freq) * sample/samples * duration/1000
|
112
|
-
factor = Math.sin(angle)
|
113
|
-
x = 32768.0*factor*volume
|
114
|
-
|
115
|
-
if sample < ramp
|
116
|
-
x *= sample/ramp
|
117
|
-
end
|
118
|
-
if samples - sample < ramp
|
119
|
-
x *= (samples - sample)/ramp
|
120
|
-
end
|
121
|
-
|
122
|
-
data << x.floor
|
123
|
-
end
|
124
|
-
|
125
|
-
data
|
126
|
-
|
127
|
-
end
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
|
2
|
-
module Win32
|
3
|
-
|
4
|
-
# Define an HWAVEOUT struct for use by all the waveOut functions.
|
5
|
-
# It is a handle to a waveOut stream, so starting up multiple
|
6
|
-
# streams using different handles allows for simultaneous playback.
|
7
|
-
# You never need to actually look at the struct, C takes care of
|
8
|
-
# its value.
|
9
|
-
|
10
|
-
class HWAVEOUT < FFI::Struct
|
11
|
-
|
12
|
-
layout :i, :int
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
# define WAVEFORMATEX which defines the format (PCM in this case)
|
17
|
-
# and various properties like sampling rate, number of channels, etc.
|
18
|
-
|
19
|
-
class WAVEFORMATEX < FFI::Struct
|
20
|
-
|
21
|
-
def initialize(nSamplesPerSec = 44100, wBitsPerSample = 16, nChannels = 1, cbSize = 0)
|
22
|
-
self[:wFormatTag] = WAVE_FORMAT_PCM
|
23
|
-
self[:nChannels] = nChannels
|
24
|
-
self[:nSamplesPerSec] = nSamplesPerSec
|
25
|
-
self[:wBitsPerSample] = wBitsPerSample
|
26
|
-
self[:cbSize] = cbSize
|
27
|
-
self[:nBlockAlign] = (self[:wBitsPerSample] >> 3) * self[:nChannels]
|
28
|
-
self[:nAvgBytesPerSec] = self[:nBlockAlign] * self[:nSamplesPerSec]
|
29
|
-
end
|
30
|
-
|
31
|
-
layout :wFormatTag, :ushort,
|
32
|
-
:nChannels, :ushort,
|
33
|
-
:nSamplesPerSec, :ulong,
|
34
|
-
:nAvgBytesPerSec, :ulong,
|
35
|
-
:nBlockAlign, :ushort,
|
36
|
-
:wBitsPerSample, :ushort,
|
37
|
-
:cbSize, :ushort
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
#define WAVEHDR which is a header to a block of audio
|
42
|
-
#lpData is a pointer to the block of native memory that,
|
43
|
-
# in this case, is an integer array of PCM data
|
44
|
-
|
45
|
-
class WAVEHDR < FFI::Struct
|
46
|
-
|
47
|
-
def initialize(lpData, dwBufferLength, dwFlags = 0, dwLoops = 1)
|
48
|
-
self[:lpData] = lpData
|
49
|
-
self[:dwBufferLength] = dwBufferLength
|
50
|
-
# self[:dwBytesRecorded] = dwBytesRecorded # used for waveIn, not waveOut
|
51
|
-
# self[:dwUser] = dwUser
|
52
|
-
self[:dwFlags] = dwFlags
|
53
|
-
self[:dwLoops] = dwLoops
|
54
|
-
end
|
55
|
-
|
56
|
-
layout :lpData, :pointer,
|
57
|
-
:dwBufferLength, :ulong,
|
58
|
-
:dwBytesRecorded, :ulong,
|
59
|
-
:dwUser, :ulong,
|
60
|
-
:dwFlags, :ulong,
|
61
|
-
:dwLoops, :ulong,
|
62
|
-
:lpNext, :pointer,
|
63
|
-
:reserved, :ulong
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|