mirror of
https://github.com/McSinyx/palace
synced 2023-12-14 09:02:59 +01:00
Implement MessageHandler
Since this introduce a new source files, all sources are moved to a new directory src.
This commit is contained in:
parent
9b6b3164bf
commit
19ac906048
|
@ -1 +1 @@
|
|||
include alure.pxd palace.pyx CMakeLists.txt examples/*.py tests/*.py
|
||||
include src/*.pxd src/*.pyx src/*.h tests/*.py examples/*.py CMakeLists.txt
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[metadata]
|
||||
name = palace
|
||||
version = 0.0.8
|
||||
version = 0.0.9
|
||||
url = https://github.com/McSinyx/palace
|
||||
author = Nguyễn Gia Phong
|
||||
author_email = vn.mcsinyx@gmail.com
|
||||
|
|
2
setup.py
2
setup.py
|
@ -50,7 +50,7 @@ class BuildAlure2Ext(build_ext):
|
|||
|
||||
setup(cmdclass={'build_ext': BuildAlure2Ext},
|
||||
ext_modules=cythonize(
|
||||
Extension(name='palace', sources=['palace.pyx'],
|
||||
Extension(name='palace', sources=[join('src', 'palace.pyx')],
|
||||
language='c++', define_macros=[('CYTHON_TRACE', 1)]),
|
||||
compiler_directives=dict(language_level='3str', c_string_type='str',
|
||||
c_string_encoding='utf8', linetrace=True,
|
||||
|
|
|
@ -25,31 +25,9 @@ from libcpp.string cimport string
|
|||
from libcpp.utility cimport pair
|
||||
from libcpp.vector cimport vector
|
||||
|
||||
|
||||
# C++ standard library
|
||||
cdef extern from '<chrono>' namespace 'std::chrono' nogil:
|
||||
cdef cppclass duration[Rep, Period=*]:
|
||||
ctypedef Rep rep
|
||||
duration() except +
|
||||
duration(const rep&) except + # ugly hack, see cython/cython#3198
|
||||
rep count() except +
|
||||
|
||||
ctypedef duration[int64_t, nano] nanoseconds
|
||||
ctypedef duration[int64_t, milli] milliseconds
|
||||
from std cimport duration, nanoseconds, milliseconds, shared_future
|
||||
|
||||
|
||||
cdef extern from '<future>' namespace 'std' nogil:
|
||||
cdef cppclass shared_future[R]:
|
||||
pass
|
||||
|
||||
|
||||
cdef extern from '<ratio>' namespace 'std' nogil:
|
||||
cdef cppclass nano:
|
||||
pass
|
||||
cdef cppclass milli:
|
||||
pass
|
||||
|
||||
|
||||
# OpenAL and Alure auxiliary declarations
|
||||
cdef extern from 'alc.h' nogil:
|
||||
cdef int ALC_FALSE
|
80
src/bases.h
Normal file
80
src/bases.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Base classes for Cython compatibility
|
||||
// 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/>.
|
||||
|
||||
#ifndef PALACE_BASES_H
|
||||
#define PALACE_BASES_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "alure2.h"
|
||||
|
||||
namespace palace {
|
||||
|
||||
// Due to the lack of support for noexcept keyword in Cython, this is
|
||||
// created to work around the looser throw specifier error in C++.
|
||||
class BaseMessageHandler : public alure::MessageHandler {
|
||||
public:
|
||||
virtual void device_disconnected (alure::Device device) = 0;
|
||||
inline void
|
||||
deviceDisconnected (alure::Device device) noexcept override
|
||||
{
|
||||
device_disconnected (device);
|
||||
}
|
||||
|
||||
virtual void source_stopped (alure::Source source) = 0;
|
||||
inline void
|
||||
sourceStopped (alure::Source source) noexcept override
|
||||
{
|
||||
source_stopped (source);
|
||||
}
|
||||
|
||||
virtual void source_force_stopped (alure::Source source) = 0;
|
||||
inline void
|
||||
sourceForceStopped (alure::Source source) noexcept override
|
||||
{
|
||||
source_force_stopped (source);
|
||||
}
|
||||
|
||||
virtual void buffer_loading (std::string name, std::string channel_config,
|
||||
std::string sample_type, unsigned sample_rate,
|
||||
std::vector<signed char> data) = 0;
|
||||
inline void
|
||||
bufferLoading (alure::StringView name, alure::ChannelConfig channels,
|
||||
alure::SampleType type, ALuint samplerate,
|
||||
alure::ArrayView<ALbyte> data) noexcept override
|
||||
{
|
||||
std::vector<signed char> std_data (data.size());
|
||||
// FIXME: This defeats the entire point of alure::ArrayView.
|
||||
std::copy (data.begin(), data.end(), std_data.begin());
|
||||
buffer_loading (name.data(), alure::GetChannelConfigName (channels),
|
||||
alure::GetSampleTypeName (type), samplerate, std_data);
|
||||
}
|
||||
|
||||
virtual std::string resource_not_found (std::string name) = 0;
|
||||
inline alure::String
|
||||
resourceNotFound (alure::StringView name) noexcept override
|
||||
{
|
||||
return resource_not_found (name.data());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace palace
|
||||
|
||||
#endif // PALACE_BASES_H
|
30
src/bases.pxd
Normal file
30
src/bases.pxd
Normal file
|
@ -0,0 +1,30 @@
|
|||
# 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)
|
|
@ -31,7 +31,8 @@ device_name_default : Dict[str, str]
|
|||
__all__ = ['ALC_FALSE', 'ALC_TRUE', 'ALC_HRTF_SOFT', 'ALC_HRTF_ID_SOFT',
|
||||
'device_name_default', 'device_names',
|
||||
'query_extension', 'use_context',
|
||||
'Device', 'Context', 'Buffer', 'Source', 'SourceGroup', 'Decoder']
|
||||
'Device', 'Context', 'Buffer', 'Source', 'SourceGroup',
|
||||
'AuxiliaryEffectSlot', 'Decoder', 'MessageHandler']
|
||||
|
||||
|
||||
from types import TracebackType
|
||||
|
@ -40,9 +41,12 @@ from warnings import warn
|
|||
|
||||
from libcpp cimport bool as boolean, nullptr
|
||||
from libcpp.memory cimport shared_ptr
|
||||
from libcpp.string cimport string
|
||||
from libcpp.utility cimport pair
|
||||
from libcpp.vector cimport vector
|
||||
|
||||
from std cimport milliseconds
|
||||
from bases cimport BaseMessageHandler
|
||||
cimport alure
|
||||
|
||||
# Type aliases
|
||||
|
@ -128,8 +132,9 @@ cdef class Device:
|
|||
|
||||
Parameters
|
||||
----------
|
||||
name : str, optional
|
||||
The name of the playback device.
|
||||
name : Optional[str], optional
|
||||
The name of the playback device. If it is `None`,
|
||||
the object is left uninitialized.
|
||||
fail_safe : bool, optional
|
||||
On failure, fallback to the default device if this is `True`,
|
||||
otherwise `RuntimeError` is raised. Default to `False`.
|
||||
|
@ -152,7 +157,9 @@ cdef class Device:
|
|||
"""
|
||||
cdef alure.Device impl
|
||||
|
||||
def __init__(self, name: str = '', fail_safe: bool = False) -> None:
|
||||
def __init__(self, name: Optional[str] = '',
|
||||
fail_safe: bool = False) -> None:
|
||||
if name is None: return
|
||||
try:
|
||||
self.impl = devmgr.open_playback(name)
|
||||
except RuntimeError as exc:
|
||||
|
@ -229,7 +236,7 @@ cdef class Device:
|
|||
def efx_version(self) -> Tuple[int, int]:
|
||||
"""EFX version supported by this device.
|
||||
|
||||
If the ALC_EXT_EFX extension is unsupported,
|
||||
If the `ALC_EXT_EFX` extension is unsupported,
|
||||
this will be `(0, 0)`.
|
||||
"""
|
||||
cdef alure.Version version = self.impl.get_efx_version()
|
||||
|
@ -244,7 +251,7 @@ cdef class Device:
|
|||
def max_auxiliary_sends(self) -> int:
|
||||
"""Maximum number of auxiliary source sends.
|
||||
|
||||
If ALC_EXT_EFX is unsupported, this will be 0.
|
||||
If `ALC_EXT_EFX` is unsupported, this will be 0.
|
||||
"""
|
||||
return self.impl.get_max_auxiliary_sends()
|
||||
|
||||
|
@ -252,7 +259,7 @@ cdef class Device:
|
|||
def hrtf_names(self) -> List[str]:
|
||||
"""List of available HRTF names, sorted as OpenAL gives them,
|
||||
such that the index of a given name is the ID to use with
|
||||
ALC_HRTF_ID_SOFT.
|
||||
`ALC_HRTF_ID_SOFT`.
|
||||
|
||||
If the `ALC_SOFT_HRTF` extension is unavailable,
|
||||
this will be an empty list.
|
||||
|
@ -351,6 +358,10 @@ cdef class Context:
|
|||
----------
|
||||
device : Device
|
||||
The device this context was created from.
|
||||
listener : Listener
|
||||
The listener instance of this context.
|
||||
message_handler : MessageHandler
|
||||
Handler of some certain events.
|
||||
|
||||
Raises
|
||||
------
|
||||
|
@ -360,11 +371,15 @@ cdef class Context:
|
|||
cdef alure.Context impl
|
||||
cdef readonly Device device
|
||||
cdef readonly Listener listener
|
||||
cdef public MessageHandler message_handler
|
||||
|
||||
def __init__(self, device: Device, attrs: Dict[int, int] = {}) -> None:
|
||||
self.impl = device.impl.create_context(mkattrs(attrs.items()))
|
||||
self.device = device
|
||||
self.listener = Listener(self)
|
||||
self.message_handler = MessageHandler()
|
||||
self.impl.set_message_handler(
|
||||
shared_ptr[alure.MessageHandler](new CppMessageHandler(self)))
|
||||
|
||||
def __enter__(self) -> Context:
|
||||
use_context(self)
|
||||
|
@ -493,6 +508,11 @@ cdef class Buffer:
|
|||
Audio file or resource name. Multiple calls with the same name
|
||||
will return the same buffer.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name : str
|
||||
Audio file or resource name.
|
||||
|
||||
Raises
|
||||
------
|
||||
RuntimeError
|
||||
|
@ -667,7 +687,7 @@ cdef class Source:
|
|||
which should be called regularly (30 to 50 times per second)
|
||||
for the fading to be smooth.
|
||||
"""
|
||||
self.impl.fade_out_to_stop(gain, alure.milliseconds(ms))
|
||||
self.impl.fade_out_to_stop(gain, milliseconds(ms))
|
||||
|
||||
def pause(self) -> None:
|
||||
"""Pause the source if it is playing."""
|
||||
|
@ -1482,3 +1502,114 @@ cdef class Decoder:
|
|||
if source is None: source = Source(self.context)
|
||||
(<Source> source).impl.play(self.pimpl, chunk_len, queue_size)
|
||||
return source
|
||||
|
||||
|
||||
cdef class MessageHandler:
|
||||
"""Message handler interface.
|
||||
|
||||
Applications may derive from this and set an instance on a context
|
||||
to receive messages. The base methods are no-ops, so subclasses
|
||||
only need to implement methods for relevant messages.
|
||||
"""
|
||||
def device_disconnected(self, device: Device) -> None:
|
||||
"""Handle disconnected device messages.
|
||||
|
||||
This is called when the given device has been disconnected and
|
||||
is no longer usable for output. As per the `ALC_EXT_disconnect`
|
||||
specification, disconnected devices remain valid, however all
|
||||
playing sources are automatically stopped, any sources that are
|
||||
attempted to play will immediately stop, and new contexts may
|
||||
not be created on the device.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Connection status is checked during `Context.update` calls, so
|
||||
method must be called regularly to be notified when a device is
|
||||
disconnected. This method may not be called if the device lacks
|
||||
support for the `ALC_EXT_disconnect` extension.
|
||||
"""
|
||||
|
||||
def source_stopped(self, source: Source) -> None:
|
||||
"""Handle end-of-buffer/stream messages.
|
||||
|
||||
This is called when the given source reaches the end of buffer
|
||||
or stream, which is detected upon a call to `Context.update`.
|
||||
"""
|
||||
|
||||
def source_force_stopped(self, source: Source) -> None:
|
||||
"""Handle forcefully stopped sources.
|
||||
|
||||
This is called when the given source was forced to stop,
|
||||
because of one of the following reasons:
|
||||
|
||||
* There were no more mixing sources and a higher-priority source
|
||||
preempted it.
|
||||
* `source` is part of a `SourceGroup` (or sub-group thereof)
|
||||
that had its `SourceGroup.stop_all` method called.
|
||||
* `source` was playing a buffer that's getting removed.
|
||||
"""
|
||||
|
||||
def buffer_loading(self, name: str, channel_config: str, sample_type: str,
|
||||
sample_rate: int, data: List[int]) -> None:
|
||||
"""Handle messages from Buffer initialization.
|
||||
|
||||
This is called when a new buffer is about to be created
|
||||
and loaded. which may be called asynchronously for buffers
|
||||
being loaded asynchronously.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Resource name passed to `Buffer`.
|
||||
channel_config : str
|
||||
Channel configuration of the given audio data.
|
||||
sample_type : str
|
||||
Sample type of the given audio data.
|
||||
sample_rate : int
|
||||
Sample rate of the given audio data.
|
||||
data : List[int]
|
||||
The audio data that is about to be fed to the OpenAL buffer.
|
||||
"""
|
||||
|
||||
def resource_not_found(self, name: str) -> str:
|
||||
"""Return the fallback resource for the one of the given name.
|
||||
|
||||
This is called when `name` is not found, allowing substitution
|
||||
of a different resource until the returned string either points
|
||||
to a valid resource or is empty (default).
|
||||
|
||||
For buffers being cached, the original name will still be used
|
||||
for the cache entry so one does not have to keep track of
|
||||
substituted resource names.
|
||||
"""
|
||||
return ''
|
||||
|
||||
|
||||
cdef cppclass CppMessageHandler(BaseMessageHandler):
|
||||
Context context
|
||||
|
||||
CppMessageHandler(Context ctx):
|
||||
this.context = ctx # Will this be garbage collected?
|
||||
|
||||
void device_disconnected(alure.Device alure_device):
|
||||
cdef Device device = Device(None)
|
||||
device.impl = alure_device
|
||||
context.message_handler.device_disconnected(device)
|
||||
|
||||
void source_stopped(alure.Source alure_source):
|
||||
cdef Source source = Source(None)
|
||||
source.impl = alure_source
|
||||
context.message_handler.source_stopped(source)
|
||||
|
||||
void source_force_stopped(alure.Source alure_source):
|
||||
cdef Source source = Source(None)
|
||||
source.impl = alure_source
|
||||
context.message_handler.source_force_stopped(source)
|
||||
|
||||
void buffer_loading(string name, string channel_config, string sample_type,
|
||||
unsigned sample_rate, vector[signed char] data):
|
||||
context.message_handler.buffer_loading(name, channel_config,
|
||||
sample_type, sample_rate, data)
|
||||
|
||||
string resource_not_found(string name):
|
||||
return context.message_handler.resource_not_found(name)
|
43
src/std.pxd
Normal file
43
src/std.pxd
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Cython declarations of some missing C++ standard libraries
|
||||
# Copyright (C) 2019, 2020 Nguyễn Gia Phong
|
||||
# Copyright (C) 2020 Ngô Ngọc Đức Huy
|
||||
#
|
||||
# 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 libc.stdint cimport int64_t
|
||||
|
||||
|
||||
cdef extern from '<chrono>' namespace 'std::chrono' nogil:
|
||||
cdef cppclass duration[Rep, Period=*]:
|
||||
ctypedef Rep rep
|
||||
duration() except +
|
||||
duration(const rep&) except + # ugly hack, see cython/cython#3198
|
||||
rep count() except +
|
||||
|
||||
ctypedef duration[int64_t, nano] nanoseconds
|
||||
ctypedef duration[int64_t, milli] milliseconds
|
||||
|
||||
|
||||
cdef extern from '<future>' namespace 'std' nogil:
|
||||
cdef cppclass shared_future[R]:
|
||||
pass
|
||||
|
||||
|
||||
cdef extern from '<ratio>' namespace 'std' nogil:
|
||||
cdef cppclass nano:
|
||||
pass
|
||||
cdef cppclass milli:
|
||||
pass
|
Loading…
Reference in a new issue