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