audio-playback 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/README.md +81 -0
- data/bin/playback +66 -0
- data/lib/audio-playback.rb +55 -0
- data/lib/audio-playback/commandline.rb +50 -0
- data/lib/audio-playback/device.rb +61 -0
- data/lib/audio-playback/device/output.rb +113 -0
- data/lib/audio-playback/device/stream.rb +134 -0
- data/lib/audio-playback/file.rb +37 -0
- data/lib/audio-playback/playback.rb +152 -0
- data/lib/audio-playback/playback/frame.rb +72 -0
- data/lib/audio-playback/playback/frame_set.rb +117 -0
- data/lib/audio-playback/playback/stream_data.rb +52 -0
- data/lib/audio-playback/sound.rb +54 -0
- data/test/device/output_test.rb +141 -0
- data/test/device/stream_test.rb +54 -0
- data/test/device_test.rb +75 -0
- data/test/file_test.rb +105 -0
- data/test/helper.rb +59 -0
- data/test/media/1-mono-44100.aiff +0 -0
- data/test/media/1-mono-44100.wav +0 -0
- data/test/media/1-stereo-44100.aiff +0 -0
- data/test/media/1-stereo-44100.wav +0 -0
- data/test/playback/frame_set_test.rb +115 -0
- data/test/playback/frame_test.rb +46 -0
- data/test/playback/stream_data_test.rb +27 -0
- data/test/playback_test.rb +111 -0
- data/test/sound_test.rb +80 -0
- metadata +213 -0
data/test/helper.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
$:.unshift(File.join("..", "lib"))
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "mocha/test_unit"
|
5
|
+
require "shoulda-context"
|
6
|
+
require "audio-playback"
|
7
|
+
|
8
|
+
class MockOutput
|
9
|
+
|
10
|
+
attr_reader :num_channels
|
11
|
+
|
12
|
+
def initialize(id, options = {})
|
13
|
+
@id = id
|
14
|
+
@num_channels = options[:num_channels] || 2
|
15
|
+
end
|
16
|
+
|
17
|
+
def latency
|
18
|
+
1
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class MockStream
|
24
|
+
|
25
|
+
def initialize(output)
|
26
|
+
@output = output
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
module TestHelper
|
32
|
+
|
33
|
+
DEFAULT_OUTPUT_ID = 0
|
34
|
+
OUTPUT_INFO = [
|
35
|
+
{
|
36
|
+
:defaultHighOutputLatency => 2.3,
|
37
|
+
:maxOutputChannels => 1,
|
38
|
+
:name => "Test Output 1"
|
39
|
+
},
|
40
|
+
{
|
41
|
+
:defaultHighOutputLatency => 3.2,
|
42
|
+
:maxOutputChannels => 2,
|
43
|
+
:name => "Test Output 2"
|
44
|
+
}
|
45
|
+
]
|
46
|
+
|
47
|
+
def self.stub_portaudio
|
48
|
+
FFI::PortAudio::API.stubs(:Pa_GetDeviceCount).returns(2)
|
49
|
+
FFI::PortAudio::API.expects(:Pa_GetDeviceInfo).with(0).at_least_once.returns(TestHelper::OUTPUT_INFO[0])
|
50
|
+
FFI::PortAudio::API.expects(:Pa_GetDeviceInfo).with(1).at_least_once.returns(TestHelper::OUTPUT_INFO[1])
|
51
|
+
FFI::PortAudio::API.stubs(:Pa_GetDefaultOutputDevice).returns(TestHelper::DEFAULT_OUTPUT_ID)
|
52
|
+
FFI::PortAudio::API.stubs(:Pa_Initialize).returns(true)
|
53
|
+
FFI::PortAudio::API.stubs(:Pa_Terminate).returns(true)
|
54
|
+
AudioPlayback::Device::Stream.any_instance.stubs(:close).returns(true)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
TestHelper.stub_portaudio
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class AudioPlayback::Playback::FrameSetTest < Minitest::Test
|
4
|
+
|
5
|
+
context "FrameSet" do
|
6
|
+
|
7
|
+
context "#ensure_structure" do
|
8
|
+
|
9
|
+
context "sound has same number of channels as output" do
|
10
|
+
|
11
|
+
context "mono file, mono output" do
|
12
|
+
|
13
|
+
setup do
|
14
|
+
@path = "test/media/1-mono-44100.wav"
|
15
|
+
@file = AudioPlayback::File.new(@path)
|
16
|
+
@sound = AudioPlayback::Sound.new(@file)
|
17
|
+
@output = MockOutput.new(1, :num_channels => 1)
|
18
|
+
@playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
|
19
|
+
@data = @sound.data
|
20
|
+
@frames = AudioPlayback::Playback::FrameSet.new(@playback).slice(AudioPlayback::Playback::METADATA.size..-1)
|
21
|
+
end
|
22
|
+
|
23
|
+
should "have two channels of valid data" do
|
24
|
+
refute_nil @frames
|
25
|
+
assert @frames.kind_of?(Array)
|
26
|
+
refute_empty @frames
|
27
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @output.num_channels }
|
28
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @sound.num_channels }
|
29
|
+
assert @frames.all? { |frame_channels| frame_channels.all? { |frame| frame.kind_of?(Float) } }
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
context "stereo file, stereo output" do
|
35
|
+
|
36
|
+
setup do
|
37
|
+
@path = "test/media/1-stereo-44100.wav"
|
38
|
+
@file = AudioPlayback::File.new(@path)
|
39
|
+
@sound = AudioPlayback::Sound.new(@file)
|
40
|
+
@output = MockOutput.new(1, :num_channels => 2)
|
41
|
+
@playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
|
42
|
+
@data = @sound.data
|
43
|
+
@frames = AudioPlayback::Playback::FrameSet.new(@playback).slice(AudioPlayback::Playback::METADATA.size..-1)
|
44
|
+
end
|
45
|
+
|
46
|
+
should "have one channel of valid data" do
|
47
|
+
refute_nil @frames
|
48
|
+
assert @frames.kind_of?(Array)
|
49
|
+
refute_empty @frames
|
50
|
+
assert @frames.all? { |frame_channels| frame_channels.kind_of?(AudioPlayback::Playback::Frame) }
|
51
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @output.num_channels }
|
52
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @sound.num_channels }
|
53
|
+
assert @frames.all? { |frame_channels| frame_channels.all? { |frame| frame.kind_of?(Float) } }
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
context "sound has different number of channels than output" do
|
61
|
+
|
62
|
+
context "mono file, stereo output" do
|
63
|
+
|
64
|
+
setup do
|
65
|
+
@path = "test/media/1-mono-44100.wav"
|
66
|
+
@file = AudioPlayback::File.new(@path)
|
67
|
+
@sound = AudioPlayback::Sound.new(@file)
|
68
|
+
@output = MockOutput.new(1, :num_channels => 2)
|
69
|
+
@playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
|
70
|
+
@data = @sound.data
|
71
|
+
@frames = AudioPlayback::Playback::FrameSet.new(@playback).slice(AudioPlayback::Playback::METADATA.size..-1)
|
72
|
+
end
|
73
|
+
|
74
|
+
should "have two channels of valid data" do
|
75
|
+
refute_nil @frames
|
76
|
+
assert @frames.kind_of?(Array)
|
77
|
+
refute_empty @frames
|
78
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @output.num_channels }
|
79
|
+
refute @frames.any? { |frame_channels| frame_channels.count == @sound.num_channels }
|
80
|
+
assert @frames.all? { |frame_channels| frame_channels.all? { |frame| frame.kind_of?(Float) } }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
context "stereo file, mono output" do
|
86
|
+
|
87
|
+
setup do
|
88
|
+
@path = "test/media/1-stereo-44100.wav"
|
89
|
+
@file = AudioPlayback::File.new(@path)
|
90
|
+
@sound = AudioPlayback::Sound.new(@file)
|
91
|
+
@output = MockOutput.new(1, :num_channels => 1)
|
92
|
+
@playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
|
93
|
+
@data = @sound.data
|
94
|
+
@frames = AudioPlayback::Playback::FrameSet.new(@playback).slice(AudioPlayback::Playback::METADATA.size..-1)
|
95
|
+
end
|
96
|
+
|
97
|
+
should "have one channel of valid data" do
|
98
|
+
refute_nil @frames
|
99
|
+
assert @frames.kind_of?(Array)
|
100
|
+
refute_empty @frames
|
101
|
+
assert @frames.all? { |frame_channels| frame_channels.kind_of?(AudioPlayback::Playback::Frame) }
|
102
|
+
assert @frames.all? { |frame_channels| frame_channels.count == @output.num_channels }
|
103
|
+
refute @frames.any? { |frame_channels| frame_channels.count == @sound.num_channels }
|
104
|
+
assert @frames.all? { |frame_channels| frame_channels.all? { |frame| frame.kind_of?(Float) } }
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class AudioPlayback::Playback::FrameTest < Minitest::Test
|
4
|
+
|
5
|
+
context "Frame" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@frame = AudioPlayback::Playback::Frame.new([1, 2, 4])
|
9
|
+
assert_equal 3, @frame.size
|
10
|
+
end
|
11
|
+
|
12
|
+
context "#truncate" do
|
13
|
+
|
14
|
+
should "truncate the frame to the given size" do
|
15
|
+
@frame.truncate(1)
|
16
|
+
assert_equal 1, @frame.size
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
context "#fill" do
|
22
|
+
|
23
|
+
context "with :num_channels option" do
|
24
|
+
|
25
|
+
should "add n elements to the frame" do
|
26
|
+
@frame.fill(3, :num_channels => 3, :channels => [1,2,3])
|
27
|
+
assert_equal 4, @frame.size
|
28
|
+
assert_equal 0, @frame[0]
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with no options" do
|
34
|
+
|
35
|
+
should "add n elements to the frame" do
|
36
|
+
@frame.fill(4)
|
37
|
+
assert_equal 7, @frame.size
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class AudioPlayback::Playback::StreamDataTest < Minitest::Test
|
4
|
+
|
5
|
+
context "StreamData" do
|
6
|
+
|
7
|
+
context "#to_pointer" do
|
8
|
+
|
9
|
+
setup do
|
10
|
+
@path = "test/media/1-mono-44100.wav"
|
11
|
+
@file = AudioPlayback::File.new(@path)
|
12
|
+
@sound = AudioPlayback::Sound.new(@file)
|
13
|
+
@output = MockOutput.new(1, :num_channels => 1)
|
14
|
+
@playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
|
15
|
+
@data = @playback.data
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return a pointer" do
|
19
|
+
refute_nil @data
|
20
|
+
assert_kind_of FFI::Pointer, @data
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class AudioPlayback::PlaybackTest < Minitest::Test
|
4
|
+
|
5
|
+
context "Playback" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@path = "test/media/1-mono-44100.wav"
|
9
|
+
@sound = AudioPlayback::Sound.load(@path)
|
10
|
+
@output = AudioPlayback::Device::Output.by_id(0)
|
11
|
+
end
|
12
|
+
|
13
|
+
context ".play" do
|
14
|
+
|
15
|
+
setup do
|
16
|
+
AudioPlayback::Device::Stream.any_instance.expects(:start).once.returns(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
teardown do
|
20
|
+
AudioPlayback::Device::Stream.any_instance.unstub(:start)
|
21
|
+
end
|
22
|
+
|
23
|
+
should "start playback" do
|
24
|
+
@playback = AudioPlayback::Playback.play(@sound, @output)
|
25
|
+
refute_nil @playback
|
26
|
+
assert_kind_of AudioPlayback::Playback::Action, @playback
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
context "#start" do
|
32
|
+
|
33
|
+
setup do
|
34
|
+
@playback = AudioPlayback::Playback.new(@sound, @output)
|
35
|
+
AudioPlayback::Device::Stream.any_instance.expects(:start).once.returns(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
teardown do
|
39
|
+
AudioPlayback::Device::Stream.any_instance.unstub(:start)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "start playback" do
|
43
|
+
@result = @playback.start
|
44
|
+
assert_kind_of AudioPlayback::Playback::Action, @result
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "#block" do
|
50
|
+
|
51
|
+
setup do
|
52
|
+
@playback = AudioPlayback::Playback.new(@sound, @output)
|
53
|
+
AudioPlayback::Device::Stream.any_instance.expects(:block).once.returns(true)
|
54
|
+
end
|
55
|
+
|
56
|
+
teardown do
|
57
|
+
AudioPlayback::Device::Stream.any_instance.unstub(:block)
|
58
|
+
end
|
59
|
+
|
60
|
+
should "defer to stream" do
|
61
|
+
assert @playback.block
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
context "#report" do
|
67
|
+
|
68
|
+
setup do
|
69
|
+
@logger = Object.new
|
70
|
+
@playback = AudioPlayback::Playback.new(@sound, @output)
|
71
|
+
@logger.expects(:puts).at_least_once
|
72
|
+
end
|
73
|
+
|
74
|
+
teardown do
|
75
|
+
@logger.unstub(:puts)
|
76
|
+
end
|
77
|
+
|
78
|
+
should "do logging" do
|
79
|
+
assert @playback.report(@logger)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "#data_size" do
|
85
|
+
|
86
|
+
setup do
|
87
|
+
@playback = AudioPlayback::Playback.new(@sound, @output)
|
88
|
+
@size = @playback.data_size
|
89
|
+
@metadata_size = AudioPlayback::Playback::METADATA.count * AudioPlayback::Playback::FRAME_SIZE.size
|
90
|
+
@sound_data_size = (@sound.size * @sound.num_channels) * AudioPlayback::Playback::FRAME_SIZE.size
|
91
|
+
@total_size = @metadata_size + @sound_data_size
|
92
|
+
end
|
93
|
+
|
94
|
+
should "have data size" do
|
95
|
+
refute_nil @size
|
96
|
+
assert @size > 0
|
97
|
+
end
|
98
|
+
|
99
|
+
should "be larger than sound file data size" do
|
100
|
+
assert @size > @sound_data_size
|
101
|
+
end
|
102
|
+
|
103
|
+
should "be equal to sound file plus metadata size" do
|
104
|
+
assert_equal @total_size, @size
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
data/test/sound_test.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class AudioPlayback::SoundTest < Minitest::Test
|
4
|
+
|
5
|
+
context "Sound" do
|
6
|
+
|
7
|
+
context "#populate" do
|
8
|
+
|
9
|
+
context "mono" do
|
10
|
+
|
11
|
+
setup do
|
12
|
+
@path = "test/media/1-mono-44100.wav"
|
13
|
+
@file = AudioPlayback::File.new(@path)
|
14
|
+
@sound = AudioPlayback::Sound.new(@file)
|
15
|
+
@data = @sound.data
|
16
|
+
@size = @sound.size
|
17
|
+
end
|
18
|
+
|
19
|
+
should "populate data" do
|
20
|
+
refute_nil @data
|
21
|
+
refute_nil @size
|
22
|
+
assert @data.kind_of?(Array)
|
23
|
+
refute_empty @data
|
24
|
+
assert_equal @size, @data.size
|
25
|
+
refute_empty @data
|
26
|
+
assert @data.all? { |frame| frame.kind_of?(Float) }
|
27
|
+
assert @data.all? { |frame| frame >= -1 }
|
28
|
+
assert @data.all? { |frame| frame <= 1 }
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
context "stereo" do
|
34
|
+
|
35
|
+
setup do
|
36
|
+
@path = "test/media/1-stereo-44100.wav"
|
37
|
+
@file = AudioPlayback::File.new(@path)
|
38
|
+
@sound = AudioPlayback::Sound.new(@file)
|
39
|
+
@data = @sound.data
|
40
|
+
@size = @sound.size
|
41
|
+
end
|
42
|
+
|
43
|
+
should "populate data" do
|
44
|
+
refute_nil @data
|
45
|
+
refute_nil @size
|
46
|
+
assert @data.kind_of?(Array)
|
47
|
+
refute_empty @data
|
48
|
+
assert_equal @size, @data.size
|
49
|
+
assert @data.all? { |frame_channels| frame_channels.kind_of?(Array) }
|
50
|
+
assert @data.all? { |frame_channels| frame_channels.all? { |frame| frame.kind_of?(Float) } }
|
51
|
+
assert @data.all? { |frame_channels| frame_channels.all? { |frame| frame >= -1 } }
|
52
|
+
assert @data.all? { |frame_channels| frame_channels.all? { |frame| frame <= 1 } }
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
context "#report" do
|
60
|
+
|
61
|
+
setup do
|
62
|
+
@logger = Object.new
|
63
|
+
@path = "test/media/1-mono-44100.wav"
|
64
|
+
@sound = AudioPlayback::Sound.load(@path)
|
65
|
+
@logger.expects(:puts).times(4)
|
66
|
+
end
|
67
|
+
|
68
|
+
teardown do
|
69
|
+
@logger.unstub(:puts)
|
70
|
+
end
|
71
|
+
|
72
|
+
should "do logging" do
|
73
|
+
assert @sound.report(@logger)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|