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