feep 0.0.1
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 +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:
|