sound 0.0.9 → 0.1.0

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.
@@ -0,0 +1,44 @@
1
+ require 'sound/device_library'
2
+
3
+ module Sound
4
+ module Library
5
+ module Base
6
+ def Base.please_implement
7
+ raise NoMethodError, "Please implement ##{method} for your Library."
8
+ end
9
+ class Handle
10
+ def pointer
11
+ raise NoMethodError, "Please implement #{self.class}##{__method__} for your Library"
12
+ end
13
+ def id
14
+ raise NoMethodError, "Please implement #{self.class}##{__method__} for your Library"
15
+ end
16
+ end
17
+ private
18
+ def open_device
19
+ Base.please_implement(__method__)
20
+ end
21
+ def prepare_buffer
22
+ Base.please_implement(__method__)
23
+ end
24
+ def write_to_device
25
+ Base.please_implement(__method__)
26
+ end
27
+ def unprepare_buffer
28
+ Base.please_implement(__method__)
29
+ end
30
+ def close_device
31
+ Base.please_implement(__method__)
32
+ end
33
+ def handle
34
+ Thread.current[:handle] ||= Handle.new
35
+ end
36
+ def data
37
+ Thread.current[:data] ||= Sound::Data.new
38
+ end
39
+ def data_buffer
40
+ Thread.current[:data_buffer] ||= FFI::MemoryPointer.new(:int, data.pcm_data.size).write_array_of_int data.pcm_data
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,237 @@
1
+ #gem 'ffi', '=1.3.1'
2
+ require 'ffi'
3
+
4
+ module Sound
5
+ module DeviceLibrary
6
+ module MMLib
7
+ extend self
8
+
9
+ class Handle
10
+ def initialize
11
+ @handle = FFI::MemoryPointer.new(:pointer)
12
+ end
13
+ def pointer
14
+ @handle
15
+ end
16
+ def id
17
+ @handle.read_int
18
+ end
19
+ end
20
+
21
+ extend FFI::Library
22
+
23
+ typedef :ulong, :dword
24
+ typedef :uintptr_t, :hwaveout
25
+ typedef :uint, :mmresult
26
+
27
+ ffi_lib :winmm
28
+ ffi_convention :stdcall
29
+
30
+ callback :waveOutProc, [:hwaveout, :uint, :pointer, :ulong, :ulong], :void
31
+ attach_function :waveOutOpen, [:pointer, :uint, :pointer, :dword, :dword, :dword], :mmresult
32
+ attach_function :waveOutPrepareHeader, [:hwaveout, :pointer, :uint], :mmresult
33
+ attach_function :waveOutWrite, [:hwaveout, :pointer, :uint], :mmresult
34
+ attach_function :waveOutUnprepareHeader, [:hwaveout, :pointer, :uint], :mmresult
35
+ attach_function :waveOutClose, [:hwaveout], :mmresult
36
+ attach_function :midiOutOpen, [:pointer, :uint, :dword, :dword, :dword], :mmresult
37
+ attach_function :midiOutClose, [:uintptr_t], :mmresult
38
+ attach_function :midiOutShortMsg, [:uintptr_t, :ulong], :mmresult
39
+
40
+ WaveOutProc = Proc.new do |hwo, uMsg, dwInstance, dwParam1, dwParam2|
41
+ # explicit returns in this callback will result in an error
42
+ case uMsg
43
+ when WOM_OPEN
44
+ puts "haha"
45
+ sleep 3
46
+ when WOM_DONE
47
+ block_mutex.lock
48
+ dwInstance.write_int(dwInstance.read_int + 1)
49
+ block_mutex.unlock
50
+ when WOM_CLOSE
51
+ end
52
+ end
53
+
54
+ WAVE_MAPPER = -1
55
+ DEFAULT_DEVICE_ID = WAVE_MAPPER
56
+
57
+ WOM_OPEN = 0x3BB
58
+ WOM_CLOSE = 0x3BC
59
+ WOM_DONE = 0x3BD
60
+ CALLBACK_FUNCTION = 0x30000
61
+
62
+ def play_midi_notes
63
+ handle = FFI::MemoryPointer.new(:pointer)
64
+ midiOutOpen(handle, -1, 0, 0, 0)
65
+ midiOutShortMsg(handle.read_int, 0x007F3C90)
66
+ sleep 0.3
67
+ midiOutShortMsg(handle.read_int, 0x007F4090)
68
+ sleep 0.3
69
+ midiOutShortMsg(handle.read_int, 0x007F4390)
70
+ sleep 2
71
+ midiOutShortMsg(handle.read_int, 0x00003C90)
72
+ midiOutShortMsg(handle.read_int, 0x00004090)
73
+ midiOutShortMsg(handle.read_int, 0x00004390)
74
+ midiOutClose(handle.read_int)
75
+ end
76
+
77
+ def play_with_multiple_buffers(buffer_count = 2)
78
+
79
+ data = Sound::Data.new.sine_wave(440, 200, 1)
80
+ free_blocks.write_int buffer_count
81
+ waveOutOpen(handle, id, data.format.pointer, WaveOutProc, free_blocks.address, CALLBACK_FUNCTION)
82
+
83
+ data = data.data
84
+
85
+ data1 = data[0...(data.length/2)]
86
+ data_buffer = FFI::MemoryPointer.new(:int, data1.size)
87
+ data_buffer.write_array_of_int data1
88
+ buffer_length = wfx[:nAvgBytesPerSec]*100/1000
89
+ header = WAVEHDR.new(data_buffer, buffer_length)
90
+ waveOutPrepareHeader(handle.id, header.pointer, header.size)
91
+ block_mutex.lock
92
+ free_blocks.write_int(free_blocks.read_int - 1)
93
+ block_mutex.unlock
94
+
95
+
96
+ data2 = data[(data.length/2)..-1]
97
+ data_buffer = FFI::MemoryPointer.new(:int, data2.size)
98
+ data_buffer.write_array_of_int data2
99
+ buffer_length = wfx[:nAvgBytesPerSec]*100/1000
100
+ header = WAVEHDR.new(data_buffer, buffer_length)
101
+ waveOutPrepareHeader(handle.id, header2.pointer, header2.size)
102
+ block_mutex.lock
103
+ free_blocks.write_int(free_blocks.read_int - 1)
104
+ block_mutex.unlock
105
+
106
+ waveOutWrite(handle.id, header.pointer, header.size)
107
+ waveOutWrite(handle.id, header2.pointer, header2.size)
108
+
109
+ until free_blocks.read_int == 2
110
+ sleep 0.01
111
+ end
112
+
113
+ waveOutUnprepareHeader(handle.id, header.pointer, header.size)
114
+ waveOutUnprepareHeader(handle.id, header2.pointer, header2.size)
115
+
116
+ waveOutClose(handle.id)
117
+
118
+ end
119
+
120
+ def open_device(device)
121
+ waveOutOpen(handle.pointer, device.id, data.format.pointer, 0, 0, 0)
122
+ end
123
+
124
+ def prepare_buffer
125
+ waveOutPrepareHeader(handle.id, header.pointer, header.size)
126
+ end
127
+
128
+ def write_to_device
129
+ waveOutWrite(handle.id, header.pointer, header.size)
130
+ end
131
+
132
+ def unprepare_buffer
133
+ while waveOutUnprepareHeader(handle.id, header.pointer, header.size) == 33
134
+ sleep 0.001
135
+ end
136
+ end
137
+
138
+ def close_device
139
+ waveOutClose(handle.id)
140
+ end
141
+
142
+ def handle
143
+ Thread.current[:handle] ||= Handle.new
144
+ end
145
+
146
+ def data
147
+ Thread.current[:data] ||= Sound::Data.new
148
+ end
149
+
150
+ def data_buffer
151
+ Thread.current[:data_buffer] ||= FFI::MemoryPointer.new(:int, data.pcm_data.size).write_array_of_int data.pcm_data
152
+ end
153
+
154
+ def header
155
+ Thread.current[:header] ||= WAVEHDR.new(data_buffer, buffer_length)
156
+ end
157
+
158
+ def buffer_length
159
+ Thread.current[:buffer_length] ||= data.format.avg_bps*data.duration/1000
160
+ end
161
+
162
+ def free_blocks
163
+ Thread.current[:free_blocks] ||= FFI::MemoryPointer.new(:ulong)
164
+ end
165
+
166
+ def block_mutex
167
+ Thread.current[:block_mutex] ||= Mutex.new
168
+ end
169
+
170
+ def pointer
171
+ self.wfx.pointer
172
+ end
173
+
174
+ #define WAVEHDR which is a header to a block of audio
175
+ #lpData is a pointer to the block of native memory that,
176
+ # in this case, is an integer array of PCM data
177
+
178
+ class WAVEHDR < FFI::Struct
179
+
180
+ # Initializes struct with sensible defaults for most commonly used
181
+ # values. While setting these manually is possible, please be
182
+ # sure you know what changes will result in, as an incorrectly
183
+ # set struct will result in unpredictable behavior.
184
+ #
185
+ def initialize(lpData, dwBufferLength, dwFlags = 0, dwLoops = 1)
186
+ self[:lpData] = lpData
187
+ self[:dwBufferLength] = dwBufferLength
188
+ self[:dwFlags] = dwFlags
189
+ self[:dwLoops] = dwLoops
190
+ end
191
+
192
+ layout(
193
+ :lpData, :pointer,
194
+ :dwBufferLength, :ulong,
195
+ :dwBytesRecorded, :ulong,
196
+ :dwUser, :ulong,
197
+ :dwFlags, :ulong,
198
+ :dwLoops, :ulong,
199
+ :lpNext, :pointer,
200
+ :reserved, :ulong
201
+ )
202
+ end
203
+
204
+ # Define WAVEFORMATEX which defines the format (PCM in this case)
205
+ # and various properties like sampling rate, number of channels, etc.
206
+ #
207
+ class WAVEFORMATEX < FFI::Struct
208
+
209
+ # Initializes struct with sensible defaults for most commonly used
210
+ # values. While setting these manually is possible, please be
211
+ # sure you know what changes will result in, as an incorrectly
212
+ # set struct will result in unpredictable behavior.
213
+ #
214
+ def initialize(nSamplesPerSec = 44100, wBitsPerSample = 16, nChannels = 1, cbSize = 0)
215
+ self[:wFormatTag] = WAVE_FORMAT_PCM
216
+ self[:nChannels] = nChannels
217
+ self[:nSamplesPerSec] = nSamplesPerSec
218
+ self[:wBitsPerSample] = wBitsPerSample
219
+ self[:cbSize] = cbSize
220
+ self[:nBlockAlign] = (self[:wBitsPerSample] >> 3) * self[:nChannels]
221
+ self[:nAvgBytesPerSec] = self[:nBlockAlign] * self[:nSamplesPerSec]
222
+ end
223
+
224
+ layout(
225
+ :wFormatTag, :ushort,
226
+ :nChannels, :ushort,
227
+ :nSamplesPerSec, :ulong,
228
+ :nAvgBytesPerSec, :ulong,
229
+ :nBlockAlign, :ushort,
230
+ :wBitsPerSample, :ushort,
231
+ :cbSize, :ushort
232
+ )
233
+ end
234
+
235
+ end
236
+ end
237
+ end
@@ -1,38 +1,17 @@
1
-
1
+ require 'sound/format_library'
2
2
 
3
3
  module Sound
4
-
5
- WAVE_FORMAT_PCM = 1
6
4
 
7
5
  class Format
8
-
9
- PCM = WAVE_FORMAT_PCM
10
- attr_accessor :channels, :sample_rate, :bps, :alsa_format
11
- def initialize(format_type = PCM)
6
+ include FormatLibrary
7
+ attr_accessor :channels, :sample_rate, :bits_per_sample, :type
8
+ alias :bps :bits_per_sample
9
+ def initialize(type = FormatLibrary::DEFAULT_FORMAT)
12
10
  @channels = 1
13
11
  @sample_rate = 44100
14
- @bps = 16
15
- if OS.windows?
16
- @wfx = Win32::WAVEFORMATEX.new
17
- @wfx[:wFormatTag] = format_type
18
- @wfx[:nChannels] = channels
19
- @wfx[:nSamplesPerSec] = sample_rate
20
- @wfx[:wBitsPerSample] = bps
21
- @wfx[:cbSize] = 0
22
- @wfx[:nBlockAlign] = block_align
23
- @wfx[:nAvgBytesPerSec] = avg_bps
24
- end
25
- end
26
- def block_align
27
- (bps >> 3) * channels
28
- end
29
- def avg_bps
30
- block_align * sample_rate
31
- end
32
- def pointer
33
- if OS.windows?
34
- @wfx.pointer
35
- end
12
+ @bits_per_sample = 16
13
+ @type = type
14
+ new_format
36
15
  end
37
16
  end
38
17
 
@@ -0,0 +1,15 @@
1
+ require 'forwardable'
2
+
3
+ module Sound
4
+ module FormatLibrary
5
+ #extend Forwardable
6
+ DEFAULT_FORMAT = Sound.format_library::DEFAULT_FORMAT
7
+ include Sound.format_library
8
+ #duties = [
9
+ # :new_format,
10
+ # :pointer,
11
+ # :avg_bps
12
+ #]
13
+ #delegate duties => Sound.format_library
14
+ end
15
+ end
@@ -0,0 +1,10 @@
1
+ require 'sound/format_library/base'
2
+
3
+ module Sound
4
+ module FormatLibrary
5
+ module ALSA
6
+ include FormatLibrary::Base
7
+ DEFAULT_FORMAT = 0
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Sound
3
+ module FormatLibrary
4
+ module Base
5
+ def new_format
6
+
7
+ end
8
+ def block_align
9
+ (bps >> 3) * channels
10
+ end
11
+ def avg_bps
12
+ block_align * sample_rate
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,61 @@
1
+ require 'sound/format_library/base'
2
+
3
+ module Sound
4
+ module FormatLibrary
5
+ module MMLib
6
+ include FormatLibrary::Base
7
+
8
+ WAVE_FORMAT_PCM = 1
9
+ DEFAULT_FORMAT = WAVE_FORMAT_PCM
10
+
11
+ attr_accessor :wfx
12
+
13
+ def new_format
14
+ self.wfx = WAVEFORMATEX.new
15
+ self.wfx[:wFormatTag] = type
16
+ self.wfx[:nChannels] = channels
17
+ self.wfx[:nSamplesPerSec] = sample_rate
18
+ self.wfx[:wBitsPerSample] = bps
19
+ self.wfx[:cbSize] = 0
20
+ self.wfx[:nBlockAlign] = block_align
21
+ self.wfx[:nAvgBytesPerSec] = avg_bps
22
+ end
23
+
24
+ def pointer
25
+ self.wfx.pointer
26
+ end
27
+
28
+ # Define WAVEFORMATEX which defines the format (PCM in this case)
29
+ # and various properties like sampling rate, number of channels, etc.
30
+ #
31
+ class WAVEFORMATEX < FFI::Struct
32
+
33
+ # Initializes struct with sensible defaults for most commonly used
34
+ # values. While setting these manually is possible, please be
35
+ # sure you know what changes will result in, as an incorrectly
36
+ # set struct will result in unpredictable behavior.
37
+ #
38
+ def initialize(nSamplesPerSec = 44100, wBitsPerSample = 16, nChannels = 1, cbSize = 0)
39
+ self[:wFormatTag] = WAVE_FORMAT_PCM
40
+ self[:nChannels] = nChannels
41
+ self[:nSamplesPerSec] = nSamplesPerSec
42
+ self[:wBitsPerSample] = wBitsPerSample
43
+ self[:cbSize] = cbSize
44
+ self[:nBlockAlign] = (self[:wBitsPerSample] >> 3) * self[:nChannels]
45
+ self[:nAvgBytesPerSec] = self[:nBlockAlign] * self[:nSamplesPerSec]
46
+ end
47
+
48
+ layout(
49
+ :wFormatTag, :ushort,
50
+ :nChannels, :ushort,
51
+ :nSamplesPerSec, :ulong,
52
+ :nAvgBytesPerSec, :ulong,
53
+ :nBlockAlign, :ushort,
54
+ :wBitsPerSample, :ushort,
55
+ :cbSize, :ushort
56
+ )
57
+ end
58
+
59
+ end
60
+ end
61
+ end
metadata CHANGED
@@ -1,35 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sound
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominic Muller
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-13 00:00:00.000000000 Z
11
+ date: 2014-08-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
18
- - !ruby/object:Gem::Version
19
- version: '1.9'
20
- - - ! '>='
17
+ - - '='
21
18
  - !ruby/object:Gem::Version
22
- version: 1.9.3
19
+ version: 1.3.1
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '1.9'
30
- - - ! '>='
24
+ - - '='
31
25
  - !ruby/object:Gem::Version
32
- version: 1.9.3
26
+ version: 1.3.1
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: rspec
35
29
  requirement: !ruby/object:Gem::Requirement
@@ -60,11 +54,17 @@ files:
60
54
  - examples/example.rb
61
55
  - lib/os/os.rb
62
56
  - lib/sound.rb
63
- - lib/sound/alsa.rb
64
57
  - lib/sound/data.rb
65
58
  - lib/sound/device.rb
59
+ - lib/sound/device_library.rb
60
+ - lib/sound/device_library/alsa.rb
61
+ - lib/sound/device_library/base.rb
62
+ - lib/sound/device_library/mmlib.rb
66
63
  - lib/sound/format.rb
67
- - lib/sound/win32.rb
64
+ - lib/sound/format_library.rb
65
+ - lib/sound/format_library/alsa.rb
66
+ - lib/sound/format_library/base.rb
67
+ - lib/sound/format_library/mmlib.rb
68
68
  homepage: https://github.com/RSMP/sound
69
69
  licenses:
70
70
  - MIT