audiofile 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +45 -0
- data/Rakefile +10 -0
- data/audiofile.gemspec +18 -0
- data/lib/audiofile.rb +6 -0
- data/lib/audiofile/notes.rb +142 -0
- data/lib/audiofile/version.rb +3 -0
- data/lib/audiofile/wav_file.rb +129 -0
- data/test/test.wav +0 -0
- data/test/test_helper.rb +4 -0
- data/test/test_wav_header.rb +14 -0
- metadata +63 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Alejandro Ciniglio
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Audiofile
|
2
|
+
|
3
|
+
Parses WAV files lazily. This makes operations much faster than other libraries.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'audiofile'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install audiofile
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
To initialize a file:
|
22
|
+
|
23
|
+
a = Audiofile::WavFile.new 'myfile.wav'
|
24
|
+
|
25
|
+
To get the samples as a string of bytes (great for parsing with C):
|
26
|
+
|
27
|
+
d = a.data_chunk
|
28
|
+
|
29
|
+
To get info about the file:
|
30
|
+
|
31
|
+
a.sample_rate
|
32
|
+
a.num_channels
|
33
|
+
|
34
|
+
To get the left and right channel as array of integers:
|
35
|
+
|
36
|
+
a.left
|
37
|
+
a.right
|
38
|
+
|
39
|
+
## Contributing
|
40
|
+
|
41
|
+
1. Fork it
|
42
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
43
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
44
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
45
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/audiofile.gemspec
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/audiofile/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Alejandro Ciniglio"]
|
6
|
+
gem.email = ["mail@alejandrociniglio.com"]
|
7
|
+
gem.description = %q{Gem to parse Audio files and provide
|
8
|
+
samples as a string of bytes}
|
9
|
+
gem.summary = %q{Gem to parse .Wav files}
|
10
|
+
gem.homepage = "http://github.com/ciniglio/audiofile"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
14
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
|
+
gem.name = "audiofile"
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
gem.version = Audiofile::VERSION
|
18
|
+
end
|
data/lib/audiofile.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
module Audiofile
|
2
|
+
NOTES = {
|
3
|
+
C_0: 16.35,
|
4
|
+
CS_0: 17.32,
|
5
|
+
D_0: 18.35,
|
6
|
+
Eb_0: 19.45,
|
7
|
+
E_0: 20.60,
|
8
|
+
F_0: 21.83,
|
9
|
+
FS_0: 23.12,
|
10
|
+
G_0: 24.50,
|
11
|
+
GS_0: 25.96,
|
12
|
+
A_0: 27.50,
|
13
|
+
Bb_0: 29.14,
|
14
|
+
B_0: 30.87,
|
15
|
+
C_1: 32.70,
|
16
|
+
CS_1: 34.65,
|
17
|
+
D_1: 36.71,
|
18
|
+
Eb_1: 38.89,
|
19
|
+
E_1: 41.20,
|
20
|
+
F_1: 43.65,
|
21
|
+
FS_1: 46.25,
|
22
|
+
G_1: 49.00,
|
23
|
+
GS_1: 51.91,
|
24
|
+
A_1: 55.00,
|
25
|
+
Bb_1: 58.27,
|
26
|
+
B_1: 61.74,
|
27
|
+
C_2: 65.41,
|
28
|
+
CS_2: 69.30,
|
29
|
+
D_2: 73.42,
|
30
|
+
Eb_2: 77.78,
|
31
|
+
E_2: 82.41,
|
32
|
+
F_2: 87.31,
|
33
|
+
FS_2: 92.50,
|
34
|
+
G_2: 98.00,
|
35
|
+
GS_2: 103.8,
|
36
|
+
A_2: 110.0,
|
37
|
+
Bb_2: 116.5,
|
38
|
+
B_2: 123.5,
|
39
|
+
C_3: 130.8,
|
40
|
+
CS_3: 138.6,
|
41
|
+
D_3: 146.8,
|
42
|
+
Eb_3: 155.6,
|
43
|
+
E_3: 164.8,
|
44
|
+
F_3: 174.6,
|
45
|
+
FS_3: 185.0,
|
46
|
+
G_3: 196.0,
|
47
|
+
GS_3: 207.7,
|
48
|
+
A_3: 220.0,
|
49
|
+
Bb_3: 233.1,
|
50
|
+
B_3: 246.9,
|
51
|
+
C_4: 261.6,
|
52
|
+
CS_4: 277.2,
|
53
|
+
D_4: 293.7,
|
54
|
+
Eb_4: 311.1,
|
55
|
+
E_4: 329.6,
|
56
|
+
F_4: 349.2,
|
57
|
+
FS_4: 370.0,
|
58
|
+
G_4: 392.0,
|
59
|
+
GS_4: 415.3,
|
60
|
+
A_4: 440.0,
|
61
|
+
Bb_4: 466.2,
|
62
|
+
B_4: 493.9,
|
63
|
+
C_5: 523.3,
|
64
|
+
CS_5: 554.4,
|
65
|
+
D_5: 587.3,
|
66
|
+
Eb_5: 622.3,
|
67
|
+
E_5: 659.3,
|
68
|
+
F_5: 698.5,
|
69
|
+
FS_5: 740.0,
|
70
|
+
G_5: 784.0,
|
71
|
+
GS_5: 830.6,
|
72
|
+
A_5: 880.0,
|
73
|
+
Bb_5: 932.3,
|
74
|
+
B_5: 987.8,
|
75
|
+
C_6: 1047,
|
76
|
+
CS_6: 1109,
|
77
|
+
D_6: 1175,
|
78
|
+
Eb_6: 1245,
|
79
|
+
E_6: 1319,
|
80
|
+
F_6: 1397,
|
81
|
+
FS_6: 1480,
|
82
|
+
G_6: 1568,
|
83
|
+
GS_6: 1661,
|
84
|
+
A_6: 1760,
|
85
|
+
Bb_6: 1865,
|
86
|
+
B_6: 1976,
|
87
|
+
C_7: 2093,
|
88
|
+
CS_7: 2217,
|
89
|
+
D_7: 2349,
|
90
|
+
Eb_7: 2489,
|
91
|
+
E_7: 2637,
|
92
|
+
F_7: 2794,
|
93
|
+
FS_7: 2960,
|
94
|
+
G_7: 3136,
|
95
|
+
GS_7: 3322,
|
96
|
+
A_7: 3520,
|
97
|
+
Bb_7: 3729,
|
98
|
+
B_7: 3951,
|
99
|
+
C_8: 4186,
|
100
|
+
CS_8: 4435,
|
101
|
+
D_8: 4699,
|
102
|
+
Eb_8: 4978,
|
103
|
+
E_8: 5274,
|
104
|
+
F_8: 5588,
|
105
|
+
FS_8: 5920,
|
106
|
+
G_8: 6272,
|
107
|
+
GS_8: 6645,
|
108
|
+
A_8: 7040,
|
109
|
+
Bb_8: 7459,
|
110
|
+
B_8: 7902,
|
111
|
+
}
|
112
|
+
|
113
|
+
def Audiofile::find_closest_note(freq)
|
114
|
+
f = binary_search(freq, Audiofile::NOTES.values)
|
115
|
+
Audiofile::NOTES.key(f)
|
116
|
+
end
|
117
|
+
|
118
|
+
def Audiofile::binary_search(val, arr)
|
119
|
+
s = arr.size
|
120
|
+
if s == 1
|
121
|
+
arr[0]
|
122
|
+
end
|
123
|
+
midp = arr[s/2]
|
124
|
+
midl = arr[s/2-1]
|
125
|
+
if val >= midl and val <= midp
|
126
|
+
find_closest(val, midl, midp)
|
127
|
+
elsif val < midl
|
128
|
+
binary_search(val, arr[0..s/2 - 1])
|
129
|
+
else
|
130
|
+
binary_search(val, arr[s/2..-1])
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def Audiofile::find_closest(x, y, z)
|
135
|
+
if (x-y).abs < (x-z).abs
|
136
|
+
y
|
137
|
+
else
|
138
|
+
z
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Audiofile
|
2
|
+
|
3
|
+
##############################################################################
|
4
|
+
#
|
5
|
+
# Using wavfile definition from :
|
6
|
+
# https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
7
|
+
#
|
8
|
+
##############################################################################
|
9
|
+
|
10
|
+
class WavFile
|
11
|
+
attr_reader :raw
|
12
|
+
def initialize(filename)
|
13
|
+
@file = open(filename).read
|
14
|
+
@raw = @file.unpack('a*').first
|
15
|
+
end
|
16
|
+
|
17
|
+
def data_chunk
|
18
|
+
chunks.select{|c| c.name == "data"}.first.payload
|
19
|
+
end
|
20
|
+
|
21
|
+
def fmt_chunk
|
22
|
+
chunks.select{|c| c.name == "fmt "}.first.payload
|
23
|
+
end
|
24
|
+
|
25
|
+
def left
|
26
|
+
if !@left
|
27
|
+
parse_header
|
28
|
+
parse_data
|
29
|
+
end
|
30
|
+
@left
|
31
|
+
end
|
32
|
+
|
33
|
+
def right
|
34
|
+
if !@right
|
35
|
+
parse_header
|
36
|
+
parse_data
|
37
|
+
end
|
38
|
+
@right
|
39
|
+
end
|
40
|
+
|
41
|
+
def sample_rate
|
42
|
+
if !@sample_rate
|
43
|
+
parse_header
|
44
|
+
end
|
45
|
+
@sample_rate
|
46
|
+
end
|
47
|
+
|
48
|
+
def bits_per_sample
|
49
|
+
if !@bits_per_sample
|
50
|
+
parse_header
|
51
|
+
end
|
52
|
+
@bits_per_sample
|
53
|
+
end
|
54
|
+
|
55
|
+
def num_channels
|
56
|
+
if !@num_channels
|
57
|
+
parse_header
|
58
|
+
end
|
59
|
+
@num_channels
|
60
|
+
end
|
61
|
+
|
62
|
+
def filesize
|
63
|
+
if !@filesize
|
64
|
+
parse_header
|
65
|
+
end
|
66
|
+
@filesize
|
67
|
+
end
|
68
|
+
|
69
|
+
def chunks
|
70
|
+
if !@chunks
|
71
|
+
chunk
|
72
|
+
end
|
73
|
+
@chunks
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def parse_header
|
78
|
+
raise "Please use uncompressed wav" unless
|
79
|
+
fmt_chunk[0..1].unpack('S<').first == 1
|
80
|
+
@num_channels = fmt_chunk[2..3].unpack('S<').first
|
81
|
+
@sample_rate = fmt_chunk[4..7].unpack('L<').first
|
82
|
+
@bits_per_sample = fmt_chunk[14..15].unpack('S<').first
|
83
|
+
end
|
84
|
+
|
85
|
+
def chunk
|
86
|
+
raise "Invalid File" unless @raw[0..3] == "RIFF" and @raw[8..11] == "WAVE"
|
87
|
+
@filesize = @raw[4..7].unpack('L<').first + 8
|
88
|
+
counter = 12
|
89
|
+
@chunks = []
|
90
|
+
while counter < filesize
|
91
|
+
name = @raw[counter..counter+3]
|
92
|
+
size = @raw[counter+4..counter+7].unpack('L<').first
|
93
|
+
payload = @raw[counter+8..counter+8+size-1]
|
94
|
+
counter += 8 + size
|
95
|
+
@chunks << Chunk.new(name, size, payload)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def parse_data
|
100
|
+
if @bits_per_sample == 8
|
101
|
+
sample_fmt = 'C'
|
102
|
+
sample_size = 1
|
103
|
+
else
|
104
|
+
sample_fmt = 's<'
|
105
|
+
sample_size = 2
|
106
|
+
end
|
107
|
+
@left = []
|
108
|
+
@right = []
|
109
|
+
bytes_left = data_chunk.size
|
110
|
+
i = 0
|
111
|
+
while (i < bytes_left)
|
112
|
+
@left << data_chunk[i..i+sample_size].unpack(sample_fmt).first
|
113
|
+
i += sample_size
|
114
|
+
@right << data_chunk[i..i+sample_size].unpack(sample_fmt).first
|
115
|
+
i += sample_size
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Chunk
|
121
|
+
attr_accessor :name, :size, :payload
|
122
|
+
|
123
|
+
def initialize(name, size, payload)
|
124
|
+
@name = name
|
125
|
+
@size = size
|
126
|
+
@payload = payload
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/test/test.wav
ADDED
Binary file
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative './test_helper.rb'
|
2
|
+
|
3
|
+
Dir.chdir(File.dirname(__FILE__))
|
4
|
+
WAVFILE = 'test.wav'
|
5
|
+
|
6
|
+
class TestWavHeader < Test::Unit::TestCase
|
7
|
+
def test_headers
|
8
|
+
w = Audiofile::WavFile.new WAVFILE
|
9
|
+
assert_equal w.sample_rate, 44100
|
10
|
+
assert_equal w.bits_per_sample, 16
|
11
|
+
assert_equal w.num_channels, 2
|
12
|
+
assert_equal w.filesize, File.size(WAVFILE)
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: audiofile
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alejandro Ciniglio
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! 'Gem to parse Audio files and provide
|
15
|
+
|
16
|
+
samples as a string of bytes'
|
17
|
+
email:
|
18
|
+
- mail@alejandrociniglio.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- .gitignore
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- audiofile.gemspec
|
29
|
+
- lib/audiofile.rb
|
30
|
+
- lib/audiofile/notes.rb
|
31
|
+
- lib/audiofile/version.rb
|
32
|
+
- lib/audiofile/wav_file.rb
|
33
|
+
- test/test.wav
|
34
|
+
- test/test_helper.rb
|
35
|
+
- test/test_wav_header.rb
|
36
|
+
homepage: http://github.com/ciniglio/audiofile
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.24
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Gem to parse .Wav files
|
60
|
+
test_files:
|
61
|
+
- test/test.wav
|
62
|
+
- test/test_helper.rb
|
63
|
+
- test/test_wav_header.rb
|