beeps 0.1.32 → 0.1.34

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 +14 -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 +14 -2
  50. data/src/file_in.cpp +94 -0
  51. data/src/gain.cpp +55 -0
  52. data/src/mic_in.cpp +271 -0
  53. data/src/mic_in.h +22 -0
  54. data/src/openal.cpp +1 -2
  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/mic_in.cpp ADDED
@@ -0,0 +1,271 @@
1
+ #include "mic_in.h"
2
+
3
+
4
+ #include <limits.h>
5
+ #include <vector>
6
+ #include <memory>
7
+ #include <algorithm>
8
+ #include "beeps/beeps.h"
9
+ #include "beeps/exception.h"
10
+ #include "openal.h"
11
+ #include "signals.h"
12
+
13
+
14
+ namespace Beeps
15
+ {
16
+
17
+
18
+ struct Mic
19
+ {
20
+
21
+ void start ()
22
+ {
23
+ if (self->started) return;
24
+
25
+ self->create();
26
+ if (!is_valid())
27
+ invalid_state_error(__FILE__, __LINE__);
28
+
29
+ alcCaptureStart(self->device);
30
+ OpenAL_check_error(__FILE__, __LINE__);
31
+
32
+ self->started = true;
33
+ }
34
+
35
+ void stop ()
36
+ {
37
+ if (!self->started) return;
38
+
39
+ if (!is_valid())
40
+ invalid_state_error(__FILE__, __LINE__);
41
+
42
+ alcCaptureStop(self->device);
43
+ OpenAL_check_error(__FILE__, __LINE__);
44
+
45
+ self->started = false;
46
+ }
47
+
48
+ bool is_started () const
49
+ {
50
+ return self->started;
51
+ }
52
+
53
+ void buffer_samples ()
54
+ {
55
+ if (!is_valid())
56
+ invalid_state_error(__FILE__, __LINE__);
57
+
58
+ ALint nsamples = 0;
59
+ alcGetIntegerv(
60
+ self->device, ALC_CAPTURE_SAMPLES, sizeof(nsamples), &nsamples);
61
+ OpenAL_check_error(__FILE__, __LINE__);
62
+ if (nsamples <= 0) return;
63
+
64
+ auto& buffer = self->buffer;
65
+ if (buffer.size() < (size_t) nsamples) buffer.resize(nsamples);
66
+
67
+ alcCaptureSamples(self->device, (ALCvoid*) &buffer[0], nsamples);
68
+ OpenAL_check_error(__FILE__, __LINE__);
69
+
70
+ auto& samples = self->samples;
71
+ samples.reserve(samples.size() + nsamples);
72
+ samples.insert(samples.end(), &buffer[0], &buffer[0] + nsamples);
73
+
74
+ if (samples.size() > samples_size_max())
75
+ {
76
+ size_t size = samples.size() - samples_size_max();
77
+ samples.erase(samples.begin(), samples.begin() + size);
78
+ self->offset += (uint) size;
79
+ }
80
+ }
81
+
82
+ void get_signals (Signals* signals, uint* gen_offset)
83
+ {
84
+ assert(signals && signals->empty() && gen_offset);
85
+
86
+ if (!is_valid()) return;
87
+
88
+ Frames* frames = Signals_get_frames(signals);
89
+ assert(frames);
90
+
91
+ auto& samples = self->samples;
92
+ uint nchannels = self->nchannels;
93
+ uint nsamples = (uint) (samples.size() / nchannels);
94
+ uint offset = *gen_offset < self->offset ? 0 : *gen_offset - self->offset;
95
+ if (offset >= nsamples) return;
96
+
97
+ nsamples = std::min(frames->nframes(), nsamples - offset);
98
+ for (uint ch = 0; ch < frames->nchannels(); ++ch)
99
+ {
100
+ uint samples_ch = ch < nchannels ? ch : 0;
101
+ float div = 1.0f / SHRT_MAX;
102
+ for (uint i = 0; i < nsamples; ++i)
103
+ (*frames)(i, ch) = samples[(offset + i) * nchannels + samples_ch] * div;
104
+ }
105
+ Signals_set_nsamples(signals, nsamples);
106
+
107
+ *gen_offset = self->offset + offset + nsamples;
108
+ }
109
+
110
+ uint samples_size_max () const
111
+ {
112
+ assert(self->sample_rate > 0);
113
+
114
+ return self->sample_rate * 1;
115
+ }
116
+
117
+ bool is_valid () const
118
+ {
119
+ return self->sample_rate > 0 && self->nchannels > 0 && self->device;
120
+ }
121
+
122
+ struct Data
123
+ {
124
+
125
+ double sample_rate = 0;
126
+
127
+ uint nchannels = 0;
128
+
129
+ uint offset = 0;
130
+
131
+ bool started = false;
132
+
133
+ ALCdevice* device = NULL;
134
+
135
+ std::vector<ALshort> samples, buffer;
136
+
137
+ ~Data ()
138
+ {
139
+ clear();
140
+ }
141
+
142
+ void create ()
143
+ {
144
+ assert(sample_rate > 0 && nchannels > 0);
145
+
146
+ if (device) return;
147
+
148
+ device = alcCaptureOpenDevice(
149
+ NULL,
150
+ sample_rate,
151
+ nchannels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16,
152
+ sample_rate / 10);
153
+ if (!device)
154
+ openal_error(__FILE__, __LINE__, "failed to create a mic device");
155
+ }
156
+
157
+ void clear ()
158
+ {
159
+ if (!device) return;
160
+
161
+ if (alcCaptureCloseDevice(device) == ALC_FALSE)
162
+ openal_error(__FILE__, __LINE__, "failed to close the mic device");
163
+
164
+ device = NULL;
165
+ }
166
+
167
+ };// Data
168
+
169
+ Xot::PSharedImpl<Data> self;
170
+
171
+ };// Mic
172
+
173
+
174
+ struct MicIn::Data
175
+ {
176
+
177
+ Mic mic;
178
+
179
+ };// MicIn::Data
180
+
181
+
182
+ namespace global
183
+ {
184
+
185
+ static std::vector<Mic> mics;
186
+
187
+ };// global
188
+
189
+
190
+ void
191
+ MicIn_process_streams ()
192
+ {
193
+ for (auto& mic : global::mics)
194
+ mic.buffer_samples();
195
+ }
196
+
197
+ void
198
+ MicIn_clear_streams ()
199
+ {
200
+ for (auto& mic : global::mics)
201
+ mic.stop();
202
+
203
+ global::mics.clear();
204
+ }
205
+
206
+
207
+ MicIn::MicIn (uint nchannels, double sample_rate)
208
+ {
209
+ if (nchannels > 2)
210
+ argument_error(__FILE__, __LINE__);
211
+
212
+ if (sample_rate <= 0) sample_rate = Beeps::sample_rate();
213
+
214
+ self->mic.self->sample_rate = sample_rate;
215
+ self->mic.self->nchannels = nchannels;
216
+ }
217
+
218
+ MicIn::~MicIn ()
219
+ {
220
+ }
221
+
222
+ void
223
+ MicIn::start ()
224
+ {
225
+ if (self->mic.is_started()) return;
226
+
227
+ self->mic.start();
228
+
229
+ global::mics.emplace_back(self->mic);
230
+ }
231
+
232
+ void
233
+ MicIn::stop ()
234
+ {
235
+ if (!self->mic.is_started()) return;
236
+
237
+ std::erase_if(
238
+ global::mics,
239
+ [&](auto& mic) {return mic.self.get() == self->mic.self.get();});
240
+
241
+ self->mic.stop();
242
+ }
243
+
244
+ double
245
+ MicIn::sample_rate () const
246
+ {
247
+ return self->mic.self->sample_rate;
248
+ }
249
+
250
+ uint
251
+ MicIn::nchannels () const
252
+ {
253
+ return self->mic.self->nchannels;
254
+ }
255
+
256
+ void
257
+ MicIn::generate (Context* context, Signals* signals, uint* offset)
258
+ {
259
+ Super::generate(context, signals, offset);
260
+
261
+ self->mic.get_signals(signals, offset);
262
+ }
263
+
264
+ MicIn::operator bool () const
265
+ {
266
+ if (!Super::operator bool()) return false;
267
+ return self->mic.is_valid();
268
+ }
269
+
270
+
271
+ }// Beeps
data/src/mic_in.h ADDED
@@ -0,0 +1,22 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __BEEPS_SRC_MIC_IN_H__
4
+ #define __BEEPS_SRC_MIC_IN_H__
5
+
6
+
7
+ #include <beeps/generator.h>
8
+
9
+
10
+ namespace Beeps
11
+ {
12
+
13
+
14
+ void MicIn_process_streams ();
15
+
16
+ void MicIn_clear_streams ();
17
+
18
+
19
+ }// Beeps
20
+
21
+
22
+ #endif//EOH
data/src/openal.cpp CHANGED
@@ -23,8 +23,6 @@ namespace Beeps
23
23
  static void
24
24
  cleanup ()
25
25
  {
26
- Sound_cleanup_sources();
27
-
28
26
  alcMakeContextCurrent(NULL);
29
27
 
30
28
  if (global::context)
@@ -94,6 +92,7 @@ namespace Beeps
94
92
  case ALC_INVALID_DEVICE: return "ALC_INVALID_DEVICE";
95
93
  case ALC_INVALID_CONTEXT: return "ALC_INVALID_CONTEXT";
96
94
  case ALC_INVALID_ENUM: return "ALC_INVALID_ENUM";
95
+ case ALC_INVALID_VALUE: return "ALC_INVALID_VALUE";
97
96
  case ALC_OUT_OF_MEMORY: return "ALC_OUT_OF_MEMORY";
98
97
  default: return "UNKNOWN ERROR";
99
98
  }
@@ -0,0 +1,145 @@
1
+ #include "beeps/generator.h"
2
+
3
+
4
+ #include "SineWave.h"
5
+ #include "BlitSquare.h"
6
+ #include "BlitSaw.h"
7
+ #include "beeps/exception.h"
8
+ #include "signals.h"
9
+
10
+
11
+ namespace Beeps
12
+ {
13
+
14
+
15
+ struct Oscillator::Data
16
+ {
17
+
18
+ Type type = NONE;
19
+
20
+ float frequency = 440;
21
+
22
+ std::unique_ptr<stk::SineWave> sine;
23
+
24
+ std::unique_ptr<stk::BlitSquare> square;
25
+
26
+ std::unique_ptr<stk::BlitSaw> saw;
27
+
28
+ };// Oscillator::Data
29
+
30
+
31
+ Oscillator::Oscillator (Type type)
32
+ {
33
+ set_type(type);
34
+ }
35
+
36
+ Oscillator::~Oscillator ()
37
+ {
38
+ }
39
+
40
+ void
41
+ Oscillator::reset ()
42
+ {
43
+ Super::reset();
44
+
45
+ if (self->sine) self->sine->reset();
46
+ if (self->square) self->square->reset();
47
+ if (self->saw) self->saw->reset();
48
+ }
49
+
50
+ void
51
+ Oscillator::set_type (Type type)
52
+ {
53
+ if (type == self->type) return;
54
+
55
+ self->type = type;
56
+
57
+ self->sine .reset();
58
+ self->square.reset();
59
+ self->saw .reset();
60
+
61
+ switch (self->type)
62
+ {
63
+ case SINE: self->sine .reset(new stk::SineWave()); break;
64
+ case SQUARE: self->square.reset(new stk::BlitSquare()); break;
65
+ case SAWTOOTH: self->saw .reset(new stk::BlitSaw()); break;
66
+ default:
67
+ argument_error(
68
+ __FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
69
+ break;
70
+ }
71
+
72
+ set_updated();
73
+ }
74
+
75
+ Oscillator::Type
76
+ Oscillator::type () const
77
+ {
78
+ return self->type;
79
+ }
80
+
81
+ void
82
+ Oscillator::set_frequency (float frequency)
83
+ {
84
+ if (frequency <= 0)
85
+ argument_error(__FILE__, __LINE__);
86
+
87
+ self->frequency = frequency;
88
+
89
+ set_updated();
90
+ }
91
+
92
+ float
93
+ Oscillator::frequency () const
94
+ {
95
+ return self->frequency;
96
+ }
97
+
98
+ void
99
+ Oscillator::generate (Context* context, Signals* signals, uint* offset)
100
+ {
101
+ Super::generate(context, signals, offset);
102
+
103
+ Frames* frames = Signals_get_frames(signals);
104
+ if (!frames)
105
+ argument_error(__FILE__, __LINE__);
106
+
107
+ switch (self->type)
108
+ {
109
+ case SINE:
110
+ assert(self->sine);
111
+ self->sine->setFrequency(self->frequency);
112
+ self->sine->tick(*frames);
113
+ break;
114
+
115
+ case SQUARE:
116
+ assert(self->square);
117
+ self->square->setFrequency(self->frequency);
118
+ self->square->tick(*frames);
119
+ break;
120
+
121
+ case SAWTOOTH:
122
+ assert(self->saw);
123
+ self->saw->setFrequency(self->frequency);
124
+ self->saw->tick(*frames);
125
+ break;
126
+
127
+ default:
128
+ invalid_state_error(
129
+ __FILE__, __LINE__, "unknown oscilator type '%d'", self->type);
130
+ break;
131
+ }
132
+
133
+ Signals_set_nsamples(signals, frames->nframes());
134
+ }
135
+
136
+ Oscillator::operator bool () const
137
+ {
138
+ if (!Super::operator bool()) return false;
139
+ return
140
+ self->type != NONE && self->frequency > 0 &&
141
+ (self->sine || self->square || self->saw);
142
+ }
143
+
144
+
145
+ }// Beeps
@@ -0,0 +1,83 @@
1
+ // -*- c++ -*-
2
+ #include "../signals.h"
3
+
4
+
5
+ #import <AVFoundation/AVFoundation.h>
6
+ #include <beeps/exception.h>
7
+
8
+
9
+ namespace Beeps
10
+ {
11
+
12
+
13
+ [[noreturn]]
14
+ void
15
+ objc_error (const char* file, int line, NSError* error, const char* format = NULL, ...)
16
+ {
17
+ if (!error)
18
+ argument_error(__FILE__, __LINE__);
19
+
20
+ XOT_STRINGF(format, s);
21
+ beeps_error(
22
+ file, line,
23
+ Xot::stringf("%s (%s)", s.c_str(), error.localizedDescription.UTF8String));
24
+ }
25
+
26
+
27
+ static AVAudioPCMBuffer*
28
+ load_buffer (const char* path)
29
+ {
30
+ if (!path)
31
+ argument_error(__FILE__, __LINE__);
32
+
33
+ NSURL* url = [NSURL fileURLWithPath: [NSString stringWithUTF8String:path]];
34
+ if (!url)
35
+ system_error(__FILE__, __LINE__, "failed to get url object for path");
36
+
37
+ NSError* error = nil;
38
+
39
+ AVAudioFile* file =
40
+ [[[AVAudioFile alloc] initForReading:url error:&error] autorelease];
41
+ if (error)
42
+ objc_error(__FILE__, __LINE__, error, "failed to open file '%s'", path);
43
+
44
+ AVAudioFormat* format = file.processingFormat;
45
+ if (format.channelCount <= 0)
46
+ beeps_error(__FILE__, __LINE__, "invalid channel count %d", format.channelCount);
47
+
48
+ AVAudioPCMBuffer* buffer = [[[AVAudioPCMBuffer alloc]
49
+ initWithPCMFormat:format frameCapacity:(AVAudioFrameCount) file.length]
50
+ autorelease];
51
+
52
+ BOOL result = [file readIntoBuffer:buffer error:&error];
53
+ if (error)
54
+ objc_error(__FILE__, __LINE__, error, "failed to read into the buffer");
55
+ if (!result)
56
+ system_error(__FILE__, __LINE__, "failed to read into the buffer");
57
+
58
+ return buffer;
59
+ }
60
+
61
+ Signals
62
+ Signals_load (const char* path)
63
+ {
64
+ AVAudioPCMBuffer* buffer = load_buffer(path);
65
+ if (!buffer) return Signals();
66
+
67
+ uint nchannels = buffer.format.channelCount;
68
+ if (nchannels <= 0)
69
+ beeps_error(__FILE__, __LINE__, "invalid nchannels %d", nchannels);
70
+
71
+ uint len = buffer.frameLength;
72
+ if (len <= 0)
73
+ beeps_error(__FILE__, __LINE__, "invalid buffer length %d", len);
74
+
75
+ const float* const* data = buffer.floatChannelData;
76
+ if (!data)
77
+ beeps_error(__FILE__, __LINE__, "failed to get channel data");
78
+
79
+ return Signals_create(data, len, nchannels, (uint) buffer.format.sampleRate);
80
+ }
81
+
82
+
83
+ }// Beeps
@@ -0,0 +1,82 @@
1
+ #include "beeps/filter.h"
2
+
3
+
4
+ #include "PitShift.h"
5
+ #include "signalsmith-stretch.h"
6
+ #include "signals.h"
7
+
8
+
9
+ namespace Beeps
10
+ {
11
+
12
+
13
+ struct PitchShift::Data
14
+ {
15
+
16
+ signalsmith::stretch::SignalsmithStretch<float> stretch;
17
+
18
+ float shift = 1;
19
+
20
+ };// PitchShift::Data
21
+
22
+
23
+ PitchShift::PitchShift (Processor* input)
24
+ : Super(input)
25
+ {
26
+ set_buffering_seconds(1);
27
+ }
28
+
29
+ PitchShift::~PitchShift ()
30
+ {
31
+ }
32
+
33
+ void
34
+ PitchShift::reset ()
35
+ {
36
+ Super::reset();
37
+
38
+ self->stretch.reset();
39
+ }
40
+
41
+ void
42
+ PitchShift::set_shift (float shift)
43
+ {
44
+ self->shift = shift;
45
+
46
+ set_updated();
47
+ }
48
+
49
+ float
50
+ PitchShift::shift () const
51
+ {
52
+ return self->shift;
53
+ }
54
+
55
+ void
56
+ PitchShift::filter (Context* context, Signals* signals, uint* offset)
57
+ {
58
+ Super::filter(context, signals, offset);
59
+
60
+ if (self->shift == 1 || signals->nsamples() <= 0)
61
+ return;
62
+
63
+ SignalSamples<float> input(*signals);
64
+ SignalSamples<float> output(input.nsamples(), signals->nchannels());
65
+
66
+ self->stretch.presetDefault(signals->nchannels(), signals->sample_rate());
67
+ self->stretch.setTransposeFactor(self->shift);
68
+ self->stretch.process(
69
+ input.channels(), input.nsamples(),
70
+ output.channels(), output.nsamples());
71
+
72
+ Signals_write_samples(signals, output, signals->nsamples());
73
+ }
74
+
75
+ PitchShift::operator bool () const
76
+ {
77
+ if (!Super::operator bool()) return false;
78
+ return self->shift > 0;
79
+ }
80
+
81
+
82
+ }// Beeps