beeps 0.1.32 → 0.1.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) 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 +4 -1
  16. data/ChangeLog.md +8 -0
  17. data/Rakefile +26 -4
  18. data/VERSION +1 -1
  19. data/beeps.gemspec +2 -2
  20. data/ext/beeps/adsr.cpp +150 -0
  21. data/ext/beeps/analyser.cpp +134 -0
  22. data/ext/beeps/beeps.cpp +10 -1
  23. data/ext/beeps/extconf.rb +1 -2
  24. data/ext/beeps/file_in.cpp +60 -9
  25. data/ext/beeps/gain.cpp +67 -0
  26. data/ext/beeps/mic_in.cpp +88 -0
  27. data/ext/beeps/native.cpp +20 -8
  28. data/ext/beeps/oscillator.cpp +93 -0
  29. data/ext/beeps/pitch_shift.cpp +67 -0
  30. data/ext/beeps/processor.cpp +34 -2
  31. data/ext/beeps/sound.cpp +99 -7
  32. data/ext/beeps/sound_player.cpp +169 -0
  33. data/ext/beeps/time_stretch.cpp +67 -0
  34. data/include/beeps/beeps.h +2 -1
  35. data/include/beeps/filter.h +179 -0
  36. data/include/beeps/generator.h +120 -0
  37. data/include/beeps/processor.h +37 -68
  38. data/include/beeps/ruby/filter.h +78 -0
  39. data/include/beeps/ruby/generator.h +60 -0
  40. data/include/beeps/ruby/processor.h +5 -45
  41. data/include/beeps/ruby/sound.h +14 -3
  42. data/include/beeps/signals.h +10 -4
  43. data/include/beeps/sound.h +67 -2
  44. data/lib/beeps/beeps.rb +6 -1
  45. data/lib/beeps/processor.rb +95 -15
  46. data/lib/beeps/sound.rb +29 -2
  47. data/src/adsr.cpp +245 -0
  48. data/src/analyser.cpp +254 -0
  49. data/src/beeps.cpp +11 -2
  50. data/src/file_in.cpp +94 -0
  51. data/src/gain.cpp +55 -0
  52. data/src/mic_in.cpp +262 -0
  53. data/src/mic_in.h +20 -0
  54. data/src/openal.cpp +2 -1
  55. data/src/oscillator.cpp +145 -0
  56. data/src/osx/signals.mm +83 -0
  57. data/src/pitch_shift.cpp +82 -0
  58. data/src/processor.cpp +202 -88
  59. data/src/processor.h +98 -0
  60. data/src/signals.cpp +326 -20
  61. data/src/signals.h +192 -2
  62. data/src/sound.cpp +735 -113
  63. data/src/sound.h +6 -1
  64. data/src/time_stretch.cpp +91 -0
  65. data/test/helper.rb +2 -1
  66. data/test/test_beeps.rb +10 -7
  67. data/test/test_beeps_init.rb +18 -0
  68. data/test/test_file_in.rb +15 -0
  69. data/test/test_processor.rb +50 -0
  70. data/test/test_sound.rb +87 -11
  71. data/test/test_sound_player.rb +134 -0
  72. metadata +54 -16
  73. data/.doc/ext/beeps/sawtooth_wave.cpp +0 -61
  74. data/.doc/ext/beeps/sine_wave.cpp +0 -61
  75. data/.doc/ext/beeps/square_wave.cpp +0 -61
  76. data/ext/beeps/sawtooth_wave.cpp +0 -64
  77. data/ext/beeps/sine_wave.cpp +0 -64
  78. 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