1
1
Fork 0
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:
Nguyễn Gia Phong 2020-02-14 21:42:06 +07:00
parent 19ac906048
commit aac24f99a1
6 changed files with 133 additions and 91 deletions

View file

@ -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)):

View file

@ -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)} /'

View file

@ -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

View file

@ -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)

View file

@ -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}')

View file

@ -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