raiff 0.1.0
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.
- data/.document +5 -0
- data/.gitignore +2 -0
- data/README.rdoc +9 -0
- data/Rakefile +39 -0
- data/VERSION +1 -0
- data/lib/raiff.rb +63 -0
- data/lib/raiff/chunk.rb +25 -0
- data/lib/raiff/chunk/common.rb +26 -0
- data/lib/raiff/chunk/data.rb +19 -0
- data/lib/raiff/chunk/form.rb +35 -0
- data/lib/raiff/chunk/sound_data.rb +43 -0
- data/lib/raiff/file.rb +68 -0
- data/raiff.gemspec +56 -0
- data/sample/example-24bit.aiff +0 -0
- data/test/helper.rb +13 -0
- data/test/test_raiff.rb +28 -0
- metadata +80 -0
data/.document
ADDED
data/.gitignore
ADDED
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "raiff"
|
8
|
+
gem.summary = %Q{Ruby AIFF Reading Library}
|
9
|
+
gem.description = %Q{Reads in AIFF file data and audio contents}
|
10
|
+
gem.email = "github@tadman.ca"
|
11
|
+
gem.homepage = "http://github.com/twg/raiff"
|
12
|
+
gem.authors = [ "Scott Tadman" ]
|
13
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rake/testtask'
|
21
|
+
Rake::TestTask.new(:test) do |test|
|
22
|
+
test.libs << 'lib' << 'test'
|
23
|
+
test.pattern = 'test/**/test_*.rb'
|
24
|
+
test.verbose = true
|
25
|
+
end
|
26
|
+
|
27
|
+
task :test => :check_dependencies
|
28
|
+
|
29
|
+
task :default => :test
|
30
|
+
|
31
|
+
require 'rake/rdoctask'
|
32
|
+
Rake::RDocTask.new do |rdoc|
|
33
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
34
|
+
|
35
|
+
rdoc.rdoc_dir = 'rdoc'
|
36
|
+
rdoc.title = "raiff #{version}"
|
37
|
+
rdoc.rdoc_files.include('README*')
|
38
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
39
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/raiff.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
class Raiff
|
2
|
+
# == Submodules ===========================================================
|
3
|
+
|
4
|
+
autoload(:Chunk, 'raiff/chunk')
|
5
|
+
autoload(:File, 'raiff/file')
|
6
|
+
|
7
|
+
# == Properties ===========================================================
|
8
|
+
|
9
|
+
attr_reader :channels, :sample_frames, :sample_size, :sample_rate
|
10
|
+
attr_reader :samples
|
11
|
+
|
12
|
+
# == Callbacks ============================================================
|
13
|
+
|
14
|
+
# == Class Methods ========================================================
|
15
|
+
|
16
|
+
def self.open(file)
|
17
|
+
raiff = new(file)
|
18
|
+
|
19
|
+
yield(raiff) if (block_given?)
|
20
|
+
|
21
|
+
raiff
|
22
|
+
end
|
23
|
+
|
24
|
+
# == Instance Methods =====================================================
|
25
|
+
|
26
|
+
def initialize(file = nil)
|
27
|
+
@offset = 0
|
28
|
+
|
29
|
+
if (file)
|
30
|
+
@file = Raiff::File.new(file)
|
31
|
+
|
32
|
+
@form = Raiff::Chunk::Form.new(@file)
|
33
|
+
@chunks = @form.chunks
|
34
|
+
|
35
|
+
common_chunk = (@chunks['COMM'] and @chunks['COMM'][0])
|
36
|
+
|
37
|
+
if (common_chunk)
|
38
|
+
@channels = common_chunk.channels
|
39
|
+
@sample_frames = common_chunk.sample_frames
|
40
|
+
@sample_size = common_chunk.sample_size
|
41
|
+
@sample_rate = common_chunk.sample_rate
|
42
|
+
end
|
43
|
+
|
44
|
+
@samples = [ ]
|
45
|
+
|
46
|
+
if (@chunks['SSND'])
|
47
|
+
@chunks['SSND'].each do |sound_data|
|
48
|
+
@samples += sound_data.samples
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def inspect
|
55
|
+
"<Raiff\##{object_id} channels=#{channels} sample_frames=#{sample_frames} sample_size=#{sample_size} sample_rate=#{sample_rate}>"
|
56
|
+
end
|
57
|
+
|
58
|
+
def each_sample(&block)
|
59
|
+
@chunks['SSND'].each do |sound_data|
|
60
|
+
sound_data.samples.each(&:block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/raiff/chunk.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Raiff::Chunk
|
2
|
+
# == Constants ============================================================
|
3
|
+
|
4
|
+
autoload(:Common, 'raiff/chunk/common')
|
5
|
+
autoload(:Data, 'raiff/chunk/data')
|
6
|
+
autoload(:Form, 'raiff/chunk/form')
|
7
|
+
autoload(:SoundData, 'raiff/chunk/sound_data')
|
8
|
+
|
9
|
+
# == Properties ===========================================================
|
10
|
+
|
11
|
+
attr_reader :id, :size
|
12
|
+
|
13
|
+
# == Class Methods ========================================================
|
14
|
+
|
15
|
+
# == Instance Methods =====================================================
|
16
|
+
|
17
|
+
def initialize(file)
|
18
|
+
@id = file.read(4)
|
19
|
+
@size = file.unpack('N')[0]
|
20
|
+
end
|
21
|
+
|
22
|
+
def inspect
|
23
|
+
"<#{self.class}\##{object_id} #{@id} #{@size}>"
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Raiff::Chunk::Common < Raiff::Chunk
|
2
|
+
# == Properties ===========================================================
|
3
|
+
|
4
|
+
attr_reader :channels, :sample_frames, :sample_size, :sample_rate
|
5
|
+
attr_reader :bytes_per_sample
|
6
|
+
|
7
|
+
# == Class Methods ========================================================
|
8
|
+
|
9
|
+
# == Instance Methods =====================================================
|
10
|
+
|
11
|
+
def initialize(file)
|
12
|
+
super(file)
|
13
|
+
|
14
|
+
chunk_end = file.offset + @size
|
15
|
+
|
16
|
+
@channels, @sample_frames, @sample_size = file.unpack('nNn')
|
17
|
+
|
18
|
+
@sample_rate = file.unpack_extended_float
|
19
|
+
|
20
|
+
@bytes_per_sample = (@sample_size - 1) / 8 + 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"<#{self.class}\##{object_id} #{@id} #{@size} channels=#{channels} sample_frames=#{sample_frames} sample_size=#{sample_size} sample_rate=#{'%.2f' % sample_rate}>"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Raiff::Chunk::Data < Raiff::Chunk
|
2
|
+
# == Properties ===========================================================
|
3
|
+
|
4
|
+
attr_reader :data
|
5
|
+
|
6
|
+
# == Class Methods ========================================================
|
7
|
+
|
8
|
+
# == Instance Methods =====================================================
|
9
|
+
|
10
|
+
def initialize(file)
|
11
|
+
super(file)
|
12
|
+
|
13
|
+
@data = file.read(@size)
|
14
|
+
|
15
|
+
if (@size % 2 == 1)
|
16
|
+
file.read(1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Raiff::Chunk::Form < Raiff::Chunk
|
2
|
+
# == Properties ===========================================================
|
3
|
+
|
4
|
+
attr_reader :chunks
|
5
|
+
|
6
|
+
# == Class Methods ========================================================
|
7
|
+
|
8
|
+
# == Instance Methods =====================================================
|
9
|
+
|
10
|
+
def initialize(file)
|
11
|
+
super(file)
|
12
|
+
|
13
|
+
@type = file.read(4)
|
14
|
+
|
15
|
+
@chunks = { }
|
16
|
+
common_chunk = nil
|
17
|
+
|
18
|
+
while (!file.eof?)
|
19
|
+
chunk =
|
20
|
+
case (file.peek(4))
|
21
|
+
when 'COMM'
|
22
|
+
common_chunk = Raiff::Chunk::Common.new(file)
|
23
|
+
when 'SSND'
|
24
|
+
# FIX: Raise exception when there is no common block, but
|
25
|
+
# a SoundData block occurs.
|
26
|
+
Raiff::Chunk::SoundData.new(file, common_chunk)
|
27
|
+
else
|
28
|
+
Raiff::Chunk::Data.new(file)
|
29
|
+
end
|
30
|
+
|
31
|
+
@chunks[chunk.id] ||= [ ]
|
32
|
+
@chunks[chunk.id] << chunk
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Raiff::Chunk::SoundData < Raiff::Chunk
|
2
|
+
# == Properties ===========================================================
|
3
|
+
|
4
|
+
attr_reader :offset, :block_size
|
5
|
+
attr_reader :samples
|
6
|
+
|
7
|
+
# == Class Methods ========================================================
|
8
|
+
|
9
|
+
# == Instance Methods =====================================================
|
10
|
+
|
11
|
+
def initialize(file, common)
|
12
|
+
super(file)
|
13
|
+
|
14
|
+
end_offset = file.offset + @size
|
15
|
+
|
16
|
+
@offset, @block_size = file.unpack('NN')
|
17
|
+
|
18
|
+
file.read(@offset)
|
19
|
+
|
20
|
+
sample_count = (end_offset - file.offset) / common.bytes_per_sample
|
21
|
+
bit_offset = common.bytes_per_sample * 8 - common.sample_size
|
22
|
+
value_offset = (1 << common.sample_size) - 1
|
23
|
+
|
24
|
+
@samples = [ ]
|
25
|
+
|
26
|
+
case (common.bytes_per_sample)
|
27
|
+
when 3
|
28
|
+
format = 'B24' * common.channels
|
29
|
+
|
30
|
+
sample_count.times do
|
31
|
+
if (sample = file.unpack(format))
|
32
|
+
@samples << sample.collect { |s| (s.to_i(2) >> bit_offset) - value_offset }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
format = %w[ C n x N ][common.bytes_per_sample] * common.channels
|
37
|
+
|
38
|
+
sample_count.times do
|
39
|
+
@samples << file.unpack(format).collect { |i| (i >> bit_offset) - value_offset }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/raiff/file.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
class Raiff::File
|
2
|
+
# == Constants ============================================================
|
3
|
+
|
4
|
+
# == Properties ===========================================================
|
5
|
+
|
6
|
+
attr_reader :offset
|
7
|
+
|
8
|
+
# == Class Methods ========================================================
|
9
|
+
|
10
|
+
# == Instance Methods =====================================================
|
11
|
+
|
12
|
+
def initialize(file)
|
13
|
+
case (file)
|
14
|
+
when IO
|
15
|
+
@handle = file
|
16
|
+
else
|
17
|
+
@handle = File.open(file.to_s, 'r:BINARY')
|
18
|
+
end
|
19
|
+
|
20
|
+
@data = @handle.read
|
21
|
+
@offset = 0
|
22
|
+
end
|
23
|
+
|
24
|
+
def eof?
|
25
|
+
@offset >= @data.length
|
26
|
+
end
|
27
|
+
|
28
|
+
def advance(count = 1)
|
29
|
+
@offset += count
|
30
|
+
end
|
31
|
+
|
32
|
+
def seek(offset)
|
33
|
+
@offset = offset
|
34
|
+
end
|
35
|
+
|
36
|
+
def peek(bytes)
|
37
|
+
@data[@offset, bytes]
|
38
|
+
end
|
39
|
+
|
40
|
+
def read(bytes)
|
41
|
+
data = @data[@offset, bytes]
|
42
|
+
|
43
|
+
@offset += bytes
|
44
|
+
|
45
|
+
data
|
46
|
+
end
|
47
|
+
|
48
|
+
def unpack_extended_float
|
49
|
+
extended = read(10).unpack('B80')[0]
|
50
|
+
|
51
|
+
sign = extended[0, 1]
|
52
|
+
exponent = extended[1, 15].to_i(2) - ((1 << 14) - 1)
|
53
|
+
fraction = extended[16, 64].to_i(2)
|
54
|
+
|
55
|
+
((sign == '1') ? -1.0 : 1.0) * (fraction.to_f / ((1 << 63) - 1)) * (2 ** exponent)
|
56
|
+
end
|
57
|
+
|
58
|
+
def unpack(format)
|
59
|
+
return if (@offset >= @data.length)
|
60
|
+
|
61
|
+
part = @data[@offset, @data.length].unpack(format)
|
62
|
+
advance(part.pack(format).length)
|
63
|
+
part
|
64
|
+
|
65
|
+
rescue TypeError
|
66
|
+
[ ]
|
67
|
+
end
|
68
|
+
end
|
data/raiff.gemspec
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{raiff}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Scott Tadman"]
|
12
|
+
s.date = %q{2010-12-06}
|
13
|
+
s.description = %q{Reads in AIFF file data and audio contents}
|
14
|
+
s.email = %q{github@tadman.ca}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.rdoc"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"README.rdoc",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"lib/raiff.rb",
|
25
|
+
"lib/raiff/chunk.rb",
|
26
|
+
"lib/raiff/chunk/common.rb",
|
27
|
+
"lib/raiff/chunk/data.rb",
|
28
|
+
"lib/raiff/chunk/form.rb",
|
29
|
+
"lib/raiff/chunk/sound_data.rb",
|
30
|
+
"lib/raiff/file.rb",
|
31
|
+
"raiff.gemspec",
|
32
|
+
"sample/example-24bit.aiff",
|
33
|
+
"test/helper.rb",
|
34
|
+
"test/test_raiff.rb"
|
35
|
+
]
|
36
|
+
s.homepage = %q{http://github.com/twg/raiff}
|
37
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.7}
|
40
|
+
s.summary = %q{Ruby AIFF Reading Library}
|
41
|
+
s.test_files = [
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/test_raiff.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
51
|
+
else
|
52
|
+
end
|
53
|
+
else
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
Binary file
|
data/test/helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
5
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
require 'raiff'
|
8
|
+
|
9
|
+
class Test::Unit::TestCase
|
10
|
+
def sample_file(name)
|
11
|
+
File.expand_path(File.join('..', 'sample', name), File.dirname(__FILE__))
|
12
|
+
end
|
13
|
+
end
|
data/test/test_raiff.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path('helper', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class TestRaiff < Test::Unit::TestCase
|
4
|
+
def test_empty
|
5
|
+
raiff = Raiff.new
|
6
|
+
|
7
|
+
assert raiff
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_open_short_sample
|
11
|
+
raiff = Raiff.open(sample_file('example-24bit.aiff'))
|
12
|
+
|
13
|
+
assert raiff
|
14
|
+
|
15
|
+
assert_equal 23493, raiff.sample_frames
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_failed_open
|
19
|
+
raised = false
|
20
|
+
|
21
|
+
raiff = Raiff.open('does.not.exist')
|
22
|
+
|
23
|
+
rescue Errno::ENOENT
|
24
|
+
raised = true
|
25
|
+
ensure
|
26
|
+
assert raised
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: raiff
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Scott Tadman
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-06 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Reads in AIFF file data and audio contents
|
22
|
+
email: github@tadman.ca
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.rdoc
|
29
|
+
files:
|
30
|
+
- .document
|
31
|
+
- .gitignore
|
32
|
+
- README.rdoc
|
33
|
+
- Rakefile
|
34
|
+
- VERSION
|
35
|
+
- lib/raiff.rb
|
36
|
+
- lib/raiff/chunk.rb
|
37
|
+
- lib/raiff/chunk/common.rb
|
38
|
+
- lib/raiff/chunk/data.rb
|
39
|
+
- lib/raiff/chunk/form.rb
|
40
|
+
- lib/raiff/chunk/sound_data.rb
|
41
|
+
- lib/raiff/file.rb
|
42
|
+
- raiff.gemspec
|
43
|
+
- sample/example-24bit.aiff
|
44
|
+
- test/helper.rb
|
45
|
+
- test/test_raiff.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://github.com/twg/raiff
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
segments:
|
69
|
+
- 0
|
70
|
+
version: "0"
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.3.7
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Ruby AIFF Reading Library
|
78
|
+
test_files:
|
79
|
+
- test/helper.rb
|
80
|
+
- test/test_raiff.rb
|