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.
@@ -0,0 +1,9 @@
1
+ module Muse
2
+ module Config
3
+ class << self
4
+ def default(input)
5
+ Math.cos(2* Math::PI*input).to_f
6
+ end
7
+ end
8
+ end
9
+ end
@@ -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
@@ -0,0 +1 @@
1
+ Dir["#{File.dirname(__FILE__)}/config/*.rb"].each{|f| require f }
@@ -0,0 +1,3 @@
1
+ module Muse
2
+ VERSION = "0.0.4"
3
+ end
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: []