json-waveform 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6865a7f5c7f07c8c15f4f58a06b1c6ff840dae4e
4
+ data.tar.gz: 3b6d9f92cdc05efc309b0125e7480149620c6aba
5
+ SHA512:
6
+ metadata.gz: 6147921f5a577d809f1fff7ebc1adfb5d29177c030bb1882d7043768ceb5e8705976fbb8a53e632c2a72f1eb3e4cf63591b8094ab44596ed47e69a40aaab11d3
7
+ data.tar.gz: aaeea25e81dd1e3a7d68090e8398e191b2c37234a11263f05d625f0b3708407ec741d42b9f113559d91e82e44d3f6c004bc71c75257cb74b65bb324707703b4e
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2014 Esteban Pastorino
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ JsonWaveform
2
+ ========
3
+
4
+ JsonWaveform is a class to generate waveform json files from audio files. You can combine it with http://waveformjs.org/ to create awesome waveforms in webpages. It also comes with a handy CLI you can use to generate waveform json files on the command line.
5
+
6
+ It is *heavily* influenced by https://github.com/benalavi/waveform
7
+
8
+ Installation
9
+ ============
10
+
11
+ Waveform depends on `ruby-audio`, which in turn depends on libsndfile.
12
+
13
+ Build libsndfile from (http://www.mega-nerd.com/libsndfile/), install it via `apt` (`sudo apt-get install libsndfile1-dev`), `libsndfile` in brew, etc...
14
+
15
+ Then:
16
+
17
+ $ gem install json-waveform
18
+
19
+ NOTE: If `ruby-audio` fails to compile and you have `libsndfile` available, it may be because of this: http://stackoverflow.com/questions/19919640/ruby-audio-1-6-1-install-error-with-installed-libsndfile-1-0-25
20
+
21
+ CLI Usage
22
+ =========
23
+
24
+ $ json-waveform song.wav
25
+
26
+ There are some nifty options you can supply to switch things up:
27
+
28
+ -s sets the samples of the waveform.
29
+ -A sets the max amplitude.
30
+ -m sets the method used to sample the source audio file, it can either be
31
+ 'peak' or 'rms'. 'peak' is probably what you want because it looks
32
+ cooler, but 'rms' is closer to what you actually hear.
33
+
34
+ And to see (almost) this same info:
35
+
36
+ -h will print out a help screen with all this info.
37
+
38
+ $ json-waveform Motley\ Crüe/Kickstart\ my\ Heart.wav
39
+
40
+ Usage in code
41
+ =============
42
+
43
+ The CLI is really just a thin wrapper around the JsonWaveform class, which you can also use in your programs for reasons I haven't thought of. The JsonWaveform class takes pretty much the same options as the CLI when generating waveforms.
44
+
45
+ ```ruby
46
+ JsonWaveform("foo.wav", samples: 1000) # => [ 0, 0.1, 0.15, ... ]
47
+ ```
48
+
49
+ Requirements
50
+ ============
51
+
52
+ `ruby-audio`
53
+
54
+ The gem version, *not* the old outdated library listed on RAA. `ruby-audio` is a wrapper for `libsndfile`. To install the necessary libs to build `ruby-audio` you can do `sudo apt-get install libsndfile1-dev` on Ubuntu or `brew install libsndfile` on OSX.
55
+
56
+
57
+ Tests
58
+ =====
59
+
60
+ **They are not there yet, but will be run using: **
61
+
62
+ $ make
63
+
64
+ Sample sound file used in tests is in the Public Domain from soundbible.com: <http://soundbible.com/1598-Electronic-Chime.html>.
65
+
data/bin/json-waveform ADDED
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env ruby
2
+ require "json-waveform"
3
+ require "optparse"
4
+
5
+ options = JsonWaveform::DEFAULT_OPTIONS
6
+ optparse = OptionParser.new do |o|
7
+ o.banner = "Usage: waveform [options] source_audio"
8
+ o.version = Waveform::VERSION
9
+
10
+ o.on("-s", "--samples SAMPLES", "Samples generated from the original file (i.e.: waveform width) -- Default #{Waveform::DefaultOptions[:samples]}.") do |samples|
11
+ options[:samples] = samples.to_i
12
+ end
13
+
14
+ o.on("-A", "--amplitude AMPLITUDE", "Max amplitude of the waveform values-- Default #{Waveform::DefaultOptions[:amplitude]}.") do |amplitude|
15
+ options[:amplitude] = amplitude.to_i
16
+ end
17
+
18
+ o.on("-a", "--autosample MSEC", "Sets the width of the waveform based on the audio length, using a resolution of the given msec per sample.") do |msec|
19
+ options[:auto_sample] = msec.to_i
20
+ end
21
+
22
+ o.on("-m", "--method METHOD", "Wave analyzation method (can be 'peak' or 'rms') -- Default '#{Waveform::DefaultOptions[:method]}'.") do |method|
23
+ options[:method] = method.to_sym
24
+ end
25
+
26
+ o.on("-h", "--help", "Display this screen") do
27
+ puts o
28
+ exit
29
+ end
30
+ end
31
+
32
+ optparse.parse!
33
+
34
+ begin
35
+ result = Waveform.generate(ARGV[0], options)
36
+ puts result
37
+ rescue Waveform::ArgumentError => e
38
+ puts e.message + "\n\n"
39
+ puts optparse
40
+ rescue Waveform::RuntimeError => e
41
+ puts e.message
42
+ end
43
+
@@ -0,0 +1,25 @@
1
+ require "./lib/json-waveform/version"
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "json-waveform"
5
+ s.version = JsonWaveform::VERSION
6
+ s.summary = "Generate waveform JSON files from audio files"
7
+ s.description = "Generate waveform JSON information from audio files, compatible with http://waveformjs.org/."
8
+ s.authors = ["Esteban pastorino"]
9
+ s.email = ["ejpastorino@gmail.com"]
10
+ s.homepage = "http://github.com/kitop/json-waveform"
11
+
12
+ s.files = Dir[
13
+ "LICENSE",
14
+ "README.md",
15
+ "makefile",
16
+ "lib/**/*.rb",
17
+ "*.gemspec",
18
+ "test/**/*.rb",
19
+ "bin/*"
20
+ ]
21
+
22
+ s.executables << "json-waveform"
23
+
24
+ s.add_dependency "ruby-audio"
25
+ end
@@ -0,0 +1,3 @@
1
+ class JsonWaveform
2
+ VERSION = "0.0.1".freeze
3
+ end
@@ -0,0 +1,150 @@
1
+ require File.join(File.dirname(__FILE__), "json-waveform/version")
2
+
3
+ require "ruby-audio"
4
+
5
+ class JsonWaveform
6
+ DEFAULT_OPTIONS = {
7
+ :method => :peak,
8
+ :samples => 1800,
9
+ :amplitude => 1
10
+ }
11
+
12
+ # Scope these under Waveform so you can catch the ones generated by just this
13
+ # class.
14
+ class RuntimeError < ::RuntimeError;end;
15
+ class ArgumentError < ::ArgumentError;end;
16
+
17
+ class << self
18
+ # Generate a Waveform JSON file from the given filename with the given options.
19
+ #
20
+ # Available options (all optional) are:
21
+ #
22
+ # :method => The method used to read sample frames, available methods
23
+ # are peak and rms. peak is probably what you're used to seeing, it uses
24
+ # the maximum amplitude per sample to generate the waveform, so the
25
+ # waveform looks more dynamic. RMS gives a more fluid waveform and
26
+ # probably more accurately reflects what you hear, but isn't as
27
+ # pronounced (typically).
28
+ #
29
+ # Can be :rms or :peak
30
+ # Default is :peak.
31
+ #
32
+ # :samples => The amount of samples wanted
33
+ # Default is 1800.
34
+ #
35
+ # :amplitude => The amplitude of the final values
36
+ # Default is 1.
37
+ #
38
+ # :auto_width => msec per sample. This will overwrite the sample of the
39
+ # final waveform depending on the length of the audio file.
40
+ # Example:
41
+ # 100 => 1 sample per 100 msec; a one minute audio file will result in a width of 600 samples
42
+ #
43
+ # Example:
44
+ # JsonWaveform.generate("Kickstart My Heart.wav")
45
+ # JsonWaveform.generate("Kickstart My Heart.wav", :method => :rms)
46
+ #
47
+ def generate(source, options={})
48
+ options = DEFAULT_OPTIONS.merge(options)
49
+
50
+ raise ArgumentError.new("No source audio filename given, must be an existing sound file.") unless source
51
+ raise RuntimeError.new("Source audio file '#{source}' not found.") unless File.exist?(source)
52
+
53
+ if options[:auto_samples]
54
+ RubyAudio::Sound.open(source) do |audio|
55
+ options[:samples] = (audio.info.length * 1000 / options[:auto_samples].to_i).ceil
56
+ end
57
+ end
58
+
59
+ # Frames gives the amplitudes for each channel, for our waveform we're
60
+ # saying the "visual" amplitude is the average of the amplitude across all
61
+ # the channels. This might be a little weird w/ the "peak" method if the
62
+ # frames are very wide (i.e. the image width is very small) -- I *think*
63
+ # the larger the frames are, the more "peaky" the waveform should get,
64
+ # perhaps to the point of inaccurately reflecting the actual sound.
65
+ samples = frames(source, options[:samples], options[:method]).collect do |frame|
66
+ frame.inject(0.0) { |sum, peak| sum + peak } / frame.size
67
+ end
68
+
69
+ normalize(samples, options)
70
+ end
71
+
72
+ private
73
+
74
+ # Returns a sampling of frames from the given RubyAudio::Sound using the
75
+ # given method
76
+ def frames(source, samples, method = :peak)
77
+ raise ArgumentError.new("Unknown sampling method #{method}") unless [ :peak, :rms ].include?(method)
78
+
79
+ frames = []
80
+
81
+ RubyAudio::Sound.open(source) do |audio|
82
+ frames_read = 0
83
+ frames_per_sample = (audio.info.frames.to_f / samples.to_f).to_i
84
+ sample = RubyAudio::Buffer.new("float", frames_per_sample, audio.info.channels)
85
+
86
+ while(frames_read = audio.read(sample)) > 0
87
+ frames << send(method, sample, audio.info.channels)
88
+ end
89
+ end
90
+
91
+ frames
92
+ rescue RubyAudio::Error => e
93
+ raise e unless e.message == "File contains data in an unknown format."
94
+ raise JsonWaveform::RuntimeError.new("Source audio file #{source} could not be read by RubyAudio library -- Hint: non-WAV files are no longer supported, convert to WAV first using something like ffmpeg (RubyAudio: #{e.message})")
95
+ end
96
+
97
+ def normalize(samples, options)
98
+ samples.map do |sample|
99
+ # Half the amplitude goes above zero, half below
100
+ amplitude = sample * options[:amplitude].to_f
101
+ rounded = amplitude.round(2)
102
+ rounded.zero? || rounded == 1 ? rounded.to_i : rounded
103
+ end
104
+ end
105
+
106
+ # Returns an array of the peak of each channel for the given collection of
107
+ # frames -- the peak is individual to the channel, and the returned collection
108
+ # of peaks are not (necessarily) from the same frame(s).
109
+ def peak(frames, channels=1)
110
+ peak_frame = []
111
+ (0..channels-1).each do |channel|
112
+ peak_frame << channel_peak(frames, channel)
113
+ end
114
+ peak_frame
115
+ end
116
+
117
+ # Returns an array of rms values for the given frameset where each rms value is
118
+ # the rms value for that channel.
119
+ def rms(frames, channels=1)
120
+ rms_frame = []
121
+ (0..channels-1).each do |channel|
122
+ rms_frame << channel_rms(frames, channel)
123
+ end
124
+ rms_frame
125
+ end
126
+
127
+ # Returns the peak voltage reached on the given channel in the given collection
128
+ # of frames.
129
+ #
130
+ # TODO: Could lose some resolution and only sample every other frame, would
131
+ # likely still generate the same waveform as the waveform is so comparitively
132
+ # low resolution to the original input (in most cases), and would increase
133
+ # the analyzation speed (maybe).
134
+ def channel_peak(frames, channel=0)
135
+ peak = 0.0
136
+ frames.each do |frame|
137
+ next if frame.nil?
138
+ frame = Array(frame)
139
+ peak = frame[channel].abs if frame[channel].abs > peak
140
+ end
141
+ peak
142
+ end
143
+
144
+ # Returns the rms value across the given collection of frames for the given
145
+ # channel.
146
+ def channel_rms(frames, channel=0)
147
+ Math.sqrt(frames.inject(0.0){ |sum, frame| sum += (frame ? Array(frame)[channel] ** 2 : 0) } / frames.size)
148
+ end
149
+ end
150
+ end
data/makefile ADDED
@@ -0,0 +1,4 @@
1
+ .PHONY: console
2
+
3
+ console:
4
+ irb -I./lib -r json-waveform
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json-waveform
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Esteban pastorino
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-audio
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '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'
27
+ description: Generate waveform JSON information from audio files, compatible with
28
+ http://waveformjs.org/.
29
+ email:
30
+ - ejpastorino@gmail.com
31
+ executables:
32
+ - json-waveform
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - LICENSE
37
+ - README.md
38
+ - bin/json-waveform
39
+ - json-waveform.gemspec
40
+ - lib/json-waveform.rb
41
+ - lib/json-waveform/version.rb
42
+ - makefile
43
+ homepage: http://github.com/kitop/json-waveform
44
+ licenses: []
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.2.2
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Generate waveform JSON files from audio files
66
+ test_files: []