beeps 0.2 → 0.3

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.doc/ext/beeps/adsr.cpp +1 -1
  3. data/.doc/ext/beeps/analyser.cpp +1 -1
  4. data/.doc/ext/beeps/file_in.cpp +1 -1
  5. data/.doc/ext/beeps/gain.cpp +1 -1
  6. data/.doc/ext/beeps/mic_in.cpp +1 -1
  7. data/.doc/ext/beeps/oscillator.cpp +1 -1
  8. data/.doc/ext/beeps/pitch_shift.cpp +1 -1
  9. data/.doc/ext/beeps/processor.cpp +1 -1
  10. data/.doc/ext/beeps/sound.cpp +1 -1
  11. data/.doc/ext/beeps/sound_player.cpp +1 -1
  12. data/.doc/ext/beeps/time_stretch.cpp +1 -1
  13. data/.github/workflows/release-gem.yml +2 -2
  14. data/.github/workflows/tag.yml +1 -1
  15. data/.github/workflows/test.yml +10 -1
  16. data/ChangeLog.md +15 -0
  17. data/Gemfile.lock +1 -1
  18. data/Rakefile +3 -1
  19. data/VERSION +1 -1
  20. data/beeps.gemspec +2 -2
  21. data/ext/beeps/adsr.cpp +1 -1
  22. data/ext/beeps/analyser.cpp +1 -1
  23. data/ext/beeps/defs.h +2 -0
  24. data/ext/beeps/extconf.rb +11 -3
  25. data/ext/beeps/file_in.cpp +1 -1
  26. data/ext/beeps/gain.cpp +1 -1
  27. data/ext/beeps/mic_in.cpp +1 -1
  28. data/ext/beeps/oscillator.cpp +1 -1
  29. data/ext/beeps/pitch_shift.cpp +1 -1
  30. data/ext/beeps/processor.cpp +1 -1
  31. data/ext/beeps/sound.cpp +1 -1
  32. data/ext/beeps/sound_player.cpp +1 -1
  33. data/ext/beeps/time_stretch.cpp +1 -1
  34. data/include/beeps/debug.h +0 -1
  35. data/include/beeps/defs.h +7 -0
  36. data/include/beeps/ruby/beeps.h +1 -1
  37. data/include/beeps/ruby/exception.h +2 -2
  38. data/include/beeps/ruby/filter.h +7 -7
  39. data/include/beeps/ruby/generator.h +5 -5
  40. data/include/beeps/ruby/processor.h +2 -2
  41. data/include/beeps/ruby/sound.h +4 -4
  42. data/include/beeps/ruby.h +3 -1
  43. data/include/beeps.h +2 -0
  44. data/lib/beeps/extension.rb +4 -0
  45. data/src/beeps.cpp +3 -1
  46. data/src/beeps.h +22 -0
  47. data/src/openal.cpp +6 -0
  48. data/src/openal.h +2 -0
  49. data/src/osx/beeps.mm +19 -0
  50. data/src/signals.cpp +17 -15
  51. data/src/signals.h +6 -6
  52. data/src/sound.cpp +41 -18
  53. data/src/win32/beeps.cpp +36 -0
  54. data/src/win32/exception.cpp +40 -0
  55. data/src/win32/exception.h +40 -0
  56. data/src/win32/signals.cpp +186 -0
  57. data/test/helper.rb +0 -5
  58. data/test/test_sound_player.rb +4 -0
  59. metadata +12 -6
data/src/osx/beeps.mm ADDED
@@ -0,0 +1,19 @@
1
+ #include "../beeps.h"
2
+
3
+
4
+ namespace Beeps
5
+ {
6
+
7
+
8
+ void
9
+ Beeps_init ()
10
+ {
11
+ }
12
+
13
+ void
14
+ Beeps_fin ()
15
+ {
16
+ }
17
+
18
+
19
+ }// Beeps
data/src/signals.cpp CHANGED
@@ -24,21 +24,6 @@ namespace Beeps
24
24
  };// Signals::Data
25
25
 
26
26
 
27
- Frames*
28
- Signals_get_frames (Signals* signals)
29
- {
30
- if (!signals)
31
- argument_error(__FILE__, __LINE__);
32
-
33
- return signals->self->frames.get();
34
- }
35
-
36
- const Frames*
37
- Signals_get_frames (const Signals* signals)
38
- {
39
- return Signals_get_frames(const_cast<Signals*>(signals));
40
- }
41
-
42
27
  Signals
43
28
  Signals_create (uint capacity, uint nchannels, double sample_rate)
44
29
  {
@@ -67,8 +52,10 @@ namespace Beeps
67
52
  frames->setDataRate(sample_rate);
68
53
 
69
54
  for (uint channel = 0; channel < nchannels; ++channel)
55
+ {
70
56
  for (uint sample = 0; sample < nsamples; ++sample)
71
57
  (*frames)(sample, channel) = channels[channel][sample];
58
+ }
72
59
 
73
60
  Signals s;
74
61
  s.self->frames.reset(frames);
@@ -296,6 +283,21 @@ namespace Beeps
296
283
  return (float) (signals.nsamples() / signals.sample_rate());
297
284
  }
298
285
 
286
+ Frames*
287
+ Signals_get_frames (Signals* signals)
288
+ {
289
+ if (!signals)
290
+ argument_error(__FILE__, __LINE__);
291
+
292
+ return signals->self->frames.get();
293
+ }
294
+
295
+ const Frames*
296
+ Signals_get_frames (const Signals* signals)
297
+ {
298
+ return Signals_get_frames(const_cast<Signals*>(signals));
299
+ }
300
+
299
301
  static void
300
302
  make_audio_buffer (
301
303
  AudioFile<float>::AudioBuffer* buffer, const Signals& signals)
data/src/signals.h CHANGED
@@ -24,10 +24,6 @@ namespace Beeps
24
24
  template <typename T> class SignalSamples;
25
25
 
26
26
 
27
- Frames* Signals_get_frames ( Signals* signals);
28
-
29
- const Frames* Signals_get_frames (const Signals* signals);
30
-
31
27
  Signals Signals_create (
32
28
  uint capacity, uint nchannels = 1, double sample_rate = 0);
33
29
 
@@ -53,6 +49,10 @@ namespace Beeps
53
49
 
54
50
  float Signals_get_seconds (const Signals& signals);
55
51
 
52
+ Frames* Signals_get_frames ( Signals* signals);
53
+
54
+ const Frames* Signals_get_frames (const Signals* signals);
55
+
56
56
  void Signals_save (const Signals& signals, const char* path);
57
57
 
58
58
  Signals Signals_load (const char* path);
@@ -63,8 +63,8 @@ namespace Beeps
63
63
 
64
64
  public:
65
65
 
66
- Frames (unsigned int nFrames = 0, unsigned int nChannels = 0)
67
- : stk::StkFrames(nFrames, nChannels)
66
+ Frames (unsigned int nframes = 0, unsigned int nchannels = 0)
67
+ : stk::StkFrames(nframes, nchannels)
68
68
  {
69
69
  }
70
70
 
data/src/sound.cpp CHANGED
@@ -67,8 +67,10 @@ namespace Beeps
67
67
  std::vector<short> buffer;
68
68
  buffer.reserve(nsamples * nchannels);
69
69
  for (uint sample = 0; sample < nsamples; ++sample)
70
+ {
70
71
  for (uint channel = 0; channel < nchannels; ++channel)
71
72
  buffer.push_back((*frames)(sample, channel) * SHRT_MAX);
73
+ }
72
74
 
73
75
  alBufferData(
74
76
  self->id,
@@ -94,9 +96,11 @@ namespace Beeps
94
96
  struct Data
95
97
  {
96
98
 
97
- ALint id = -1;
99
+ void* context = NULL;
100
+
101
+ ALint id = -1;
98
102
 
99
- bool owner = false;
103
+ bool owner = false;
100
104
 
101
105
  ~Data ()
102
106
  {
@@ -111,26 +115,28 @@ namespace Beeps
111
115
  alGenBuffers(1, &id_);
112
116
  OpenAL_check_error(__FILE__, __LINE__);
113
117
 
114
- id = id_;
115
- owner = true;
118
+ context = OpenAL_get_context();
119
+ id = id_;
120
+ owner = true;
116
121
  }
117
122
 
118
123
  void clear ()
119
124
  {
120
- if (owner && id >= 0)
125
+ if (owner && is_valid())
121
126
  {
122
127
  ALuint id_ = id;
123
128
  alDeleteBuffers(1, &id_);
124
129
  OpenAL_check_error(__FILE__, __LINE__);
125
130
  }
126
131
 
127
- id = -1;
128
- owner = false;
132
+ context = NULL;
133
+ id = -1;
134
+ owner = false;
129
135
  }
130
136
 
131
137
  bool is_valid () const
132
138
  {
133
- return id >= 0;
139
+ return id >= 0 && context == OpenAL_get_context();
134
140
  }
135
141
 
136
142
  };// Data
@@ -145,9 +151,7 @@ namespace Beeps
145
151
 
146
152
  void create ()
147
153
  {
148
- ALuint id_ = 0;
149
- alGenSources(1, &id_);
150
- if (OpenAL_no_error()) self->id = id_;
154
+ self->create();
151
155
  }
152
156
 
153
157
  void clear ()
@@ -310,7 +314,7 @@ namespace Beeps
310
314
 
311
315
  operator bool () const
312
316
  {
313
- return self->id >= 0;
317
+ return self->is_valid();
314
318
  }
315
319
 
316
320
  bool operator ! () const
@@ -321,22 +325,41 @@ namespace Beeps
321
325
  struct Data
322
326
  {
323
327
 
324
- ALint id = -1;
328
+ void* context = NULL;
329
+
330
+ ALint id = -1;
325
331
 
326
332
  ~Data ()
327
333
  {
328
334
  clear();
329
335
  }
330
336
 
337
+ void create ()
338
+ {
339
+ ALuint id_ = 0;
340
+ alGenSources(1, &id_);
341
+ if (!OpenAL_no_error()) return;
342
+
343
+ context = OpenAL_get_context();
344
+ id = id_;
345
+ }
346
+
331
347
  void clear ()
332
348
  {
333
- if (id < 0) return;
349
+ if (is_valid())
350
+ {
351
+ ALuint id_ = id;
352
+ alDeleteSources(1, &id_);
353
+ OpenAL_check_error(__FILE__, __LINE__);
354
+ }
334
355
 
335
- ALuint id_ = id;
336
- alDeleteSources(1, &id_);
337
- OpenAL_check_error(__FILE__, __LINE__);
356
+ context = NULL;
357
+ id = -1;
358
+ }
338
359
 
339
- id = -1;
360
+ bool is_valid () const
361
+ {
362
+ return id >= 0 && context == OpenAL_get_context();
340
363
  }
341
364
 
342
365
  };// Data
@@ -0,0 +1,36 @@
1
+ #include "../beeps.h"
2
+
3
+
4
+ #include <mfapi.h>
5
+ #include <xot/windows.h>
6
+ #include "exception.h"
7
+
8
+
9
+ namespace Beeps
10
+ {
11
+
12
+
13
+ void
14
+ Beeps_init ()
15
+ {
16
+ check_media_foundation_error(
17
+ CoInitializeEx(NULL, COINIT_MULTITHREADED),
18
+ __FILE__, __LINE__);
19
+
20
+ check_media_foundation_error(
21
+ MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET),
22
+ __FILE__, __LINE__);
23
+ }
24
+
25
+ void
26
+ Beeps_fin ()
27
+ {
28
+ check_media_foundation_error(
29
+ MFShutdown(),
30
+ __FILE__, __LINE__);
31
+
32
+ CoUninitialize();
33
+ }
34
+
35
+
36
+ }// Beeps
@@ -0,0 +1,40 @@
1
+ #include "exception.h"
2
+
3
+
4
+ #include <xot/string.h>
5
+
6
+
7
+ namespace Beeps
8
+ {
9
+
10
+
11
+ MediaFoundationError::MediaFoundationError (const char* str)
12
+ : Super(str)
13
+ {
14
+ }
15
+
16
+
17
+ namespace ErrorFunctions
18
+ {
19
+
20
+ void
21
+ media_foundation_error (
22
+ const char* file, int line, HRESULT hresult, const char* format, ...)
23
+ {
24
+ XOT_STRINGF(format, s);
25
+ throw MediaFoundationError(
26
+ Xot::error_text(file, line, s + Xot::stringf(" (0x%p)", hresult)));
27
+ }
28
+
29
+ }// ErrorFunctions
30
+
31
+
32
+ void
33
+ check_media_foundation_error (HRESULT hresult, const char* file, int line)
34
+ {
35
+ if (FAILED(hresult))
36
+ media_foundation_error(file, line, hresult);
37
+ }
38
+
39
+
40
+ }// Beeps
@@ -0,0 +1,40 @@
1
+ // -*- c++ -*-
2
+ #pragma once
3
+ #ifndef __BEEPS_SRC_WIN32_EXCEPTION_H__
4
+ #define __BEEPS_SRC_WIN32_EXCEPTION_H__
5
+
6
+
7
+ #include <xot/windows.h>
8
+ #include "beeps/exception.h"
9
+
10
+
11
+ namespace Beeps
12
+ {
13
+
14
+
15
+ class MediaFoundationError : public BeepsError
16
+ {
17
+ typedef BeepsError Super;
18
+ public: MediaFoundationError (const char* str = NULL);
19
+ };
20
+
21
+
22
+ namespace ErrorFunctions
23
+ {
24
+
25
+ [[noreturn]]
26
+ void media_foundation_error (
27
+ const char* file, int line,
28
+ HRESULT hresult, const char* format = NULL, ...);
29
+
30
+ }// ErrorFunctions
31
+
32
+
33
+ void check_media_foundation_error (
34
+ HRESULT hresult, const char* file, int line);
35
+
36
+
37
+ }// Beeps
38
+
39
+
40
+ #endif//EOH
@@ -0,0 +1,186 @@
1
+ #include "../signals.h"
2
+
3
+
4
+ #include <mfapi.h>
5
+ #include <mfidl.h>
6
+ #include <mfreadwrite.h>
7
+ #include <xot/noncopyable.h>
8
+ #include <xot/string.h>
9
+ #include <xot/windows.h>
10
+ #include "exception.h"
11
+
12
+
13
+ namespace Beeps
14
+ {
15
+
16
+
17
+ template <typename T, void (*Delete) (T*)>
18
+ struct Ptr : public Xot::NonCopyable
19
+ {
20
+
21
+ T* ptr;
22
+
23
+ Ptr () : ptr(NULL) {}
24
+
25
+ ~Ptr () {Delete(ptr);}
26
+
27
+ T* operator -> () {return ptr;}
28
+
29
+ operator bool () const {return ptr;}
30
+
31
+ bool operator ! () const {return !operator bool();}
32
+
33
+ };// Ptr
34
+
35
+ template <typename T>
36
+ static void
37
+ release (T* object)
38
+ {
39
+ if (object) object->Release();
40
+ }
41
+
42
+ template <typename T>
43
+ static void
44
+ mem_free (T* object)
45
+ {
46
+ if (object) CoTaskMemFree(object);
47
+ }
48
+
49
+ template <typename T>
50
+ using ReleasePtr = Ptr<T, release<T>>;
51
+
52
+ template <typename T>
53
+ using MemFreePtr = Ptr<T, mem_free<T>>;
54
+
55
+
56
+ static bool is_file_exist (const char* path)
57
+ {
58
+ DWORD attribs = GetFileAttributes(path);
59
+ return
60
+ attribs != INVALID_FILE_ATTRIBUTES &&
61
+ !(attribs & FILE_ATTRIBUTE_DIRECTORY);
62
+ }
63
+
64
+ static std::wstring
65
+ to_wcs (const char* mbs)
66
+ {
67
+ size_t len = strlen(mbs);
68
+ std::wstring wcs(len, L'#');
69
+ mbstowcs(&wcs[0], mbs, len);
70
+ return wcs;
71
+ }
72
+
73
+ static void
74
+ load_bytes (WAVEFORMATEX* format, std::vector<BYTE>* bytes, const char* path)
75
+ {
76
+ std::wstring wpath = to_wcs(path);
77
+
78
+ ReleasePtr<IMFMediaType> pcm_media_type;
79
+ check_media_foundation_error(
80
+ MFCreateMediaType(&pcm_media_type.ptr),
81
+ __FILE__, __LINE__);
82
+ pcm_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);
83
+ pcm_media_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);
84
+
85
+ ReleasePtr<IMFSourceReader> source_reader;
86
+ check_media_foundation_error(
87
+ MFCreateSourceReaderFromURL(wpath.c_str(), NULL, &source_reader.ptr),
88
+ __FILE__, __LINE__);
89
+
90
+ check_media_foundation_error(
91
+ source_reader->SetCurrentMediaType(
92
+ MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pcm_media_type.ptr),
93
+ __FILE__, __LINE__);
94
+
95
+ ReleasePtr<IMFMediaType> media_type;
96
+ check_media_foundation_error(
97
+ source_reader->GetCurrentMediaType(
98
+ MF_SOURCE_READER_FIRST_AUDIO_STREAM, &media_type.ptr),
99
+ __FILE__, __LINE__);
100
+
101
+ MemFreePtr<WAVEFORMATEX> format_;
102
+ check_media_foundation_error(
103
+ MFCreateWaveFormatExFromMFMediaType(media_type.ptr, &format_.ptr, NULL),
104
+ __FILE__, __LINE__);
105
+
106
+ *format = *format_.ptr;
107
+ if (format->wFormatTag != WAVE_FORMAT_PCM)
108
+ beeps_error(__FILE__, __LINE__, "'%s' is not a PCM file.", path);
109
+
110
+ while (true)
111
+ {
112
+ ReleasePtr<IMFSample> sample;
113
+ DWORD flags = 0;
114
+ check_media_foundation_error(
115
+ source_reader->ReadSample(
116
+ MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &flags, NULL, &sample.ptr),
117
+ __FILE__, __LINE__);
118
+ if (flags & MF_SOURCE_READERF_ENDOFSTREAM)
119
+ break;
120
+
121
+ ReleasePtr<IMFMediaBuffer> buffer;
122
+ check_media_foundation_error(
123
+ sample->ConvertToContiguousBuffer(&buffer.ptr),
124
+ __FILE__, __LINE__);
125
+
126
+ BYTE* data = NULL;
127
+ DWORD size = 0;
128
+ check_media_foundation_error(
129
+ buffer->Lock(&data, NULL, &size),
130
+ __FILE__, __LINE__);
131
+
132
+ bytes->resize(bytes->size() + size);
133
+ memcpy(&(*bytes)[0] + bytes->size() - size, data, size);
134
+
135
+ check_media_foundation_error(
136
+ buffer->Unlock(),
137
+ __FILE__, __LINE__);
138
+ }
139
+ }
140
+
141
+ Signals
142
+ Signals_load (const char* path)
143
+ {
144
+ if (!is_file_exist(path))
145
+ beeps_error(__FILE__, __LINE__, "'%s' not found.", path);
146
+
147
+ WAVEFORMATEX format = {0};
148
+ std::vector<BYTE> bytes;
149
+ load_bytes(&format, &bytes, path);
150
+ if (bytes.empty())
151
+ beeps_error(__FILE__, __LINE__, "failed to read bytes: '%s'", path);
152
+
153
+ uint Bps = format.wBitsPerSample / 8;
154
+ uint nchannels = format.nChannels;
155
+ uint nsamples = bytes.size() / Bps / nchannels;
156
+ Signals signals = Signals_create(nsamples, nchannels, format.nSamplesPerSec);
157
+ Frames* frames = Signals_get_frames(&signals);
158
+
159
+ for (uint channel = 0; channel < nchannels; ++channel)
160
+ {
161
+ switch (Bps)
162
+ {
163
+ case 1:
164
+ {
165
+ const uchar* p = ((uchar*) &bytes[0]) + channel;
166
+ for (uint sample = 0; sample < nsamples; ++sample, p += nchannels)
167
+ (*frames)(sample, channel) = (*p - 128) / 128.f;
168
+ break;
169
+ }
170
+
171
+ case 2:
172
+ {
173
+ const ushort* p = ((ushort*) &bytes[0]) + channel;
174
+ for (uint sample = 0; sample < nsamples; ++sample, p += nchannels)
175
+ (*frames)(sample, channel) = *p / 32768.f;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+
181
+ Signals_set_nsamples(&signals, nsamples);
182
+ return signals;
183
+ }
184
+
185
+
186
+ }// Beeps
data/test/helper.rb CHANGED
@@ -8,8 +8,3 @@ require 'beeps'
8
8
  require 'test/unit'
9
9
 
10
10
  include Xot::Test
11
-
12
-
13
- unless defined?($BEEPS_NOAUTOINIT) && $BEEPS_NOAUTOINIT
14
- def Beeps.fin!() end
15
- end
@@ -54,6 +54,10 @@ class TestSoundPlayer < Test::Unit::TestCase
54
54
  end
55
55
 
56
56
  def test_play_end_then_stop()
57
+ # FIXME: not tested because it fails due to GHA's "Null Audio Device".
58
+ # https://github.com/xord/beeps/actions/runs/9044146493/job/24852497305
59
+ return if github_actions?
60
+
57
61
  s = sound
58
62
  sec = s.seconds
59
63
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: beeps
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.2'
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - xordog
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-13 00:00:00.000000000 Z
11
+ date: 2024-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: xot
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.2'
19
+ version: '0.3'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.2'
26
+ version: '0.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rucy
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.2'
33
+ version: '0.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.2'
40
+ version: '0.3'
41
41
  description: Synthesize and play beep sounds.
42
42
  email: xordog@gmail.com
43
43
  executables: []
@@ -128,6 +128,7 @@ files:
128
128
  - src/adsr.cpp
129
129
  - src/analyser.cpp
130
130
  - src/beeps.cpp
131
+ - src/beeps.h
131
132
  - src/exception.cpp
132
133
  - src/file_in.cpp
133
134
  - src/gain.cpp
@@ -136,6 +137,7 @@ files:
136
137
  - src/openal.cpp
137
138
  - src/openal.h
138
139
  - src/oscillator.cpp
140
+ - src/osx/beeps.mm
139
141
  - src/osx/signals.mm
140
142
  - src/pitch_shift.cpp
141
143
  - src/processor.cpp
@@ -145,6 +147,10 @@ files:
145
147
  - src/sound.cpp
146
148
  - src/sound.h
147
149
  - src/time_stretch.cpp
150
+ - src/win32/beeps.cpp
151
+ - src/win32/exception.cpp
152
+ - src/win32/exception.h
153
+ - src/win32/signals.cpp
148
154
  - test/helper.rb
149
155
  - test/test_beeps.rb
150
156
  - test/test_beeps_init.rb