audio-playback 0.0.6 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,7 +17,7 @@ module AudioPlayback
17
17
  end
18
18
 
19
19
  # Truncate the frame to the given size
20
- # @param [Fixnum] num
20
+ # @param [Integer] num
21
21
  # @return [Frame]
22
22
  def truncate(num)
23
23
  @frame.slice!(num..-1)
@@ -26,10 +26,10 @@ module AudioPlayback
26
26
 
27
27
  # Fill up the given number of channels at the end of the frame with duplicate data from the last
28
28
  # existing channel
29
- # @param [Fixnum] num
29
+ # @param [Integer] num
30
30
  # @param [Hash] options
31
- # @option options [Array<Fixnum>] :channels (required if :num_channels is provided)
32
- # @option options [Fixnum] :num_channels (required if :channels is provided)
31
+ # @option options [Array<Integer>] :channels (required if :num_channels is provided)
32
+ # @option options [Integer] :num_channels (required if :channels is provided)
33
33
  # @return [Boolean]
34
34
  def fill(num, options = {})
35
35
  if (channels = options[:channels]).nil?
@@ -43,8 +43,8 @@ module AudioPlayback
43
43
  private
44
44
 
45
45
  # Zero out the given number of channels in the frame starting with the given index
46
- # @param [Fixnum] index
47
- # @param [Fixnum] num_channels
46
+ # @param [Integer] index
47
+ # @param [Integer] num_channels
48
48
  # @return [Frame]
49
49
  def silence_channels(index, num_channels)
50
50
  @frame.fill(0, index, num_channels)
@@ -52,8 +52,8 @@ module AudioPlayback
52
52
  end
53
53
 
54
54
  # Fill the entire frame for the given channels
55
- # @param [Fixnum] num_channels
56
- # @param [Array<Fixnum>] channels
55
+ # @param [Integer] num_channels
56
+ # @param [Array<Integer>] channels
57
57
  # @return [Boolean]
58
58
  def fill_for_channels(num_channels, channels)
59
59
  values = @frame.dup
@@ -7,7 +7,7 @@ module AudioPlayback
7
7
 
8
8
  extend Forwardable
9
9
 
10
- def_delegators :@data, :[], :[]=, :flatten, :slice, :to_ary, :unshift
10
+ def_delegators :@data, :[], :[]=, :flatten, :length, :size, :slice, :to_ary, :unshift
11
11
 
12
12
  # @param [Playback::Action] playback
13
13
  def initialize(playback)
@@ -83,9 +83,9 @@ module AudioPlayback
83
83
  # Ensure that the channel structure of the frameset is according to the given number of channels
84
84
  # and to the given particular channels when provided
85
85
  # @param [Array<Frame>] data
86
- # @param [Fixnum] num_channels
86
+ # @param [Integer] num_channels
87
87
  # @param [Hash] options
88
- # @option options [Array<Fixnum>] :channels
88
+ # @option options [Array<Integer>] :channels
89
89
  # @return [Array<Frame>]
90
90
  def ensure_num_channels(data, num_channels, options = {})
91
91
  data.each do |frame|
@@ -6,21 +6,21 @@ module AudioPlayback
6
6
  class Mixer
7
7
 
8
8
  # Mix multiple sounds at equal amplitude
9
- # @param [Array<Array<Array<Fixnum>>>] sounds_data
10
- # @return [Array<Array<Fixnum>>]
9
+ # @param [Array<Array<Array<Integer>>>] sounds_data
10
+ # @return [Array<Array<Integer>>]
11
11
  def self.mix(sounds_data)
12
12
  mixer = new(sounds_data)
13
13
  mixer.mix
14
14
  end
15
15
 
16
- # @param [Array<Array<Array<Fixnum>>>] sounds_data
16
+ # @param [Array<Array<Array<Integer>>>] sounds_data
17
17
  def initialize(sounds_data)
18
18
  @data = sounds_data
19
19
  populate
20
20
  end
21
21
 
22
22
  # Mix multiple sounds at equal amplitude
23
- # @return [Array<Array<Fixnum>>]
23
+ # @return [Array<Array<Integer>>]
24
24
  def mix
25
25
  (0..@length-1).to_a.map { |index| mix_frame(index) }
26
26
  end
@@ -37,8 +37,8 @@ module AudioPlayback
37
37
 
38
38
  # Get all of the data frames for the given index
39
39
  # For example for index 3, two two channel sounds, frames(3) might give you [[1, 3], [2, 3]]
40
- # @param [Fixnum] index
41
- # @return [Array<Array<Fixnum>>]
40
+ # @param [Integer] index
41
+ # @return [Array<Array<Integer>>]
42
42
  def frames(index)
43
43
  @data.map { |sound_data| sound_data[index] }
44
44
  end
@@ -46,8 +46,8 @@ module AudioPlayback
46
46
  # Mix the frame with the given index
47
47
  # whereas frames(3) might give you [[1, 3], [2, 3]]
48
48
  # mix_frame(3) might give you [1.5, 3]
49
- # @param [Fixnum] index
50
- # @return [Array<Fixnum>]
49
+ # @param [Integer] index
50
+ # @return [Array<Integer>]
51
51
  def mix_frame(index)
52
52
  totals = frames(index).compact.transpose.map { |x| x && x.reduce(:+) || 0 }
53
53
  totals.map { |frame| frame / @depth }
@@ -5,6 +5,11 @@ module AudioPlayback
5
5
  # Playback data for the Device::Stream
6
6
  class StreamData
7
7
 
8
+ extend Forwardable
9
+
10
+ attr_reader :num_frames
11
+ def_delegators :@data, :[], :at, :length, :size
12
+
8
13
  # A C pointer version of the audio data
9
14
  # @param [Playback::Action] playback
10
15
  # @return [FFI::Pointer]
@@ -22,28 +27,40 @@ module AudioPlayback
22
27
  # Reset the stream metadata
23
28
  # @param [Boolean]
24
29
  def reset
25
- indexes = [
26
- Playback::METADATA.index(:pointer),
27
- Playback::METADATA.index(:is_eof)
28
- ]
29
- indexes.each { |index| @data[index] = 0.0 }
30
+ [:is_eof, :pointer].each { |key| set_metadata(key, 0.0) }
30
31
  true
31
32
  end
32
33
 
33
34
  # A C pointer version of the audio data
34
35
  # @return [FFI::Pointer]
35
36
  def to_pointer
36
- @pointer ||= FFI::LibC.malloc(@playback.data_size)
37
- @pointer.write_array_of_float(@data.flatten)
37
+ if @pointer.nil?
38
+ @pointer = FFI::LibC.malloc(@playback.data_size)
39
+ @pointer.write_array_of_float(@data.flatten)
40
+ end
38
41
  @pointer
39
42
  end
40
43
 
41
44
  private
42
45
 
46
+ # Set the metadata value with the given key to the given value
47
+ # @param [Symbol] key
48
+ # @param [Object] value
49
+ # @return [Object]
50
+ def set_metadata(key, value)
51
+ index = Playback::METADATA.index(key)
52
+ @data[index] = value
53
+ unless @pointer.nil?
54
+ @pointer.put_float32(index * Playback::FRAME_SIZE, value)
55
+ end
56
+ value
57
+ end
58
+
43
59
  # Populate the playback stream data
44
60
  # @return [FrameSet]
45
61
  def populate
46
62
  @data = FrameSet.new(@playback)
63
+ @num_frames = @data.size
47
64
  add_metadata
48
65
  @data
49
66
  end
@@ -51,10 +68,18 @@ module AudioPlayback
51
68
  # Add playback metadata to the stream data
52
69
  # @return [FrameSet]
53
70
  def add_metadata
54
- @data.unshift(0.0) # 3. is_eof
55
- @data.unshift(0.0) # 2. counter
71
+ if @playback.truncate?
72
+ end_frame = @playback.truncate[:end_frame]
73
+ start_frame = @playback.truncate[:start_frame]
74
+ end
75
+ @data.unshift(0.0) # 6. is_eof
76
+ @data.unshift(start_frame || 0.0) # 5. counter
77
+ loop_value = @playback.looping? ? 1.0 : 0.0
78
+ @data.unshift(loop_value) # 4. is_looping
79
+ @data.unshift(end_frame || @num_frames.to_f) # 3. end frame
80
+ @data.unshift(start_frame || 0.0) # 2. start frame
56
81
  @data.unshift(@playback.output.num_channels.to_f) # 1. num_channels
57
- @data.unshift(@playback.size.to_f) # 0. sample size
82
+ @data.unshift(@num_frames.to_f) # 0. frame set size (without metadata)
58
83
  @data
59
84
  end
60
85
 
@@ -0,0 +1,89 @@
1
+ module AudioPlayback
2
+
3
+ # A time position in a sound
4
+ class Position
5
+
6
+ class InvalidTime < RuntimeError
7
+ end
8
+
9
+ extend Forwardable
10
+
11
+ UNITS = [
12
+ 1, # second
13
+ 60, # minute
14
+ 3600 # hour
15
+ ].freeze
16
+
17
+ # Time format like (hh:)(mm:)ss(.ss)
18
+ FORMAT = /((\d+\:)(\d{2}\:)|(\d{2}\:))?\d{1,2}(\.\d+)?/.freeze
19
+
20
+ attr_reader :seconds
21
+ alias_method :to_seconds, :seconds
22
+ def_delegators :@seconds, :to_f
23
+
24
+ # @param [Numeric, String] seconds_or_time Time as (hh:)(mm:)ss(.ss)
25
+ def initialize(seconds_or_time)
26
+ seconds_or_time = seconds_or_time.to_s
27
+ validate_time(seconds_or_time)
28
+ populate(seconds_or_time)
29
+ end
30
+
31
+ # Multiply the seconds value of this Position by the given value
32
+ # @param [Numeric, Position] another
33
+ # @return [Float]
34
+ def *(another)
35
+ @seconds * another.to_f
36
+ end
37
+
38
+ # Add the seconds value of this Position to the given value
39
+ # @param [Numeric, Position] another
40
+ # @return [Float]
41
+ def +(another)
42
+ @seconds + another.to_f
43
+ end
44
+
45
+ private
46
+
47
+ # Validate that the time that was passed into the constructor is in the correct
48
+ # format. Raises InvalidTime error if not
49
+ # @param [Numeric, String] seconds_or_time Time as (hh:)(mm:)ss(.ss)
50
+ # @return [Boolean]
51
+ def validate_time(seconds_or_time)
52
+ unless seconds_or_time.match(FORMAT)
53
+ raise(InvalidTime)
54
+ end
55
+ true
56
+ end
57
+
58
+ # Validate that the segments of the time that was passed into the constructor
59
+ # are valid. For example that the minutes and or seconds values are below 60.
60
+ # Raises InvalidTime error if not
61
+ # @param [Array<String>] segments Time as [(hh), (mm), ss(.ss)]
62
+ # @return [Boolean]
63
+ def validate_segments(segments)
64
+ seconds = segments[0]
65
+ minutes = segments[1]
66
+ [seconds, minutes].compact.each do |segment|
67
+ if segment >= 60
68
+ raise(InvalidTime)
69
+ end
70
+ end
71
+ true
72
+ end
73
+
74
+ # Populate the seconds ivar using the time that was passed into the constructor
75
+ # @param [Numeric, String] seconds_or_time Time as (hh:)(mm:)ss(.ss)
76
+ # @return [Float]
77
+ def populate(seconds_or_time)
78
+ segments = seconds_or_time.split(":").map(&:to_f).reverse
79
+ validate_segments(segments)
80
+ @seconds = 0
81
+ segments.each_with_index do |segment, i|
82
+ @seconds += segment * UNITS[i]
83
+ end
84
+ @seconds
85
+ end
86
+
87
+ end
88
+
89
+ end
@@ -47,7 +47,7 @@ class AudioPlayback::Device::OutputTest < Minitest::Test
47
47
 
48
48
  should "populate id" do
49
49
  refute_nil @output.id
50
- assert_kind_of Fixnum, @output.id
50
+ assert_kind_of Integer, @output.id
51
51
  end
52
52
 
53
53
  should "populate latency" do
@@ -57,7 +57,7 @@ class AudioPlayback::Device::OutputTest < Minitest::Test
57
57
 
58
58
  should "populate number of channels" do
59
59
  refute_nil @output.num_channels
60
- assert_kind_of Fixnum, @output.num_channels
60
+ assert_kind_of Integer, @output.num_channels
61
61
  end
62
62
 
63
63
  should "populate name" do
@@ -115,7 +115,7 @@ class AudioPlayback::Device::OutputTest < Minitest::Test
115
115
 
116
116
  should "return correct num_channels" do
117
117
  refute_nil @output.num_channels
118
- assert_kind_of Fixnum, @output.num_channels
118
+ assert_kind_of Integer, @output.num_channels
119
119
  assert_equal @test_info[:maxOutputChannels], @output.num_channels
120
120
  end
121
121
 
@@ -130,7 +130,7 @@ class AudioPlayback::Device::OutputTest < Minitest::Test
130
130
 
131
131
  should "return correct id" do
132
132
  refute_nil @output.id
133
- assert_kind_of Fixnum, @output.id
133
+ assert_kind_of Integer, @output.id
134
134
  assert_equal @test_id, @output.id
135
135
  end
136
136
 
@@ -4,20 +4,108 @@ class AudioPlayback::Playback::StreamDataTest < Minitest::Test
4
4
 
5
5
  context "StreamData" do
6
6
 
7
+ setup do
8
+ @path = "test/media/1-mono-44100.wav"
9
+ @file = AudioPlayback::File.new(@path)
10
+ @sound = AudioPlayback::Sound.new(@file)
11
+ @output = MockOutput.new(1, :num_channels => 1)
12
+ @playback = AudioPlayback::Playback.new(@sound, @output, :stream => MockStream.new(@output))
13
+ end
14
+
15
+ context "#add_metadata" do
16
+
17
+ setup do
18
+ @data = AudioPlayback::Playback::StreamData.new(@playback)
19
+ end
20
+
21
+ should "add default metadata" do
22
+ assert_equal 0.0, @data[AudioPlayback::Playback::METADATA.index(:is_eof)]
23
+ assert_equal 0.0, @data[AudioPlayback::Playback::METADATA.index(:start_frame)]
24
+ assert_equal 0.0, @data[AudioPlayback::Playback::METADATA.index(:pointer)]
25
+ assert_equal @data.num_frames.to_f, @data[AudioPlayback::Playback::METADATA.index(:end_frame)]
26
+ assert_equal @data.num_frames.to_f, @data[AudioPlayback::Playback::METADATA.index(:size)]
27
+ assert_equal @playback.output.num_channels.to_f, @data[AudioPlayback::Playback::METADATA.index(:num_channels)]
28
+ end
29
+
30
+ end
31
+
32
+ context "#set_metadata" do
33
+
34
+ setup do
35
+ @data = AudioPlayback::Playback::StreamData.new(@playback)
36
+
37
+ # Validate
38
+ @pointer_index = AudioPlayback::Playback::METADATA.index(:pointer)
39
+ @eof_index = AudioPlayback::Playback::METADATA.index(:is_eof)
40
+
41
+ assert_equal 0.0, @data[@pointer_index]
42
+ assert_equal 0.0, @data[@eof_index]
43
+
44
+ # Set metadata to non zero values
45
+ @data.send(:set_metadata, :pointer, 4.0)
46
+ @data.send(:set_metadata, :is_eof, 1.0)
47
+ end
48
+
49
+ should "change values" do
50
+ assert_equal 4.0, @data[@pointer_index]
51
+ assert_equal 1.0, @data[@eof_index]
52
+ end
53
+
54
+ end
55
+
56
+ context "#reset" do
57
+
58
+ setup do
59
+ @data = AudioPlayback::Playback::StreamData.new(@playback)
60
+
61
+ # Set metadata to non zero values
62
+ @data.send(:set_metadata, :pointer, 4.0)
63
+ @data.send(:set_metadata, :is_eof, 1.0)
64
+
65
+ # Validate metadata
66
+ indexes = [:pointer, :is_eof].map do |key|
67
+ AudioPlayback::Playback::METADATA.index(key)
68
+ end
69
+ @nonzero_values = indexes.map { |index| @data[index] }
70
+ assert @nonzero_values.all? { |value| value > 0.0 }
71
+
72
+ # Run reset
73
+ @data.reset
74
+
75
+ # Collect metadata
76
+ @reset_values = indexes.map { |index| @data[index] }
77
+ end
78
+
79
+ should "reset data" do
80
+ refute_empty @reset_values
81
+ assert @reset_values.all? { |value| value == 0.0 }
82
+ end
83
+
84
+ end
85
+
7
86
  context "#to_pointer" do
8
87
 
9
88
  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
89
+ @data = AudioPlayback::Playback::StreamData.new(@playback)
90
+ @pointer = @data.to_pointer
91
+ end
92
+
93
+ should "return a ffi pointer" do
94
+ refute_nil @pointer
95
+ assert_kind_of FFI::Pointer, @pointer
96
+ end
97
+
98
+ end
99
+
100
+ context ".to_pointer" do
101
+
102
+ setup do
103
+ @pointer = AudioPlayback::Playback::StreamData.to_pointer(@playback)
16
104
  end
17
105
 
18
- should "return a stream data object" do
19
- refute_nil @data
20
- assert_kind_of AudioPlayback::Playback::StreamData, @data
106
+ should "return a ffi pointer" do
107
+ refute_nil @pointer
108
+ assert_kind_of FFI::Pointer, @pointer
21
109
  end
22
110
 
23
111
  end
@@ -5,15 +5,225 @@ class AudioPlayback::PlaybackTest < Minitest::Test
5
5
  context "Playback" do
6
6
 
7
7
  setup do
8
- @path = "test/media/1-mono-44100.wav"
8
+ @sample_rate = 44100
9
+ @path = "test/media/1-mono-#{@sample_rate}.wav"
9
10
  @sound = AudioPlayback::Sound.load(@path)
10
11
  @output = AudioPlayback::Device::Output.by_id(0)
11
12
  end
12
13
 
14
+ context "#initialize" do
15
+
16
+ context "with no truncation" do
17
+
18
+ setup do
19
+ @playback = AudioPlayback::Playback.new(@sound, @output)
20
+ end
21
+
22
+ should "have no truncation params" do
23
+ assert_nil @playback.truncate
24
+ end
25
+
26
+ end
27
+
28
+ context "with truncation" do
29
+
30
+ context "with seek only" do
31
+
32
+ setup do
33
+ @seek = 0.1
34
+ @playback = AudioPlayback::Playback.new(@sound, @output, seek: @seek)
35
+ end
36
+
37
+ should "have truncation params" do
38
+ refute_nil @playback.truncate
39
+ assert_kind_of Hash, @playback.truncate
40
+ end
41
+
42
+ should "have correct start frame value" do
43
+ refute_nil @playback.truncate[:start_frame]
44
+ assert_equal (@seek * @sample_rate).to_i, @playback.truncate[:start_frame]
45
+ end
46
+
47
+ should "have no duration value" do
48
+ assert_nil @playback.truncate[:end_frame]
49
+ end
50
+
51
+ end
52
+
53
+ context "with duration only" do
54
+
55
+ setup do
56
+ @duration = 0.2
57
+ @playback = AudioPlayback::Playback.new(@sound, @output, duration: @duration)
58
+ end
59
+
60
+ should "have truncation params" do
61
+ refute_nil @playback.truncate
62
+ assert_kind_of Hash, @playback.truncate
63
+ end
64
+
65
+ should "have no start frame value" do
66
+ assert_nil @playback.truncate[:start_frame]
67
+ end
68
+
69
+ should "have correct end frame value" do
70
+ refute_nil @playback.truncate[:end_frame]
71
+ assert_equal (@duration * @sample_rate).to_i, @playback.truncate[:end_frame]
72
+ end
73
+
74
+ end
75
+
76
+ context "with end position only" do
77
+
78
+ setup do
79
+ @end_position = 0.3
80
+ @playback = AudioPlayback::Playback.new(@sound, @output, end_position: @end_position)
81
+ end
82
+
83
+ should "have truncation params" do
84
+ refute_nil @playback.truncate
85
+ assert_kind_of Hash, @playback.truncate
86
+ end
87
+
88
+ should "have no start frame value" do
89
+ assert_nil @playback.truncate[:start_frame]
90
+ end
91
+
92
+ should "have correct end frame value" do
93
+ refute_nil @playback.truncate[:end_frame]
94
+ assert_equal (@end_position * @sample_rate).to_i, @playback.truncate[:end_frame]
95
+ end
96
+
97
+ end
98
+
99
+ context "with seek and duration" do
100
+
101
+ setup do
102
+ @seek = 0.4
103
+ @duration = 0.5
104
+ @playback = AudioPlayback::Playback.new(@sound, @output, seek: @seek, duration: @duration)
105
+ end
106
+
107
+ should "have truncation params" do
108
+ refute_nil @playback.truncate
109
+ assert_kind_of Hash, @playback.truncate
110
+ end
111
+
112
+ should "have correct start frame value" do
113
+ refute_nil @playback.truncate[:start_frame]
114
+ assert_equal (@seek * @sample_rate).to_i, @playback.truncate[:start_frame]
115
+ end
116
+
117
+ should "have correct end frame value" do
118
+ refute_nil @playback.truncate[:end_frame]
119
+ assert_equal ((@duration + @seek) * @sample_rate).to_i, @playback.truncate[:end_frame]
120
+ end
121
+
122
+ end
123
+
124
+ context "with seek and end position" do
125
+
126
+ context "valid" do
127
+
128
+ setup do
129
+ @seek = 0.4
130
+ @end_position = 0.5
131
+ @playback = AudioPlayback::Playback.new(@sound, @output, seek: @seek, end_position: @end_position)
132
+ end
133
+
134
+ should "have truncation params" do
135
+ refute_nil @playback.truncate
136
+ assert_kind_of Hash, @playback.truncate
137
+ end
138
+
139
+ should "have correct start frame value" do
140
+ refute_nil @playback.truncate[:start_frame]
141
+ assert_equal (@seek * @sample_rate).to_i, @playback.truncate[:start_frame]
142
+ end
143
+
144
+ should "have correct end frame value" do
145
+ refute_nil @playback.truncate[:end_frame]
146
+ assert_equal (@end_position * @sample_rate).to_i, @playback.truncate[:end_frame]
147
+ end
148
+
149
+ end
150
+
151
+ context "invalid" do
152
+
153
+ setup do
154
+ @seek = 0.7
155
+ @end_position = 0.6
156
+ end
157
+
158
+ should "raise exception" do
159
+ assert_raises AudioPlayback::Playback::InvalidTruncation do
160
+ @playback = AudioPlayback::Playback.new(@sound, @output, seek: @seek, end_position: @end_position)
161
+ end
162
+ end
163
+
164
+ end
165
+
166
+ end
167
+
168
+ context "with duration and end position" do
169
+
170
+ setup do
171
+ @duration = 0.8
172
+ @end_position = 0.9
173
+ @playback = AudioPlayback::Playback.new(@sound, @output, duration: @duration, end_position: @end_position)
174
+ end
175
+
176
+ should "have truncation params" do
177
+ refute_nil @playback.truncate
178
+ assert_kind_of Hash, @playback.truncate
179
+ end
180
+
181
+ should "have no start frame value" do
182
+ assert_nil @playback.truncate[:start_frame]
183
+ end
184
+
185
+ should "ignore end_position, use duration" do
186
+ refute_nil @playback.truncate[:end_frame]
187
+ assert_equal (@duration * @sample_rate).to_i, @playback.truncate[:end_frame]
188
+ end
189
+
190
+ end
191
+
192
+ context "with seek, duration and end position" do
193
+
194
+ setup do
195
+ @duration = 1.0
196
+ @seek = 1.1
197
+ @end_position = 1.2
198
+ @playback = AudioPlayback::Playback.new(@sound, @output, seek: @seek, duration: @duration, end_position: @end_position)
199
+ end
200
+
201
+ should "have truncation params" do
202
+ refute_nil @playback.truncate
203
+ assert_kind_of Hash, @playback.truncate
204
+ end
205
+
206
+ should "have correct start frame value" do
207
+ refute_nil @playback.truncate[:start_frame]
208
+ assert_equal (@seek * @sample_rate).to_i, @playback.truncate[:start_frame]
209
+ end
210
+
211
+ should "ignore end_position, use duration" do
212
+ refute_nil @playback.truncate[:end_frame]
213
+ assert_equal ((@duration + @seek) * @sample_rate).to_i, @playback.truncate[:end_frame]
214
+ end
215
+
216
+ end
217
+
218
+ end
219
+
220
+ end
221
+
13
222
  context ".play" do
14
223
 
15
224
  setup do
16
225
  AudioPlayback::Device::Stream.any_instance.expects(:start).once.returns(true)
226
+ @playback = AudioPlayback::Playback.play(@sound, @output)
17
227
  end
18
228
 
19
229
  teardown do
@@ -21,11 +231,14 @@ class AudioPlayback::PlaybackTest < Minitest::Test
21
231
  end
22
232
 
23
233
  should "start playback" do
24
- @playback = AudioPlayback::Playback.play(@sound, @output)
25
234
  refute_nil @playback
26
235
  assert_kind_of AudioPlayback::Playback::Action, @playback
27
236
  end
28
237
 
238
+ should "not have truncation params" do
239
+ assert_nil @playback.truncate
240
+ end
241
+
29
242
  end
30
243
 
31
244
  context "#start" do
@@ -81,6 +294,49 @@ class AudioPlayback::PlaybackTest < Minitest::Test
81
294
 
82
295
  end
83
296
 
297
+ context "#number_of_seconds_to_number_of_frames" do
298
+
299
+ setup do
300
+ @playback = AudioPlayback::Playback.new(@sound, @output)
301
+ end
302
+
303
+ should "convert seconds to frames" do
304
+ assert_equal 3 * 44100, @playback.send(:number_of_seconds_to_number_of_frames, 3)
305
+ assert_equal 10 * 44100, @playback.send(:number_of_seconds_to_number_of_frames, 10)
306
+ assert_equal 30 * 44100, @playback.send(:number_of_seconds_to_number_of_frames, 30)
307
+ assert_equal 5000 * 44100, @playback.send(:number_of_seconds_to_number_of_frames, 5000)
308
+ end
309
+
310
+ end
311
+
312
+ context "#truncate_requested?" do
313
+
314
+ context "with truncation" do
315
+
316
+ setup do
317
+ @playback = AudioPlayback::Playback.new(@sound, @output)
318
+ end
319
+
320
+ should "have truncation params" do
321
+ assert @playback.send(:truncate_requested?, seek: 3, duration: 2)
322
+ end
323
+
324
+ end
325
+
326
+ context "without truncation" do
327
+
328
+ setup do
329
+ @playback = AudioPlayback::Playback.new(@sound, @output)
330
+ end
331
+
332
+ should "have truncation params" do
333
+ refute @playback.send(:truncate_requested?, {})
334
+ end
335
+
336
+ end
337
+
338
+ end
339
+
84
340
  context "#data_size" do
85
341
 
86
342
  setup do