mirror of https://github.com/McSinyx/palace
Allow falling back on current context
This commit is contained in:
parent
2a3bda152f
commit
49072f101e
|
@ -57,7 +57,7 @@ def play(files: Iterable[str], device: str, hrtf_name: str,
|
||||||
except ValueError:
|
except ValueError:
|
||||||
stderr.write(f'HRTF {hrtf_name!r} not found\n')
|
stderr.write(f'HRTF {hrtf_name!r} not found\n')
|
||||||
|
|
||||||
with Context(dev, attrs) as ctx, Source(ctx) as src:
|
with Context(dev, attrs) as ctx, Source() as src:
|
||||||
if dev.hrtf_enabled:
|
if dev.hrtf_enabled:
|
||||||
print(f'Using HRTF {dev.current_hrtf!r}')
|
print(f'Using HRTF {dev.current_hrtf!r}')
|
||||||
else:
|
else:
|
||||||
|
@ -66,11 +66,11 @@ def play(files: Iterable[str], device: str, hrtf_name: str,
|
||||||
|
|
||||||
for filename in files:
|
for filename in files:
|
||||||
try:
|
try:
|
||||||
decoder = Decoder(ctx, filename)
|
decoder = Decoder(filename)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
stderr.write(f'Failed to open file: {filename}\n')
|
stderr.write(f'Failed to open file: {filename}\n')
|
||||||
continue
|
continue
|
||||||
decoder.play(src, CHUNK_LEN, QUEUE_SIZE)
|
decoder.play(CHUNK_LEN, QUEUE_SIZE, src)
|
||||||
print(f'Playing {filename} ({decoder.sample_type},',
|
print(f'Playing {filename} ({decoder.sample_type},',
|
||||||
f'{decoder.channel_config}, {decoder.frequency} Hz)')
|
f'{decoder.channel_config}, {decoder.frequency} Hz)')
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ def play(files: Iterable[str], device: str) -> None:
|
||||||
ctx.message_handler = LoadingBufferHandler()
|
ctx.message_handler = LoadingBufferHandler()
|
||||||
for filename in files:
|
for filename in files:
|
||||||
try:
|
try:
|
||||||
buffer = Buffer(ctx, filename)
|
buffer = Buffer(filename)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
stderr.write(f'Failed to open file: {filename}\n')
|
stderr.write(f'Failed to open file: {filename}\n')
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -43,11 +43,11 @@ def pretty_time(seconds: float) -> str:
|
||||||
|
|
||||||
def play(files: Iterable[str], device: str) -> None:
|
def play(files: Iterable[str], device: str) -> None:
|
||||||
"""Load and play files on the given device."""
|
"""Load and play files on the given device."""
|
||||||
with Device(device) as dev, Context(dev) as ctx:
|
with Device(device) as dev, Context(dev):
|
||||||
print('Opened', dev.name)
|
print('Opened', dev.name)
|
||||||
for filename in files:
|
for filename in files:
|
||||||
try:
|
try:
|
||||||
buffer = Buffer(ctx, filename)
|
buffer = Buffer(filename)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
stderr.write(f'Failed to open file: {filename}\n')
|
stderr.write(f'Failed to open file: {filename}\n')
|
||||||
continue
|
continue
|
||||||
|
|
150
src/palace.pyx
150
src/palace.pyx
|
@ -71,7 +71,7 @@ __all__ = [
|
||||||
'sample_types', 'channel_configs', 'device_names', 'decoder_factories',
|
'sample_types', 'channel_configs', 'device_names', 'decoder_factories',
|
||||||
'sample_size', 'sample_length', 'query_extension', 'thread_local',
|
'sample_size', 'sample_length', 'query_extension', 'thread_local',
|
||||||
'current_context', 'use_context', 'current_fileio', 'use_fileio',
|
'current_context', 'use_context', 'current_fileio', 'use_fileio',
|
||||||
'Device', 'Context', 'Buffer', 'Source', 'SourceGroup',
|
'Device', 'Context', 'Listener', 'Buffer', 'Source', 'SourceGroup',
|
||||||
'AuxiliaryEffectSlot', 'Effect', 'Decoder', 'BaseDecoder', 'FileIO',
|
'AuxiliaryEffectSlot', 'Effect', 'Decoder', 'BaseDecoder', 'FileIO',
|
||||||
'MessageHandler']
|
'MessageHandler']
|
||||||
|
|
||||||
|
@ -723,18 +723,28 @@ cdef class Context:
|
||||||
|
|
||||||
|
|
||||||
cdef class Listener:
|
cdef class Listener:
|
||||||
"""Listener instance of the context, i.e each context
|
"""Listener instance of the given context.
|
||||||
will only have one listener.
|
|
||||||
|
It is recommended that application access the listener via
|
||||||
|
`Context.listener`, which avoid the overhead caused by the
|
||||||
|
creation of the wrapper object.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
context : Optional[Context], optional
|
||||||
The `context` on which the listener instance is to be created.
|
The context on which the listener instance is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is neither any context specified nor current.
|
||||||
"""
|
"""
|
||||||
cdef alure.Listener impl
|
cdef alure.Listener impl
|
||||||
|
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Optional[Context] = None) -> None:
|
||||||
self.impl = context.impl.get_listener()
|
if context is None: context = current_context()
|
||||||
|
self.impl = (<Context> context).impl.get_listener()
|
||||||
|
|
||||||
def __bool__(self) -> bool:
|
def __bool__(self) -> bool:
|
||||||
return <boolean> self.impl
|
return <boolean> self.impl
|
||||||
|
@ -797,11 +807,12 @@ cdef class Buffer:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
|
||||||
The context from which the buffer is to be created and cached.
|
|
||||||
name : str
|
name : str
|
||||||
Audio file or resource name. Multiple calls with the same name
|
Audio file or resource name. Multiple calls with the same name
|
||||||
will return the same buffer.
|
will return the same buffer.
|
||||||
|
context : Optional[Context], optional
|
||||||
|
The context from which the buffer is to be created and cached.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
|
@ -811,20 +822,20 @@ cdef class Buffer:
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
RuntimeError
|
RuntimeError
|
||||||
If the buffer can neither be loaded
|
If there is neither any context specified nor current.
|
||||||
nor be found when `existed` is set.
|
|
||||||
"""
|
"""
|
||||||
cdef alure.Buffer impl
|
cdef alure.Buffer impl
|
||||||
cdef Context context
|
cdef Context context
|
||||||
cdef readonly str name
|
cdef readonly str name
|
||||||
|
|
||||||
def __init__(self, context: Context, name: str,
|
def __init__(self, name: str, context: Optional[Context] = None) -> None:
|
||||||
existed: bool = False) -> None:
|
if context is None: context = current_context()
|
||||||
self.impl = context.impl.find_buffer(name)
|
|
||||||
if not self:
|
|
||||||
decoder: Decoder = Decoder.smart(context, name)
|
|
||||||
self.impl = context.impl.create_buffer_from(name, decoder.pimpl)
|
|
||||||
self.context, self.name = context, name
|
self.context, self.name = context, name
|
||||||
|
self.impl = self.context.impl.find_buffer(self.name)
|
||||||
|
if not self:
|
||||||
|
decoder: Decoder = Decoder.smart(self.name, self.context)
|
||||||
|
self.impl = self.context.impl.create_buffer_from(
|
||||||
|
self.name, decoder.pimpl)
|
||||||
|
|
||||||
def __enter__(self) -> Buffer:
|
def __enter__(self) -> Buffer:
|
||||||
return self
|
return self
|
||||||
|
@ -869,27 +880,32 @@ cdef class Buffer:
|
||||||
return f'{self.__class__.__name__}({self.name!r})'
|
return f'{self.__class__.__name__}({self.name!r})'
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_decoder(decoder: Decoder, context: Context, name: str) -> Buffer:
|
def from_decoder(decoder: Decoder, name: str,
|
||||||
|
context: Optional[Context] = None) -> Buffer:
|
||||||
"""Return a buffer created by reading the given decoder.
|
"""Return a buffer created by reading the given decoder.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
decoder : Decoder
|
decoder : Decoder
|
||||||
The decoder from which the buffer is to be cached.
|
The decoder from which the buffer is to be cached.
|
||||||
context : Context
|
|
||||||
The context from which the buffer is to be created.
|
|
||||||
name : str
|
name : str
|
||||||
The name to give to the buffer. It may alias an audio file,
|
The name to give to the buffer. It may alias an audio file,
|
||||||
but it must not currently exist in the buffer cache.
|
but it must not currently exist in the buffer cache.
|
||||||
|
context : Optional[Context], optional
|
||||||
|
The context from which the buffer is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
RuntimeError
|
RuntimeError
|
||||||
If the buffer cannot be created.
|
If there is neither any context specified nor current;
|
||||||
|
or if `name` is already used for another buffer.
|
||||||
"""
|
"""
|
||||||
|
if context is None: context = current_context()
|
||||||
buffer: Buffer = Buffer.__new__(Buffer)
|
buffer: Buffer = Buffer.__new__(Buffer)
|
||||||
buffer.impl = context.impl.create_buffer_from(name, decoder.pimpl)
|
|
||||||
buffer.context, buffer.name = context, name
|
buffer.context, buffer.name = context, name
|
||||||
|
buffer.impl = buffer.context.impl.create_buffer_from(
|
||||||
|
buffer.name, decoder.pimpl)
|
||||||
return buffer
|
return buffer
|
||||||
|
|
||||||
@getter
|
@getter
|
||||||
|
@ -1009,13 +1025,20 @@ cdef class Source:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
context : Optional[Context], optional
|
||||||
The context from which the source is to be created.
|
The context from which the source is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is neither any context specified nor current.
|
||||||
"""
|
"""
|
||||||
cdef alure.Source impl
|
cdef alure.Source impl
|
||||||
|
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Optional[Context] = None) -> None:
|
||||||
self.impl = context.impl.create_source()
|
if context is None: context = current_context()
|
||||||
|
self.impl = (<Context> context).impl.create_source()
|
||||||
|
|
||||||
def __enter__(self) -> Source:
|
def __enter__(self) -> Source:
|
||||||
return self
|
return self
|
||||||
|
@ -1586,13 +1609,20 @@ cdef class SourceGroup:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
context : Optional[Context], optional
|
||||||
The context from which the source group is to be created.
|
The context from which the source group is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is neither any context specified nor current.
|
||||||
"""
|
"""
|
||||||
cdef alure.SourceGroup impl
|
cdef alure.SourceGroup impl
|
||||||
|
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Optional[Context] = None) -> None:
|
||||||
self.impl = context.impl.create_source_group()
|
if context is None: context = current_context()
|
||||||
|
self.impl = (<Context> context).impl.create_source_group()
|
||||||
|
|
||||||
def __enter__(self) -> SourceGroup:
|
def __enter__(self) -> SourceGroup:
|
||||||
return self
|
return self
|
||||||
|
@ -1730,18 +1760,20 @@ cdef class AuxiliaryEffectSlot:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
context : Optional[Context], optional
|
||||||
The context to create the auxiliary effect slot.
|
The context to create the auxiliary effect slot.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
RuntimeError
|
RuntimeError
|
||||||
If the effect slot can't be created.
|
If there is neither any context specified nor current.
|
||||||
"""
|
"""
|
||||||
cdef alure.AuxiliaryEffectSlot impl
|
cdef alure.AuxiliaryEffectSlot impl
|
||||||
|
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Optional[Context] = None) -> None:
|
||||||
self.impl = context.impl.create_auxiliary_effect_slot()
|
if context is None: context = current_context()
|
||||||
|
self.impl = (<Context> context).impl.create_auxiliary_effect_slot()
|
||||||
|
|
||||||
def __enter__(self) -> AuxiliaryEffectSlot:
|
def __enter__(self) -> AuxiliaryEffectSlot:
|
||||||
return self
|
return self
|
||||||
|
@ -1843,13 +1875,20 @@ cdef class Effect:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
context : Optional[Context], optional
|
||||||
The context from which the effect is to be created.
|
The context from which the effect is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is neither any context specified nor current.
|
||||||
"""
|
"""
|
||||||
cdef alure.Effect impl
|
cdef alure.Effect impl
|
||||||
|
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Optional[Context] = None) -> None:
|
||||||
self.impl = context.impl.create_effect()
|
if context is None: context = current_context()
|
||||||
|
self.impl = (<Context> context).impl.create_effect()
|
||||||
|
|
||||||
def __enter__(self) -> Effect:
|
def __enter__(self) -> Effect:
|
||||||
return self
|
return self
|
||||||
|
@ -1952,15 +1991,16 @@ cdef class Decoder:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
context : Context
|
|
||||||
The context from which the decoder is to be created.
|
|
||||||
name : str
|
name : str
|
||||||
Audio file or resource name.
|
Audio file or resource name.
|
||||||
|
context : Optional[Context], optional
|
||||||
|
The context from which the decoder is to be created.
|
||||||
|
By default `current_context()` is used.
|
||||||
|
|
||||||
Raises
|
Raises
|
||||||
------
|
------
|
||||||
RuntimeError
|
RuntimeError
|
||||||
If decoder creation fails.
|
If there is neither any context specified nor current.
|
||||||
|
|
||||||
See Also
|
See Also
|
||||||
--------
|
--------
|
||||||
|
@ -1976,15 +2016,25 @@ cdef class Decoder:
|
||||||
"""
|
"""
|
||||||
cdef shared_ptr[alure.Decoder] pimpl
|
cdef shared_ptr[alure.Decoder] pimpl
|
||||||
|
|
||||||
def __init__(self, context: Context, name: str) -> None:
|
def __init__(self, name: str, context: Optional[Context] = None) -> None:
|
||||||
self.pimpl = context.impl.create_decoder(name)
|
if context is None: context = current_context()
|
||||||
|
self.pimpl = (<Context> context).impl.create_decoder(name)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def smart(context: Context, name: str) -> Decoder:
|
def smart(name: str, context: Optional[Context] = None) -> Decoder:
|
||||||
"""Return the decoder created from the given resource name.
|
"""Return the decoder created from the given resource name.
|
||||||
|
|
||||||
This first tries user-registered decoder factories in
|
This first tries user-registered decoder factories in
|
||||||
lexicographical order, then fallback to the internal ones.
|
lexicographical order, then fallback to the internal ones.
|
||||||
|
|
||||||
|
Raises
|
||||||
|
------
|
||||||
|
RuntimeError
|
||||||
|
If there is neither any context specified nor current.
|
||||||
|
|
||||||
|
See Also
|
||||||
|
--------
|
||||||
|
decoder_factories : Simple object for storing decoder factories
|
||||||
"""
|
"""
|
||||||
def find_resource(name, subst):
|
def find_resource(name, subst):
|
||||||
if not name: raise RuntimeError('Failed to open file')
|
if not name: raise RuntimeError('Failed to open file')
|
||||||
|
@ -1996,6 +2046,7 @@ cdef class Decoder:
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return find_resource(subst(name), subst)
|
return find_resource(subst(name), subst)
|
||||||
|
|
||||||
|
if context is None: context = current_context()
|
||||||
resource = find_resource(
|
resource = find_resource(
|
||||||
name, context.message_handler.resource_not_found)
|
name, context.message_handler.resource_not_found)
|
||||||
for decoder_factory in decoder_factories:
|
for decoder_factory in decoder_factories:
|
||||||
|
@ -2004,7 +2055,7 @@ cdef class Decoder:
|
||||||
return decoder_factory(resource)
|
return decoder_factory(resource)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
continue
|
continue
|
||||||
return Decoder(context, name)
|
return Decoder(name, context)
|
||||||
|
|
||||||
@getter
|
@getter
|
||||||
def frequency(self) -> int:
|
def frequency(self) -> int:
|
||||||
|
@ -2089,7 +2140,8 @@ cdef class Decoder:
|
||||||
PyMem_RawFree(ptr)
|
PyMem_RawFree(ptr)
|
||||||
return samples
|
return samples
|
||||||
|
|
||||||
def play(self, source: Source, chunk_len: int, queue_size: int) -> None:
|
def play(self, chunk_len: int, queue_size: int,
|
||||||
|
source: Optional[Source] = None) -> Source:
|
||||||
"""Stream audio asynchronously from the decoder.
|
"""Stream audio asynchronously from the decoder.
|
||||||
|
|
||||||
The decoder must NOT have its `read` or `seek` called
|
The decoder must NOT have its `read` or `seek` called
|
||||||
|
@ -2097,8 +2149,6 @@ cdef class Decoder:
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
source : Source
|
|
||||||
The source object to play audio.
|
|
||||||
chunk_len : int
|
chunk_len : int
|
||||||
The number of sample frames to read for each chunk update.
|
The number of sample frames to read for each chunk update.
|
||||||
Smaller values will require more frequent updates and
|
Smaller values will require more frequent updates and
|
||||||
|
@ -2107,8 +2157,16 @@ cdef class Decoder:
|
||||||
The number of chunks to keep queued during playback.
|
The number of chunks to keep queued during playback.
|
||||||
Smaller values use less memory while larger values
|
Smaller values use less memory while larger values
|
||||||
improve protection against underruns.
|
improve protection against underruns.
|
||||||
|
source : Optional[Source], optional
|
||||||
|
The source object to play audio. If `None` is given,
|
||||||
|
a new one will be created from the current context.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
The source used for playing.
|
||||||
"""
|
"""
|
||||||
source.impl.play(self.pimpl, chunk_len, queue_size)
|
if source is None: source = Source()
|
||||||
|
(<Source> source).impl.play(self.pimpl, chunk_len, queue_size)
|
||||||
|
|
||||||
|
|
||||||
# Decoder interface
|
# Decoder interface
|
||||||
|
|
Loading…
Reference in New Issue