muse 0.0.4
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.
- data/lib/muse/config/adsr.rb +9 -0
- data/lib/muse/config/chords.rb +30 -0
- data/lib/muse/config.rb +1 -0
- data/lib/muse/version.rb +3 -0
- data/lib/muse/wav.rb +79 -0
- data/lib/muse.rb +149 -0
- metadata +66 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
module Muse
|
2
|
+
module Config
|
3
|
+
class << self
|
4
|
+
|
5
|
+
# major
|
6
|
+
def _CM(options={})
|
7
|
+
add_to_stream chord(%w(c2 e2 g2), options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def _FM(options={})
|
11
|
+
add_to_stream chord(%w(f2 a2 c3), options)
|
12
|
+
end
|
13
|
+
|
14
|
+
# minor
|
15
|
+
def _Dm(options={})
|
16
|
+
add_to_stream chord(%w(d2 f2 a2), options)
|
17
|
+
end
|
18
|
+
|
19
|
+
# dominant 7th
|
20
|
+
def _C7(options={})
|
21
|
+
add_to_stream chord(%w(c2 e2 g2 ais2), options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _BbM(options={})
|
25
|
+
add_to_stream chord(%w(ais2 d3 f3), options)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/muse/config.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Dir["#{File.dirname(__FILE__)}/config/*.rb"].each{|f| require f }
|
data/lib/muse/version.rb
ADDED
data/lib/muse/wav.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
module Muse
|
4
|
+
class RiffChunk < BinData::Record
|
5
|
+
int32be :chunk_id
|
6
|
+
int32le :chunk_size
|
7
|
+
int32be :format
|
8
|
+
end
|
9
|
+
|
10
|
+
class FormatChunk < BinData::Record
|
11
|
+
int32be :chunk_id
|
12
|
+
int32le :chunk_size
|
13
|
+
int16le :audio_format
|
14
|
+
int16le :num_channels
|
15
|
+
int32le :sample_rate
|
16
|
+
int32le :byte_rate
|
17
|
+
int16le :block_align
|
18
|
+
int16le :bits_per_sample
|
19
|
+
end
|
20
|
+
|
21
|
+
class DataChunk < BinData::Record
|
22
|
+
int32be :chunk_id
|
23
|
+
int32le :chunk_size
|
24
|
+
array :stream do
|
25
|
+
int16le :left
|
26
|
+
int16le :right
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class WavFormat < BinData::Record
|
31
|
+
riff_chunk :riff_chunk
|
32
|
+
format_chunk :format_chunk
|
33
|
+
data_chunk :data_chunk
|
34
|
+
end
|
35
|
+
|
36
|
+
class Wav
|
37
|
+
SAMPLE_RATE = 44100
|
38
|
+
attr :wav, :file, :sample_rate, :format_chunk, :riff_chunk, :data_chunk
|
39
|
+
|
40
|
+
def initialize(filename)
|
41
|
+
@sample_rate = SAMPLE_RATE
|
42
|
+
@file = File.open(filename, "wb")
|
43
|
+
|
44
|
+
@riff_chunk = RiffChunk.new
|
45
|
+
@riff_chunk.chunk_id = "RIFF".unpack("N").first
|
46
|
+
@riff_chunk.format = "WAVE".unpack("N").first
|
47
|
+
|
48
|
+
@format_chunk = FormatChunk.new
|
49
|
+
@format_chunk.chunk_id = "fmt ".unpack("N").first
|
50
|
+
@format_chunk.chunk_size = 16
|
51
|
+
@format_chunk.audio_format = 1
|
52
|
+
@format_chunk.num_channels = 2
|
53
|
+
@format_chunk.bits_per_sample = 16
|
54
|
+
@format_chunk.sample_rate = @sample_rate
|
55
|
+
@format_chunk.byte_rate = @format_chunk.sample_rate * @format_chunk.num_channels * @format_chunk.bits_per_sample/2
|
56
|
+
@format_chunk.block_align = @format_chunk.num_channels * @format_chunk.bits_per_sample/2
|
57
|
+
@data_chunk = DataChunk.new
|
58
|
+
@data_chunk.chunk_id = "data".unpack("N").first
|
59
|
+
end
|
60
|
+
|
61
|
+
def write(stream_data)
|
62
|
+
stream_data.each_with_index do |s,i|
|
63
|
+
@data_chunk.stream[i].left = s[0]
|
64
|
+
@data_chunk.stream[i].right = s[1]
|
65
|
+
end
|
66
|
+
@data_chunk.chunk_size = stream_data.length * @format_chunk.num_channels * @format_chunk.bits_per_sample/8
|
67
|
+
@riff_chunk.chunk_size = 36 + @data_chunk.chunk_size
|
68
|
+
@wav = WavFormat.new
|
69
|
+
@wav.riff_chunk = @riff_chunk
|
70
|
+
@wav.format_chunk = @format_chunk
|
71
|
+
@wav.data_chunk = @data_chunk
|
72
|
+
@wav.write(@file)
|
73
|
+
end
|
74
|
+
|
75
|
+
def close
|
76
|
+
@file.close
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/muse.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require "muse/wav"
|
2
|
+
require "muse/config"
|
3
|
+
|
4
|
+
module Muse
|
5
|
+
class Song
|
6
|
+
attr :wav_file, :bars
|
7
|
+
|
8
|
+
def self.record(name, &block)
|
9
|
+
puts "Start recording song named #{name}.wav"
|
10
|
+
@wav_file = Wav.new(name + ".wav")
|
11
|
+
@bars = {}
|
12
|
+
puts "Processing ..."
|
13
|
+
instance_eval &block
|
14
|
+
save
|
15
|
+
puts "done."
|
16
|
+
end
|
17
|
+
|
18
|
+
class Bar
|
19
|
+
attr :bpm, :beats, :adsr
|
20
|
+
attr_accessor :stream
|
21
|
+
|
22
|
+
NOTES = %w(_ a ais b c cis d dis e f fis g gis)
|
23
|
+
FREQUENCIES = {
|
24
|
+
:a2 => -24, :ais2 => -23, :b2 => -22, :c3 => -21, :cis3 => -20,
|
25
|
+
:d3 => -19, :dis3 => -18, :e3 => -17, :f3 => -16, :fis3 => -15,
|
26
|
+
:g3 => -14, :gis3 => -13, :a3 => -12, :ais3 => -11, :b3 => -10,
|
27
|
+
:c4 => -9, :cis4 => -8, :d4 => -7, :dis4 => -6, :e4 => -5,
|
28
|
+
:f4 => -4, :fis4 => -3, :g4 => -2, :gis4 => -1, :a4 => 0,
|
29
|
+
:ais4 => 1, :b4 => 2, :c5 => 3, :cis5 => 4, :d5 => 5,
|
30
|
+
:dis5 => 6, :e5 => 7, :f5 => 8, :fis5 => 9, :g5 => 10,
|
31
|
+
:gis5 => 11, :a5 => 12, :ais5 => 13, :b5 => 14, :c6 => 15,
|
32
|
+
:cis6 => 16, :d6 => 17, :dis6 => 18, :e6 => 19, :f6 => 20,
|
33
|
+
:fis6 => 21, :g6 => 22, :gis6 => 23
|
34
|
+
}
|
35
|
+
|
36
|
+
def initialize(id, options={})
|
37
|
+
@bpm = options[:bpm] || 120
|
38
|
+
@beats = (options[:b] || 1).to_f
|
39
|
+
@adsr = options[:adsr] || 'default'
|
40
|
+
@stream = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def notes(&block)
|
44
|
+
instance_eval &block
|
45
|
+
end
|
46
|
+
|
47
|
+
def frequency_of(step)
|
48
|
+
440.0*(2**(step.to_f/12.0))
|
49
|
+
end
|
50
|
+
|
51
|
+
def chord(notes,options={})
|
52
|
+
puts "chord with #{notes}"
|
53
|
+
triad =[]
|
54
|
+
notes.each do |name|
|
55
|
+
if name.start_with? *NOTES
|
56
|
+
octave = name[name.length-1].to_i
|
57
|
+
note = octave > 0 ? name.chop : name
|
58
|
+
octave = 3 if octave == 0
|
59
|
+
triad << note_data(note, octave, options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
triad.transpose.map {|x| x.transpose.map {|y| y.reduce(:+)}}
|
63
|
+
end
|
64
|
+
|
65
|
+
def note_data(note, octave=3, options={})
|
66
|
+
stream = []
|
67
|
+
if options
|
68
|
+
beats = options[:b].nil? ? (@beats || 1) : options[:b].to_f
|
69
|
+
volume = (options[:v].nil? ? 10 : options[:v].to_i) * 1000
|
70
|
+
adsr = options[:a].nil? ? @adsr : 'default'
|
71
|
+
else
|
72
|
+
beats, volume, adsr = (@beats || 1), 10000, 'default'
|
73
|
+
end
|
74
|
+
puts "[#{note}] -> beats : #{beats}, :octave : #{octave}"
|
75
|
+
duration = ((60 * Wav::SAMPLE_RATE * beats)/@bpm)/Wav::SAMPLE_RATE.to_f
|
76
|
+
note_frequency = note + octave.to_s
|
77
|
+
unless note == '_'
|
78
|
+
freq = frequency_of(FREQUENCIES[note_frequency.to_sym])
|
79
|
+
else
|
80
|
+
freq = 0
|
81
|
+
end
|
82
|
+
(0.0..duration.to_f).step(1.0/Wav::SAMPLE_RATE) do |i|
|
83
|
+
x = (Config.send(adsr.to_sym,i) * volume * Math.sin(2 * Math::PI * freq * i)).to_i
|
84
|
+
stream << [x,x]
|
85
|
+
end
|
86
|
+
return stream
|
87
|
+
end
|
88
|
+
|
89
|
+
def truncate_stream_by(num)
|
90
|
+
num.times {@stream.pop}
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_to_stream(str)
|
94
|
+
@stream += str
|
95
|
+
end
|
96
|
+
|
97
|
+
def method_missing(name, *args, &block)
|
98
|
+
name = name.to_s
|
99
|
+
if name.start_with? *NOTES
|
100
|
+
if name.split('_').length > 1
|
101
|
+
notes = name.split('_')
|
102
|
+
add_to_stream chord(notes, args[0])
|
103
|
+
else
|
104
|
+
octave = name[name.length-1].to_i
|
105
|
+
note = octave > 0 ? name.chop : name
|
106
|
+
octave = 3 if octave == 0
|
107
|
+
add_to_stream note_data(note, octave, args[0])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
class << self
|
115
|
+
def bar(id, options={})
|
116
|
+
puts "bar #{id}"
|
117
|
+
unless @bars[id]
|
118
|
+
@bars[id] = []
|
119
|
+
end
|
120
|
+
@bars[id] << Bar.new(id, options)
|
121
|
+
@bars[id].last
|
122
|
+
end
|
123
|
+
|
124
|
+
def right_size(bars)
|
125
|
+
container = []
|
126
|
+
min_bar = bars.min_by {|x| x.stream.length}
|
127
|
+
bars.map do |bar|
|
128
|
+
bar.truncate_stream_by(bar.stream.length - min_bar.stream.length)
|
129
|
+
bar
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def save
|
134
|
+
puts "Saving music file"
|
135
|
+
stream = []
|
136
|
+
@bars.each do |id, item|
|
137
|
+
container = []
|
138
|
+
item = right_size item
|
139
|
+
item.each do |i|
|
140
|
+
container << i.stream
|
141
|
+
end
|
142
|
+
stream += container.transpose.map {|x| x.transpose.map {|y| y.reduce(:+)}}
|
143
|
+
end
|
144
|
+
@wav_file.write(stream)
|
145
|
+
@wav_file.close
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: muse
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Chang Sau Sheong
|
9
|
+
- Ng Tze Yang
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-03-11 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: bindata
|
17
|
+
requirement: &2168647260 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2168647260
|
26
|
+
description: Muse is a complete music creation package including writing and up to
|
27
|
+
creating WAV files
|
28
|
+
email:
|
29
|
+
- sausheong@gmail.com
|
30
|
+
- ngty77@gmail.com
|
31
|
+
executables: []
|
32
|
+
extensions: []
|
33
|
+
extra_rdoc_files: []
|
34
|
+
files:
|
35
|
+
- lib/muse/config/adsr.rb
|
36
|
+
- lib/muse/config/chords.rb
|
37
|
+
- lib/muse/config.rb
|
38
|
+
- lib/muse/version.rb
|
39
|
+
- lib/muse/wav.rb
|
40
|
+
- lib/muse.rb
|
41
|
+
homepage: https://github.com/sausheong/muse
|
42
|
+
licenses: []
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
requirements: []
|
61
|
+
rubyforge_project:
|
62
|
+
rubygems_version: 1.8.17
|
63
|
+
signing_key:
|
64
|
+
specification_version: 3
|
65
|
+
summary: Muse is a Ruby DSL for making music
|
66
|
+
test_files: []
|