sound 0.0.4 → 0.0.5

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YWI0OThmZTUxYjUyYmJkZDBiNzM4N2MwNDE3ZWQzODdkMTRmNjgwNg==
4
+ ZDQzMDA4MjZkZWY3ODQ1YTVmOWJkYzFkOWYxZGUyMWQxMTViZDY5MQ==
5
5
  data.tar.gz: !binary |-
6
- MjM5NjE3OTM3YTc3MzNjNTQ2ODcwYmFjNWU2YWM5ZmFlOGJlNjM0Zg==
6
+ ZDU0NDk4ZTBmNjQ5ZmIxOGUyNDI4N2NmOWE5MGMzNWU2ZDQwYTZiOQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- ZGE1MjU2MjQ4YmJmMzgxMGFkMDg1YThjZGNkZGE3ODE3YmUwNmYzYjA2YmY2
10
- ZjZlNGFhMmVlMGYwMmJlMGUxYTIyYmMzYjM2NjNhYTM3NWQwNTdkMGE0ZGYz
11
- ZGQxYzliNGZhODM3OTlmOGE1NDE4YzMwNTczZmQwMTczZTVjM2I=
9
+ YTk5MWFlNmRmZGIwYmM5MzBkNmU2NGYyYjViYjY2NWY1YTc1ZjdiOTU1Y2U3
10
+ MjdmMmMzMGQ1NDhkZjczMjFhMzM2ZTc2YjNjOWU1OTJiODc1YmQ5ODI5MDJh
11
+ MzBhOTAxYzU1MWQwNzdiYmNkNGY2YTJmNjM2NGU2Mjg5NjdlNmQ=
12
12
  data.tar.gz: !binary |-
13
- NWU3YjE1YjI1OTQyNWExZjZiMjQyM2MyNDg4YmI1YTI2ZDYwMjc0ZTk0ODMx
14
- MmY0OTAxMzgxNDJlNWI2NDk0Yzk5MjJiYjk4MDg2MTZjZDUwM2JmMjcxZTdk
15
- MzJjYTM1ZDI0ZjljZDFlYWMxZDMzMzJhM2NhMjU3YzE0YTk0MGE=
13
+ NmZmMzIxNmQ1NDIyOWYyZDk2MzEwNmNiMzAxOWU2MGMwNWE2ZGRkYWJmZDBl
14
+ OWI4NTM1MDQxNmU0ZDM2OTRiMzQ4MWI0Yzc3NGMwMjQ0MWZhNDg0NTZlOGY3
15
+ NmI5ZjMzZTJhZTE3Y2EwMGZmZDA3NDkyZGQ3ZTg3ZTYwYjdjN2M=
@@ -0,0 +1,21 @@
1
+ require 'sound'
2
+ Sound.verbose = true
3
+
4
+ # Don't trust these examples. The readme is more up to date.
5
+
6
+ # Example of opening a device, writing to it, and closing it up
7
+
8
+ device = Sound::Device.new
9
+ data = Sound::Data.new(device.format)
10
+ data.generate_sine_wave(440, 500, 1)
11
+ device.write data
12
+ device.close
13
+
14
+ # This will close the device on its own after the block finishes
15
+
16
+
17
+ #data = WaveData.new
18
+ #
19
+ #device = Device.open
20
+ #device.write(data)
21
+ #device.close
data/lib/sound.rb CHANGED
@@ -1,10 +1,16 @@
1
- require_relative 'os/os'
1
+
2
+ require 'ffi'
3
+ require 'pry'
4
+ require 'os/os'
2
5
 
3
6
  if OS.windows?
4
- require 'win32/sound'
7
+ require 'sound/win32/sound'
8
+ elsif OS.linux?
9
+ require 'sound/linux/sound'
5
10
  else
6
- warn("Sound output not yet implemented for this platform: #{OS.os}")
11
+ warn("warning: Sound output not yet implemented for this platform: #{OS.os}")
7
12
  end
8
13
 
9
- require_relative 'sound/sound'
10
- require_relative 'sound/out'
14
+ require 'sound/data'
15
+ require 'sound/format'
16
+ require 'sound/device'
data/lib/sound/data.rb ADDED
@@ -0,0 +1,36 @@
1
+
2
+ module Sound
3
+
4
+ class Data
5
+ attr_reader :data, :duration
6
+ def initialize(format)
7
+ @format = format
8
+ @data = []
9
+ end
10
+ def generate_sine_wave(freq, duration, volume)
11
+ @data = []
12
+ @duration = duration
13
+ ramp = 200.0
14
+ samples = (@format.sample_rate/2*duration/1000.0).floor
15
+
16
+ samples.times do |sample|
17
+
18
+ angle = (2.0*Math::PI*freq) * sample/samples * duration/1000
19
+ factor = Math.sin(angle)
20
+ x = 32768.0*factor*volume
21
+
22
+ if sample < ramp
23
+ x *= sample/ramp
24
+ end
25
+ if samples - sample < ramp
26
+ x *= (samples - sample)/ramp
27
+ end
28
+
29
+ @data << x.floor
30
+ end
31
+
32
+ self
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,172 @@
1
+
2
+ module Sound
3
+
4
+ @verbose = false
5
+
6
+ class << self
7
+ attr_accessor :verbose
8
+ end
9
+
10
+ WAVE_MAPPER = -1
11
+
12
+ class Device
13
+
14
+ class Handle
15
+ def initialize
16
+ if OS.windows?
17
+ @handle = Win32::HWAVEOUT.new
18
+ elsif OS.linux?
19
+ @handle = FFI::MemoryPointer.new(:pointer)
20
+ end
21
+ end
22
+ def pointer
23
+ if OS.windows?
24
+ @handle.pointer
25
+ elsif OS.linux?
26
+ @handle
27
+ end
28
+ end
29
+ def id
30
+ if OS.windows?
31
+ @handle[:i]
32
+ elsif OS.linux?
33
+ @handle.read_pointer
34
+ end
35
+ end
36
+ end
37
+
38
+ attr_accessor :closed, :id, :handle, :format
39
+
40
+ def initialize(format = Format::PCM, direction = "w", id = nil)
41
+ if OS.windows?
42
+ id ||= WAVE_MAPPER
43
+ elsif OS.linux?
44
+ id ||= "default"
45
+ end
46
+ @id = id
47
+ closed = false
48
+ @queue = []
49
+ @mutex = Mutex.new
50
+ @handle = Device::Handle.new
51
+ @format = format
52
+ @direction = direction
53
+ end
54
+
55
+ class << self
56
+ # Opens a sound device for reading or writing
57
+ # device is one of several devices, usually defined by a constant
58
+ # direction is reading or writing or both
59
+ # format is MIDI vs PCM or others
60
+ # this method can take a block and if so closes the device after execution
61
+ def open(device = Device::DEFAULT, direction = "w", format = Format::PCM, &block)
62
+ device.format = format
63
+ puts "opening device_#{device.id}" if Sound.verbose
64
+ if block_given?
65
+ block.call(device)
66
+ device.close
67
+ else
68
+ device.closed = false
69
+ device
70
+ end
71
+ end
72
+ end
73
+
74
+ def play(data = "beep boop")
75
+ write(data)
76
+ flush
77
+ end
78
+
79
+ def write(data = "beep boop")
80
+ if closed?
81
+ puts "cannot write to a closed device"
82
+ else
83
+ @mutex.lock
84
+ @queue << Thread.new do
85
+ write_thread(data)
86
+ end
87
+ @mutex.unlock
88
+ puts "writing to device_#{id}_queue: #{data.class}" if Sound.verbose
89
+ end
90
+ end
91
+
92
+ def close
93
+ if closed?
94
+ puts "cannot close a closed device"
95
+ else
96
+ flush
97
+ puts "device is closing now" if Sound.verbose
98
+ closed = true
99
+ end
100
+ end
101
+
102
+ def flush
103
+ until @queue.empty?
104
+ output = @queue.shift
105
+ output[:stop] = false
106
+ puts "writing to device_#{id}: #{output}" if Sound.verbose
107
+ output.run.join
108
+ end
109
+ end
110
+
111
+ def closed?
112
+ closed
113
+ end
114
+
115
+ def write_thread(data)
116
+ Thread.current[:stop] = true if Thread.current[:stop].nil?
117
+ if OS.windows?
118
+ windows_write_thread(data)
119
+ elsif OS.linux?
120
+ linux_write_thread(data)
121
+ else
122
+ warn("warning: playback is not yet supported on this platform")
123
+ end
124
+ end
125
+
126
+ def windows_write_thread(data)
127
+ handle = Handle.new
128
+ Win32::Sound.waveOutOpen(handle.pointer, id, format.pointer, 0, 0, 0)
129
+ data_buffer = FFI::MemoryPointer.new(:int, data.data.size)
130
+ data_buffer.write_array_of_int data.data
131
+ buffer_length = format.avg_bps*data.duration/1000
132
+ puts buffer_length
133
+ header = Win32::WAVEHDR.new(data_buffer, buffer_length)
134
+ Win32::Sound.waveOutPrepareHeader(handle.id, header.pointer, header.size)
135
+ Thread.stop if Thread.current[:stop]
136
+ Win32::Sound.waveOutWrite(handle.id, header.pointer, header.size)
137
+ while Win32::Sound.waveOutUnprepareHeader(handle.id, header.pointer, header.size) == 33
138
+ sleep 0.001
139
+ end
140
+ Win32::Sound.waveOutClose(handle.id)
141
+ end
142
+
143
+ def linux_write_thread(data)
144
+ handle = Handle.new
145
+ AlsaPCM::Sound.snd_pcm_open(handle.pointer, id, 0, 0)
146
+ data_buffer = FFI::MemoryPointer.new(:int, data.data.size)
147
+ data_buffer.write_array_of_int data.data
148
+ buffer_length = data_buffer.size/2
149
+ params = FFI::MemoryPointer.new(:pointer)
150
+ AlsaPCM::Sound.snd_pcm_hw_params_malloc(params)
151
+ AlsaPCM::Sound.snd_pcm_hw_params_any(handle.id, params.read_pointer)
152
+
153
+ AlsaPCM::Sound.snd_pcm_hw_params_set_access(handle.id, params.read_pointer, 3)
154
+ AlsaPCM::Sound.snd_pcm_hw_params_set_format(handle.id, params.read_pointer, 2)
155
+ AlsaPCM::Sound.snd_pcm_hw_params_set_rate(handle.id, params.read_pointer, 44100, 0)
156
+ AlsaPCM::Sound.snd_pcm_hw_params_set_channels(handle.id, params.read_pointer, 1)
157
+
158
+ AlsaPCM::Sound.snd_pcm_hw_params(handle.id, params.read_pointer)
159
+ AlsaPCM::Sound.snd_pcm_hw_params_free(params.read_pointer)
160
+
161
+ AlsaPCM::Sound.snd_pcm_prepare(handle.id)
162
+ Thread.stop if Thread.current[:stop]
163
+ AlsaPCM::Sound.snd_pcm_writei(handle.id, data_buffer, buffer_length)
164
+
165
+
166
+ AlsaPCM::Sound.snd_pcm_drain(handle.id)
167
+ AlsaPCM::Sound.snd_pcm_close(handle.id)
168
+
169
+ end
170
+ end
171
+
172
+ end
@@ -0,0 +1,38 @@
1
+
2
+
3
+ module Sound
4
+
5
+ WAVE_FORMAT_PCM = 1
6
+
7
+ class Format
8
+ attr_accessor :channels, :sample_rate, :bps
9
+ def initialize(format_type = WAVE_FORMAT_PCM)
10
+ @channels = 1
11
+ @sample_rate = 44100
12
+ @bps = 16
13
+ if OS.windows?
14
+ @wfx = Win32::WAVEFORMATEX.new
15
+ @wfx[:wFormatTag] = format_type
16
+ @wfx[:nChannels] = channels
17
+ @wfx[:nSamplesPerSec] = sample_rate
18
+ @wfx[:wBitsPerSample] = bps
19
+ @wfx[:cbSize] = 0
20
+ @wfx[:nBlockAlign] = block_align
21
+ @wfx[:nAvgBytesPerSec] = avg_bps
22
+ end
23
+ end
24
+ def block_align
25
+ (bps >> 3) * channels
26
+ end
27
+ def avg_bps
28
+ block_align * sample_rate
29
+ end
30
+ def pointer
31
+ if OS.windows?
32
+ @wfx.pointer
33
+ end
34
+ end
35
+ PCM = self.new
36
+ end
37
+
38
+ end
@@ -0,0 +1,25 @@
1
+ require 'ffi'
2
+
3
+ module AlsaPCM
4
+ class Sound
5
+ extend FFI::Library
6
+ ffi_lib 'asound'
7
+ ffi_convention :stdcall
8
+
9
+ attach_function :snd_pcm_open, [:pointer, :string, :int, :int], :int
10
+ attach_function :snd_pcm_close, [:pointer], :int
11
+ attach_function :snd_pcm_drain, [:pointer], :int
12
+ attach_function :snd_pcm_prepare, [:pointer], :int
13
+ attach_function :snd_pcm_writen, [:pointer, :pointer, :ulong], :long
14
+ attach_function :snd_pcm_writei, [:pointer, :pointer, :ulong], :long
15
+ attach_function :snd_pcm_hw_params_malloc, [:pointer], :int
16
+ attach_function :snd_pcm_hw_params_any, [:pointer, :pointer], :int
17
+ attach_function :snd_pcm_hw_params_set_access, [:pointer, :pointer, :int], :int
18
+ attach_function :snd_pcm_hw_params_set_format, [:pointer, :pointer, :int], :int
19
+ attach_function :snd_pcm_hw_params_set_rate, [:pointer, :pointer, :uint, :int], :int
20
+ attach_function :snd_pcm_hw_params_set_channels, [:pointer, :pointer, :int], :int
21
+ attach_function :snd_pcm_hw_params, [:pointer, :pointer], :int
22
+ attach_function :snd_pcm_hw_params_free, [:pointer], :void
23
+
24
+ end
25
+ end
@@ -0,0 +1,95 @@
1
+
2
+ module Sound
3
+ module Win32
4
+
5
+ class Sound
6
+ extend FFI::Library
7
+
8
+ typedef :ulong, :dword
9
+ typedef :uintptr_t, :hwaveout
10
+ typedef :uint, :mmresult
11
+
12
+ ffi_lib :winmm
13
+
14
+ attach_function :waveOutOpen, [:pointer, :uint, :pointer, :dword, :dword, :dword], :mmresult
15
+ attach_function :waveOutPrepareHeader, [:hwaveout, :pointer, :uint], :mmresult
16
+ attach_function :waveOutWrite, [:hwaveout, :pointer, :uint], :mmresult
17
+ attach_function :waveOutUnprepareHeader, [:hwaveout, :pointer, :uint], :mmresult
18
+ attach_function :waveOutClose, [:hwaveout], :mmresult
19
+ end
20
+
21
+ WAVE_FORMAT_PCM = 1
22
+
23
+ # Define an HWAVEOUT struct for use by all the waveOut functions.
24
+ # It is a handle to a waveOut stream, so starting up multiple
25
+ # streams using different handles allows for simultaneous playback.
26
+ # You never need to actually look at the struct, C takes care of
27
+ # its value.
28
+ #
29
+ class HWAVEOUT < FFI::Struct
30
+ layout :i, :int
31
+ end
32
+
33
+ # Define WAVEFORMATEX which defines the format (PCM in this case)
34
+ # and various properties like sampling rate, number of channels, etc.
35
+ #
36
+ class WAVEFORMATEX < FFI::Struct
37
+
38
+ # Initializes struct with sensible defaults for most commonly used
39
+ # values. While setting these manually is possible, please be
40
+ # sure you know what changes will result in, as an incorrectly
41
+ # set struct will result in unpredictable behavior.
42
+ #
43
+ def initialize(nSamplesPerSec = 44100, wBitsPerSample = 16, nChannels = 1, cbSize = 0)
44
+ self[:wFormatTag] = WAVE_FORMAT_PCM
45
+ self[:nChannels] = nChannels
46
+ self[:nSamplesPerSec] = nSamplesPerSec
47
+ self[:wBitsPerSample] = wBitsPerSample
48
+ self[:cbSize] = cbSize
49
+ self[:nBlockAlign] = (self[:wBitsPerSample] >> 3) * self[:nChannels]
50
+ self[:nAvgBytesPerSec] = self[:nBlockAlign] * self[:nSamplesPerSec]
51
+ end
52
+
53
+ layout(
54
+ :wFormatTag, :ushort,
55
+ :nChannels, :ushort,
56
+ :nSamplesPerSec, :ulong,
57
+ :nAvgBytesPerSec, :ulong,
58
+ :nBlockAlign, :ushort,
59
+ :wBitsPerSample, :ushort,
60
+ :cbSize, :ushort
61
+ )
62
+ end
63
+
64
+ #define WAVEHDR which is a header to a block of audio
65
+ #lpData is a pointer to the block of native memory that,
66
+ # in this case, is an integer array of PCM data
67
+
68
+ class WAVEHDR < FFI::Struct
69
+
70
+ # Initializes struct with sensible defaults for most commonly used
71
+ # values. While setting these manually is possible, please be
72
+ # sure you know what changes will result in, as an incorrectly
73
+ # set struct will result in unpredictable behavior.
74
+ #
75
+ def initialize(lpData, dwBufferLength, dwFlags = 0, dwLoops = 1)
76
+ self[:lpData] = lpData
77
+ self[:dwBufferLength] = dwBufferLength
78
+ self[:dwFlags] = dwFlags
79
+ self[:dwLoops] = dwLoops
80
+ end
81
+
82
+ layout(
83
+ :lpData, :pointer,
84
+ :dwBufferLength, :ulong,
85
+ :dwBytesRecorded, :ulong,
86
+ :dwUser, :ulong,
87
+ :dwFlags, :ulong,
88
+ :dwLoops, :ulong,
89
+ :lpNext, :pointer,
90
+ :reserved, :ulong
91
+ )
92
+ end
93
+
94
+ end
95
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sound
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
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-03 00:00:00.000000000 Z
11
+ date: 2014-08-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Allows for effiecent cross-platform sound libraries in pure Ruby by tapping
14
14
  into native libraries.
@@ -17,10 +17,14 @@ executables: []
17
17
  extensions: []
18
18
  extra_rdoc_files: []
19
19
  files:
20
+ - examples/example.rb
20
21
  - lib/os/os.rb
21
22
  - lib/sound.rb
22
- - lib/sound/out.rb
23
- - lib/sound/sound.rb
23
+ - lib/sound/data.rb
24
+ - lib/sound/device.rb
25
+ - lib/sound/format.rb
26
+ - lib/sound/linux/sound.rb
27
+ - lib/sound/win32/sound.rb
24
28
  homepage: https://github.com/RSMP/sound
25
29
  licenses:
26
30
  - MIT
@@ -39,8 +43,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
43
  - - ! '>='
40
44
  - !ruby/object:Gem::Version
41
45
  version: '0'
42
- requirements:
43
- - win32-sound, '>= 0.6.0' on Windows
46
+ requirements: []
44
47
  rubyforge_project:
45
48
  rubygems_version: 2.2.2
46
49
  signing_key:
data/lib/sound/out.rb DELETED
@@ -1,17 +0,0 @@
1
-
2
- module Sound
3
-
4
- module Out
5
-
6
- def self.play_freq(frequency = 440, duration = 1000, volume = 1, immediate_playback = true)
7
- if OS.windows?
8
- Win32::Sound.play_freq(frequency, duration, volume, immediate_playback)
9
- else
10
- warn("play_freq not implemented for this platform: #{RUBY_PLATFORM}")
11
- end
12
-
13
- end
14
-
15
- end
16
-
17
- end
data/lib/sound/sound.rb DELETED
@@ -1,4 +0,0 @@
1
-
2
- module Sound
3
-
4
- end