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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 86feb1c8a53cb706283d6aefbf0cbee23d028ca8
4
- data.tar.gz: 8679d8cc6876bedd0cce0a22878b424c2fda5e80
3
+ metadata.gz: 0040badaf2c68a1886248157f4448ab24609dccf
4
+ data.tar.gz: f22b0f7d1615d6b863e493ea5744574ea06a9321
5
5
  SHA512:
6
- metadata.gz: 48cf6147b24c28099b0e32cf04cbadde493405ab99da91a102603850cd5c6588d3485814bccf8ca73c6227ae74f4c9be6f65df9ced1e7554d1440f7d0da78133
7
- data.tar.gz: 723554296063b7c2169e7ec20b6197a47c18b5088cc5ef9e45723871a271db2c1dd65ef2565e4ae345dcc8999c9d41547147228168b29dc469d5e9b8dce6c1c6
6
+ metadata.gz: cfdddbf4c6f4ea7050ddeff2a9ddf2a37d1484d0fe15d2f056f86001b6fed3cb2aefd02ae6488ed2fa08ec06eef0125e280ea1f215eb9f53803d443e5b67e350
7
+ data.tar.gz: 04f79ca3da8f374692d5a79ace294362ecfa79455f46180ac5d8c9e6eff00d779c2a9647218674d9b2b47ead49e09c7ce5da05e37e497a3ee923321ac02b9760
data/README.md CHANGED
@@ -35,10 +35,18 @@ Or if you're using Bundler, add this to your Gemfile
35
35
 
36
36
  * `-c` Output audio to the given channel(s). Eg `-c 0,1` will direct audio to channels 0 and 1. Defaults to use channels 0 and 1 on the selected device
37
37
 
38
+ * `-d` Duration. Will stop after the given amount of time. Eg `-d 56` stops after 56 seconds of playback
39
+
40
+ * `-e` End position. Will stop at the given absolute time, irregardless of seek. Eg `-e 56` stops at 56 seconds. `-s 01:09:30 -e 01:10:00` stops at 1 hour 10 minutes after 30 seconds of playback
41
+
38
42
  * `-o` Output device id or name. Defaults to the system default
39
43
 
44
+ * `-s` Seek to given time position. Eg `-s 56` seeks to 56 seconds and `-s 01:10:00` seeks to 1 hour 10 min.
45
+
40
46
  * `-v` or `--verbose` Verbose
41
47
 
48
+ * `--loop` Loop playback continuously
49
+
42
50
  * `--list-devices` List the available audio output devices
43
51
 
44
52
 
@@ -94,4 +102,4 @@ More Ruby code examples:
94
102
 
95
103
  Licensed under Apache 2.0, See the file LICENSE
96
104
 
97
- Copyright (c) 2015-2016 [Ari Russo](http://arirusso.com)
105
+ Copyright (c) 2015-2017 [Ari Russo](http://arirusso.com)
@@ -14,12 +14,19 @@ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
14
14
  #
15
15
  # * `-c` Output audio to the given channel(s). Eg `-c 0,1` will direct audio to channels 0 and 1. Defaults to use channels 0 and 1 on the selected device
16
16
  #
17
+ # * `-d` Duration. Will stop after the given amount of time. Eg `-d 56` stops after 56 seconds of playback
18
+ #
19
+ # * `-e` End position. Will stop at the given absolute time, irregardless of seek. Eg `-e 56` stops at 56 seconds. `-s 01:09:30 -e 01:10:00` stops at 1 hour 10 minutes after 30 seconds of playback
20
+ #
17
21
  # * `-o` Output device id or name. Defaults to the system default
18
22
  #
23
+ # * `-s` Seek to given time position. Eg `-s 56` seeks to 56 seconds and `-s 01:10:00` seeks to 1 hour 10 min.
24
+ #
19
25
  # * `-v` or `--verbose` Verbose
20
- #
26
+ #
21
27
  # * `--list-devices` List the available audio output devices
22
28
  #
29
+ # * `--loop` Loop playback continuously
23
30
  #
24
31
 
25
32
  require "audio-playback"
@@ -2,7 +2,7 @@
2
2
  # AudioPlayback
3
3
  # Play audio files at the command line or with Ruby
4
4
  #
5
- # (c)2015 Ari Russo
5
+ # (c)2015-2017 Ari Russo
6
6
  # Apache 2.0 License
7
7
  # https://github.com/arirusso/audio-playback
8
8
  #
@@ -19,25 +19,31 @@ require "audio-playback/playback"
19
19
 
20
20
  # classes
21
21
  require "audio-playback/file"
22
+ require "audio-playback/position"
22
23
  require "audio-playback/sound"
23
24
 
24
25
  # Play audio files
25
26
  module AudioPlayback
26
27
 
27
- VERSION = "0.0.6"
28
+ VERSION = "0.0.8"
28
29
 
29
30
  # Convenience method to play an audio file
30
31
  # @param [Array<::File>, Array<String>, ::File, String] file_paths
31
32
  # @param [Hash] options
32
- # @option options [Fixnum] :buffer_size Buffer size in bytes. Defaults to 4096
33
- # @option options [Array<Fixnum>, Fixnum] :channels (or: :channel) Output audio to the given channel(s). Eg `:channels => [0,1]` will direct the audio to channels 0 and 1. Defaults to use all available channels
33
+ # @option options [Integer] :buffer_size Buffer size in bytes. Defaults to 4096
34
+ # @option options [Array<Integer>, Integer] :channels (or: :channel) Output audio to the given channel(s). Eg `:channels => [0,1]` will direct the audio to channels 0 and 1. Defaults to use all available channels
35
+ # @option options [Numeric] :duration Play for given time in seconds
36
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
37
+ # @option options [Boolean] :is_looping (or :loop) Whether to loop audio
34
38
  # @option options [Float] :latency Latency in seconds. Defaults to use the default latency for the selected output device
35
39
  # @option options [IO] :logger Logger object
36
- # @option options [Fixnum, String] :output_device (or: :output) Output device id or name
40
+ # @option options [Numeric] :seek Start at given time position in seconds
41
+ # @option options [Integer, String] :output_device (or: :output) Output device id or name
37
42
  def self.play(file_paths, options = {})
38
43
  sounds = Array(file_paths).map { |path| Sound.load(path, options) }
39
44
  requested_device = options[:output_device] || options[:output]
40
45
  output = Device::Output.by_name(requested_device) || Device::Output.by_id(requested_device) || Device.default_output
46
+ options[:is_looping] ||= options[:loop]
41
47
  Playback.play(sounds, output, options)
42
48
  end
43
49
 
@@ -18,6 +18,20 @@ module AudioPlayback
18
18
  :name => "Direct to channel(s)"
19
19
  },
20
20
 
21
+ :duration => {
22
+ :short => "-d",
23
+ :long => "--duration [seconds]",
24
+ :name => "Duration",
25
+ :type => String
26
+ },
27
+
28
+ :end_position => {
29
+ :short => "-e",
30
+ :long => "--end-position [seconds]",
31
+ :name => "End position",
32
+ :type => String
33
+ },
34
+
21
35
  :latency => {
22
36
  :short => "-l",
23
37
  :long => "--latency [seconds]",
@@ -30,6 +44,18 @@ module AudioPlayback
30
44
  :name => "List devices"
31
45
  },
32
46
 
47
+ :loop => {
48
+ :long => "--loop",
49
+ :name => "Loop"
50
+ },
51
+
52
+ :seek => {
53
+ :short => "-s",
54
+ :long => "--seek [seconds]",
55
+ :name => "Seek",
56
+ :type => String
57
+ },
58
+
33
59
  :logger => {
34
60
  :short => "-v",
35
61
  :long => "--verbose",
@@ -43,7 +69,7 @@ module AudioPlayback
43
69
  :type => String,
44
70
  :name => "Output device for playback"
45
71
  }
46
- }
72
+ }.freeze
47
73
 
48
74
  end
49
75
 
@@ -21,7 +21,7 @@ module AudioPlayback
21
21
  end
22
22
 
23
23
  # Get a device by its ID
24
- # @param [Fixnum] id
24
+ # @param [Integer] id
25
25
  # @return [Output]
26
26
  def by_id(id)
27
27
  outputs.find { |device| [device, device.id].include?(id) }
@@ -41,7 +41,7 @@ module AudioPlayback
41
41
  end
42
42
 
43
43
  # Get system device info given a device ID
44
- # @param [Fixnum] id
44
+ # @param [Integer] id
45
45
  # @return [FFI::PortAudio::API::PaDeviceInfo]
46
46
  def device_info(id)
47
47
  FFI::PortAudio::API.Pa_GetDeviceInfo(id)
@@ -50,7 +50,7 @@ module AudioPlayback
50
50
  private
51
51
 
52
52
  # Is the device with the given ID an output?
53
- # @param [Fixnum] id
53
+ # @param [Integer] id
54
54
  # @return [Boolean]
55
55
  def output?(id)
56
56
  device_info(id)[:maxOutputChannels] > 0
@@ -42,7 +42,7 @@ module AudioPlayback
42
42
  end
43
43
 
44
44
  # Select an output device by ID
45
- # @param [Fixnum] id
45
+ # @param [Integer] id
46
46
  # @return [Output]
47
47
  def self.by_id(id)
48
48
  Device.by_id(id)
@@ -55,7 +55,7 @@ module AudioPlayback
55
55
  Device.by_name(name)
56
56
  end
57
57
 
58
- # @param [Fixnum] id
58
+ # @param [Integer] id
59
59
  # @param [Hash] options
60
60
  # @option options [Float] :latency Device latency in seconds
61
61
  def initialize(id, options = {})
@@ -71,13 +71,13 @@ module AudioPlayback
71
71
  end
72
72
 
73
73
  # Number of channels the device supports
74
- # @return [Fixnum]
74
+ # @return [Integer]
75
75
  def num_channels
76
76
  @resource[:channelCount]
77
77
  end
78
78
 
79
79
  # ID of the device
80
- # @return [Fixnum]
80
+ # @return [Integer]
81
81
  def id
82
82
  @resource[:device]
83
83
  end
@@ -91,7 +91,7 @@ module AudioPlayback
91
91
  end
92
92
 
93
93
  # Populate the output
94
- # @param [Fixnum] id
94
+ # @param [Integer] id
95
95
  # @param [Hash] options
96
96
  # @option options [Float] :latency
97
97
  # @return [FFI::PortAudio::API::PaStreamParameters]
@@ -54,10 +54,10 @@ module AudioPlayback
54
54
  def block
55
55
  begin
56
56
  while active?
57
- sleep(0.0001)
57
+ sleep(0.001)
58
58
  end
59
59
  while FFI::PortAudio::API.Pa_IsStreamActive(@stream.read_pointer) != :paNoError
60
- sleep(1)
60
+ sleep(0.1)
61
61
  end
62
62
  rescue SystemExit, Interrupt
63
63
  # Control-C
@@ -74,7 +74,7 @@ module AudioPlayback
74
74
  # @param [Playback] playback
75
75
  # @return [Boolean]
76
76
  def open_stream(playback)
77
- @userdata = playback.data.to_pointer
77
+ @userdata ||= playback.data.to_pointer
78
78
  FFI::PortAudio::API.Pa_OpenStream(@stream, @input, @output, @freq, @frames, @flags, @method, @userdata)
79
79
  true
80
80
  end
@@ -112,15 +112,23 @@ module AudioPlayback
112
112
  # @param [Playback] playback
113
113
  # @return [Boolean]
114
114
  def open_playback(playback)
115
- @freq = playback.sample_rate.to_i
116
- @frames = playback.buffer_size
117
- @flags = FFI::PortAudio::API::NoFlag
118
- @stream = FFI::Buffer.new(:pointer)
119
- @method = method(:process)
115
+ populate_stream_playback(playback)
120
116
  open_stream(playback)
121
117
  true
122
118
  end
123
119
 
120
+ # Initialize the stream's playback properties
121
+ # @param [Playback] playback
122
+ # @return [Boolean]
123
+ def populate_stream_playback(playback)
124
+ @freq ||= playback.sample_rate.to_i
125
+ @frames ||= playback.buffer_size
126
+ @flags ||= FFI::PortAudio::API::NoFlag
127
+ @stream ||= FFI::Buffer.new(:pointer)
128
+ @method ||= method(:process)
129
+ true
130
+ end
131
+
124
132
  # Report about the stream
125
133
  # @param [Playback] playback
126
134
  # @param [IO] logger
@@ -135,41 +143,58 @@ module AudioPlayback
135
143
  #puts "Entering callback at #{Time.now.to_f}"
136
144
  counter = user_data.get_float32(Playback::METADATA.index(:pointer) * Playback::FRAME_SIZE).to_i
137
145
  #puts "Frame: #{counter}"
138
- sample_size = user_data.get_float32(Playback::METADATA.index(:size) * Playback::FRAME_SIZE).to_i
139
- #puts "Sample size: #{sample_size}"
146
+ audio_data_size = user_data.get_float32(Playback::METADATA.index(:size) * Playback::FRAME_SIZE).to_i
147
+ #puts "Sample size: #{audio_data_size}"
140
148
  num_channels = user_data.get_float32(Playback::METADATA.index(:num_channels) * Playback::FRAME_SIZE).to_i
141
149
  #puts "Num Channels: #{num_channels}"
150
+ start_frame = user_data.get_float32(Playback::METADATA.index(:start_frame) * Playback::FRAME_SIZE).to_i
151
+ #puts "Start point: #{start_frame}"
152
+ end_frame = user_data.get_float32(Playback::METADATA.index(:end_frame) * Playback::FRAME_SIZE).to_i
153
+ #puts "Duration: #{duration}"
154
+ is_looping = user_data.get_float32(Playback::METADATA.index(:is_looping) * Playback::FRAME_SIZE).to_i > 0
155
+ #puts "Is looping: #{is_looping}"
156
+ end_frame = [end_frame, audio_data_size].min
142
157
  is_eof = false
143
- if counter >= sample_size - frames_per_buffer
144
- if counter < sample_size
145
- buffer_size = sample_size.divmod(frames_per_buffer).last
158
+ end_window = end_frame - frames_per_buffer
159
+ if counter >= end_window
160
+ if counter == end_frame
161
+ is_eof = true
162
+ elsif counter < end_frame
163
+ buffer_size = end_frame.divmod(frames_per_buffer).last
146
164
  #puts "Truncated buffer size: #{buffer_size}"
147
165
  difference = frames_per_buffer - buffer_size
148
166
  #puts "Adding #{difference} frames of null audio"
149
167
  extra_data = [0] * difference * num_channels
150
168
  is_eof = true
151
169
  else
170
+ # p "Aborting (counter: #{counter}, end_frame: #{end_frame})"
152
171
  return :paAbort
153
172
  end
154
173
  end
155
174
  buffer_size ||= frames_per_buffer
156
175
  #puts "Size per buffer per channel: #{frames_per_buffer}"
157
- offset = ((counter * num_channels) + Playback::METADATA.count) * Playback::FRAME_SIZE
176
+ offset = (((counter + start_frame) * num_channels) + Playback::METADATA.count) * Playback::FRAME_SIZE
158
177
  #puts "Starting at location: #{offset}"
159
178
  data = user_data.get_array_of_float32(offset, buffer_size * num_channels)
160
179
  data += extra_data unless extra_data.nil?
161
180
  #puts "This buffer size: #{data.size}"
162
181
  #puts "Writing to output"
163
182
  output.write_array_of_float(data)
164
- counter += frames_per_buffer
165
- user_data.put_float32(Playback::METADATA.index(:pointer) * Playback::FRAME_SIZE, counter.to_f) # update counter
183
+ next_counter = counter + frames_per_buffer
166
184
  if is_eof
167
- #puts "Marking eof"
168
- user_data.put_float32(Playback::METADATA.index(:is_eof) * Playback::FRAME_SIZE, 1.0) # mark eof
169
- :paComplete
185
+ if is_looping
186
+ #puts "Looping to beginning"
187
+ next_counter = start_frame
188
+ :paContinue
189
+ else
190
+ #puts "Marking eof"
191
+ user_data.put_float32(Playback::METADATA.index(:is_eof) * Playback::FRAME_SIZE, 1.0) # mark eof
192
+ :paComplete
193
+ end
170
194
  else
171
195
  :paContinue
172
196
  end
197
+ user_data.put_float32(Playback::METADATA.index(:pointer) * Playback::FRAME_SIZE, next_counter.to_f) # update counter
173
198
  #puts "Exiting callback at #{Time.now.to_f}"
174
199
  result
175
200
  end
@@ -13,23 +13,41 @@ module AudioPlayback
13
13
 
14
14
  FRAME_SIZE = FFI::TYPE_FLOAT32.size
15
15
 
16
- METADATA = [:size, :num_channels, :pointer, :is_eof].freeze
16
+ METADATA = [:size, :num_channels, :start_frame, :end_frame, :is_looping, :pointer, :is_eof].freeze
17
+
18
+ class InvalidChannels < RuntimeError
19
+ end
20
+
21
+ class InvalidTruncation < RuntimeError
22
+ end
17
23
 
18
24
  # Action of playing back an audio file
19
25
  class Action
20
26
 
21
27
  extend Forwardable
22
28
 
23
- attr_reader :buffer_size, :channels, :data, :output, :num_channels, :sounds, :stream
29
+ attr_reader :buffer_size,
30
+ :channels,
31
+ :data,
32
+ :num_channels,
33
+ :output,
34
+ :sounds,
35
+ :stream,
36
+ :truncate
37
+
24
38
  def_delegators :@sounds, :audio_files
25
- def_delegators :@data, :reset
39
+ def_delegators :@data, :reset, :size
26
40
 
27
41
  # @param [Array<Sound>, Sound] sounds
28
42
  # @param [Output] output
29
43
  # @param [Hash] options
30
- # @option options [Fixnum] :buffer_size
31
- # @option options [Array<Fixnum>, Fixnum] :channels (or: :channel)
44
+ # @option options [Integer] :buffer_size
45
+ # @option options [Array<Integer>, Integer] :channels (or: :channel)
46
+ # @option options [Numeric] :duration Play for given time in seconds
47
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
48
+ # @option options [Boolean] :is_looping Whether to loop audio
32
49
  # @option options [IO] :logger
50
+ # @option options [Numeric] :seek Start at given time position in seconds
33
51
  # @option options [Stream] :stream
34
52
  def initialize(sounds, output, options = {})
35
53
  @sounds = Array(sounds)
@@ -41,17 +59,11 @@ module AudioPlayback
41
59
  end
42
60
 
43
61
  # Sample rate of the playback sound
44
- # @return [Fixnum]
62
+ # @return [Integer]
45
63
  def sample_rate
46
64
  @sounds.last.sample_rate
47
65
  end
48
66
 
49
- # Size of the playback sound
50
- # @return [Fixnum]
51
- def size
52
- @sounds.map(&:size).max
53
- end
54
-
55
67
  # Start playback
56
68
  # @return [Playback]
57
69
  def start
@@ -66,6 +78,13 @@ module AudioPlayback
66
78
  @stream.block
67
79
  end
68
80
 
81
+ # Should playback be truncated?
82
+ # eg :start 3 seconds, :duration 1 second
83
+ # @return [Boolean]
84
+ def truncate?
85
+ !@truncate.nil? && !@truncate.values.empty?
86
+ end
87
+
69
88
  # Log a report about playback
70
89
  # @param [IO] logger
71
90
  # @return [Boolean]
@@ -80,9 +99,9 @@ module AudioPlayback
80
99
  end
81
100
 
82
101
  # Total size of the playback's sound frames in bytes
83
- # @return [Fixnum]
102
+ # @return [Integer]
84
103
  def data_size
85
- frames = (size * @num_channels) + METADATA.count
104
+ frames = size * @num_channels
86
105
  frames * FRAME_SIZE.size
87
106
  end
88
107
 
@@ -92,21 +111,28 @@ module AudioPlayback
92
111
  !@channels.nil?
93
112
  end
94
113
 
114
+ # Is audio looping ?
115
+ # @return [Boolean]
116
+ def looping?
117
+ @is_looping
118
+ end
119
+
95
120
  private
96
121
 
97
122
  # Are the requested channels available in the current environment?
98
- # @param [Array<Fixnum>] channels
123
+ # @param [Array<Integer>] channels
99
124
  # @return [Boolean]
100
125
  def validate_requested_channels(channels)
101
126
  if channels.count > @output.num_channels
102
- raise "Only #{@output.num_channels} channels available on #{@output.name} output"
127
+ message = "Only #{@output.num_channels} channels available on #{@output.name} output"
128
+ raise(InvalidChannels.new(message))
103
129
  false
104
130
  end
105
131
  true
106
132
  end
107
133
 
108
134
  # Validate and populate the variables containing information about the requested channels
109
- # @param [Fixnum, Array<Fixnum>] request Channel(s)
135
+ # @param [Integer, Array<Integer>] request Channel(s)
110
136
  # @return [Boolean]
111
137
  def populate_requested_channels(request)
112
138
  request = Array(request)
@@ -122,7 +148,7 @@ module AudioPlayback
122
148
 
123
149
  # Populate the playback channels
124
150
  # @param [Hash] options
125
- # @option options [Fixnum, Array<Fixnum>] :channels (or: :channel)
151
+ # @option options [Integer, Array<Integer>] :channels (or: :channel)
126
152
  # @return [Boolean]
127
153
  def populate_channels(options = {})
128
154
  request = options[:channels] || options[:channel]
@@ -134,12 +160,92 @@ module AudioPlayback
134
160
  end
135
161
  end
136
162
 
163
+ # Populate the truncation parameters. Converts the seconds based Position arguments
164
+ # to number of frames
165
+ # @param [Position, nil] seek Start at given time position in seconds
166
+ # @param [Position, nil] duration Play for given time in seconds
167
+ # @param [Position, nil] end_position Stop at given time position in seconds (will use duration arg if both are included)
168
+ # @return [Hash]
169
+ def populate_truncation(seek, duration, end_position)
170
+ @truncate = {}
171
+ end_position = if duration.nil?
172
+ end_position
173
+ elsif seek.nil?
174
+ duration || end_position
175
+ else
176
+ duration + seek || end_position
177
+ end
178
+ unless seek.nil?
179
+ @truncate[:start_frame] = number_of_seconds_to_number_of_frames(seek)
180
+ end
181
+ unless end_position.nil?
182
+ @truncate[:end_frame] = number_of_seconds_to_number_of_frames(end_position)
183
+ end
184
+ @truncate
185
+ end
186
+
187
+ # Convert number of seconds to number of sample frames given the sample rate
188
+ # @param [Numeric] num_seconds
189
+ # @return [Integer]
190
+ def number_of_seconds_to_number_of_frames(num_seconds)
191
+ (num_seconds * sample_rate).to_i
192
+ end
193
+
194
+ # Are the options for truncation valid? eg is the :end_position option after the
195
+ # :seek option?
196
+ # @param [Hash] options
197
+ # @option options [Numeric] :duration Play for given time in seconds
198
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
199
+ # @option options [Numeric] :seek Start at given time position in seconds
200
+ # @return [Boolean]
201
+ def truncate_valid?(options)
202
+ options[:end_position].nil? || options[:seek].nil? ||
203
+ options[:end_position] > options[:seek]
204
+ end
205
+
206
+ # Has truncation been requested in the constructor options?
207
+ # @param [Hash] options
208
+ # @option options [Numeric] :duration Play for given time in seconds
209
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
210
+ # @option options [Numeric] :seek Start at given time position in seconds
211
+ # @return [Boolean]
212
+ def truncate_requested?(options)
213
+ !options[:seek].nil? || !options[:duration].nil? || !options[:end_position].nil?
214
+ end
215
+
216
+ # Populate Position objects using the the truncation parameters.
217
+ # @param [Hash] options
218
+ # @option options [Numeric] :duration Play for given time in seconds
219
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
220
+ # @option options [Numeric] :seek Start at given time position in seconds
221
+ # @return [Array<Position>]
222
+ def truncate_options_as_positions(options = {})
223
+ seek = Position.new(options[:seek]) unless options[:seek].nil?
224
+ duration = Position.new(options[:duration]) unless options[:duration].nil?
225
+ end_position = Position.new(options[:end_position]) unless options[:end_position].nil?
226
+ [seek, duration, end_position]
227
+ end
228
+
137
229
  # Populate the playback action
138
230
  # @param [Hash] options
139
- # @option options [Fixnum, Array<Fixnum>] :channels (or: :channel)
231
+ # @option options [Integer, Array<Integer>] :channels (or: :channel)
232
+ # @option options [Numeric] :duration Play for given time in seconds
233
+ # @option options [Numeric] :end_position Stop at given time position in seconds (will use :duration if both are included)
234
+ # @option options [Boolean] :is_looping Whether to loop audio
235
+ # @option options [Numeric] :seek Start at given time position in seconds
140
236
  # @return [Playback::Action]
141
237
  def populate(options = {})
142
238
  populate_channels(options)
239
+ if truncate_requested?(options)
240
+ if truncate_valid?(options)
241
+ seek, duration, end_position = *truncate_options_as_positions(options)
242
+ populate_truncation(seek, duration, end_position)
243
+ else
244
+ message = "Truncation options are not valid"
245
+ raise(InvalidTruncation.new(message))
246
+ end
247
+ end
248
+ @is_looping = !!options[:is_looping]
143
249
  @data = StreamData.new(self)
144
250
  self
145
251
  end
@@ -155,8 +261,8 @@ module AudioPlayback
155
261
  # @param [Sound] sound
156
262
  # @param [Output] output
157
263
  # @param [Hash] options
158
- # @option options [Fixnum] :buffer_size
159
- # @option options [Array<Fixnum>, Fixnum] :channels (or: :channel)
264
+ # @option options [Integer] :buffer_size
265
+ # @option options [Array<Integer>, Integer] :channels (or: :channel)
160
266
  # @option options [IO] :logger
161
267
  # @option options [Stream] :stream
162
268
  # @return [Playback]