audio-playback 0.0.6 → 0.0.8

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.
@@ -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