beeps 0.1.31 → 0.1.33

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.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/beeps/adsr.cpp +139 -0
  3. data/.doc/ext/beeps/analyser.cpp +128 -0
  4. data/.doc/ext/beeps/beeps.cpp +9 -1
  5. data/.doc/ext/beeps/file_in.cpp +55 -9
  6. data/.doc/ext/beeps/gain.cpp +64 -0
  7. data/.doc/ext/beeps/mic_in.cpp +83 -0
  8. data/.doc/ext/beeps/native.cpp +20 -8
  9. data/.doc/ext/beeps/oscillator.cpp +88 -0
  10. data/.doc/ext/beeps/pitch_shift.cpp +64 -0
  11. data/.doc/ext/beeps/processor.cpp +31 -2
  12. data/.doc/ext/beeps/sound.cpp +90 -7
  13. data/.doc/ext/beeps/sound_player.cpp +156 -0
  14. data/.doc/ext/beeps/time_stretch.cpp +64 -0
  15. data/.github/workflows/release-gem.yml +62 -0
  16. data/.github/workflows/test.yml +0 -6
  17. data/.github/workflows/utils.rb +13 -5
  18. data/ChangeLog.md +13 -0
  19. data/Rakefile +27 -6
  20. data/VERSION +1 -1
  21. data/beeps.gemspec +2 -2
  22. data/ext/beeps/adsr.cpp +150 -0
  23. data/ext/beeps/analyser.cpp +134 -0
  24. data/ext/beeps/beeps.cpp +10 -1
  25. data/ext/beeps/extconf.rb +1 -2
  26. data/ext/beeps/file_in.cpp +60 -9
  27. data/ext/beeps/gain.cpp +67 -0
  28. data/ext/beeps/mic_in.cpp +88 -0
  29. data/ext/beeps/native.cpp +20 -8
  30. data/ext/beeps/oscillator.cpp +93 -0
  31. data/ext/beeps/pitch_shift.cpp +67 -0
  32. data/ext/beeps/processor.cpp +34 -2
  33. data/ext/beeps/sound.cpp +99 -7
  34. data/ext/beeps/sound_player.cpp +169 -0
  35. data/ext/beeps/time_stretch.cpp +67 -0
  36. data/include/beeps/beeps.h +2 -1
  37. data/include/beeps/filter.h +179 -0
  38. data/include/beeps/generator.h +120 -0
  39. data/include/beeps/processor.h +37 -68
  40. data/include/beeps/ruby/filter.h +78 -0
  41. data/include/beeps/ruby/generator.h +60 -0
  42. data/include/beeps/ruby/processor.h +5 -45
  43. data/include/beeps/ruby/sound.h +14 -3
  44. data/include/beeps/signals.h +10 -4
  45. data/include/beeps/sound.h +67 -2
  46. data/lib/beeps/beeps.rb +6 -1
  47. data/lib/beeps/processor.rb +95 -15
  48. data/lib/beeps/sound.rb +29 -2
  49. data/src/adsr.cpp +245 -0
  50. data/src/analyser.cpp +254 -0
  51. data/src/beeps.cpp +11 -2
  52. data/src/file_in.cpp +94 -0
  53. data/src/gain.cpp +55 -0
  54. data/src/mic_in.cpp +262 -0
  55. data/src/mic_in.h +20 -0
  56. data/src/openal.cpp +2 -1
  57. data/src/oscillator.cpp +145 -0
  58. data/src/osx/signals.mm +83 -0
  59. data/src/pitch_shift.cpp +82 -0
  60. data/src/processor.cpp +202 -88
  61. data/src/processor.h +98 -0
  62. data/src/signals.cpp +326 -20
  63. data/src/signals.h +192 -2
  64. data/src/sound.cpp +735 -113
  65. data/src/sound.h +6 -1
  66. data/src/time_stretch.cpp +91 -0
  67. data/test/helper.rb +2 -1
  68. data/test/test_beeps.rb +10 -7
  69. data/test/test_beeps_init.rb +18 -0
  70. data/test/test_file_in.rb +15 -0
  71. data/test/test_processor.rb +50 -0
  72. data/test/test_sound.rb +87 -11
  73. data/test/test_sound_player.rb +134 -0
  74. metadata +55 -17
  75. data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
  76. data/.doc/ext/beeps/sine_wave.cpp +0 -61
  77. data/.doc/ext/beeps/square_wave.cpp +0 -61
  78. data/.github/workflows/release.yml +0 -34
  79. data/ext/beeps/sawtooth_wave.cpp +0 -64
  80. data/ext/beeps/sine_wave.cpp +0 -64
  81. data/ext/beeps/square_wave.cpp +0 -64
data/src/sound.cpp CHANGED
@@ -1,11 +1,15 @@
1
- #include "beeps/sound.h"
1
+ #include "sound.h"
2
2
 
3
3
 
4
4
  #include <limits.h>
5
+ #include <memory>
6
+ #include <algorithm>
5
7
  #include "Stk.h"
6
- #include "beeps/processor.h"
8
+ #include "beeps/beeps.h"
7
9
  #include "beeps/exception.h"
10
+ #include "beeps/generator.h"
8
11
  #include "openal.h"
12
+ #include "processor.h"
9
13
  #include "signals.h"
10
14
 
11
15
 
@@ -20,55 +24,229 @@ namespace Beeps
20
24
  {
21
25
 
22
26
 
23
- static ALuint get_buffer_id (const Sound& sound);
27
+ struct SoundBuffer
28
+ {
29
+
30
+ SoundBuffer (bool create = false)
31
+ {
32
+ if (create) self->create();
33
+ }
34
+
35
+ SoundBuffer (const Signals& signals)
36
+ {
37
+ self->create();
38
+ write(signals);
39
+ }
40
+
41
+ SoundBuffer (ALint id)
42
+ {
43
+ self->id = id;
44
+ }
45
+
46
+ void clear ()
47
+ {
48
+ self->clear();
49
+ }
50
+
51
+ uint write (const Signals& signals)
52
+ {
53
+ assert(signals);
54
+
55
+ if (!*this)
56
+ invalid_state_error(__FILE__, __LINE__);
57
+
58
+ double sample_rate = signals.sample_rate();
59
+ uint nchannels = signals.nchannels();
60
+ uint nsamples = signals.nsamples();
61
+ assert(sample_rate > 0 && nchannels > 0 && nsamples > 0);
62
+
63
+ const Frames* frames = Signals_get_frames(&signals);
64
+ assert(frames);
65
+
66
+ std::vector<short> buffer;
67
+ buffer.reserve(nsamples * nchannels);
68
+ for (uint sample = 0; sample < nsamples; ++sample)
69
+ for (uint channel = 0; channel < nchannels; ++channel)
70
+ buffer.push_back((*frames)(sample, channel) * SHRT_MAX);
71
+
72
+ alBufferData(
73
+ self->id,
74
+ nchannels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
75
+ &buffer[0],
76
+ sizeof(short) * nsamples * nchannels,
77
+ sample_rate);
78
+ OpenAL_check_error(__FILE__, __LINE__);
79
+
80
+ return nsamples;
81
+ }
82
+
83
+ operator bool () const
84
+ {
85
+ return self->is_valid();
86
+ }
87
+
88
+ bool operator ! () const
89
+ {
90
+ return !operator bool();
91
+ }
92
+
93
+ struct Data
94
+ {
95
+
96
+ ALint id = -1;
97
+
98
+ bool owner = false;
99
+
100
+ ~Data ()
101
+ {
102
+ clear();
103
+ }
104
+
105
+ void create ()
106
+ {
107
+ clear();
108
+
109
+ ALuint id_ = 0;
110
+ alGenBuffers(1, &id_);
111
+ OpenAL_check_error(__FILE__, __LINE__);
112
+
113
+ id = id_;
114
+ owner = true;
115
+ }
116
+
117
+ void clear ()
118
+ {
119
+ if (owner && id >= 0)
120
+ {
121
+ ALuint id_ = id;
122
+ alDeleteBuffers(1, &id_);
123
+ OpenAL_check_error(__FILE__, __LINE__);
124
+ }
125
+
126
+ id = -1;
127
+ owner = false;
128
+ }
129
+
130
+ bool is_valid () const
131
+ {
132
+ return id >= 0;
133
+ }
134
+
135
+ };// Data
136
+
137
+ Xot::PSharedImpl<Data> self;
138
+
139
+ };// SoundBuffer
24
140
 
25
141
 
26
142
  struct SoundSource
27
143
  {
28
144
 
29
- typedef SoundSource This;
145
+ void create ()
146
+ {
147
+ ALuint id_ = 0;
148
+ alGenSources(1, &id_);
149
+ if (OpenAL_no_error()) self->id = id_;
150
+ }
151
+
152
+ void clear ()
153
+ {
154
+ stop();
155
+ self->clear();
156
+ }
30
157
 
31
- typedef std::shared_ptr<This> Ptr;
158
+ SoundSource reuse ()
159
+ {
160
+ stop();
161
+ set_gain(1);
162
+ set_loop(false);
32
163
 
33
- ALint id;
164
+ SoundSource source;
165
+ source.self.swap(self);
166
+ return source;
167
+ }
34
168
 
35
- static Ptr create ()
169
+ void attach (const SoundBuffer& buffer)
36
170
  {
37
- ALuint id_;
38
- alGenSources(1, &id_);
39
- if (!OpenAL_no_error()) return Ptr();
171
+ assert(buffer);
40
172
 
41
- return Ptr(new This(id_));
173
+ if (!*this)
174
+ invalid_state_error(__FILE__, __LINE__);
175
+
176
+ alSourcei(self->id, AL_BUFFER, buffer.self->id);
177
+ OpenAL_check_error(__FILE__, __LINE__);
42
178
  }
43
179
 
44
- ~SoundSource ()
180
+ void queue (const SoundBuffer& buffer)
45
181
  {
46
- if (!*this) return;
182
+ assert(buffer);
47
183
 
48
- ALuint id_ = id;
49
- alDeleteSources(1, &id_);
184
+ ALuint id = buffer.self->id;
185
+ alSourceQueueBuffers(self->id, 1, &id);
50
186
  OpenAL_check_error(__FILE__, __LINE__);
187
+
188
+ LOG("queue: %d", buffer.self->id);
51
189
  }
52
190
 
53
- void play (const Sound& sound)
191
+ bool unqueue (SoundBuffer* buffer = NULL)
54
192
  {
55
- assert(sound);
193
+ ALint count = 0;
194
+ alGetSourcei(self->id, AL_BUFFERS_PROCESSED, &count);
195
+ OpenAL_check_error(__FILE__, __LINE__);
196
+
197
+ if (count <= 0) return false;
198
+
199
+ ALuint id = 0;
200
+ alSourceUnqueueBuffers(self->id, 1, &id);
201
+ OpenAL_check_error(__FILE__, __LINE__);
202
+
203
+ if (buffer) *buffer = SoundBuffer((ALint) id);
204
+ return true;
205
+ }
56
206
 
207
+ void play ()
208
+ {
57
209
  if (!*this)
58
210
  invalid_state_error(__FILE__, __LINE__);
59
211
 
60
- alSourcei(id, AL_BUFFER, get_buffer_id(sound));
61
- alSourcePlay(id);
212
+ alSourcePlay(self->id);
213
+ OpenAL_check_error(__FILE__, __LINE__);
214
+ }
215
+
216
+ void pause ()
217
+ {
218
+ if (!*this) return;
219
+
220
+ alSourcePause(self->id);
221
+ OpenAL_check_error(__FILE__, __LINE__);
222
+ }
223
+
224
+ void rewind ()
225
+ {
226
+ if (!*this) return;
227
+
228
+ alSourceRewind(self->id);
62
229
  OpenAL_check_error(__FILE__, __LINE__);
63
230
  }
64
231
 
65
232
  void stop ()
66
233
  {
67
- if (!*this)
68
- invalid_state_error(__FILE__, __LINE__);
234
+ if (!*this) return;
235
+
236
+ alSourceStop(self->id);
237
+ OpenAL_check_error(__FILE__, __LINE__);
69
238
 
70
- alSourceStop(id);
239
+ ALint type = 0;
240
+ alGetSourcei(self->id, AL_SOURCE_TYPE, &type);
71
241
  OpenAL_check_error(__FILE__, __LINE__);
242
+
243
+ if (type == AL_STREAMING)
244
+ while (unqueue());
245
+ else if (type == AL_STATIC)
246
+ {
247
+ alSourcei(self->id, AL_BUFFER, 0);
248
+ OpenAL_check_error(__FILE__, __LINE__);
249
+ }
72
250
  }
73
251
 
74
252
  bool is_playing () const
@@ -76,15 +254,78 @@ namespace Beeps
76
254
  if (!*this) return false;
77
255
 
78
256
  ALint state = 0;
79
- alGetSourcei(id, AL_SOURCE_STATE, &state);
257
+ alGetSourcei(self->id, AL_SOURCE_STATE, &state);
80
258
  OpenAL_check_error(__FILE__, __LINE__);
81
259
 
82
260
  return state == AL_PLAYING;
83
261
  }
84
262
 
263
+ bool is_paused () const
264
+ {
265
+ if (!*this) return false;
266
+
267
+ ALint state = 0;
268
+ alGetSourcei(self->id, AL_SOURCE_STATE, &state);
269
+ OpenAL_check_error(__FILE__, __LINE__);
270
+
271
+ return state == AL_PAUSED;
272
+ }
273
+
274
+ bool is_stopped () const
275
+ {
276
+ if (!*this) return true;
277
+
278
+ ALint state = 0;
279
+ alGetSourcei(self->id, AL_SOURCE_STATE, &state);
280
+ OpenAL_check_error(__FILE__, __LINE__);
281
+
282
+ return state == AL_STOPPED;
283
+ }
284
+
285
+ void set_gain (float gain)
286
+ {
287
+ if (gain < 0)
288
+ argument_error(__FILE__, __LINE__);
289
+
290
+ if (!*this) return;
291
+
292
+ alSourcef(self->id, AL_GAIN, gain);
293
+ OpenAL_check_error(__FILE__, __LINE__);
294
+ }
295
+
296
+ float gain () const
297
+ {
298
+ float gain = 1;
299
+ if (!*this) return gain;
300
+
301
+ alGetSourcef(self->id, AL_GAIN, &gain);
302
+ OpenAL_check_error(__FILE__, __LINE__);
303
+
304
+ return gain;
305
+ }
306
+
307
+ void set_loop (bool loop)
308
+ {
309
+ if (!*this) return;
310
+
311
+ alSourcei(self->id, AL_LOOPING, loop ? AL_TRUE : AL_FALSE);
312
+ OpenAL_check_error(__FILE__, __LINE__);
313
+ }
314
+
315
+ bool loop () const
316
+ {
317
+ if (!*this) return false;
318
+
319
+ ALint loop = AL_FALSE;
320
+ alGetSourcei(self->id, AL_LOOPING, &loop);
321
+ OpenAL_check_error(__FILE__, __LINE__);
322
+
323
+ return loop != AL_FALSE;
324
+ }
325
+
85
326
  operator bool () const
86
327
  {
87
- return id >= 0;
328
+ return self->id >= 0;
88
329
  }
89
330
 
90
331
  bool operator ! () const
@@ -92,181 +333,562 @@ namespace Beeps
92
333
  return !operator bool();
93
334
  }
94
335
 
95
- private:
336
+ struct Data
337
+ {
338
+
339
+ ALint id = -1;
96
340
 
97
- SoundSource (ALint id = -1)
98
- : id(id)
341
+ ~Data ()
99
342
  {
343
+ clear();
100
344
  }
101
345
 
346
+ void clear ()
347
+ {
348
+ if (id < 0) return;
349
+
350
+ ALuint id_ = id;
351
+ alDeleteSources(1, &id_);
352
+ OpenAL_check_error(__FILE__, __LINE__);
353
+
354
+ id = -1;
355
+ }
356
+
357
+ };// Data
358
+
359
+ Xot::PSharedImpl<Data> self;
360
+
102
361
  };// SoundSource
103
362
 
104
363
 
105
- typedef std::vector<SoundSource::Ptr> SoundSourceList;
364
+ struct SoundPlayer::Data
365
+ {
366
+
367
+ SoundSource source;
368
+
369
+ std::vector<SoundBuffer> buffers;
370
+
371
+ Processor::Ref processor;
372
+
373
+ std::unique_ptr<ProcessorContext> processor_context;
374
+
375
+ void clear ()
376
+ {
377
+ source.clear();
378
+
379
+ for (auto& buffer : buffers) buffer.clear();
380
+ buffers.clear();
381
+ }
382
+
383
+ void attach_signals (const Signals& signals)
384
+ {
385
+ assert(signals);
386
+
387
+ SoundBuffer buffer(signals);
388
+ source.attach(buffer);
389
+ buffers.emplace_back(buffer);
390
+ }
391
+
392
+ void attach_stream (
393
+ Processor* processor, uint nchannels, double sample_rate)
394
+ {
395
+ assert(processor && *processor && nchannels > 0 && sample_rate > 0);
396
+
397
+ this->processor = processor;
398
+ processor_context.reset(
399
+ new ProcessorContext(sample_rate / 10, nchannels, sample_rate));
400
+
401
+ for (int i = 0; i < 2; ++i)
402
+ {
403
+ SoundBuffer buffer(true);
404
+ if (!process_stream(&buffer)) break;
405
+
406
+ source.queue(buffer);
407
+ buffers.emplace_back(buffer);
408
+ }
409
+ }
410
+
411
+ bool process_stream (SoundBuffer* buffer)
412
+ {
413
+ assert(buffer && processor && processor_context);
414
+
415
+ Signals signals = processor_context->process_signals(processor);
416
+ if (!signals) return false;
417
+
418
+ if (processor_context->is_finished())
419
+ processor_context.reset();
420
+
421
+ return buffer->write(signals) > 0;
422
+ }
423
+
424
+ void process_and_queue_stream_buffers ()
425
+ {
426
+ SoundBuffer buffer;
427
+ while (is_streaming())
428
+ {
429
+ if (!source.unqueue(&buffer))
430
+ return;
431
+
432
+ if (!process_stream(&buffer))
433
+ return;
434
+
435
+ source.queue(buffer);
436
+ if (source.is_stopped()) source.play();
437
+ }
438
+ }
439
+
440
+ bool is_streaming () const
441
+ {
442
+ return processor && processor_context && *processor_context;
443
+ }
444
+
445
+ };// SoundPlayer::Data
446
+
447
+
448
+ static SoundPlayer
449
+ create_player ()
450
+ {
451
+ SoundPlayer player;
452
+ player.self->source.create();
453
+ return player;
454
+ }
455
+
456
+ static SoundPlayer
457
+ reuse_player (SoundPlayer* player)
458
+ {
459
+ SoundPlayer newplayer;
460
+ newplayer.self->source = player->self->source.reuse();
461
+ player->self->clear();
462
+ return newplayer;
463
+ }
106
464
 
107
465
 
108
466
  namespace global
109
467
  {
110
468
 
111
- static SoundSourceList sources;
469
+ static std::vector<SoundPlayer> players;
112
470
 
113
471
  }// global
114
472
 
115
473
 
116
- void
117
- Sound_cleanup_sources ()
474
+ static void
475
+ remove_inactive_players ()
118
476
  {
119
- global::sources.clear();
477
+ auto it = std::remove_if(
478
+ global::players.begin(), global::players.end(),
479
+ [](auto& player) {return !player || player.is_stopped();});
480
+
481
+ for (auto jt = it; jt != global::players.end(); ++jt)
482
+ jt->self->clear();
483
+
484
+ global::players.erase(it, global::players.end());
120
485
  }
121
486
 
122
- static SoundSource*
123
- get_next_source ()
487
+ static SoundPlayer
488
+ get_next_player ()
124
489
  {
125
- SoundSource::Ptr source;
490
+ SoundPlayer player = create_player();
491
+
492
+ if (player)
493
+ LOG("new player");
126
494
 
127
- auto end = global::sources.end();
128
- for (auto it = global::sources.begin(); it != end; ++it)
495
+ if (!player)
129
496
  {
130
- const SoundSource::Ptr& p = *it;
131
- if (p && *p && !p->is_playing())
497
+ for (auto& p : global::players)
132
498
  {
133
- source = p;
134
- global::sources.erase(it);
135
- LOG("reuse source");
136
- break;
499
+ if (p && p.is_stopped())
500
+ {
501
+ player = reuse_player(&p);
502
+ LOG("reuse stopped player");
503
+ break;
504
+ }
137
505
  }
138
506
  }
139
507
 
140
- if (!source)
508
+ if (!player && !global::players.empty())
141
509
  {
142
- source = SoundSource::create();
143
- LOG("new source");
510
+ player = reuse_player(&global::players.front());
511
+ LOG("reuse oldest player");
144
512
  }
145
513
 
146
- if (!source)
514
+ remove_inactive_players();
515
+
516
+ if (player)
517
+ global::players.emplace_back(player);
518
+
519
+ return player;
520
+ }
521
+
522
+ void
523
+ SoundPlayer_process_streams ()
524
+ {
525
+ for (auto& player : global::players)
147
526
  {
148
- source = *global::sources.begin();
149
- if (source) source->stop();
150
- global::sources.erase(global::sources.begin());
151
- LOG("stop and reuse oldest source");
527
+ if (player.self->is_streaming())
528
+ player.self->process_and_queue_stream_buffers();
152
529
  }
530
+ }
531
+
532
+ void
533
+ SoundPlayer_clear_all ()
534
+ {
535
+ for (auto& player : global::players)
536
+ player.self->clear();
537
+
538
+ global::players.clear();
539
+ }
153
540
 
154
- if (!source)
155
- return NULL;
541
+ void
542
+ stop_all_sound_players ()
543
+ {
544
+ for (auto& player : global::players)
545
+ player.stop();
546
+ }
547
+
548
+
549
+ SoundPlayer::SoundPlayer ()
550
+ {
551
+ }
552
+
553
+ SoundPlayer::~SoundPlayer ()
554
+ {
555
+ }
556
+
557
+ void
558
+ SoundPlayer::play ()
559
+ {
560
+ self->source.play();
561
+ }
156
562
 
157
- global::sources.push_back(source);
158
- return source.get();
563
+ void
564
+ SoundPlayer::pause ()
565
+ {
566
+ self->source.pause();
159
567
  }
160
568
 
569
+ void
570
+ SoundPlayer::rewind ()
571
+ {
572
+ not_implemented_error(__FILE__, __LINE__);
573
+ }
161
574
 
162
- struct Sound::Data
575
+ void
576
+ SoundPlayer::stop ()
163
577
  {
578
+ self->source.stop();
579
+ }
164
580
 
165
- ALint id;
581
+ bool
582
+ SoundPlayer::is_playing () const
583
+ {
584
+ return
585
+ self->source.is_playing() ||
586
+ (self->is_streaming() && self->source.is_stopped());
587
+ }
166
588
 
167
- Data ()
168
- : id(-1)
589
+ bool
590
+ SoundPlayer::is_paused () const
591
+ {
592
+ return self->source.is_paused();
593
+ }
594
+
595
+ bool
596
+ SoundPlayer::is_stopped () const
597
+ {
598
+ return self->source.is_stopped() && !self->is_streaming();
599
+ }
600
+
601
+ void
602
+ SoundPlayer::set_gain (float gain)
603
+ {
604
+ self->source.set_gain(gain);
605
+ }
606
+
607
+ float
608
+ SoundPlayer::gain () const
609
+ {
610
+ return self->source.gain();
611
+ }
612
+
613
+ void
614
+ SoundPlayer::set_loop (bool loop)
615
+ {
616
+ self->source.set_loop(loop);
617
+ }
618
+
619
+ bool
620
+ SoundPlayer::loop () const
621
+ {
622
+ return self->source.loop();
623
+ }
624
+
625
+ SoundPlayer::operator bool () const
626
+ {
627
+ return self->source;
628
+ }
629
+
630
+ bool
631
+ SoundPlayer::operator ! () const
632
+ {
633
+ return !operator bool();
634
+ }
635
+
636
+
637
+ struct Sound::Data {
638
+
639
+ float gain = 1;
640
+
641
+ bool loop = false;
642
+
643
+ virtual ~Data ()
169
644
  {
170
645
  }
171
646
 
172
- ~Data ()
647
+ virtual void attach_to (SoundPlayer* player)
173
648
  {
174
- clear();
649
+ not_implemented_error(__FILE__, __LINE__);
175
650
  }
176
651
 
177
- void create ()
652
+ virtual void save (const char* path) const
178
653
  {
179
- if (is_valid()) return;
180
-
181
- ALuint id_ = 0;
182
- alGenBuffers(1, &id_);
183
- OpenAL_check_error(__FILE__, __LINE__);
654
+ not_implemented_error(__FILE__, __LINE__);
655
+ }
184
656
 
185
- id = id_;
657
+ virtual double sample_rate () const
658
+ {
659
+ return 0;
186
660
  }
187
661
 
188
- void clear ()
662
+ virtual uint nchannels () const
189
663
  {
190
- if (id >= 0)
191
- {
192
- ALuint id_ = id;
193
- alDeleteBuffers(1, &id_);
194
- OpenAL_check_error(__FILE__, __LINE__);
195
- }
664
+ return 0;
665
+ }
196
666
 
197
- id = -1;
667
+ virtual float seconds () const
668
+ {
669
+ return 0;
198
670
  }
199
671
 
200
- bool is_valid () const
672
+ virtual bool is_valid () const
201
673
  {
202
- return id >= 0;
674
+ return false;
203
675
  }
204
676
 
205
677
  };// Sound::Data
206
678
 
207
679
 
208
- ALuint
209
- get_buffer_id (const Sound& sound)
680
+ struct SoundData : public Sound::Data
210
681
  {
211
- return sound.self->id;
212
- }
213
682
 
683
+ typedef Sound::Data Super;
214
684
 
215
- Sound::Sound ()
685
+ Signals signals;
686
+
687
+ SoundData (
688
+ Processor* processor, float seconds, uint nchannels, double sample_rate)
689
+ {
690
+ assert(
691
+ processor && *processor &&
692
+ seconds > 0 && nchannels > 0 && sample_rate > 0);
693
+
694
+ ProcessorContext context(seconds * sample_rate, nchannels, sample_rate);
695
+ Signals signals = context.process_signals(processor);
696
+ if (!signals)
697
+ beeps_error(__FILE__, __LINE__, "failed to process signals");
698
+
699
+ this->signals = signals;
700
+ }
701
+
702
+ void attach_to (SoundPlayer* player) override
703
+ {
704
+ assert(player && *player);
705
+
706
+ player->self->attach_signals(signals);
707
+ }
708
+
709
+ void save (const char* path) const override
710
+ {
711
+ if (!signals)
712
+ invalid_state_error(__FILE__, __LINE__);
713
+
714
+ Signals_save(signals, path);
715
+ }
716
+
717
+ double sample_rate () const override
718
+ {
719
+ return signals ? signals.sample_rate() : Super::sample_rate();
720
+ }
721
+
722
+ uint nchannels () const override
723
+ {
724
+ return signals ? signals.nchannels() : Super::nchannels();
725
+ }
726
+
727
+ float seconds () const override
728
+ {
729
+ return signals ? Signals_get_seconds(signals) : Super::seconds();
730
+ }
731
+
732
+ bool is_valid () const override
733
+ {
734
+ return signals;
735
+ }
736
+
737
+ };// SoundData
738
+
739
+
740
+ struct StreamSoundData : public Sound::Data
216
741
  {
217
- }
218
742
 
219
- Sound::Sound (Processor* processor, float seconds)
743
+ Processor::Ref processor;
744
+
745
+ double sample_rate_ = 0;
746
+
747
+ uint nchannels_ = 0;
748
+
749
+ StreamSoundData (Processor* processor, uint nchannels, double sample_rate)
750
+ {
751
+ assert(processor && *processor && nchannels > 0 && sample_rate > 0);
752
+
753
+ this->processor = processor;
754
+ this->sample_rate_ = sample_rate;
755
+ this->nchannels_ = nchannels;
756
+ }
757
+
758
+ void attach_to (SoundPlayer* player) override
759
+ {
760
+ assert(player && *player);
761
+
762
+ player->self->attach_stream(processor, nchannels_, sample_rate_);
763
+ }
764
+
765
+ double sample_rate () const override
766
+ {
767
+ return sample_rate_;
768
+ }
769
+
770
+ uint nchannels () const override
771
+ {
772
+ return nchannels_;
773
+ }
774
+
775
+ float seconds () const override
776
+ {
777
+ return -1;
778
+ }
779
+
780
+ bool is_valid () const override
781
+ {
782
+ return processor && sample_rate_ > 0 && nchannels_ > 0;
783
+ }
784
+
785
+ };// StreamSoundData
786
+
787
+
788
+ Sound
789
+ load_sound (const char* path)
220
790
  {
221
- if (!processor || !*processor || seconds <= 0)
222
- return;
791
+ FileIn* f = new FileIn(path);
792
+ return Sound(f, f->seconds(), f->nchannels(), f->sample_rate());
793
+ }
223
794
 
224
- self->create();
225
795
 
226
- Signals signals(seconds, 1);
227
- processor->process(&signals);
796
+ Sound::Sound ()
797
+ {
798
+ }
228
799
 
229
- stk::StkFrames* frames = Signals_get_frames(&signals);
230
- if (!frames)
231
- return;
800
+ Sound::Sound (
801
+ Processor* processor, float seconds, uint nchannels, double sample_rate)
802
+ {
803
+ Processor::Ref ref = processor;
232
804
 
233
- ALsizei size = frames->frames();
234
- if (size <= 0)
235
- return;
805
+ if (!processor || !*processor)
806
+ argument_error(__FILE__, __LINE__);
236
807
 
237
- std::vector<short> buffer;
238
- buffer.reserve(size);
239
- for (ALsizei i = 0; i < size; ++i)
240
- buffer.push_back((*frames)[i] * SHRT_MAX);
808
+ if (sample_rate <= 0) sample_rate = Beeps::sample_rate();
241
809
 
242
- alBufferData(
243
- self->id, AL_FORMAT_MONO16, &buffer[0], sizeof(short) * size,
244
- frames->dataRate());
245
- OpenAL_check_error(__FILE__, __LINE__);
810
+ if (seconds > 0)
811
+ self.reset(new SoundData(processor, seconds, nchannels, sample_rate));
812
+ else
813
+ self.reset(new StreamSoundData(processor, nchannels, sample_rate));
246
814
  }
247
815
 
248
816
  Sound::~Sound ()
249
817
  {
250
818
  }
251
819
 
252
- void
820
+ SoundPlayer
253
821
  Sound::play ()
254
822
  {
255
- if (!*this)
823
+ SoundPlayer player = get_next_player();
824
+ if (!player)
256
825
  invalid_state_error(__FILE__, __LINE__);
257
826
 
258
- SoundSource* source = get_next_source();
259
- if (!source || !*source)
260
- invalid_state_error(__FILE__, __LINE__);
827
+ player.set_gain(gain());
828
+ player.set_loop(loop());
261
829
 
262
- source->play(*this);
830
+ self->attach_to(&player);
831
+ player.play();
263
832
 
264
833
  #if 0
265
834
  std::string ox = "";
266
- for (size_t i = 0; i < global::sources.size(); ++i)
267
- ox += global::sources[i]->is_playing() ? 'o' : 'x';
268
- LOG("playing with %d sources. (%s)", global::sources.size(), ox.c_str());
835
+ for (auto& player : global::players)
836
+ ox += player.is_playing() ? 'o' : 'x';
837
+ LOG("%d players. (%s)", global::players.size(), ox.c_str());
269
838
  #endif
839
+
840
+ return player;
841
+ }
842
+
843
+ void
844
+ Sound::save (const char* path) const
845
+ {
846
+ self->save(path);
847
+ }
848
+
849
+ double
850
+ Sound::sample_rate () const
851
+ {
852
+ return self->sample_rate();
853
+ }
854
+
855
+ uint
856
+ Sound::nchannels () const
857
+ {
858
+ return self->nchannels();
859
+ }
860
+
861
+ float
862
+ Sound::seconds () const
863
+ {
864
+ return self->seconds();
865
+ }
866
+
867
+ void
868
+ Sound::set_gain (float gain)
869
+ {
870
+ if (gain < 0)
871
+ argument_error(__FILE__, __LINE__);
872
+
873
+ self->gain = gain;
874
+ }
875
+
876
+ float
877
+ Sound::gain () const
878
+ {
879
+ return self->gain;
880
+ }
881
+
882
+ void
883
+ Sound::set_loop (bool loop)
884
+ {
885
+ self->loop = loop;
886
+ }
887
+
888
+ bool
889
+ Sound::loop () const
890
+ {
891
+ return self->loop;
270
892
  }
271
893
 
272
894
  Sound::operator bool () const