seal 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6a4ec328c05b480d1e4771accd5feddc120359f0
4
+ data.tar.gz: 5006a20741965f24f45f4e0f1d1f5ccc41859956
5
+ SHA512:
6
+ metadata.gz: be6c6509925a48baddd68cf0089becceaff0d84394c85cae4e68c6b485a5037728dabac4b5492555ea833ad8c0fd95c895bbbf0145ce31b68a6fa33074549806
7
+ data.tar.gz: 589899ce37a1f214815c3aa5860752cecf1398cf39e0236104bd4ce2d9dc4b0a3d8760e91febd767608c9bd302d80baac74bac7baa69f319773c67edc725bd7f
data/README.md CHANGED
@@ -3,10 +3,17 @@
3
3
  Seal is a C library with Ruby binding for audio rendering and manipulation,
4
4
  such as direction and distance attenuation, the simulation of the Doppler
5
5
  effect and reverberation, in a 3D space. It is built on top of [OpenAL]
6
- (http://connect.creativelabs.com/openal/default.aspx), adding support for
7
- audio streaming and audio formats like Ogg Vorbis, MPEG Audio and WAVE.
6
+ (http://connect.creativelabs.com/openal/default.aspx).
8
7
 
9
- ## Basic Use
8
+ ## Why Seal? Why not Simply Use OpenAL?
9
+
10
+ 0. Seal has a Ruby binding and many syntactic sugars.
11
+ 1. Seal supports audio formats like Ogg Vorbis, MPEG Audio and WAVE.
12
+ 2. Seal abstracts automatic audio streaming (also preventing problems like
13
+ discontinued stream due to I/O stress).
14
+ 3. More stuff in the future...
15
+
16
+ ## Basic Usage
10
17
 
11
18
  Initialize Seal:
12
19
 
@@ -167,6 +174,11 @@ CC=/usr/bin/gcc-4.2 cmake -DCMAKE_BUILD_TYPE=Release ..
167
174
  After OpenAL is installed, you can start building Seal. Seal will dynamically
168
175
  link OpenAL.
169
176
 
177
+ Note that depending on where OpenAL's `make install` installs the actual
178
+ shared library, you may need to add the lib path to `LD_LIBRARY_PATH` in
179
+ order for Seal to find the shared `libopenal.so` file.
180
+
181
+
170
182
  ### Install as a Gem (in a sane environment)
171
183
 
172
184
  ```Bash
@@ -12,7 +12,7 @@ mpg123_dir = File.join root_dir, 'mpg123'
12
12
  mpg123_src_dir = File.join mpg123_dir, 'src'
13
13
  mpg123_lib_dir = File.join mpg123_src_dir, 'libmpg123', '.libs'
14
14
 
15
- $defs << '-DNDEBUG'
15
+ $defs << '-DNDEBUG' << '-DMPG123_NO_LARGENAME'
16
16
  $LDFLAGS << ' -s'
17
17
 
18
18
  unless File.exists?(File.join(mpg123_src_dir, 'config.h'))
@@ -1,8 +1,4 @@
1
1
  /*
2
- * seal.h is part of the Scorched End Audio Library (SEAL) and is licensed
3
- * under the terms of the GNU Lesser General Public License. See COPYING
4
- * attached with the library.
5
- *
6
2
  * seal.h is the master header file for the Scorched End Audio Library. By
7
3
  * including seal.h, there will be no need to include the inidividual modules'
8
4
  * header files.
@@ -166,10 +166,14 @@ seal_err_t SEAL_API seal_set_src_stream(seal_src_t*, seal_stream_t*);
166
166
  seal_err_t SEAL_API seal_feed_efs(seal_src_t*, seal_efs_t*, int /*index*/);
167
167
 
168
168
  /*
169
- * Updates a streaming source. If the source is not up-to-date, the playback
170
- * will end before the end of the stream is reached. Does nothing if the
171
- * passed-in source is not a streaming source. Also does nothing if auto
172
- * update is on.
169
+ * Updates a streaming source by filling up the audio queue until it is full.
170
+ * If the source is not up-to-date, the playback will end before the end of
171
+ * the stream is reached. Does nothing if the passed-in source is not a
172
+ * streaming source. Also does nothing if auto update is on. When auto update
173
+ * is off, it is the caller's responsibility to handle cases where the consumer
174
+ * (OpenAL) consumes faster than the producer (the caller) produces and causes
175
+ * the playback to stop. Automatic sources automatically resume playing in such
176
+ * cases.
173
177
  *
174
178
  * @param src the source to update
175
179
  */
@@ -453,6 +457,7 @@ struct seal_src_t
453
457
  size_t queue_size : 6;
454
458
  unsigned int looping : 1;
455
459
  unsigned int automatic : 1;
460
+ unsigned int early_stop : 1;
456
461
  };
457
462
 
458
463
  #endif /* _SEAL_SRC_H_ */
@@ -1,15 +1,27 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Seal do
4
- it 'should initialize and finalize correctly on default device' do
5
- expect do
6
- Seal.startup
7
- Seal.cleanup
8
- end.to_not raise_error
9
- end
4
+ context 'initially' do
5
+ # Examples in this group needs to call Seal.startup themselves instead of
6
+ # relying on it being implicitly called by the global before hook.
7
+ # Therefore, Seal must be finalized in order to avoid double startup, which
8
+ # may fail for some devices due to opening an already opened, busy device.
9
+ before { Seal.cleanup }
10
+ # Since there is a global after hook for Seal.cleanup, we insert this after
11
+ # hook before the global one so the global one will then cleans up
12
+ # properly.
13
+ prepend_after { Seal.startup }
14
+
15
+ it 'starts up and cleans up properly on default device' do
16
+ expect do
17
+ Seal.startup
18
+ Seal.cleanup
19
+ end.to_not raise_error
20
+ end
10
21
 
11
- it 'should fail when trying to initialize non-existing device' do
12
- expect { Seal.startup 'foo42' }.to raise_error SealError
22
+ it 'fails when trying to start up on non-existing device' do
23
+ expect { Seal.startup 'foo42' }.to raise_error SealError
24
+ end
13
25
  end
14
26
 
15
27
  it 'has only one instance of Listener' do
@@ -11,7 +11,7 @@ describe EffectSlot do
11
11
 
12
12
  it_validates 'the boolean attribute', :auto
13
13
  it_validates 'the float attribute', :gain, "[0, 1]"
14
- it_defines 'boolean reader aliases', [:auto]
14
+ it_defines 'boolean reader aliases', %i(auto)
15
15
 
16
16
  it 'can initialize with an effect' do
17
17
  reverb = Reverb.new
@@ -30,7 +30,7 @@ describe Reverb do
30
30
  it_validates 'the float attribute', :reflections_delay, '[0, 0.3]'
31
31
  it_validates 'the float attribute', :reflections_gain, '[0, 3.16]'
32
32
  it_validates 'the float attribute', :room_rolloff_factor, '[0, 10]'
33
- it_defines 'boolean reader aliases', [:hfdecay_limited]
33
+ it_defines 'boolean reader aliases', %i(hfdecay_limited)
34
34
 
35
35
  specify_preset_loading = -> mod do
36
36
  mod.constants.each do |const_sym|
@@ -28,7 +28,7 @@ describe Source do
28
28
  it_validates 'the boolean attribute', :auto
29
29
  it_validates 'the float attribute', :pitch, "[0, +inf.)"
30
30
  it_validates 'the float attribute', :gain, "[0, +inf.)"
31
- it_defines 'boolean reader aliases', [:auto, :relative, :looping]
31
+ it_defines 'boolean reader aliases', %i(auto relative looping)
32
32
 
33
33
  it 'validates its queue size is in [2, 63]' do
34
34
  error_pattern = /Invalid parameter value/
@@ -773,9 +773,13 @@ feed_efs(VALUE rsrc, VALUE rslot, VALUE rindex)
773
773
  * call-seq:
774
774
  * source.update -> source
775
775
  *
776
- * Updates _source_. If _source_ is not up-to-date, the playback will end
777
- * before the end of the stream is reached. Does nothing if _source_ is not a
778
- * streaming source. Also does nothing if auto update is on.
776
+ * Updates a streaming _source_ by filling up the audio queue until it is full.
777
+ * If _source_ is not up-to-date, the playback will end before the end of the
778
+ * stream is reached. Does nothing if _source_ is not a streaming source. Also
779
+ * does nothing if auto update is on. When auto update is off, it is the
780
+ * caller's responsibility to handle cases where the consumer (OpenAL) consumes
781
+ * faster than the producer (the caller) produces and causes the playback to
782
+ * stop. Automatic sources automatically resume playing in such cases.
779
783
  */
780
784
  static
781
785
  VALUE update_src(VALUE rsrc)
@@ -1701,6 +1705,14 @@ get_listener()
1701
1705
  return rb_const_get(mSeal, rb_intern("LISTENER"));
1702
1706
  }
1703
1707
 
1708
+ /*
1709
+ * call-seq:
1710
+ * listener.move -> listener
1711
+ *
1712
+ * Moves the listener (changes the position) based on the source velocity. This
1713
+ * is a syntactic sugar for adding the velocity vector and position vector. See
1714
+ * Source#move.
1715
+ */
1704
1716
  static
1705
1717
  VALUE
1706
1718
  move_listener(VALUE rlistener)
@@ -31,7 +31,7 @@ const char*
31
31
  SEAL_API
32
32
  seal_get_version(void)
33
33
  {
34
- return "0.1.2";
34
+ return "0.1.3";
35
35
  }
36
36
 
37
37
  static
@@ -27,6 +27,9 @@ static const size_t DEFAULT_CHUNK_SIZE = MIN_CHUNK_SIZE << 2;
27
27
  static const size_t MAX_CHUNK_SIZE = CHUNK_STORAGE_CAP -
28
28
  CHUNK_STORAGE_CAP % MIN_CHUNK_SIZE;
29
29
 
30
+ /*
31
+ * Checks if `val` is in the closed interval [`lower_bound`, `upper_bound`].
32
+ */
30
33
  static
31
34
  seal_err_t
32
35
  check_val_limit(int val, int lower_bound, int upper_bound)
@@ -39,13 +42,42 @@ check_val_limit(int val, int lower_bound, int upper_bound)
39
42
  return SEAL_OK;
40
43
  }
41
44
 
45
+ /*
46
+ * Called when the state of a source is preemptively changed by a caller.
47
+ */
42
48
  static
43
49
  seal_err_t
44
- operate(seal_src_t* src, void (*op)(unsigned int))
50
+ on_preemptive_state_change(seal_src_t* src)
45
51
  {
52
+ seal_err_t err;
53
+ seal_src_state_t state;
54
+
55
+ if ((err = seal_get_src_state(src, &state)) != SEAL_OK)
56
+ return err;
57
+
58
+ /* Set `early_stop` to true after playing so it will only be set to false
59
+ * when playback ended with the end of stream reached or the state is
60
+ * preemptively changed.
61
+ */
62
+ src->early_stop = state == SEAL_PLAYING ? 1 : 0;
63
+
64
+ return SEAL_OK;
65
+ }
66
+
67
+ /*
68
+ * Changes the source state by calling one of the four operations on a source.
69
+ */
70
+ static
71
+ seal_err_t
72
+ change_state(seal_src_t* src, void (*op)(unsigned int))
73
+ {
74
+ seal_err_t err;
75
+
46
76
  op(src->id);
77
+ if ((err = _seal_get_openal_err()) != SEAL_OK)
78
+ return err;
47
79
 
48
- return _seal_get_openal_err();
80
+ return on_preemptive_state_change(src);
49
81
  }
50
82
 
51
83
  static
@@ -76,6 +108,9 @@ wait4updater(seal_src_t* src)
76
108
  }
77
109
  }
78
110
 
111
+ /*
112
+ * The main routine for updater threads.
113
+ */
79
114
  static
80
115
  void*
81
116
  update(void* args)
@@ -85,11 +120,22 @@ update(void* args)
85
120
 
86
121
  while (alIsSource(src->id)) {
87
122
  seal_src_state_t state;
88
- err = seal_get_src_state(src, &state);
89
- if (err != SEAL_OK || state != SEAL_PLAYING)
90
- break;
123
+
124
+ /* Check source state before checking if interrupted by caller. */
125
+ if ((err = seal_get_src_state(src, &state)) != SEAL_OK)
126
+ break;
127
+ if (state != SEAL_PLAYING) {
128
+ /* Early stopping, most likely due to I/O load. Restart playing. */
129
+ if (src->early_stop) {
130
+ if ((err = change_state(src, alSourcePlay)) != SEAL_OK)
131
+ break;
132
+ } else {
133
+ break;
134
+ }
135
+ }
91
136
  if ((err = seal_update_src(src)) != SEAL_OK)
92
137
  break;
138
+
93
139
  _seal_sleep(50);
94
140
  }
95
141
 
@@ -119,6 +165,10 @@ unqueue_bufs(seal_src_t* src, int nbufs, unsigned int* bufs)
119
165
  return queue_op(src, nbufs, bufs, alSourceUnqueueBuffers);
120
166
  }
121
167
 
168
+ /*
169
+ * Cleans the queue and free any allocated buffers in the queue. This function
170
+ * assumes the source is stopped at the time of calling.
171
+ */
122
172
  static
123
173
  seal_err_t
124
174
  clean_queue(seal_src_t* src)
@@ -151,8 +201,9 @@ clean_queue(seal_src_t* src)
151
201
  }
152
202
 
153
203
  /*
154
- * Stopping a source will mark all the buffers in its queue processed so that
155
- * they can be unqueued.
204
+ * Cleans the queue after stopping the source. A stopped source will have all
205
+ * the buffers in its queue marked as processed so that they can be unqueued.
206
+ * This function assumes the source is playing at the time of calling.
156
207
  */
157
208
  static
158
209
  seal_err_t
@@ -160,7 +211,7 @@ stop_then_clean_queue(seal_src_t* src)
160
211
  {
161
212
  seal_err_t err;
162
213
 
163
- if ((err = operate(src, alSourceStop)) != SEAL_OK)
214
+ if ((err = change_state(src, alSourceStop)) != SEAL_OK)
164
215
  return err;
165
216
 
166
217
  return clean_queue(src);
@@ -178,6 +229,10 @@ restart_queuing(seal_src_t* src)
178
229
  return seal_rewind_stream(src->stream);
179
230
  }
180
231
 
232
+ /*
233
+ * This does the same thing as `stop_then_clean_queue` except it works for
234
+ * sources in any state at the time of calling.
235
+ */
181
236
  static
182
237
  seal_err_t
183
238
  empty_queue(seal_src_t* src)
@@ -185,7 +240,7 @@ empty_queue(seal_src_t* src)
185
240
  seal_err_t err;
186
241
 
187
242
  /* Need to be playing first in order to become stopped. */
188
- if ((err = operate(src, alSourcePlay)) != SEAL_OK)
243
+ if ((err = change_state(src, alSourcePlay)) != SEAL_OK)
189
244
  return err;
190
245
 
191
246
  return stop_then_clean_queue(src);
@@ -210,11 +265,13 @@ seal_init_src(seal_src_t* src)
210
265
  if (err == SEAL_OK) {
211
266
  src->buf = 0;
212
267
  src->stream = 0;
268
+ /* The id of the thread that is updating the source. */
213
269
  src->updater = 0;
214
270
  src->chunk_size = DEFAULT_CHUNK_SIZE;
215
271
  src->queue_size = DEFAULT_QUEUE_SIZE;
216
272
  src->looping = 0;
217
273
  src->automatic = 1;
274
+ src->early_stop = 0;
218
275
  }
219
276
 
220
277
  return err;
@@ -257,18 +314,23 @@ seal_play_src(seal_src_t* src)
257
314
  /* Stream some data so plackback can start immediately. */
258
315
  if ((err = seal_update_src(src)) != SEAL_OK)
259
316
  return err;
317
+ /* Actually start playing. */
318
+ if ((err = change_state(src, alSourcePlay)) != SEAL_OK)
319
+ return err;
320
+ /* Create background updater for automatic sources. */
260
321
  if (src->automatic)
261
322
  src->updater = _seal_create_thread(update, src);
323
+ return SEAL_OK;
324
+ } else {
325
+ return change_state(src, alSourcePlay);
262
326
  }
263
-
264
- return operate(src, alSourcePlay);
265
327
  }
266
328
 
267
329
  seal_err_t
268
330
  SEAL_API
269
331
  seal_pause_src(seal_src_t* src)
270
332
  {
271
- return operate(src, alSourcePause);
333
+ return change_state(src, alSourcePause);
272
334
  }
273
335
 
274
336
  seal_err_t
@@ -277,7 +339,10 @@ seal_stop_src(seal_src_t* src)
277
339
  {
278
340
  seal_err_t err;
279
341
 
280
- if ((err = operate(src, alSourceStop)) == SEAL_OK && src->stream != 0)
342
+ if ((err = change_state(src, alSourceStop)) != SEAL_OK)
343
+ return err;
344
+
345
+ if (src->stream != 0)
281
346
  /* Already stopped so all buffers are proccessed. */
282
347
  if ((err = clean_queue(src)) == SEAL_OK)
283
348
  err = seal_rewind_stream(src->stream);
@@ -299,7 +364,7 @@ seal_rewind_src(seal_src_t* src)
299
364
  return err;
300
365
  }
301
366
 
302
- return operate(src, alSourceRewind);
367
+ return change_state(src, alSourceRewind);
303
368
  }
304
369
 
305
370
  seal_err_t
@@ -312,13 +377,17 @@ seal_detach_src_audio(seal_src_t* src)
312
377
  return err;
313
378
 
314
379
  /* Sets the state to `SEAL_INITIAL' for consistency. */
315
- if ((err = operate(src, alSourceRewind)) != SEAL_OK)
380
+ if ((err = change_state(src, alSourceRewind)) != SEAL_OK)
316
381
  return err;
317
382
 
318
- if ((err = _seal_seti(src, AL_BUFFER, AL_NONE, alSourcei)) == SEAL_OK) {
319
- src->buf = 0;
320
- src->stream = 0;
321
- }
383
+ if ((err = _seal_seti(src, AL_BUFFER, AL_NONE, alSourcei)) != SEAL_OK)
384
+ return err;
385
+
386
+ if ((err = on_preemptive_state_change(src)) != SEAL_OK)
387
+ return err;
388
+
389
+ src->buf = 0;
390
+ src->stream = 0;
322
391
 
323
392
  return err;
324
393
  }
@@ -486,6 +555,7 @@ start_streaming:
486
555
  goto start_streaming;
487
556
  /* End of stream reached. */
488
557
  } else {
558
+ src->early_stop = 0;
489
559
  break;
490
560
  }
491
561
  } /* for (;;) */
metadata CHANGED
@@ -1,36 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
5
- prerelease:
4
+ version: 0.1.3
6
5
  platform: ruby
7
6
  authors:
8
7
  - Su Zhang
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-25 00:00:00.000000000 Z
11
+ date: 2013-06-22 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake-compiler
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rspec
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -46,55 +41,51 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: guard-rspec
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: yard
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: '0'
78
69
  - !ruby/object:Gem::Dependency
79
70
  name: rb-inotify
80
71
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
72
  requirements:
83
- - - ! '>='
73
+ - - '>='
84
74
  - !ruby/object:Gem::Version
85
75
  version: '0'
86
76
  type: :development
87
77
  prerelease: false
88
78
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
79
  requirements:
91
- - - ! '>='
80
+ - - '>='
92
81
  - !ruby/object:Gem::Version
93
82
  version: '0'
94
- description: ! " Seal is a library for 3D audio rendering and manipulation, supporting\n
95
- \ effects such as direction and distance attenuation, the simulation of the\n
96
- \ Doppler effect and reverberation. It is built on top of OpenAL, adding\n support
97
- for audio streaming and audio formats like Ogg Vorbis, MPEG Audio\n and WAVE.\n"
83
+ description: |2
84
+ Seal is a library for 3D audio rendering and manipulation, supporting
85
+ effects such as direction and distance attenuation, the simulation of the
86
+ Doppler effect and reverberation. It is built on top of OpenAL, adding
87
+ support for audio streaming and audio formats like Ogg Vorbis, MPEG Audio
88
+ and WAVE.
98
89
  email: me@zhang.su
99
90
  executables: []
100
91
  extensions:
@@ -525,41 +516,39 @@ files:
525
516
  homepage: https://github.com/zhangsu/seal
526
517
  licenses:
527
518
  - WTFPL
519
+ metadata: {}
528
520
  post_install_message: Enjoy!
529
521
  rdoc_options: []
530
522
  require_paths:
531
523
  - lib
532
524
  required_ruby_version: !ruby/object:Gem::Requirement
533
- none: false
534
525
  requirements:
535
- - - ~>
526
+ - - '>='
536
527
  - !ruby/object:Gem::Version
537
- version: '1.9'
528
+ version: 1.9.2
538
529
  required_rubygems_version: !ruby/object:Gem::Requirement
539
- none: false
540
530
  requirements:
541
- - - ! '>='
531
+ - - '>='
542
532
  - !ruby/object:Gem::Version
543
533
  version: '0'
544
534
  requirements:
545
535
  - libopenal
546
536
  - a sound card
547
537
  rubyforge_project:
548
- rubygems_version: 1.8.24
538
+ rubygems_version: 2.0.0
549
539
  signing_key:
550
- specification_version: 3
540
+ specification_version: 4
551
541
  summary: An OpenAL-based 3D audio library
552
542
  test_files:
553
543
  - spec/seal/buffer_spec.rb
554
544
  - spec/seal/core_spec.rb
555
- - spec/seal/stream_spec.rb
545
+ - spec/seal/effect_slot_spec.rb
556
546
  - spec/seal/listener_spec.rb
557
547
  - spec/seal/reverb_spec.rb
558
- - spec/seal/effect_slot_spec.rb
559
548
  - spec/seal/source_spec.rb
560
- - spec/support/movable_object.rb
549
+ - spec/seal/stream_spec.rb
550
+ - spec/spec_helper.rb
551
+ - spec/support/attribute_examples.rb
561
552
  - spec/support/audio_object_with_format.rb
562
553
  - spec/support/boolean_reader_aliases.rb
563
- - spec/support/attribute_examples.rb
564
- - spec/spec_helper.rb
565
- has_rdoc: yard
554
+ - spec/support/movable_object.rb