mirror of
https://github.com/McSinyx/palace
synced 2023-12-14 09:02:59 +01:00
Finish methods involving sample type and channel config
Also move cdef helpers to the end of the module
This commit is contained in:
parent
19ac906048
commit
aac24f99a1
6 changed files with 133 additions and 91 deletions
|
@ -69,9 +69,8 @@ def hrtf(files: Iterable[str], device: str, hrtf_name: str,
|
||||||
stderr.write(f'Failed to open file: {filename}\n')
|
stderr.write(f'Failed to open file: {filename}\n')
|
||||||
continue
|
continue
|
||||||
decoder.play(12000, 4, src)
|
decoder.play(12000, 4, src)
|
||||||
print(f'Playing {filename} ({decoder.sample_type_name},',
|
print(f'Playing {filename} ({decoder.sample_type},',
|
||||||
f'{decoder.channel_config_name},',
|
f'{decoder.channel_config}, {decoder.frequency} Hz)')
|
||||||
f'{decoder.frequency} Hz)')
|
|
||||||
|
|
||||||
for i in takewhile(lambda i: src.playing,
|
for i in takewhile(lambda i: src.playing,
|
||||||
count(step=PERIOD)):
|
count(step=PERIOD)):
|
||||||
|
|
|
@ -47,8 +47,8 @@ def play(files: Iterable[str], device: str) -> None:
|
||||||
stderr.write(f'Failed to open file: {filename}\n')
|
stderr.write(f'Failed to open file: {filename}\n')
|
||||||
continue
|
continue
|
||||||
with buffer, buffer.play() as src:
|
with buffer, buffer.play() as src:
|
||||||
print(f'Playing {filename} ({buffer.sample_type_name},',
|
print(f'Playing {filename} ({buffer.sample_type},',
|
||||||
f'{buffer.channel_config_name}, {buffer.frequency} Hz)')
|
f'{buffer.channel_config}, {buffer.frequency} Hz)')
|
||||||
|
|
||||||
for i in takewhile(lambda i: src.playing, count()):
|
for i in takewhile(lambda i: src.playing, count()):
|
||||||
print(f' {pretty_time(src.offset_seconds)} /'
|
print(f' {pretty_time(src.offset_seconds)} /'
|
||||||
|
|
|
@ -47,6 +47,7 @@ cdef extern from 'alure2-aliases.h' namespace 'alure' nogil:
|
||||||
cdef extern from 'alure2.h' namespace 'alure' nogil:
|
cdef extern from 'alure2.h' namespace 'alure' nogil:
|
||||||
# Type aliases:
|
# Type aliases:
|
||||||
# char*: string
|
# char*: string
|
||||||
|
# ALbyte: signed char
|
||||||
# ALfloat: float
|
# ALfloat: float
|
||||||
# ALsizei: int
|
# ALsizei: int
|
||||||
# ALuint: unsigned
|
# ALuint: unsigned
|
||||||
|
@ -70,13 +71,25 @@ cdef extern from 'alure2.h' namespace 'alure' nogil:
|
||||||
unsigned send 'mSend'
|
unsigned send 'mSend'
|
||||||
|
|
||||||
# Enum classes:
|
# Enum classes:
|
||||||
cdef cppclass SampleType:
|
cdef enum SampleType:
|
||||||
pass
|
UInt8 'alure::SampleType::UInt8' # Unsigned 8-bit
|
||||||
|
Int16 'alure::SampleType::Int16' # Signed 16-bit
|
||||||
|
Float32 'alure::SampleType::Float32' # 32-bit float
|
||||||
|
Mulaw 'alure::SampleType::Mulaw' # Mulaw
|
||||||
|
|
||||||
|
cdef enum ChannelConfig:
|
||||||
|
Mono 'alure::ChannelConfig::Mono' # Mono
|
||||||
|
Stereo 'alure::ChannelConfig::Stereo' # Stereo
|
||||||
|
Rear 'alure::ChannelConfig::Rear' # Rear
|
||||||
|
Quad 'alure::ChannelConfig::Quad' # Quadrophonic
|
||||||
|
X51 'alure::ChannelConfig::X51' # 5.1 Surround
|
||||||
|
X61 'alure::ChannelConfig::X61' # 6.1 Surround
|
||||||
|
X71 'alure::ChannelConfig::X71' # 7.1 Surround
|
||||||
|
BFormat2D 'alure::ChannelConfig::BFormat2D' # B-Format 2D
|
||||||
|
BFormat3D 'alure::ChannelConfig::BFormat3D' # B-Format 3D
|
||||||
|
|
||||||
# The following relies on C++ implicit conversion from char* to string.
|
# The following relies on C++ implicit conversion from char* to string.
|
||||||
cdef const string get_sample_type_name 'GetSampleTypeName'(SampleType) except +
|
cdef const string get_sample_type_name 'GetSampleTypeName'(SampleType) except +
|
||||||
|
|
||||||
cdef cppclass ChannelConfig:
|
|
||||||
pass
|
|
||||||
cdef const string get_channel_config_name 'GetChannelConfigName'(ChannelConfig) except +
|
cdef const string get_channel_config_name 'GetChannelConfigName'(ChannelConfig) except +
|
||||||
cdef unsigned frames_to_bytes 'FramesToBytes'(unsigned, ChannelConfig, SampleType) except +
|
cdef unsigned frames_to_bytes 'FramesToBytes'(unsigned, ChannelConfig, SampleType) except +
|
||||||
cdef unsigned bytes_to_frames 'BytesToFrames'(unsigned, ChannelConfig, SampleType)
|
cdef unsigned bytes_to_frames 'BytesToFrames'(unsigned, ChannelConfig, SampleType)
|
||||||
|
@ -235,15 +248,12 @@ cdef extern from 'alure2.h' namespace 'alure' nogil:
|
||||||
|
|
||||||
void destroy() except +
|
void destroy() except +
|
||||||
|
|
||||||
Device get_device 'getDevice'() except +
|
|
||||||
|
|
||||||
void start_batch 'startBatch'() except +
|
void start_batch 'startBatch'() except +
|
||||||
void end_batch 'endBatch'() except +
|
void end_batch 'endBatch'() except +
|
||||||
|
|
||||||
Listener get_listener 'getListener'() except +
|
Listener get_listener 'getListener'() except +
|
||||||
|
|
||||||
shared_ptr[MessageHandler] set_message_handler 'setMessageHandler'(shared_ptr[MessageHandler]) except +
|
shared_ptr[MessageHandler] set_message_handler 'setMessageHandler'(shared_ptr[MessageHandler]) except +
|
||||||
shared_ptr[MessageHandler] get_message_handler 'getMessageHandler'() except +
|
|
||||||
|
|
||||||
void set_async_wake_interval 'setAsyncWakeInterval'(milliseconds) except +
|
void set_async_wake_interval 'setAsyncWakeInterval'(milliseconds) except +
|
||||||
milliseconds get_async_wake_interval 'getAsyncWakeInterval'() except +
|
milliseconds get_async_wake_interval 'getAsyncWakeInterval'() except +
|
||||||
|
@ -578,3 +588,9 @@ cdef extern from 'alure2.h' namespace 'alure' nogil:
|
||||||
|
|
||||||
cdef cppclass MessageHandler:
|
cdef cppclass MessageHandler:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# GIL is needed for operations with Python objects.
|
||||||
|
cdef extern from 'bases.h' namespace 'palace':
|
||||||
|
cdef cppclass BaseMessageHandler(MessageHandler):
|
||||||
|
pass
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
# Cython declarations of worked-around alure base classes
|
|
||||||
# Copyright (C) 2020 Nguyễn Gia Phong
|
|
||||||
#
|
|
||||||
# This file is part of palace.
|
|
||||||
#
|
|
||||||
# palace is free software: you can redistribute it and/or modify it
|
|
||||||
# under the terms of the GNU Lesser General Public License as published
|
|
||||||
# by the Free Software Foundation, either version 3 of the License,
|
|
||||||
# or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# palace is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with palace. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
from libcpp.string cimport string
|
|
||||||
|
|
||||||
from alure cimport Device, Source, MessageHandler
|
|
||||||
|
|
||||||
|
|
||||||
# GIL is needed for operations with Python objects.
|
|
||||||
cdef extern from 'bases.h' namespace 'palace':
|
|
||||||
cdef cppclass BaseMessageHandler(MessageHandler):
|
|
||||||
void device_disconnected(Device)
|
|
||||||
void source_stopped(Source)
|
|
||||||
void source_force_stopped(Source)
|
|
||||||
string resource_not_found(string)
|
|
149
src/palace.pyx
149
src/palace.pyx
|
@ -26,28 +26,31 @@ device_names : Dict[str, List[str]]
|
||||||
Dictionary of available device names corresponding to each type.
|
Dictionary of available device names corresponding to each type.
|
||||||
device_name_default : Dict[str, str]
|
device_name_default : Dict[str, str]
|
||||||
Dictionary of the default device name corresponding to each type.
|
Dictionary of the default device name corresponding to each type.
|
||||||
|
sample_types : FrozenSet[str]
|
||||||
|
Set of sample types.
|
||||||
|
channel_configs : FrozenSet[str]
|
||||||
|
Set of channel configurations.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = ['ALC_FALSE', 'ALC_TRUE', 'ALC_HRTF_SOFT', 'ALC_HRTF_ID_SOFT',
|
__all__ = [
|
||||||
'device_name_default', 'device_names',
|
'ALC_FALSE', 'ALC_TRUE', 'ALC_HRTF_SOFT', 'ALC_HRTF_ID_SOFT',
|
||||||
'query_extension', 'use_context',
|
'device_name_default', 'device_names', 'sample_types', 'channel_configs',
|
||||||
'Device', 'Context', 'Buffer', 'Source', 'SourceGroup',
|
'query_extension', 'use_context',
|
||||||
'AuxiliaryEffectSlot', 'Decoder', 'MessageHandler']
|
'Device', 'Context', 'Buffer', 'Source', 'SourceGroup',
|
||||||
|
'AuxiliaryEffectSlot', 'Decoder', 'MessageHandler']
|
||||||
|
|
||||||
|
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type
|
from typing import Any, Dict, FrozenSet, Iterator, List, Optional, Tuple, Type
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
from libcpp cimport bool as boolean, nullptr
|
from libcpp cimport bool as boolean, nullptr # noqa
|
||||||
from libcpp.memory cimport shared_ptr
|
from libcpp.memory cimport shared_ptr
|
||||||
from libcpp.string cimport string
|
from libcpp.string cimport string
|
||||||
from libcpp.utility cimport pair
|
from libcpp.utility cimport pair
|
||||||
from libcpp.vector cimport vector
|
from libcpp.vector cimport vector
|
||||||
|
|
||||||
from std cimport milliseconds
|
from std cimport milliseconds
|
||||||
from bases cimport BaseMessageHandler
|
cimport alure # noqa
|
||||||
cimport alure
|
|
||||||
|
|
||||||
# Type aliases
|
# Type aliases
|
||||||
Vector3 = Tuple[float, float, float]
|
Vector3 = Tuple[float, float, float]
|
||||||
|
@ -74,30 +77,12 @@ device_name_default: Dict[str, str] = dict(
|
||||||
full=devmgr.default_device_name(alure.DefaultDeviceType.Full),
|
full=devmgr.default_device_name(alure.DefaultDeviceType.Full),
|
||||||
capture=devmgr.default_device_name(alure.DefaultDeviceType.Capture))
|
capture=devmgr.default_device_name(alure.DefaultDeviceType.Capture))
|
||||||
|
|
||||||
|
sample_types: FrozenSet[str] = frozenset({
|
||||||
cdef vector[alure.AttributePair] mkattrs(vector[pair[int, int]] attrs):
|
'Unsigned 8-bit', 'Signed 16-bit', '32-bit float', 'Mulaw'})
|
||||||
"""Convert attribute pairs from Python object to alure format."""
|
channel_configs: FrozenSet[str] = frozenset({
|
||||||
cdef vector[alure.AttributePair] attributes
|
'Mono', 'Stereo', 'Rear', 'Quadrophonic',
|
||||||
cdef alure.AttributePair pair
|
'5.1 Surround', '6.1 Surround', '7.1 Surround',
|
||||||
for attribute, value in attrs:
|
'B-Format 2D', 'B-Format 3D'})
|
||||||
pair.attribute = attribute
|
|
||||||
pair.value = value
|
|
||||||
attributes.push_back(pair) # insert a copy
|
|
||||||
pair.attribute = pair.value = 0
|
|
||||||
attributes.push_back(pair) # insert a copy
|
|
||||||
return attributes
|
|
||||||
|
|
||||||
|
|
||||||
cdef vector[float] from_vector3(alure.Vector3 v):
|
|
||||||
"""Convert alure::Vector3 to std::vector of 3 floats."""
|
|
||||||
cdef vector[float] result
|
|
||||||
for i in range(3): result.push_back(v[i])
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
cdef alure.Vector3 to_vector3(vector[float] v):
|
|
||||||
"""Convert std::vector of 3 floats to alure::Vector3."""
|
|
||||||
return alure.Vector3(v[0], v[1], v[2])
|
|
||||||
|
|
||||||
|
|
||||||
def query_extension(name: str) -> bool:
|
def query_extension(name: str) -> bool:
|
||||||
|
@ -430,6 +415,18 @@ cdef class Context:
|
||||||
"""
|
"""
|
||||||
self.impl.destroy()
|
self.impl.destroy()
|
||||||
|
|
||||||
|
def is_supported(self, channel_config: str, sample_type: str) -> bool:
|
||||||
|
"""Return if the channel configuration and sample type
|
||||||
|
are supported by the context.
|
||||||
|
|
||||||
|
See Also
|
||||||
|
--------
|
||||||
|
sample_types : Set of sample types
|
||||||
|
channel_configs : Set of channel configurations
|
||||||
|
"""
|
||||||
|
return self.impl.is_supported(get_channel_config(channel_config),
|
||||||
|
get_sample_type(sample_type))
|
||||||
|
|
||||||
def update(self) -> None:
|
def update(self) -> None:
|
||||||
"""Update the context and all sources belonging to this context."""
|
"""Update the context and all sources belonging to this context."""
|
||||||
self.impl.update()
|
self.impl.update()
|
||||||
|
@ -549,17 +546,15 @@ cdef class Buffer:
|
||||||
"""Buffer's frequency in hertz."""
|
"""Buffer's frequency in hertz."""
|
||||||
return self.impl.get_frequency()
|
return self.impl.get_frequency()
|
||||||
|
|
||||||
# TODO: get channel config (enum class)
|
|
||||||
@property
|
@property
|
||||||
def channel_config_name(self) -> str:
|
def channel_config(self) -> str:
|
||||||
"""Buffer's sample configuration name."""
|
"""Buffer's sample configuration."""
|
||||||
return alure.get_channel_config_name(
|
return alure.get_channel_config_name(
|
||||||
self.impl.get_channel_config())
|
self.impl.get_channel_config())
|
||||||
|
|
||||||
# TODO: get sample type (enum class)
|
|
||||||
@property
|
@property
|
||||||
def sample_type_name(self) -> str:
|
def sample_type(self) -> str:
|
||||||
"""Buffer's sample type name."""
|
"""Buffer's sample type."""
|
||||||
return alure.get_sample_type_name(
|
return alure.get_sample_type_name(
|
||||||
self.impl.get_sample_type())
|
self.impl.get_sample_type())
|
||||||
|
|
||||||
|
@ -1450,14 +1445,14 @@ cdef class Decoder:
|
||||||
return self.pimpl.get()[0].get_frequency()
|
return self.pimpl.get()[0].get_frequency()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def channel_config_name(self) -> str:
|
def channel_config(self) -> str:
|
||||||
"""Name of the channel configuration of the audio being decoded."""
|
"""Channel configuration of the audio being decoded."""
|
||||||
return alure.get_channel_config_name(
|
return alure.get_channel_config_name(
|
||||||
self.pimpl.get()[0].get_channel_config())
|
self.pimpl.get()[0].get_channel_config())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def sample_type_name(self) -> str:
|
def sample_type(self) -> str:
|
||||||
"""Name of the sample type of the audio being decoded."""
|
"""Sample type of the audio being decoded."""
|
||||||
return alure.get_sample_type_name(
|
return alure.get_sample_type_name(
|
||||||
self.pimpl.get()[0].get_sample_type())
|
self.pimpl.get()[0].get_sample_type())
|
||||||
|
|
||||||
|
@ -1510,6 +1505,8 @@ cdef class MessageHandler:
|
||||||
Applications may derive from this and set an instance on a context
|
Applications may derive from this and set an instance on a context
|
||||||
to receive messages. The base methods are no-ops, so subclasses
|
to receive messages. The base methods are no-ops, so subclasses
|
||||||
only need to implement methods for relevant messages.
|
only need to implement methods for relevant messages.
|
||||||
|
|
||||||
|
Methods of MessageHandler must not raise any exception.
|
||||||
"""
|
"""
|
||||||
def device_disconnected(self, device: Device) -> None:
|
def device_disconnected(self, device: Device) -> None:
|
||||||
"""Handle disconnected device messages.
|
"""Handle disconnected device messages.
|
||||||
|
@ -1585,7 +1582,7 @@ cdef class MessageHandler:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
cdef cppclass CppMessageHandler(BaseMessageHandler):
|
cdef cppclass CppMessageHandler(alure.BaseMessageHandler):
|
||||||
Context context
|
Context context
|
||||||
|
|
||||||
CppMessageHandler(Context ctx):
|
CppMessageHandler(Context ctx):
|
||||||
|
@ -1613,3 +1610,65 @@ cdef cppclass CppMessageHandler(BaseMessageHandler):
|
||||||
|
|
||||||
string resource_not_found(string name):
|
string resource_not_found(string name):
|
||||||
return context.message_handler.resource_not_found(name)
|
return context.message_handler.resource_not_found(name)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper cdef functions
|
||||||
|
cdef vector[alure.AttributePair] mkattrs(vector[pair[int, int]] attrs):
|
||||||
|
"""Convert attribute pairs from Python object to alure format."""
|
||||||
|
cdef vector[alure.AttributePair] attributes
|
||||||
|
cdef alure.AttributePair pair
|
||||||
|
for attribute, value in attrs:
|
||||||
|
pair.attribute = attribute
|
||||||
|
pair.value = value
|
||||||
|
attributes.push_back(pair) # insert a copy
|
||||||
|
pair.attribute = pair.value = 0
|
||||||
|
attributes.push_back(pair) # insert a copy
|
||||||
|
return attributes
|
||||||
|
|
||||||
|
|
||||||
|
cdef vector[float] from_vector3(alure.Vector3 v):
|
||||||
|
"""Convert alure::Vector3 to std::vector of 3 floats."""
|
||||||
|
cdef vector[float] result
|
||||||
|
for i in range(3): result.push_back(v[i])
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
cdef alure.Vector3 to_vector3(vector[float] v):
|
||||||
|
"""Convert std::vector of 3 floats to alure::Vector3."""
|
||||||
|
return alure.Vector3(v[0], v[1], v[2])
|
||||||
|
|
||||||
|
|
||||||
|
cdef alure.SampleType get_sample_type(str name) except +:
|
||||||
|
"""Return the specified sample type enumeration."""
|
||||||
|
if name == 'Unsigned 8-bit':
|
||||||
|
return alure.SampleType.UInt8
|
||||||
|
elif name == 'Signed 16-bit':
|
||||||
|
return alure.SampleType.Int16
|
||||||
|
elif name == '32-bit float':
|
||||||
|
return alure.SampleType.Float32
|
||||||
|
elif name == 'Mulaw':
|
||||||
|
return alure.SampleType.Mulaw
|
||||||
|
raise ValueError(f'Invalid sample type name: {name}')
|
||||||
|
|
||||||
|
|
||||||
|
cdef alure.ChannelConfig get_channel_config(str name) except +:
|
||||||
|
"""Return the specified channel configuration enumeration."""
|
||||||
|
if name == 'Mono':
|
||||||
|
return alure.ChannelConfig.Mono
|
||||||
|
elif name == 'Stereo':
|
||||||
|
return alure.ChannelConfig.Stereo
|
||||||
|
elif name == 'Rear':
|
||||||
|
return alure.ChannelConfig.Rear
|
||||||
|
elif name == 'Quadrophonic':
|
||||||
|
return alure.ChannelConfig.Quad
|
||||||
|
elif name == '5.1 Surround':
|
||||||
|
return alure.ChannelConfig.X51
|
||||||
|
elif name == '6.1 Surround':
|
||||||
|
return alure.ChannelConfig.X61
|
||||||
|
elif name == '7.1 Surround':
|
||||||
|
return alure.ChannelConfig.X71
|
||||||
|
elif name == 'B-Format 2D':
|
||||||
|
return alure.ChannelConfig.BFormat2D
|
||||||
|
elif name == 'B-Format 3D':
|
||||||
|
return alure.ChannelConfig.BFormat3D
|
||||||
|
raise ValueError(f'Invalid channel configuration name: {name}')
|
||||||
|
|
4
tox.ini
4
tox.ini
|
@ -16,9 +16,7 @@ commands =
|
||||||
filename = *.pxd, *.pyx, *.py
|
filename = *.pxd, *.pyx, *.py
|
||||||
hang-closing = True
|
hang-closing = True
|
||||||
ignore = E225, E226, E227, E701
|
ignore = E225, E226, E227, E701
|
||||||
per-file-ignores =
|
per-file-ignores = *.pxd:E501,E999
|
||||||
*.pxd:E501,E999
|
|
||||||
*.pyx:E999
|
|
||||||
; See https://github.com/PyCQA/pycodestyle/issues/906
|
; See https://github.com/PyCQA/pycodestyle/issues/906
|
||||||
;max-doc-length = 72
|
;max-doc-length = 72
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue