beeps 0.2 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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