feep 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +60 -0
- data/README.md +38 -0
- data/Rakefile +3 -0
- data/bin/feep +73 -0
- data/feep.gemspec +29 -0
- data/lib/feep.rb +213 -0
- data/lib/feep/constants.rb +186 -0
- data/lib/feep/version.rb +3 -0
- data/spec/feep_spec.rb +21 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/rspec.rake +3 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e59eb850a3b66b00df0a1f7e42523e7f1d7f6974
|
4
|
+
data.tar.gz: e032678907f285574b847199f530cd66a8eaa734
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69e83b10a38ed0b4a7a3378fe874fc5054b57a4bb8ec38ec02eec736c4fb8a36e3958d2e6973a092f141e32b9300857b7e92f402a3c085a37f2308402c9f5de2
|
7
|
+
data.tar.gz: 545f7debf3dc9801a85a27fdba7f2531d1a682b78a22cf681add3674eda27a80dd3207dabc3c978c4fa4cf716d44e2284ca4cfecde8dd0b72c3ede03c125bcda
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
feep (0.0.1)
|
5
|
+
os (~> 0.9, >= 0.9.6)
|
6
|
+
wavefile (= 0.6.0)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
byebug (3.5.1)
|
12
|
+
columnize (~> 0.8)
|
13
|
+
debugger-linecache (~> 1.2)
|
14
|
+
slop (~> 3.6)
|
15
|
+
coderay (1.1.0)
|
16
|
+
columnize (0.9.0)
|
17
|
+
debugger-linecache (1.2.0)
|
18
|
+
diff-lcs (1.2.5)
|
19
|
+
method_source (0.8.2)
|
20
|
+
os (0.9.6)
|
21
|
+
pry (0.10.1)
|
22
|
+
coderay (~> 1.1.0)
|
23
|
+
method_source (~> 0.8.1)
|
24
|
+
slop (~> 3.4)
|
25
|
+
pry (0.10.1-x64-mingw32)
|
26
|
+
coderay (~> 1.1.0)
|
27
|
+
method_source (~> 0.8.1)
|
28
|
+
slop (~> 3.4)
|
29
|
+
win32console (~> 1.3)
|
30
|
+
pry-byebug (3.0.1)
|
31
|
+
byebug (~> 3.4)
|
32
|
+
pry (~> 0.10)
|
33
|
+
rake (10.4.2)
|
34
|
+
rspec (3.2.0)
|
35
|
+
rspec-core (~> 3.2.0)
|
36
|
+
rspec-expectations (~> 3.2.0)
|
37
|
+
rspec-mocks (~> 3.2.0)
|
38
|
+
rspec-core (3.2.1)
|
39
|
+
rspec-support (~> 3.2.0)
|
40
|
+
rspec-expectations (3.2.0)
|
41
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
42
|
+
rspec-support (~> 3.2.0)
|
43
|
+
rspec-mocks (3.2.1)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.2.0)
|
46
|
+
rspec-support (3.2.2)
|
47
|
+
slop (3.6.0)
|
48
|
+
wavefile (0.6.0)
|
49
|
+
win32console (1.3.2)
|
50
|
+
|
51
|
+
PLATFORMS
|
52
|
+
ruby
|
53
|
+
x64-mingw32
|
54
|
+
|
55
|
+
DEPENDENCIES
|
56
|
+
bundler (~> 1.8)
|
57
|
+
feep!
|
58
|
+
pry-byebug (~> 3.0)
|
59
|
+
rake (~> 10.0)
|
60
|
+
rspec (>= 3.0)
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# Feep
|
2
|
+
|
3
|
+
## Wha?
|
4
|
+
Use the power of Ruby gems to make your computer [feep](http://dictionary.reference.com/browse/feep) (except more musically) using sweet [WAV-file-writing technology](http://wavefilegem.com) from [Joel Strait](https://github.com/jstrait). Works on both Windows and *nix (including OS X) as it uses the standard WAV format to do its bidding.
|
5
|
+
|
6
|
+
_Note:_ In order for the sound-playing magic to work on Windows, you will need [sounder](http://www.elifulkerson.com/projects/commandline-wav-player.php), a free command-line WAV file player on your system and in your path. Mac and *nix uses `afplay`, which should be built-in, but feel free to change either to something you already have or desire to install.
|
7
|
+
|
8
|
+
## Why?
|
9
|
+
|
10
|
+
Besides a quick way to make some kind of noise, which I always appreciate, this could be used to tie into another Ruby script for an alert tone or maybe even some wicked cool command-line game that needs musical note sound effects. The opportunities are essentially endless.
|
11
|
+
|
12
|
+
## How?
|
13
|
+
|
14
|
+
For now:
|
15
|
+
1. `git clone git@github.com:michaelchadwick/feep`
|
16
|
+
2. `cd feep`
|
17
|
+
3. `gem build feep.gemspec`
|
18
|
+
4. `gem install feep-0.0.1.gem`
|
19
|
+
5. `bundle install`
|
20
|
+
6. `feep`
|
21
|
+
|
22
|
+
Feep doesn't require any parameters, as it will play a 440Hz/A4 sine wave at 50% full volume for 1000 milliseconds unless you supply one of the below options. Feep will only save the resulting WAV file it creates if you specify the `-save` parameter.
|
23
|
+
|
24
|
+
The full usage looks like this:
|
25
|
+
|
26
|
+
`feep [-fn frequency|note_name|comma-delimited_frequencies_or_note_names] [-w waveform] [-v volume] [-d duration] [-save] [-loud]`
|
27
|
+
|
28
|
+
`-frequency|note_name|commad-delimted_frequences_or_note_names`: a number from 0 to 20000. You can try something bigger or smaller, but you may get odd results. You may also enter any note name from C0 to B9 (or even flats and sharps like C#6 or Eb5). You may also also enter some combination of these with commas between them and it'll play all of them together in a chord.
|
29
|
+
|
30
|
+
`-waveform`: a string equal to "sine", "square", "saw", "triangle", or "noise".
|
31
|
+
|
32
|
+
`-volume`: a number from 0.0 (silence (why would you do this?)) to 1.0 (blast it)
|
33
|
+
|
34
|
+
`-duration`: number of milliseconds for the sound to last
|
35
|
+
|
36
|
+
`-save`: save the resulting WAV file in the current directory. Will create it in the format of `waveform_frequency-in-Hz_volume_duration.wav`
|
37
|
+
|
38
|
+
`-loud`: displays note and file-making information
|
data/Rakefile
ADDED
data/bin/feep
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'feep'
|
5
|
+
|
6
|
+
USAGE_INSTRUCTIONS = ''
|
7
|
+
|
8
|
+
def parse_options
|
9
|
+
options = {:freq_or_note => '440.000', :waveform => 'sine', :volume => 0.5, :duration => 1000, :save => false, :loud => false}
|
10
|
+
|
11
|
+
optparse = OptionParser.new do |opts|
|
12
|
+
opts.banner = 'usage: feep [frequency|note_name|list_of_frequencies_or_note_names] [waveform] [volume] [duration] [save] [-loud]'
|
13
|
+
|
14
|
+
opts.on('-f', '-n', '--freq-or-note FREQUENCY|NOTE_NAME', 'One or more frequencies or note names to play at once, e.g. 440 or A4 or 220,440,880') do |f_or_n|
|
15
|
+
options[:freq_or_note] = f_or_n
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on('-w', '--wave WAVE_FORM_NAME', 'Waveform type to use for the sound') do |w|
|
19
|
+
options[:waveform] = w
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on('-a', '--amplitude MAX_AMPLITUDE', 'Amplitude/volume (0.0 - 1.0) to play the sound(s) at') do |a|
|
23
|
+
options[:volume] = a.to_f
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-d', '--duration DURATION', 'Duration in ms to play the sound(s)') do |d|
|
27
|
+
options[:duration] = d.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-s', '--save', 'Save the resulting WAV file in the current directory') do
|
31
|
+
options[:save] = true
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on('-l', '--loud', 'Displays information about note(s) being played') do
|
35
|
+
options[:loud] = true
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-v', '--version', 'Display version number and exit') do
|
39
|
+
puts "#{$0} #{Feep::VERSION}"
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on( '-h', '--help', 'Display this screen and exit') do
|
44
|
+
puts opts
|
45
|
+
exit
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
USAGE_INSTRUCTIONS << optparse.to_s
|
50
|
+
optparse.parse!()
|
51
|
+
|
52
|
+
return options
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_error(error)
|
56
|
+
case error
|
57
|
+
when OptionParser::InvalidOption
|
58
|
+
puts "feep: illegal option #{error.args.join(' ')}"
|
59
|
+
puts USAGE_INSTRUCTIONS
|
60
|
+
else
|
61
|
+
puts 'An unexpected error occurred while running Feep:'
|
62
|
+
puts " #{error}\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
begin
|
67
|
+
options = parse_options()
|
68
|
+
|
69
|
+
Feep.new(options)
|
70
|
+
rescue => error
|
71
|
+
print_error(error)
|
72
|
+
exit(false)
|
73
|
+
end
|
data/feep.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "feep/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'feep'
|
8
|
+
spec.version = Feep::VERSION
|
9
|
+
spec.platform = Gem::Platform::RUBY
|
10
|
+
spec.authors = ["Michael Chadwick"]
|
11
|
+
spec.email = 'mike@codana.me'
|
12
|
+
spec.homepage = "http://rubygemspec.org/gems/feep"
|
13
|
+
spec.summary = %q{Make your computer feep with Ruby}
|
14
|
+
spec.description = %q{Use Ruby to make your computer beep at a certain frequency for a certain duration. Do it for fun, or add it to other programs for easy alert sounds.}
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split("\n")
|
17
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.license = 'MIT'
|
21
|
+
|
22
|
+
spec.add_runtime_dependency 'wavefile', '= 0.6.0'
|
23
|
+
spec.add_runtime_dependency 'os', '~> 0.9', '>= 0.9.6'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.0'
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.8'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
28
|
+
spec.add_development_dependency 'rspec', '>= 3.0'
|
29
|
+
end
|
data/lib/feep.rb
ADDED
@@ -0,0 +1,213 @@
|
|
1
|
+
require 'wavefile'
|
2
|
+
require 'os'
|
3
|
+
require 'feep/constants'
|
4
|
+
|
5
|
+
class Feep
|
6
|
+
|
7
|
+
# main entry point
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
configure_sound(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# convert midi notes to frequencies
|
14
|
+
def midi_to_freq(midi_note)
|
15
|
+
return 440.0 * (2.0 ** ((midi_note.to_f-69)/12))
|
16
|
+
end
|
17
|
+
|
18
|
+
# convert frequencies to midi notes
|
19
|
+
def freq_to_midi(freq)
|
20
|
+
return (69 + 12 * (Math.log2(freq.to_i.abs / 440.0))).round
|
21
|
+
end
|
22
|
+
|
23
|
+
# makes sure that whatever kind of sound was entered on the CLI
|
24
|
+
# it is now a frequency to feed into the sample data generator
|
25
|
+
def convert_note_to_freq(freq_or_note)
|
26
|
+
if freq_or_note.match(/[A-Za-z]/)
|
27
|
+
if NOTE_FREQ.has_key?(freq_or_note)
|
28
|
+
frequency = NOTE_FREQ[freq_or_note]
|
29
|
+
else
|
30
|
+
app_error(ERROR_MSG[:note_name])
|
31
|
+
end
|
32
|
+
else
|
33
|
+
frequency = freq_or_note
|
34
|
+
end
|
35
|
+
|
36
|
+
return frequency
|
37
|
+
end
|
38
|
+
|
39
|
+
# takes CLI options, massages them, and passes them to the
|
40
|
+
# sound generation methods
|
41
|
+
def configure_sound(options)
|
42
|
+
### A. Check non-essential options
|
43
|
+
if !WAVE_TYPES.include?(options[:waveform])
|
44
|
+
app_error(ERROR_MSG[:wave_form])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convert ms to secs in order to multiply the sample rate by
|
48
|
+
duration_s = (options[:duration].to_f / 1000)
|
49
|
+
|
50
|
+
# Make the samples to write a nice integer
|
51
|
+
samples_to_write = (SAMPLE_RATE * duration_s).to_i
|
52
|
+
|
53
|
+
### B. Set frequency/note, or group of frequencies/notes, to play
|
54
|
+
|
55
|
+
# Is it a chord or a note?
|
56
|
+
if options[:freq_or_note].include?(',')
|
57
|
+
# yes, it's a chord, so create threads
|
58
|
+
threads = []
|
59
|
+
options[:freq_or_note].split(',').each do |note|
|
60
|
+
sound_to_play = convert_note_to_freq(note)
|
61
|
+
output_filename = "#{options[:waveform]}_#{sound_to_play}Hz_#{options[:volume].to_f}_#{options[:duration].to_s}.wav"
|
62
|
+
threads << Thread.new {
|
63
|
+
play_note(sound_to_play.to_f, options[:waveform], options[:volume].to_f, options[:duration].to_i, samples_to_write, output_filename)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
threads.each { |th| th.join }
|
67
|
+
else
|
68
|
+
# no, it's a single note
|
69
|
+
sound_to_play = convert_note_to_freq(options[:freq_or_note])
|
70
|
+
output_filename = "#{options[:waveform]}_#{sound_to_play}Hz_#{options[:volume].to_f}_#{options[:duration].to_s}.wav"
|
71
|
+
play_note(sound_to_play, options[:waveform], options[:volume].to_f, options[:duration].to_i, samples_to_write, output_filename)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# plays note using system wav file player
|
76
|
+
def play_sound(file, duration)
|
77
|
+
delimiter = OS.windows? ? ';' : ':'
|
78
|
+
|
79
|
+
system_apps = ENV['PATH'].split(delimiter).collect {|d| Dir.entries d if Dir.exists? d}.flatten
|
80
|
+
|
81
|
+
if OS.windows?
|
82
|
+
if system_apps.include? SNDPLAYER_WIN
|
83
|
+
display_text_beep(duration)
|
84
|
+
system("#{SNDPLAYER_WIN} #{file}")
|
85
|
+
else
|
86
|
+
puts "couldn't find #{SNDPLAYER_WIN}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if OS.mac? || OS.linux?
|
91
|
+
if system_apps.include? SNDPLAYER_UNIX
|
92
|
+
display_text_beep(duration)
|
93
|
+
system("#{SNDPLAYER_UNIX} #{file}")
|
94
|
+
else
|
95
|
+
puts "couldn't find #{SNDPLAYER_UNIX}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# displays a fun beep message
|
101
|
+
def display_text_beep(duration)
|
102
|
+
print 'Be'
|
103
|
+
1.upto(duration) {|ms|
|
104
|
+
if ms % 100 == 0
|
105
|
+
print 'e'
|
106
|
+
end
|
107
|
+
}
|
108
|
+
puts 'ep!'
|
109
|
+
end
|
110
|
+
|
111
|
+
# removes the sound, unless marked to save
|
112
|
+
def remove_sound(file)
|
113
|
+
if !@options[:save]
|
114
|
+
if OS.windows?
|
115
|
+
system("del #{file}")
|
116
|
+
else
|
117
|
+
system("rm #{file}")
|
118
|
+
end
|
119
|
+
else
|
120
|
+
if @options[:loud]
|
121
|
+
info = WaveFile::Reader.info(file)
|
122
|
+
duration = info.duration
|
123
|
+
formatted_duration = duration.minutes.to_s.rjust(2, '0') << ':' <<
|
124
|
+
duration.seconds.to_s.rjust(2, '0') << ':' <<
|
125
|
+
duration.milliseconds.to_s.rjust(3, '0')
|
126
|
+
puts ""
|
127
|
+
puts "Created #{file}"
|
128
|
+
puts "---"
|
129
|
+
puts "Length: #{formatted_duration}"
|
130
|
+
puts "Format: #{info.audio_format}"
|
131
|
+
puts "Channels: #{info.channels}"
|
132
|
+
puts "Frames: #{info.sample_frame_count}"
|
133
|
+
puts "Sample Rate: #{info.sample_rate}"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# code from Joel Strait's nanosynth to generate raw audio
|
139
|
+
def create_sound(frequency, waveform, samples, volume, output_filename)
|
140
|
+
# Generate sample data for the given frequency, amplitude, and duration.
|
141
|
+
# Since we are using a sample rate of 44,100Hz, 44,100 samples are required for one second of sound.
|
142
|
+
samples = generate_sample_data(waveform.to_sym, samples, frequency.to_f, volume.to_f)
|
143
|
+
|
144
|
+
# Wrap the array of samples in a Buffer, so that it can be written to a Wave file
|
145
|
+
# by the WaveFile gem. Since we generated samples between -1.0 and 1.0, the sample
|
146
|
+
# type should be :float
|
147
|
+
buffer = WaveFile::Buffer.new(samples, WaveFile::Format.new(:mono, :float, 44100))
|
148
|
+
|
149
|
+
# Write the Buffer containing our samples to a 16-bit, monophonic Wave file
|
150
|
+
# with a sample rate of 44,100Hz, using the WaveFile gem.
|
151
|
+
WaveFile::Writer.new(output_filename, WaveFile::Format.new(:mono, :pcm_16, 44100)) do |writer|
|
152
|
+
writer.write(buffer)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# more code from Joel Strait's nanosynth (http://joe)
|
157
|
+
# The dark heart of NanoSynth, the part that actually generates the audio data
|
158
|
+
def generate_sample_data(wave_type, num_samples, frequency, max_amplitude)
|
159
|
+
position_in_period = 0.0
|
160
|
+
position_in_period_delta = frequency / SAMPLE_RATE
|
161
|
+
|
162
|
+
# Initialize an array of samples set to 0.0. Each sample will be replaced with
|
163
|
+
# an actual value below.
|
164
|
+
samples = [].fill(0.0, 0, num_samples)
|
165
|
+
|
166
|
+
num_samples.times do |i|
|
167
|
+
# Add next sample to sample list. The sample value is determined by
|
168
|
+
# plugging position_in_period into the appropriate wave function.
|
169
|
+
if wave_type == :sine
|
170
|
+
samples[i] = Math::sin(position_in_period * TWO_PI) * max_amplitude
|
171
|
+
elsif wave_type == :square
|
172
|
+
samples[i] = (position_in_period >= 0.5) ? max_amplitude : -max_amplitude
|
173
|
+
elsif wave_type == :saw
|
174
|
+
samples[i] = ((position_in_period * 2.0) - 1.0) * max_amplitude
|
175
|
+
elsif wave_type == :triangle
|
176
|
+
samples[i] = max_amplitude - (((position_in_period * 2.0) - 1.0) * max_amplitude * 2.0).abs
|
177
|
+
elsif wave_type == :noise
|
178
|
+
samples[i] = RANDOM_GENERATOR.rand(-max_amplitude..max_amplitude)
|
179
|
+
end
|
180
|
+
|
181
|
+
position_in_period += position_in_period_delta
|
182
|
+
|
183
|
+
# Constrain the period between 0.0 and 1.0.
|
184
|
+
# That is, keep looping and re-looping over the same period.
|
185
|
+
if(position_in_period >= 1.0)
|
186
|
+
position_in_period -= 1.0
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
return samples
|
191
|
+
end
|
192
|
+
|
193
|
+
# creates, plays, and removes note
|
194
|
+
def play_note(frequency, waveform, volume, duration, samples_to_write, output_filename)
|
195
|
+
if @options[:loud]
|
196
|
+
puts 'Playing note'
|
197
|
+
puts " frequency: #{frequency.to_f.abs}"
|
198
|
+
puts " midi: #{freq_to_midi(frequency)}"
|
199
|
+
puts " duration: #{duration}"
|
200
|
+
end
|
201
|
+
create_sound(frequency, waveform, samples_to_write, volume, output_filename)
|
202
|
+
play_sound(output_filename, duration)
|
203
|
+
remove_sound(output_filename)
|
204
|
+
end
|
205
|
+
|
206
|
+
# displays error, usage, and exits
|
207
|
+
def app_error(msg)
|
208
|
+
puts "#{File.basename($0).split(".")[0]}: #{msg}"
|
209
|
+
puts 'usage: feep [frequency|note_name|list_of_frequencies_or_note_names] [sine|square|saw|triangle|noise] [volume] [duration] [save]'
|
210
|
+
exit
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
class Feep
|
2
|
+
SNDPLAYER_WIN = 'sounder.exe'
|
3
|
+
SNDPLAYER_UNIX = 'afplay'
|
4
|
+
SAMPLE_RATE = 44100
|
5
|
+
TWO_PI = 2 * Math::PI
|
6
|
+
RANDOM_GENERATOR = Random.new
|
7
|
+
WAVE_TYPES = %w[sine square saw triangle noise]
|
8
|
+
|
9
|
+
NOTE_FREQ = Hash[
|
10
|
+
'C0' => 16.351,
|
11
|
+
'C#0' => 17.324,
|
12
|
+
'Db0' => 17.324,
|
13
|
+
'D0' => 18.354,
|
14
|
+
'D#0' => 19.445,
|
15
|
+
'Eb0' => 19.445,
|
16
|
+
'E0' => 20.601,
|
17
|
+
'F0' => 21.827,
|
18
|
+
'F#0' => 23.124,
|
19
|
+
'Gb0' => 23.124,
|
20
|
+
'G0' => 24.499,
|
21
|
+
'G#0' => 25.956,
|
22
|
+
'Ab0' => 25.956,
|
23
|
+
'A0' => 27.500,
|
24
|
+
'A#0' => 29.135,
|
25
|
+
'Bb0' => 29.135,
|
26
|
+
'B0' => 30.868,
|
27
|
+
'C1' => 32.703,
|
28
|
+
'C#1' => 34.648,
|
29
|
+
'Db1' => 34.648,
|
30
|
+
'D1' => 36.708,
|
31
|
+
'D#1' => 38.891,
|
32
|
+
'Eb1' => 38.891,
|
33
|
+
'E1' => 41.203,
|
34
|
+
'F1' => 43.654,
|
35
|
+
'F#1' => 46.249,
|
36
|
+
'Gb1' => 46.249,
|
37
|
+
'G1' => 48.999,
|
38
|
+
'G#1' => 51.913,
|
39
|
+
'Ab1' => 51.913,
|
40
|
+
'A1' => 55.000,
|
41
|
+
'A#1' => 58.270,
|
42
|
+
'Bb1' => 58.270,
|
43
|
+
'B1' => 61.375,
|
44
|
+
'C2' => 65.406,
|
45
|
+
'C#2' => 69.296,
|
46
|
+
'Db2' => 69.296,
|
47
|
+
'D2' => 73.416,
|
48
|
+
'D#2' => 77.782,
|
49
|
+
'Eb2' => 77.782,
|
50
|
+
'E2' => 82.407,
|
51
|
+
'F2' => 87.307,
|
52
|
+
'F#2' => 92.499,
|
53
|
+
'Gb2' => 92.499,
|
54
|
+
'G2' => 97.999,
|
55
|
+
'G#2' => 97.999,
|
56
|
+
'Ab2' => 103.826,
|
57
|
+
'A2' => 110.000,
|
58
|
+
'A#2' => 116.541,
|
59
|
+
'Bb2' => 116.541,
|
60
|
+
'B2' => 123.471,
|
61
|
+
'C3' => 130.813,
|
62
|
+
'C#3' => 138.591,
|
63
|
+
'Db3' => 138.591,
|
64
|
+
'D3' => 146.832,
|
65
|
+
'D#3' => 155.564,
|
66
|
+
'Eb3' => 155.564,
|
67
|
+
'E3' => 164.814,
|
68
|
+
'F3' => 174.614,
|
69
|
+
'F#3' => 184.997,
|
70
|
+
'Gb3' => 184.997,
|
71
|
+
'G3' => 195.998,
|
72
|
+
'G#3' => 207.652,
|
73
|
+
'Ab3' => 207.652,
|
74
|
+
'A3' => 220.000,
|
75
|
+
'A#3' => 233.082,
|
76
|
+
'Bb3' => 233.082,
|
77
|
+
'B3' => 246.942,
|
78
|
+
'C4' => 261.626,
|
79
|
+
'C#4' => 277.183,
|
80
|
+
'Db4' => 277.183,
|
81
|
+
'D4' => 293.665,
|
82
|
+
'D#4' => 311.127,
|
83
|
+
'Eb4' => 311.127,
|
84
|
+
'E4' => 329.628,
|
85
|
+
'F4' => 349.228,
|
86
|
+
'F#4' => 369.994,
|
87
|
+
'Gb4' => 369.994,
|
88
|
+
'G4' => 391.995,
|
89
|
+
'G#4' => 415.305,
|
90
|
+
'Ab4' => 415.305,
|
91
|
+
'A4' => 440.000,
|
92
|
+
'A#4' => 466.164,
|
93
|
+
'Bb4' => 466.164,
|
94
|
+
'B4' => 493.883,
|
95
|
+
'C5' => 523.251,
|
96
|
+
'C#5' => 554.365,
|
97
|
+
'Db5' => 554.365,
|
98
|
+
'D5' => 587.330,
|
99
|
+
'D#5' => 622.254,
|
100
|
+
'Eb5' => 622.254,
|
101
|
+
'E5' => 659.255,
|
102
|
+
'F5' => 698.457,
|
103
|
+
'F#5' => 739.989,
|
104
|
+
'Gb5' => 739.989,
|
105
|
+
'G5' => 783.991,
|
106
|
+
'G#5' => 830.609,
|
107
|
+
'Ab5' => 830.609,
|
108
|
+
'A5' => 880.000,
|
109
|
+
'A#5' => 932.328,
|
110
|
+
'Bb5' => 932.328,
|
111
|
+
'B5' => 987.767,
|
112
|
+
'C6' => 1046.502,
|
113
|
+
'C#6' => 1108.731,
|
114
|
+
'Db6' => 1108.731,
|
115
|
+
'D6' => 1174.659,
|
116
|
+
'D#6' => 1244.508,
|
117
|
+
'Eb6' => 1244.508,
|
118
|
+
'E6' => 1318.510,
|
119
|
+
'F6' => 1396.913,
|
120
|
+
'F#6' => 1479.978,
|
121
|
+
'Gb6' => 1479.978,
|
122
|
+
'G6' => 1567.982,
|
123
|
+
'G#6' => 1661.219,
|
124
|
+
'Ab6' => 1661.219,
|
125
|
+
'A6' => 1760.000,
|
126
|
+
'A#6' => 1864.655,
|
127
|
+
'Bb6' => 1864.655,
|
128
|
+
'B6' => 1975.533,
|
129
|
+
'C7' => 2093.005,
|
130
|
+
'C#7' => 2217.461,
|
131
|
+
'Db7' => 2217.461,
|
132
|
+
'D7' => 2349.318,
|
133
|
+
'D#7' => 2489.016,
|
134
|
+
'Eb7' => 2489.016,
|
135
|
+
'E7' => 2637.021,
|
136
|
+
'F7' => 2793.826,
|
137
|
+
'F#7' => 2959.956,
|
138
|
+
'Gb7' => 2959.956,
|
139
|
+
'G7' => 3135.964,
|
140
|
+
'G#7' => 3322.438,
|
141
|
+
'Ab7' => 3322.438,
|
142
|
+
'A7' => 3520.000,
|
143
|
+
'A#7' => 3729.310,
|
144
|
+
'Bb7' => 3729.310,
|
145
|
+
'B7' => 3951.066,
|
146
|
+
'C8' => 4186.009,
|
147
|
+
'C#8' => 4434.922,
|
148
|
+
'Db8' => 4434.922,
|
149
|
+
'D8' => 4698.636,
|
150
|
+
'D#8' => 4978.032,
|
151
|
+
'Eb8' => 4978.032,
|
152
|
+
'E8' => 5274.042,
|
153
|
+
'F8' => 5587.652,
|
154
|
+
'F#8' => 5919.910,
|
155
|
+
'Gb8' => 5919.910,
|
156
|
+
'G8' => 6271.928,
|
157
|
+
'G#8' => 6644.876,
|
158
|
+
'Ab8' => 6644.876,
|
159
|
+
'A8' => 7040.000,
|
160
|
+
'A#8' => 7458.620,
|
161
|
+
'Bb8' => 7458.620,
|
162
|
+
'B8' => 7902.132,
|
163
|
+
'C9' => 8372.018,
|
164
|
+
'C#9' => 8869.844,
|
165
|
+
'Db9' => 8869.844,
|
166
|
+
'D9' => 9397.272,
|
167
|
+
'D#9' => 9956.064,
|
168
|
+
'Eb9' => 9956.064,
|
169
|
+
'E9' => 10548.084,
|
170
|
+
'F9' => 11175.304,
|
171
|
+
'F#9' => 11839.820,
|
172
|
+
'Gb9' => 11839.820,
|
173
|
+
'G9' => 12543.856,
|
174
|
+
'G#9' => 13289.752,
|
175
|
+
'Ab9' => 13289.752,
|
176
|
+
'A9' => 14080.000,
|
177
|
+
'A#9' => 14917.240,
|
178
|
+
'Bb9' => 14917.240,
|
179
|
+
'B9' => 15804.264
|
180
|
+
]
|
181
|
+
|
182
|
+
ERROR_MSG = Hash[
|
183
|
+
:note_name => 'Note name argument is invalid.',
|
184
|
+
:wave_form => 'Wave form type is invalid.'
|
185
|
+
]
|
186
|
+
end
|
data/lib/feep/version.rb
ADDED
data/spec/feep_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Feep do
|
4
|
+
subject { Feep.new }
|
5
|
+
|
6
|
+
describe '#play_sound' do
|
7
|
+
options = {
|
8
|
+
:freq_or_note => '440.000',
|
9
|
+
:waveform => 'sine',
|
10
|
+
:volume => 0.5,
|
11
|
+
:duration => 500,
|
12
|
+
:save => false,
|
13
|
+
:loud => false
|
14
|
+
}
|
15
|
+
|
16
|
+
it 'plays a sound with no options' do
|
17
|
+
expect(Feep.new(options)).to eq "sound played"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'feep'
|
data/tasks/rspec.rake
ADDED
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: feep
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Michael Chadwick
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-03-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: wavefile
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.6.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.6.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: os
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
- - ">="
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 0.9.6
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0.9'
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.9.6
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pry-byebug
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: bundler
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.8'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.8'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: rake
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '10.0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '10.0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rspec
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '3.0'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '3.0'
|
103
|
+
description: Use Ruby to make your computer beep at a certain frequency for a certain
|
104
|
+
duration. Do it for fun, or add it to other programs for easy alert sounds.
|
105
|
+
email: mike@codana.me
|
106
|
+
executables:
|
107
|
+
- feep
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- ".gitignore"
|
112
|
+
- Gemfile
|
113
|
+
- Gemfile.lock
|
114
|
+
- README.md
|
115
|
+
- Rakefile
|
116
|
+
- bin/feep
|
117
|
+
- feep.gemspec
|
118
|
+
- lib/feep.rb
|
119
|
+
- lib/feep/constants.rb
|
120
|
+
- lib/feep/version.rb
|
121
|
+
- spec/feep_spec.rb
|
122
|
+
- spec/spec_helper.rb
|
123
|
+
- tasks/rspec.rake
|
124
|
+
homepage: http://rubygemspec.org/gems/feep
|
125
|
+
licenses:
|
126
|
+
- MIT
|
127
|
+
metadata: {}
|
128
|
+
post_install_message:
|
129
|
+
rdoc_options: []
|
130
|
+
require_paths:
|
131
|
+
- lib
|
132
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
138
|
+
requirements:
|
139
|
+
- - ">="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
requirements: []
|
143
|
+
rubyforge_project:
|
144
|
+
rubygems_version: 2.4.5
|
145
|
+
signing_key:
|
146
|
+
specification_version: 4
|
147
|
+
summary: Make your computer feep with Ruby
|
148
|
+
test_files: []
|
149
|
+
has_rdoc:
|