audio 0 → 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +60 -11
- data/audio.gemspec +1 -1
- data/lib/audio.rb +6 -1
- data/lib/core_audio.rb +54 -0
- data/lib/core_audio/audio_device.rb +97 -0
- data/lib/core_audio/audio_object.rb +188 -0
- data/lib/core_foundation.rb +87 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f7750670a7a9f4d0554765012808dbe2f6477c9
|
4
|
+
data.tar.gz: a9e49107f1c2d316f416ecacc9a0f4ede1df1e66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a813230ad12d52963266b54ab57dfef88728af3fc6b6883bc19a1c71b783e9fcae205b2f06dfcecb2f06869e91185dfb2c47c2defbdec07d286b61c2a8ab414
|
7
|
+
data.tar.gz: a6dbf92c29fbce40913e41e099f549b9f314e1c7bed5f50eeaf73f2d79a73a41cf847b94e870c609cc0bc71bb2029d7cddbf21d081d43e9e100a7e148902c6bb
|
data/README.md
CHANGED
@@ -1,6 +1,62 @@
|
|
1
1
|
# Audio
|
2
2
|
|
3
|
-
|
3
|
+
'Audio' is a cross-platform audio device interface that allows you to read and
|
4
|
+
write audio streams from the comfort and safety of Ruby.
|
5
|
+
|
6
|
+
## Cross-Platform Status
|
7
|
+
|
8
|
+
Currently, only support for OS X has been implemented. Support for Windows and
|
9
|
+
Linux will be added in the future. If you feel like working on either of those,
|
10
|
+
I'm more than happy to take Pull Requests!
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
### Listing Available Audio Devices
|
15
|
+
|
16
|
+
The Audio module provides a method that retrieves a list of all of the audio
|
17
|
+
devices available on your system.
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
require 'audio'
|
21
|
+
|
22
|
+
devices = Audio.devices # => An Array of Device objects
|
23
|
+
```
|
24
|
+
|
25
|
+
Each Device object has attributes that wrap the various properties provided by
|
26
|
+
the host operating system. For example, to get a list of device names...
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
Audio.devices.each do |device|
|
30
|
+
puts "Device Name: #{device.device_name}"
|
31
|
+
end
|
32
|
+
```
|
33
|
+
|
34
|
+
Running that on a MacBook Pro (late 2013), with a single external microphone
|
35
|
+
connected, produces...
|
36
|
+
|
37
|
+
```
|
38
|
+
Device Name: Built-in Microphone
|
39
|
+
Device Name: Built-in Output
|
40
|
+
Device Name: Blue Snowball
|
41
|
+
```
|
42
|
+
|
43
|
+
### Recording Audio
|
44
|
+
|
45
|
+
You can record audio from a particular device by calling its `start` method.
|
46
|
+
Once started, the device will continue recording until its `stop` method is
|
47
|
+
called. If you provide a block parameter to `start`, it will be called whenever
|
48
|
+
the host OS provides a new buffer of audio samples.
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
device = Audio.devices.last # Hopefully that's an input device
|
52
|
+
device.start do |*args|
|
53
|
+
# Do something fancy
|
54
|
+
end
|
55
|
+
|
56
|
+
sleep 5 # Record 5 seconds of audio
|
57
|
+
|
58
|
+
device.stop
|
59
|
+
```
|
4
60
|
|
5
61
|
## Installation
|
6
62
|
|
@@ -18,14 +74,7 @@ Or install it yourself as:
|
|
18
74
|
|
19
75
|
$ gem install audio
|
20
76
|
|
21
|
-
|
22
|
-
|
23
|
-
TODO: Write usage instructions here
|
24
|
-
|
25
|
-
## Contributing
|
77
|
+
License
|
78
|
+
-------
|
26
79
|
|
27
|
-
|
28
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
-
5. Create a new Pull Request
|
80
|
+
Copyright 2015 Brandon Fosdick <bfoz@bfoz.net> and released under the BSD license.
|
data/audio.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "audio"
|
7
|
-
spec.version = '0'
|
7
|
+
spec.version = '0.1'
|
8
8
|
spec.authors = ["Brandon Fosdick"]
|
9
9
|
spec.email = ["bfoz@bfoz.net"]
|
10
10
|
spec.summary = %q{Cross-platform Audio Device Input and Output}
|
data/lib/audio.rb
CHANGED
data/lib/core_audio.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module CoreAudio
|
4
|
+
extend FFI::Library
|
5
|
+
ffi_lib '/System/Library/Frameworks/CoreAudio.framework/CoreAudio'
|
6
|
+
|
7
|
+
typedef :uint32, :OSStatus
|
8
|
+
|
9
|
+
# @group CoreAudioTypes.h
|
10
|
+
class AudioBuffer < FFI::Struct
|
11
|
+
layout :mNumberChannels, :uint32,
|
12
|
+
:mDataByteSize, :uint32,
|
13
|
+
:mData, :pointer
|
14
|
+
|
15
|
+
# @return [String] the raw bytes
|
16
|
+
def bytes
|
17
|
+
self[:mData].get_bytes(0, self[:mDataByteSize])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class AudioBufferList < FFI::Struct
|
22
|
+
layout :mNumberBuffers, :uint32,
|
23
|
+
:mBuffers, AudioBuffer
|
24
|
+
end
|
25
|
+
# @endgroup
|
26
|
+
end
|
27
|
+
|
28
|
+
require_relative 'core_audio/audio_device'
|
29
|
+
|
30
|
+
module CoreAudio
|
31
|
+
# AudioHardware.h
|
32
|
+
# OSStatus AudioObjectGetPropertyDataSize(AudioObjectID inObjectID,
|
33
|
+
# const AudioObjectPropertyAddress* inAddress,
|
34
|
+
# UInt32 inQualifierDataSize,
|
35
|
+
# const void* inQualifierData,
|
36
|
+
# UInt32* outDataSize)
|
37
|
+
attach_function :AudioObjectGetPropertyDataSize, [AudioObject::ObjectID, AudioObject::PropertyAddress.by_ref, :uint32, :pointer, :pointer], :OSStatus
|
38
|
+
|
39
|
+
# OSStatus AudioObjectGetPropertyData(AudioObjectID inObjectID,
|
40
|
+
# const AudioObjectPropertyAddress* inAddress,
|
41
|
+
# UInt32 inQualifierDataSize,
|
42
|
+
# const void* inQualifierData,
|
43
|
+
# UInt32* ioDataSize,
|
44
|
+
# void* outData)
|
45
|
+
attach_function :AudioObjectGetPropertyData, [AudioObject::ObjectID, AudioObject::PropertyAddress.by_ref, :uint32, :pointer, :pointer, :pointer], :OSStatus
|
46
|
+
|
47
|
+
# @return [Array<AudioObject>] the list of available audio devices
|
48
|
+
def self.devices
|
49
|
+
address = AudioObject::PropertyAddress.global_master(AudioHardware::PropertyDevices)
|
50
|
+
buffer = AudioObject.system.get_property(address)
|
51
|
+
device_IDs = buffer.get_array_of_int32(0, buffer.size/4)
|
52
|
+
device_IDs.map {|id| AudioDevice.new(id)}
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative 'audio_object'
|
2
|
+
|
3
|
+
module CoreAudio
|
4
|
+
AudioDeviceIOProcID = FFI::Pointer
|
5
|
+
typedef :pointer, :AudioDeviceIOProcID
|
6
|
+
|
7
|
+
# @group AudioHardware.h
|
8
|
+
|
9
|
+
# typedef OSStatus (*AudioDeviceIOProc)(AudioObjectID inDevice,
|
10
|
+
# const AudioTimeStamp* inNow,
|
11
|
+
# const AudioBufferList* inInputData,
|
12
|
+
# const AudioTimeStamp* inInputTime,
|
13
|
+
# AudioBufferList* outOutputData,
|
14
|
+
# const AudioTimeStamp* inOutputTime,
|
15
|
+
# void* inClientData);
|
16
|
+
callback :AudioDeviceIOProc, [AudioObject::ObjectID, :pointer, AudioBufferList.by_ref, :pointer, AudioBufferList.by_ref, :pointer, :pointer], :OSStatus
|
17
|
+
|
18
|
+
# OSStatus AudioDeviceCreateIOProcID(AudioObjectID inDevice,
|
19
|
+
# AudioDeviceIOProc inProc,
|
20
|
+
# void* inClientData,
|
21
|
+
# AudioDeviceIOProcID* outIOProcID)
|
22
|
+
attach_function :AudioDeviceCreateIOProcID, [AudioObject::ObjectID, :AudioDeviceIOProc, :pointer, :pointer], :OSStatus
|
23
|
+
|
24
|
+
# OSStatus AudioDeviceDestroyIOProcID(AudioObjectID inDevice,
|
25
|
+
# AudioDeviceIOProcID inIOProcID
|
26
|
+
attach_function :AudioDeviceDestroyIOProcID, [AudioObject::ObjectID, :AudioDeviceIOProc], :OSStatus
|
27
|
+
|
28
|
+
# OSStatus AudioDeviceStart(AudioObjectID inDevice,
|
29
|
+
# AudioDeviceIOProcID inProcID)
|
30
|
+
attach_function :AudioDeviceStart, [AudioObject::ObjectID, :AudioDeviceIOProcID], :OSStatus
|
31
|
+
|
32
|
+
# OSStatus AudioDeviceStop(AudioObjectID inDevice,
|
33
|
+
# AudioDeviceIOProcID inProcID)
|
34
|
+
attach_function :AudioDeviceStop, [AudioObject::ObjectID, :AudioDeviceIOProcID], :OSStatus
|
35
|
+
|
36
|
+
# @endgroup
|
37
|
+
|
38
|
+
class AudioDevice < AudioObject
|
39
|
+
|
40
|
+
# @group AudioHardware.h: AudioDevice Properties
|
41
|
+
PropertyPlugIn = 'plug'
|
42
|
+
PropertyDeviceHasChanged = 'diff'
|
43
|
+
PropertyDeviceIsRunningSomewhere = 'gone'
|
44
|
+
ProcessorOverload = 'over'
|
45
|
+
PropertyIOStoppedAbnormally = 'stpd'
|
46
|
+
PropertyHogMode = 'oink'
|
47
|
+
PropertyBufferFrameSize = 'fsiz'
|
48
|
+
PropertyBufferFrameSizeRange = 'fsz#'
|
49
|
+
PropertyUsesVariableBufferFrameSizes = 'vfsz'
|
50
|
+
PropertyIOCycleUsage = 'ncyc'
|
51
|
+
PropertyStreamConfiguration = 'slay'
|
52
|
+
PropertyIOProcStreamUsage = 'suse'
|
53
|
+
PropertyActualSampleRate = 'asrt'
|
54
|
+
# @endgroup
|
55
|
+
|
56
|
+
# @group Properties
|
57
|
+
def actual_sample_rate
|
58
|
+
address = PropertyAddress.global_master(PropertyActualSampleRate)
|
59
|
+
get_property(address).get_float64(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
def buffer_frame_size
|
63
|
+
address = PropertyAddress.global_master(PropertyBufferFrameSize)
|
64
|
+
get_property(address).get_uint32(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
def running_somewhere?
|
68
|
+
address = PropertyAddress.global_master(PropertyDeviceIsRunningSomewhere)
|
69
|
+
0 != get_property(address).get_uint32(0)
|
70
|
+
end
|
71
|
+
# @endgroup
|
72
|
+
|
73
|
+
# Start the AudioDevice
|
74
|
+
# If a block is provided, register it as a callback before starting the device
|
75
|
+
# @note The device will continue to run until `stop` is called
|
76
|
+
def start(&block)
|
77
|
+
if block_given?
|
78
|
+
io_proc_id = FFI::MemoryPointer.new(:pointer)
|
79
|
+
status = CoreAudio.AudioDeviceCreateIOProcID(id, block, nil, io_proc_id)
|
80
|
+
|
81
|
+
raise "Couldn't create an IO Proc #{status} => '#{[status].pack('L').reverse}'" unless status.zero? # && !@proc_id.nil?
|
82
|
+
|
83
|
+
@proc_id = io_proc_id.get_pointer(0)
|
84
|
+
end
|
85
|
+
|
86
|
+
Thread.start do
|
87
|
+
CoreAudio.AudioDeviceStart(id, @proc_id)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Stop the AudioDevice and delete any registered callbacks
|
92
|
+
def stop
|
93
|
+
CoreAudio.AudioDeviceStop(id, @proc_id)
|
94
|
+
CoreAudio.AudioDeviceDestroyIOProcID(id, @proc_id)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
require_relative '../core_foundation'
|
2
|
+
|
3
|
+
module CoreAudio
|
4
|
+
extend FFI::Library
|
5
|
+
|
6
|
+
module AudioHardwareBase
|
7
|
+
# AudioHardwareBase.h: AudioDevice Properties
|
8
|
+
AudioDevicePropertyConfigurationApplication = 'capp'
|
9
|
+
AudioDevicePropertyDeviceUID = 'uid '
|
10
|
+
AudioDevicePropertyModelUID = 'muid'
|
11
|
+
AudioDevicePropertyTransportType = 'tran'
|
12
|
+
AudioDevicePropertyRelatedDevices = 'akin'
|
13
|
+
AudioDevicePropertyClockDomain = 'clkd'
|
14
|
+
AudioDevicePropertyDeviceIsAlive = 'livn'
|
15
|
+
AudioDevicePropertyDeviceIsRunning = 'goin'
|
16
|
+
AudioDevicePropertyDeviceCanBeDefaultDevice = 'dflt'
|
17
|
+
AudioDevicePropertyDeviceCanBeDefaultSystemDevice = 'sflt'
|
18
|
+
AudioDevicePropertyLatency = 'ltnc'
|
19
|
+
AudioDevicePropertyStreams = 'stm#'
|
20
|
+
AudioObjectPropertyControlList = 'ctrl'
|
21
|
+
AudioDevicePropertySafetyOffset = 'saft'
|
22
|
+
AudioDevicePropertyNominalSampleRate = 'nsrt'
|
23
|
+
AudioDevicePropertyAvailableNominalSampleRates = 'nsr#'
|
24
|
+
AudioDevicePropertyIcon = 'icon'
|
25
|
+
AudioDevicePropertyIsHidden = 'hidn'
|
26
|
+
AudioDevicePropertyPreferredChannelsForStereo = 'dch2'
|
27
|
+
AudioDevicePropertyPreferredChannelLayout = 'srnd'
|
28
|
+
end
|
29
|
+
|
30
|
+
module AudioHardware
|
31
|
+
# AudioHardware.h: AudioSystemObject Properties
|
32
|
+
PropertyDevices = 'dev#'
|
33
|
+
PropertyDefaultInputDevice = 'dIn '
|
34
|
+
PropertyDefaultOutputDevice = 'dOut'
|
35
|
+
PropertyDefaultSystemOutputDevice = 'sOut'
|
36
|
+
PropertyTranslateUIDToDevice = 'uidd'
|
37
|
+
PropertyMixStereoToMono = 'stmo'
|
38
|
+
PropertyPlugInList = 'plg#'
|
39
|
+
PropertyTranslateBundleIDToPlugIn = 'bidp'
|
40
|
+
PropertyTransportManagerList = 'tmg#'
|
41
|
+
PropertyTranslateBundleIDToTransportManager = 'tmbi'
|
42
|
+
PropertyBoxList = 'box#'
|
43
|
+
PropertyTranslateUIDToBox = 'uidb'
|
44
|
+
PropertyProcessIsMaster = 'mast'
|
45
|
+
PropertyIsInitingOrExiting = 'inot'
|
46
|
+
PropertyUserIDChanged = 'euid'
|
47
|
+
PropertyProcessIsAudible = 'pmut'
|
48
|
+
PropertySleepingIsAllowed = 'slep'
|
49
|
+
PropertyUnloadingIsAllowed = 'unld'
|
50
|
+
PropertyHogModeIsAllowed = 'hogr'
|
51
|
+
PropertyUserSessionIsActiveOrHeadless = 'user'
|
52
|
+
PropertyServiceRestarted = 'srst'
|
53
|
+
PropertyPowerHint = 'powh'
|
54
|
+
|
55
|
+
# AudioHardware.h: AudioDevice Properties
|
56
|
+
AudioDevicePropertyPlugIn = 'plug'
|
57
|
+
AudioDevicePropertyDeviceHasChanged = 'diff'
|
58
|
+
AudioDevicePropertyDeviceIsRunningSomewhere = 'gone'
|
59
|
+
AudioDeviceProcessorOverload = 'over'
|
60
|
+
AudioDevicePropertyIOStoppedAbnormally = 'stpd'
|
61
|
+
AudioDevicePropertyHogMode = 'oink'
|
62
|
+
AudioDevicePropertyBufferFrameSize = 'fsiz'
|
63
|
+
AudioDevicePropertyBufferFrameSizeRange = 'fsz#'
|
64
|
+
AudioDevicePropertyUsesVariableBufferFrameSizes = 'vfsz'
|
65
|
+
AudioDevicePropertyIOCycleUsage = 'ncyc'
|
66
|
+
AudioDevicePropertyStreamConfiguration = 'slay'
|
67
|
+
AudioDevicePropertyIOProcStreamUsage = 'suse'
|
68
|
+
AudioDevicePropertyActualSampleRate = 'asrt'
|
69
|
+
end
|
70
|
+
|
71
|
+
class AudioObject
|
72
|
+
attr_reader :id
|
73
|
+
|
74
|
+
ObjectID = FFI::Type::UINT32
|
75
|
+
PropertyElement = FFI::Type::UINT32
|
76
|
+
PropertyScope = FFI::Type::UINT32
|
77
|
+
PropertySelector = FFI::Type::UINT32
|
78
|
+
|
79
|
+
# AudioHardwareBase.h: Basic Constants
|
80
|
+
PropertyScopeGlobal = 'glob'
|
81
|
+
PropertyScopeInput = 'inpt'
|
82
|
+
PropertyScopeOutput = 'outp'
|
83
|
+
PropertyScopePlayThrough = 'ptru'
|
84
|
+
PropertyElementMaster = 0
|
85
|
+
|
86
|
+
# AudioHardwareBase.h: AudioObject Properties
|
87
|
+
PropertyBaseClass = 'bcls'
|
88
|
+
PropertyClass = 'clas'
|
89
|
+
PropertyOwner = 'stdv'
|
90
|
+
PropertyName = 'lnam'
|
91
|
+
PropertyModelName = 'lmod'
|
92
|
+
PropertyManufacturer = 'lmak'
|
93
|
+
PropertyElementName = 'lchn'
|
94
|
+
PropertyElementCategoryName = 'lccn'
|
95
|
+
PropertyElementNumberName = 'lcnn'
|
96
|
+
PropertyOwnedObjects = 'ownd'
|
97
|
+
PropertyIdentify = 'iden'
|
98
|
+
PropertySerialNumber = 'snum'
|
99
|
+
PropertyFirmwareVersion = 'fwvn'
|
100
|
+
|
101
|
+
# AudioHardware.h: Basic Constants
|
102
|
+
SystemObject = 1
|
103
|
+
|
104
|
+
def self.system
|
105
|
+
new(SystemObject)
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(id)
|
109
|
+
@id = id
|
110
|
+
end
|
111
|
+
|
112
|
+
# @return [FFI::MemoryPointer]
|
113
|
+
def get_property(address)
|
114
|
+
buffer_size = FFI::MemoryPointer.new(:uint32)
|
115
|
+
status = CoreAudio.AudioObjectGetPropertyDataSize(id, address, 0, nil, buffer_size)
|
116
|
+
raise('Could not get audio property size') unless 0 == status
|
117
|
+
|
118
|
+
# buffer_size is now the size of the buffer to be passed to AudioObjectGetPropertyData()
|
119
|
+
buffer = FFI::MemoryPointer.new(1, buffer_size.get_int32(0))
|
120
|
+
status = CoreAudio.AudioObjectGetPropertyData(id, address, 0, nil, buffer_size, buffer)
|
121
|
+
raise('Could not get the audio property data') unless 0 == status
|
122
|
+
|
123
|
+
buffer
|
124
|
+
end
|
125
|
+
|
126
|
+
# @group Convenience Attributes
|
127
|
+
def external?
|
128
|
+
not internal?
|
129
|
+
end
|
130
|
+
|
131
|
+
def internal?
|
132
|
+
transport_type == 'bltn'
|
133
|
+
end
|
134
|
+
# @endgroup
|
135
|
+
|
136
|
+
# @return [String] the name of the device
|
137
|
+
def device_name
|
138
|
+
address = PropertyAddress.global_master(PropertyName)
|
139
|
+
get_string(address)
|
140
|
+
end
|
141
|
+
|
142
|
+
def device_uid
|
143
|
+
address = PropertyAddress.global_master(AudioHardwareBase::AudioDevicePropertyDeviceUID)
|
144
|
+
get_string(address)
|
145
|
+
end
|
146
|
+
|
147
|
+
# @return [String] a persistent identifier for the model of an AudioDevice
|
148
|
+
def model_uid
|
149
|
+
address = PropertyAddress.global_master(AudioHardwareBase::AudioDevicePropertyModelUID)
|
150
|
+
get_string(address)
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [String] the 4-character transport type identifier
|
154
|
+
def transport_type
|
155
|
+
address = PropertyAddress.global_master(AudioHardwareBase::AudioDevicePropertyTransportType)
|
156
|
+
buffer = get_property(address)
|
157
|
+
buffer.get_bytes(0, buffer.size).reverse
|
158
|
+
end
|
159
|
+
|
160
|
+
class PropertyAddress < FFI::Struct
|
161
|
+
layout :mSelector, AudioObject::PropertySelector,
|
162
|
+
:mScope, AudioObject::PropertyScope,
|
163
|
+
:mElement, AudioObject::PropertyElement
|
164
|
+
|
165
|
+
def self.make(selector, scope, element)
|
166
|
+
element, scope, selector = [element, scope, selector].map {|a| a.is_a?(String) ? a.reverse.unpack('L').first : a }
|
167
|
+
new.tap do |address|
|
168
|
+
address[:mSelector] = selector
|
169
|
+
address[:mScope] = scope
|
170
|
+
address[:mElement] = element
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.global_master(selector)
|
175
|
+
make(selector, PropertyScopeGlobal, PropertyElementMaster)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
# @return [String] the String for the addressed property
|
182
|
+
def get_string(address)
|
183
|
+
buffer = get_property(address)
|
184
|
+
cf_string_ref = buffer.get_pointer(0)
|
185
|
+
CoreFoundation::CFStringRef.new(cf_string_ref).to_s
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module CoreFoundation
|
4
|
+
extend FFI::Library
|
5
|
+
ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
|
6
|
+
|
7
|
+
typedef :pointer, :CFStringRef
|
8
|
+
|
9
|
+
if FFI::Platform::ARCH == 'x86_64'
|
10
|
+
CFIndex = FFI::Type::LONG_LONG
|
11
|
+
else
|
12
|
+
CFIndex = FFI::Type::LONG
|
13
|
+
end
|
14
|
+
|
15
|
+
# CFString.h
|
16
|
+
CFStringEncoding = enum :uint32,
|
17
|
+
:MacRoman, 0,
|
18
|
+
:WindowsLatin1, 0x0500, # ANSI codepage 1252
|
19
|
+
:ISOLatin1, 0x0201, # ISO 8859-1
|
20
|
+
:NextStepLatin, 0x0B01, # NextStep encoding
|
21
|
+
:ASCII, 0x0600, # 0..127 (in creating CFString, values greater than 0x7F are treated as corresponding Unicode value)
|
22
|
+
:Unicode, 0x0100, # kTextEncodingUnicodeDefault + kTextEncodingDefaultFormat (aka kUnicode16BitFormat)
|
23
|
+
:UTF8, 0x08000100, # kTextEncodingUnicodeDefault + kUnicodeUTF8Format
|
24
|
+
:NonLossyASCII, 0x0BFF, # 7bit Unicode variants used by Cocoa & Java
|
25
|
+
:UTF16, 0x0100, # kTextEncodingUnicodeDefault + kUnicodeUTF16Format (alias of kCFStringEncodingUnicode)
|
26
|
+
:UTF16BE, 0x10000100, # kTextEncodingUnicodeDefault + kUnicodeUTF16BEFormat
|
27
|
+
:UTF16LE, 0x14000100, # kTextEncodingUnicodeDefault + kUnicodeUTF16LEFormat
|
28
|
+
:UTF32, 0x0c000100, # kTextEncodingUnicodeDefault + kUnicodeUTF32Format
|
29
|
+
:UTF32BE, 0x18000100, # kTextEncodingUnicodeDefault + kUnicodeUTF32BEFormat
|
30
|
+
:UTF32LE, 0x1c000100, # kTextEncodingUnicodeDefault + kUnicodeUTF32LEFormat
|
31
|
+
:Invalid, 0xffffffff # Invalid Encoding
|
32
|
+
|
33
|
+
class CFRange < FFI::Struct
|
34
|
+
layout :location, CFIndex,
|
35
|
+
:length, CFIndex
|
36
|
+
|
37
|
+
def self.make(location:0, length:0)
|
38
|
+
new.tap do |range|
|
39
|
+
range[:location] = location
|
40
|
+
range[:length] = length
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class CFStringRef < FFI::Pointer
|
46
|
+
# @return [CFIndex] the length of the referenced CFString
|
47
|
+
def length
|
48
|
+
CoreFoundation.CFStringGetLength(self)
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [CFIndex] the maximum size of the buffer that will hold the string
|
52
|
+
def max_size
|
53
|
+
CoreFoundation.CFStringGetMaximumSizeForEncoding(length, CFStringEncoding[:UTF8])
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [String] the CFString, converted to a UTF-8 string
|
57
|
+
def to_s
|
58
|
+
buffer = FFI::MemoryPointer.new(:char, max_size)
|
59
|
+
used_bytes = FFI::MemoryPointer.new(CFIndex)
|
60
|
+
CoreFoundation.CFStringGetBytes(self,
|
61
|
+
CFRange.make(location:0, length:length),
|
62
|
+
CFStringEncoding[:UTF8],
|
63
|
+
0,
|
64
|
+
false,
|
65
|
+
buffer,
|
66
|
+
buffer.size,
|
67
|
+
used_bytes)
|
68
|
+
|
69
|
+
used_bytes = if CFIndex == CoreFoundation.find_type(:long_long)
|
70
|
+
used_bytes.read_long_long
|
71
|
+
else
|
72
|
+
used_bytes.read_long
|
73
|
+
end
|
74
|
+
|
75
|
+
buffer.read_string(used_bytes).force_encoding(Encoding::UTF_8)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# CFIndex CFStringGetBytes(CFStringRef theString, CFRange range, CFStringEncoding encoding, UInt8 lossByte, Boolean isExternalRepresentation, UInt8 *buffer, CFIndex maxBufLen, CFIndex *usedBufLen)
|
80
|
+
attach_function :CFStringGetBytes, [:CFStringRef, CFRange.by_value, CFStringEncoding, :uint8, :bool, :buffer_out, CFIndex, :buffer_out], CFIndex
|
81
|
+
|
82
|
+
# CFIndex CFStringGetLength(CFStringRef theString)
|
83
|
+
attach_function :CFStringGetLength, [:CFStringRef], CFIndex
|
84
|
+
|
85
|
+
# CFIndex CFStringGetMaximumSizeForEncoding(CFIndex length, CFStringEncoding encoding)
|
86
|
+
attach_function :CFStringGetMaximumSizeForEncoding, [CFIndex, CFStringEncoding], CFIndex
|
87
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: audio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0'
|
4
|
+
version: '0.1'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brandon Fosdick
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-02-
|
11
|
+
date: 2015-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -51,6 +51,10 @@ files:
|
|
51
51
|
- Rakefile
|
52
52
|
- audio.gemspec
|
53
53
|
- lib/audio.rb
|
54
|
+
- lib/core_audio.rb
|
55
|
+
- lib/core_audio/audio_device.rb
|
56
|
+
- lib/core_audio/audio_object.rb
|
57
|
+
- lib/core_foundation.rb
|
54
58
|
homepage: http://github.com/bfoz/audio-ruby
|
55
59
|
licenses:
|
56
60
|
- BSD
|