mirror of https://github.com/McSinyx/palace
Add design principles
This commit is contained in:
parent
92f2454b96
commit
a8e039a9f0
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
# Sphinx build info version 1
|
||||
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
|
||||
config: 9128f906c827214aa6231ad483342bf2
|
||||
config: 5938d50d60be4fc83e2c379fb163f4ee
|
||||
tags: 645f666f9bcd5a90fca523b33c5a78b7
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
Design Principles
|
||||
=================
|
||||
|
||||
In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API alure_.
|
||||
|
||||
This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.
|
||||
|
||||
.. _alure: https://github.com/kcat/alure
|
||||
|
||||
Project Overview
|
||||
----------------
|
||||
|
||||
Before diving into the design, here is a brief overview of the functionalities
|
||||
provided by palace:
|
||||
|
||||
#. Audio device creation and auxiliary functionalities:
|
||||
:py:const:`palace.device_names`, :py:func:`palace.query_extension`
|
||||
and :py:class:`palace.Device`
|
||||
#. Context creation and management: :py:func:`palace.current_context`,
|
||||
:py:func:`palace.use_context`, :py:class:`palace.Context`,
|
||||
:py:class:`palace.Listener` and :py:class:`palace.MessageHandler`
|
||||
#. Creation and caching of internal audio decoders and user-defined ones:
|
||||
:py:class:`palace.Decoder`, :py:class:`palace.BaseDecoder`,
|
||||
:py:data:`palace.decoder_factories`, :py:class:`palace.FileIO`,
|
||||
:py:func:`palace.current_fileio`, :py:func:`palace.use_fileio`
|
||||
and :py:class:`palace.Buffer`
|
||||
#. Source of audio playback: :py:class:`palace.Source`
|
||||
and :py:class:`palace.SourceGroup`
|
||||
#. Audio effect: :py:class:`palace.AuxiliaryEffectSlot`
|
||||
and :py:class:`palace.Effect`
|
||||
|
||||
.. _impl-idiom:
|
||||
|
||||
The Impl Idiom
|
||||
--------------
|
||||
|
||||
*Not to be confused with* `the pimpl idiom`_.
|
||||
|
||||
For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef extern from 'foobar.h' namespace 'foobar':
|
||||
cdef cppclass Foo:
|
||||
Foo()
|
||||
float meth(size_t crack) except +
|
||||
...
|
||||
|
||||
The Cython extension type can then be declared as follows
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Bar:
|
||||
cdef Foo impl
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.impl = ...
|
||||
|
||||
@staticmethod
|
||||
def from_baz(baz: Baz) -> Bar:
|
||||
bar = Bar.__new__(Bar)
|
||||
bar.impl = ...
|
||||
return bar
|
||||
|
||||
def meth(self, crack: int) -> float:
|
||||
return self.impl.meth(crack)
|
||||
|
||||
.. _`the pimpl idiom`: https://wiki.c2.com/?PimplIdiom
|
||||
|
||||
The Modern Python
|
||||
-----------------
|
||||
|
||||
One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.
|
||||
|
||||
.. _getter-setter:
|
||||
|
||||
Property Attributes
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use property_ to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.
|
||||
|
||||
Due to implementation details, Cython has to hijack the ``@property`` decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to ``property`` as follows
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
getter = property
|
||||
setter = lambda fset: property(fset=fset, doc=fset.__doc__)
|
||||
|
||||
Then ``@getter`` and ``@setter`` can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.
|
||||
|
||||
.. _property: https://docs.python.org/3/library/functions.html#property
|
||||
|
||||
Context Managers
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote *The Zen of Python*,
|
||||
|
||||
| If the implementation is hard to explain, it's a bad idea.
|
||||
| If the implementation is easy to explain, it may be a good idea.
|
||||
|
||||
With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Device:
|
||||
cdef alure.Device impl
|
||||
|
||||
def __init__(self, name: str = '') -> None:
|
||||
self.impl = devmgr.open_playback(name)
|
||||
|
||||
def __enter__(self) -> Device:
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> Optional[bool]:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
self.impl.close()
|
||||
|
||||
Now if the ``with`` statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with Device() as dev:
|
||||
...
|
||||
|
||||
as it is equivalent to
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dev = Device()
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
dev.close()
|
||||
|
||||
Other than closure/destruction of objects, typical uses of `context managers`__
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
:py:class:`palace.Context`), locking and unlocking resources, etc.
|
||||
|
||||
__ https://docs.python.org/3/reference/datamodel.html#context-managers
|
||||
|
||||
The Double Reference
|
||||
--------------------
|
||||
|
||||
While wrapping C++ interfaces, :ref:`the impl idiom <impl-idiom>` might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we'll need to handle the reference count ourselves, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef cppclass CppDecoder(alure.BaseDecoder):
|
||||
Decoder pyo
|
||||
|
||||
__init__(Decoder decoder):
|
||||
this.pyo = decoder
|
||||
Py_INCREF(pyo)
|
||||
|
||||
__dealloc__():
|
||||
Py_DECREF(pyo)
|
||||
|
||||
bool seek(uint64_t pos):
|
||||
return pyo.seek(pos)
|
||||
|
||||
With this being done, we can now write the wrapper as simply as
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class BaseDecoder:
|
||||
cdef shared_ptr[alure.Decoder] pimpl
|
||||
|
||||
def __cinit__(self, *args, **kwargs) -> None:
|
||||
self.pimpl = shared_ptr[alure.Decoder](new CppDecoder(self))
|
||||
|
||||
def seek(pos: int) -> bool:
|
||||
...
|
||||
|
||||
Because ``__cinit__`` is called by ``__new__``, any Python class derived
|
||||
from ``BaseDecoder`` will be exposed to C++ as an attribute of ``CppDecoder``.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.
|
||||
|
||||
In practice, :py:class:`palace.BaseDecoder` will also need to take into account
|
||||
other guarding mechanisms like :py:class:`abc.ABC`. Due to Cython limitations,
|
||||
implementation as a pure Python class and :ref:`aliasing <getter-setter>` of
|
||||
``@getter``/``@setter`` should be considered.
|
|
@ -1,16 +1,12 @@
|
|||
.. palace documentation master file, created by
|
||||
sphinx-quickstart on Sun Jan 5 19:56:04 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to palace's documentation!
|
||||
==================================
|
||||
Welcome to our palace!
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
installation
|
||||
design
|
||||
reference
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
.. automodule:: palace
|
||||
:members:
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
|
||||
VERSION: '0.0.12',
|
||||
VERSION: '0.1.0',
|
||||
LANGUAGE: 'None',
|
||||
COLLAPSE_INDEX: false,
|
||||
BUILDER: 'html',
|
||||
FILE_SUFFIX: '.html',
|
||||
LINK_SUFFIX: '.html',
|
||||
HAS_SOURCE: true,
|
||||
SOURCELINK_SUFFIX: '.txt',
|
||||
NAVIGATION_WITH_KEYS: false
|
||||
|
|
|
@ -251,6 +251,7 @@ var Search = {
|
|||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
var requestUrl = "";
|
||||
var linkUrl = "";
|
||||
if (DOCUMENTATION_OPTIONS.BUILDER === 'dirhtml') {
|
||||
// dirhtml builder
|
||||
var dirname = item[0] + '/';
|
||||
|
@ -260,13 +261,15 @@ var Search = {
|
|||
dirname = '';
|
||||
}
|
||||
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + dirname;
|
||||
linkUrl = requestUrl;
|
||||
|
||||
} else {
|
||||
// normal html builders
|
||||
requestUrl = DOCUMENTATION_OPTIONS.URL_ROOT + item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX;
|
||||
linkUrl = item[0] + DOCUMENTATION_OPTIONS.LINK_SUFFIX;
|
||||
}
|
||||
listItem.append($('<a/>').attr('href',
|
||||
requestUrl +
|
||||
linkUrl +
|
||||
highlightstring + item[2]).html(item[1]));
|
||||
if (item[3]) {
|
||||
listItem.append($('<span> (' + item[3] + ')</span>'));
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Design Principles — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
<script src="_static/jquery.js"></script>
|
||||
<script src="_static/underscore.js"></script>
|
||||
<script src="_static/doctools.js"></script>
|
||||
<script src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Reference" href="reference.html" />
|
||||
<link rel="prev" title="Installation" href="installation.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.9, maximum-scale=0.9" />
|
||||
|
||||
</head><body>
|
||||
|
||||
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
|
||||
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="design-principles">
|
||||
<h1>Design Principles<a class="headerlink" href="#design-principles" title="Permalink to this headline">¶</a></h1>
|
||||
<p>In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API <a class="reference external" href="https://github.com/kcat/alure">alure</a>.</p>
|
||||
<p>This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.</p>
|
||||
<div class="section" id="project-overview">
|
||||
<h2>Project Overview<a class="headerlink" href="#project-overview" title="Permalink to this headline">¶</a></h2>
|
||||
<p>Before diving into the design, here is a brief overview of the functionalities
|
||||
provided by palace:</p>
|
||||
<ol class="arabic simple">
|
||||
<li><p>Audio device creation and auxiliary functionalities:
|
||||
<a class="reference internal" href="reference.html#palace.device_names" title="palace.device_names"><code class="xref py py-const docutils literal notranslate"><span class="pre">palace.device_names</span></code></a>, <a class="reference internal" href="reference.html#palace.query_extension" title="palace.query_extension"><code class="xref py py-func docutils literal notranslate"><span class="pre">palace.query_extension()</span></code></a>
|
||||
and <a class="reference internal" href="reference.html#palace.Device" title="palace.Device"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Device</span></code></a></p></li>
|
||||
<li><p>Context creation and management: <a class="reference internal" href="reference.html#palace.current_context" title="palace.current_context"><code class="xref py py-func docutils literal notranslate"><span class="pre">palace.current_context()</span></code></a>,
|
||||
<a class="reference internal" href="reference.html#palace.use_context" title="palace.use_context"><code class="xref py py-func docutils literal notranslate"><span class="pre">palace.use_context()</span></code></a>, <a class="reference internal" href="reference.html#palace.Context" title="palace.Context"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Context</span></code></a>,
|
||||
<a class="reference internal" href="reference.html#palace.Listener" title="palace.Listener"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Listener</span></code></a> and <a class="reference internal" href="reference.html#palace.MessageHandler" title="palace.MessageHandler"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.MessageHandler</span></code></a></p></li>
|
||||
<li><p>Creation and caching of internal audio decoders and user-defined ones:
|
||||
<a class="reference internal" href="reference.html#palace.Decoder" title="palace.Decoder"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Decoder</span></code></a>, <a class="reference internal" href="reference.html#palace.BaseDecoder" title="palace.BaseDecoder"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.BaseDecoder</span></code></a>,
|
||||
<a class="reference internal" href="reference.html#palace.decoder_factories" title="palace.decoder_factories"><code class="xref py py-data docutils literal notranslate"><span class="pre">palace.decoder_factories</span></code></a>, <a class="reference internal" href="reference.html#palace.FileIO" title="palace.FileIO"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.FileIO</span></code></a>,
|
||||
<a class="reference internal" href="reference.html#palace.current_fileio" title="palace.current_fileio"><code class="xref py py-func docutils literal notranslate"><span class="pre">palace.current_fileio()</span></code></a>, <a class="reference internal" href="reference.html#palace.use_fileio" title="palace.use_fileio"><code class="xref py py-func docutils literal notranslate"><span class="pre">palace.use_fileio()</span></code></a>
|
||||
and <a class="reference internal" href="reference.html#palace.Buffer" title="palace.Buffer"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Buffer</span></code></a></p></li>
|
||||
<li><p>Source of audio playback: <a class="reference internal" href="reference.html#palace.Source" title="palace.Source"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Source</span></code></a>
|
||||
and <a class="reference internal" href="reference.html#palace.SourceGroup" title="palace.SourceGroup"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.SourceGroup</span></code></a></p></li>
|
||||
<li><p>Audio effect: <a class="reference internal" href="reference.html#palace.AuxiliaryEffectSlot" title="palace.AuxiliaryEffectSlot"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.AuxiliaryEffectSlot</span></code></a>
|
||||
and <a class="reference internal" href="reference.html#palace.Effect" title="palace.Effect"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Effect</span></code></a></p></li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="section" id="the-impl-idiom">
|
||||
<span id="impl-idiom"></span><h2>The Impl Idiom<a class="headerlink" href="#the-impl-idiom" title="Permalink to this headline">¶</a></h2>
|
||||
<p><em>Not to be confused with</em> <a class="reference external" href="https://wiki.c2.com/?PimplIdiom">the pimpl idiom</a>.</p>
|
||||
<p>For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kr">extern</span> <span class="k">from</span> <span class="s">'foobar.h'</span> <span class="n">namespace</span> <span class="s">'foobar'</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">Foo</span><span class="p">:</span>
|
||||
<span class="n">Foo</span><span class="p">()</span>
|
||||
<span class="nb">float</span> <span class="n">meth</span><span class="p">(</span><span class="n">size_t</span> <span class="n">crack</span><span class="p">)</span> <span class="k">except</span> <span class="o">+</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The Cython extension type can then be declared as follows</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Bar</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">Foo</span> <span class="nf">impl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||||
|
||||
<span class="nd">@staticmethod</span>
|
||||
<span class="k">def</span> <span class="nf">from_baz</span><span class="p">(</span><span class="n">baz</span><span class="p">:</span> <span class="n">Baz</span><span class="p">)</span> <span class="o">-></span> <span class="n">Bar</span><span class="p">:</span>
|
||||
<span class="n">bar</span> <span class="o">=</span> <span class="n">Bar</span><span class="o">.</span><span class="n">__new__</span><span class="p">(</span><span class="n">Bar</span><span class="p">)</span>
|
||||
<span class="n">bar</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="o">...</span>
|
||||
<span class="k">return</span> <span class="n">bar</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">meth</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">crack</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">meth</span><span class="p">(</span><span class="n">crack</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-modern-python">
|
||||
<h2>The Modern Python<a class="headerlink" href="#the-modern-python" title="Permalink to this headline">¶</a></h2>
|
||||
<p>One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.</p>
|
||||
<div class="section" id="property-attributes">
|
||||
<span id="getter-setter"></span><h3>Property Attributes<a class="headerlink" href="#property-attributes" title="Permalink to this headline">¶</a></h3>
|
||||
<p>A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use <a class="reference external" href="https://docs.python.org/3/library/functions.html#property">property</a> to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.</p>
|
||||
<p>Due to implementation details, Cython has to hijack the <code class="docutils literal notranslate"><span class="pre">@property</span></code> decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to <code class="docutils literal notranslate"><span class="pre">property</span></code> as follows</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">getter</span> <span class="o">=</span> <span class="nb">property</span>
|
||||
<span class="n">setter</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">fset</span><span class="p">:</span> <span class="nb">property</span><span class="p">(</span><span class="n">fset</span><span class="o">=</span><span class="n">fset</span><span class="p">,</span> <span class="n">doc</span><span class="o">=</span><span class="n">fset</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Then <code class="docutils literal notranslate"><span class="pre">@getter</span></code> and <code class="docutils literal notranslate"><span class="pre">@setter</span></code> can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.</p>
|
||||
</div>
|
||||
<div class="section" id="context-managers">
|
||||
<h3>Context Managers<a class="headerlink" href="#context-managers" title="Permalink to this headline">¶</a></h3>
|
||||
<p>The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote <em>The Zen of Python</em>,</p>
|
||||
<blockquote>
|
||||
<div><div class="line-block">
|
||||
<div class="line">If the implementation is hard to explain, it’s a bad idea.</div>
|
||||
<div class="line">If the implementation is easy to explain, it may be a good idea.</div>
|
||||
</div>
|
||||
</div></blockquote>
|
||||
<p>With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">Device</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">alure</span>.<span class="kt">Device</span> <span class="nf">impl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">''</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span> <span class="o">=</span> <span class="n">devmgr</span><span class="o">.</span><span class="n">open_playback</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__enter__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="n">Device</span><span class="p">:</span>
|
||||
<span class="k">return</span> <span class="bp">self</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__exit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">exc</span><span class="p">)</span> <span class="o">-></span> <span class="n">Optional</span><span class="p">[</span><span class="nb">bool</span><span class="p">]:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">close</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">impl</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Now if the <code class="docutils literal notranslate"><span class="pre">with</span></code> statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="k">with</span> <span class="n">Device</span><span class="p">()</span> <span class="k">as</span> <span class="n">dev</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>as it is equivalent to</p>
|
||||
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">dev</span> <span class="o">=</span> <span class="n">Device</span><span class="p">()</span>
|
||||
<span class="k">try</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
<span class="k">finally</span><span class="p">:</span>
|
||||
<span class="n">dev</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Other than closure/destruction of objects, typical uses of <a class="reference external" href="https://docs.python.org/3/reference/datamodel.html#context-managers">context managers</a>
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
<a class="reference internal" href="reference.html#palace.Context" title="palace.Context"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.Context</span></code></a>), locking and unlocking resources, etc.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="the-double-reference">
|
||||
<h2>The Double Reference<a class="headerlink" href="#the-double-reference" title="Permalink to this headline">¶</a></h2>
|
||||
<p>While wrapping C++ interfaces, <a class="reference internal" href="#impl-idiom"><span class="std std-ref">the impl idiom</span></a> might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we’ll need to handle the reference count ourselves, e.g.</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="kt">cppclass</span> <span class="nf">CppDecoder</span><span class="p">(</span><span class="n">alure</span><span class="o">.</span><span class="n">BaseDecoder</span><span class="p">):</span>
|
||||
<span class="n">Decoder</span> <span class="n">pyo</span>
|
||||
|
||||
<span class="n">__init__</span><span class="p">(</span><span class="n">Decoder</span> <span class="n">decoder</span><span class="p">):</span>
|
||||
<span class="n">this</span><span class="o">.</span><span class="n">pyo</span> <span class="o">=</span> <span class="n">decoder</span>
|
||||
<span class="n">Py_INCREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||||
|
||||
<span class="n">__dealloc__</span><span class="p">():</span>
|
||||
<span class="n">Py_DECREF</span><span class="p">(</span><span class="n">pyo</span><span class="p">)</span>
|
||||
|
||||
<span class="nb">bool</span> <span class="n">seek</span><span class="p">(</span><span class="n">uint64_t</span> <span class="n">pos</span><span class="p">):</span>
|
||||
<span class="k">return</span> <span class="n">pyo</span><span class="o">.</span><span class="n">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>With this being done, we can now write the wrapper as simply as</p>
|
||||
<div class="highlight-cython notranslate"><div class="highlight"><pre><span></span><span class="k">cdef</span> <span class="k">class</span> <span class="nf">BaseDecoder</span><span class="p">:</span>
|
||||
<span class="k">cdef</span> <span class="kt">shared_ptr</span>[<span class="kt">alure</span>.<span class="kt">Decoder</span>] <span class="nf">pimpl</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">__cinit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="bp">None</span><span class="p">:</span>
|
||||
<span class="bp">self</span><span class="o">.</span><span class="n">pimpl</span> <span class="o">=</span> <span class="n">shared_ptr</span><span class="p">[</span><span class="n">alure</span><span class="o">.</span><span class="n">Decoder</span><span class="p">](</span><span class="n">new</span> <span class="n">CppDecoder</span><span class="p">(</span><span class="bp">self</span><span class="p">))</span>
|
||||
|
||||
<span class="k">def</span> <span class="nf">seek</span><span class="p">(</span><span class="n">pos</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||||
<span class="o">...</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>Because <code class="docutils literal notranslate"><span class="pre">__cinit__</span></code> is called by <code class="docutils literal notranslate"><span class="pre">__new__</span></code>, any Python class derived
|
||||
from <code class="docutils literal notranslate"><span class="pre">BaseDecoder</span></code> will be exposed to C++ as an attribute of <code class="docutils literal notranslate"><span class="pre">CppDecoder</span></code>.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.</p>
|
||||
<p>In practice, <a class="reference internal" href="reference.html#palace.BaseDecoder" title="palace.BaseDecoder"><code class="xref py py-class docutils literal notranslate"><span class="pre">palace.BaseDecoder</span></code></a> will also need to take into account
|
||||
other guarding mechanisms like <code class="xref py py-class docutils literal notranslate"><span class="pre">abc.ABC</span></code>. Due to Cython limitations,
|
||||
implementation as a pure Python class and <a class="reference internal" href="#getter-setter"><span class="std std-ref">aliasing</span></a> of
|
||||
<code class="docutils literal notranslate"><span class="pre">@getter</span></code>/<code class="docutils literal notranslate"><span class="pre">@setter</span></code> should be considered.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h1 class="logo"><a href="index.html">palace</a></h1>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<h3>Navigation</h3>
|
||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul class="current">
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1 current"><a class="current reference internal" href="#">Design Principles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#project-overview">Project Overview</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-impl-idiom">The Impl Idiom</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-modern-python">The Modern Python</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#the-double-reference">The Double Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="relations">
|
||||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="installation.html" title="previous chapter">Installation</a></li>
|
||||
<li>Next: <a href="reference.html" title="next chapter">Reference</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="searchbox" style="display: none" role="search">
|
||||
<h3 id="searchlabel">Quick search</h3>
|
||||
<div class="searchformwrapper">
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" aria-labelledby="searchlabel" />
|
||||
<input type="submit" value="Go" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>$('#searchbox').show(0);</script>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
<a href="_sources/design.rst.txt"
|
||||
rel="nofollow">Page source</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -5,7 +5,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Index — palace 0.0.12 documentation</title>
|
||||
<title>Index — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
|
@ -52,6 +52,7 @@
|
|||
| <a href="#Q"><strong>Q</strong></a>
|
||||
| <a href="#R"><strong>R</strong></a>
|
||||
| <a href="#S"><strong>S</strong></a>
|
||||
| <a href="#T"><strong>T</strong></a>
|
||||
| <a href="#U"><strong>U</strong></a>
|
||||
| <a href="#V"><strong>V</strong></a>
|
||||
|
||||
|
@ -68,6 +69,8 @@
|
|||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.auxiliary_send">auxiliary_send() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.auxiliary_send_filter">auxiliary_send_filter() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.AuxiliaryEffectSlot">AuxiliaryEffectSlot (class in palace)</a>
|
||||
</li>
|
||||
|
@ -113,10 +116,12 @@
|
|||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.FileIO.close">close (palace.FileIO attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Device.close">close() (palace.Device method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#palace.FileIO.close">(palace.FileIO method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.Source.cone_angles">cone_angles (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Context">Context (class in palace)</a>
|
||||
|
@ -162,6 +167,10 @@
|
|||
<li><a href="reference.html#palace.MessageHandler.device_disconnected">device_disconnected() (palace.MessageHandler method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.device_names">device_names (in module palace)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.direct_filter">direct_filter() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Context.distance_model">distance_model() (palace.Context property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.distance_range">distance_range (palace.Source attribute)</a>
|
||||
</li>
|
||||
|
@ -224,7 +233,11 @@
|
|||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.AuxiliaryEffectSlot.gain">gain() (palace.AuxiliaryEffectSlot property)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#palace.Listener.gain">(palace.Listener property)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.gain_auto">gain_auto (palace.Source attribute)</a>
|
||||
|
@ -283,6 +296,8 @@
|
|||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Listener">Listener (class in palace)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Context.listener">listener (palace.Context attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Buffer.loop_points">loop_points (palace.Buffer attribute)</a>
|
||||
|
@ -305,12 +320,21 @@
|
|||
</li>
|
||||
<li><a href="reference.html#palace.Device.max_auxiliary_sends">max_auxiliary_sends() (palace.Device property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Context.message_handler">message_handler (palace.Context attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.MessageHandler">MessageHandler (class in palace)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Listener.meters_per_unit">meters_per_unit() (palace.Listener property)</a>
|
||||
</li>
|
||||
<li>
|
||||
module
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#module-palace">palace</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.MONO_SOURCES">MONO_SOURCES (in module palace)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
|
@ -334,10 +358,12 @@
|
|||
<li><a href="reference.html#palace.Source.offset">offset (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.offset_seconds">offset_seconds() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.orientation">orientation (palace.Source attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.orientation">orientation (palace.Source attribute)</a>
|
||||
<li><a href="reference.html#palace.Listener.orientation">orientation() (palace.Listener property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.outer_cone_gains">outer_cone_gains (palace.Source attribute)</a>
|
||||
</li>
|
||||
|
@ -349,8 +375,13 @@
|
|||
<h2 id="P">P</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#module-palace">palace (module)</a>
|
||||
<li>
|
||||
palace
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#module-palace">module</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.SourceGroup.parent_group">parent_group (palace.SourceGroup attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.pause">pause() (palace.Source method)</a>
|
||||
|
@ -363,14 +394,14 @@
|
|||
</li>
|
||||
<li><a href="reference.html#palace.Source.pending">pending() (palace.Source property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.pitch">pitch (palace.Source attribute)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#palace.SourceGroup.pitch">(palace.SourceGroup attribute)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Buffer.play">play() (palace.Buffer method)</a>
|
||||
|
||||
<ul>
|
||||
|
@ -382,6 +413,10 @@
|
|||
<li><a href="reference.html#palace.Source.playing_or_pending">playing_or_pending() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.position">position (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Listener.position">position() (palace.Listener property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Context.precache_buffers_async">precache_buffers_async() (palace.Context method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.priority">priority (palace.Source attribute)</a>
|
||||
</li>
|
||||
|
@ -405,22 +440,22 @@
|
|||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.radius">radius (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.BaseDecoder.read">read (palace.BaseDecoder attribute)</a>
|
||||
<li><a href="reference.html#palace.BaseDecoder.read">read() (palace.BaseDecoder method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#palace.FileIO.read">(palace.FileIO attribute)</a>
|
||||
<li><a href="reference.html#palace.Decoder.read">(palace.Decoder method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.FileIO.read">(palace.FileIO method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.Decoder.read">read() (palace.Decoder method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.relative">relative (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.resampler_index">resampler_index (palace.Source attribute)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Device.reset">reset() (palace.Device method)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.MessageHandler.resource_not_found">resource_not_found() (palace.MessageHandler method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.resume">resume() (palace.Source method)</a>
|
||||
|
@ -428,6 +463,10 @@
|
|||
<li><a href="reference.html#palace.SourceGroup.resume_all">resume_all() (palace.SourceGroup method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Device.resume_dsp">resume_dsp() (palace.Device method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Effect.reverb_preset">reverb_preset() (palace.Effect property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.reverb_preset_names">reverb_preset_names (in module palace)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Effect.reverb_properties">reverb_properties() (palace.Effect property)</a>
|
||||
</li>
|
||||
|
@ -455,15 +494,17 @@
|
|||
</ul></li>
|
||||
<li><a href="reference.html#palace.sample_types">sample_types (in module palace)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.BaseDecoder.seek">seek (palace.BaseDecoder attribute)</a>
|
||||
<li><a href="reference.html#palace.BaseDecoder.seek">seek() (palace.BaseDecoder method)</a>
|
||||
|
||||
<ul>
|
||||
<li><a href="reference.html#palace.FileIO.seek">(palace.FileIO attribute)</a>
|
||||
<li><a href="reference.html#palace.Decoder.seek">(palace.Decoder method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.FileIO.seek">(palace.FileIO method)</a>
|
||||
</li>
|
||||
</ul></li>
|
||||
<li><a href="reference.html#palace.Decoder.seek">seek() (palace.Decoder method)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.AuxiliaryEffectSlot.send_auto">send_auto() (palace.AuxiliaryEffectSlot property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Source.send_filter">send_filter() (palace.Source property)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Buffer.size">size() (palace.Buffer property)</a>
|
||||
</li>
|
||||
|
@ -508,6 +549,14 @@
|
|||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
<h2 id="T">T</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.thread_local">thread_local() (in module palace)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
||||
<h2 id="U">U</h2>
|
||||
<table style="width: 100%" class="indextable genindextable"><tr>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
|
@ -532,6 +581,8 @@
|
|||
</ul></td>
|
||||
<td style="width: 33%; vertical-align: top;"><ul>
|
||||
<li><a href="reference.html#palace.Source.velocity">velocity (palace.Source attribute)</a>
|
||||
</li>
|
||||
<li><a href="reference.html#palace.Listener.velocity">velocity() (palace.Listener property)</a>
|
||||
</li>
|
||||
</ul></td>
|
||||
</tr></table>
|
||||
|
@ -557,6 +608,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -593,7 +645,7 @@
|
|||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Welcome to palace’s documentation! — palace 0.0.12 documentation</title>
|
||||
<title>Welcome to our palace! — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
|
@ -31,8 +31,8 @@
|
|||
|
||||
<div class="body" role="main">
|
||||
|
||||
<div class="section" id="welcome-to-palace-s-documentation">
|
||||
<h1>Welcome to palace’s documentation!<a class="headerlink" href="#welcome-to-palace-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="section" id="welcome-to-our-palace">
|
||||
<h1>Welcome to our palace!<a class="headerlink" href="#welcome-to-our-palace" title="Permalink to this headline">¶</a></h1>
|
||||
<div class="toctree-wrapper compound">
|
||||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
|
@ -42,9 +42,14 @@
|
|||
<li class="toctree-l2"><a class="reference internal" href="installation.html#from-source">From source</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a><ul class="simple">
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a><ul>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#project-overview">Project Overview</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-impl-idiom">The Impl Idiom</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-modern-python">The Modern Python</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="design.html#the-double-reference">The Double Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -77,6 +82,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -114,7 +120,7 @@
|
|||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Installation — palace 0.0.12 documentation</title>
|
||||
<title>Installation — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
|
@ -14,8 +14,8 @@
|
|||
<script src="_static/language_data.js"></script>
|
||||
<link rel="index" title="Index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="next" title="Reference" href="reference.html" />
|
||||
<link rel="prev" title="Welcome to palace’s documentation!" href="index.html" />
|
||||
<link rel="next" title="Design Principles" href="design.html" />
|
||||
<link rel="prev" title="Welcome to our palace!" href="index.html" />
|
||||
|
||||
<link rel="stylesheet" href="_static/custom.css" type="text/css" />
|
||||
|
||||
|
@ -89,6 +89,7 @@ pip install palace/
|
|||
<li class="toctree-l2"><a class="reference internal" href="#from-source">From source</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -96,8 +97,8 @@ pip install palace/
|
|||
<h3>Related Topics</h3>
|
||||
<ul>
|
||||
<li><a href="index.html">Documentation overview</a><ul>
|
||||
<li>Previous: <a href="index.html" title="previous chapter">Welcome to palace’s documentation!</a></li>
|
||||
<li>Next: <a href="reference.html" title="next chapter">Reference</a></li>
|
||||
<li>Previous: <a href="index.html" title="previous chapter">Welcome to our palace!</a></li>
|
||||
<li>Next: <a href="design.html" title="next chapter">Design Principles</a></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -127,7 +128,7 @@ pip install palace/
|
|||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
|
|
||||
|
|
BIN
html/objects.inv
BIN
html/objects.inv
Binary file not shown.
|
@ -4,7 +4,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Python Module Index — palace 0.0.12 documentation</title>
|
||||
<title>Python Module Index — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
|
||||
|
@ -75,6 +75,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -111,7 +112,7 @@
|
|||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
|||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Search — palace 0.0.12 documentation</title>
|
||||
<title>Search — palace 0.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/alabaster.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
|
||||
|
@ -44,10 +44,8 @@
|
|||
</p>
|
||||
</div>
|
||||
<p>
|
||||
From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing fewer words won't appear in the result list.
|
||||
Searching for multiple words only shows matches that contain
|
||||
all words.
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" aria-labelledby="search-documentation" value="" />
|
||||
|
@ -78,6 +76,7 @@
|
|||
<p class="caption"><span class="caption-text">Contents:</span></p>
|
||||
<ul>
|
||||
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="design.html">Design Principles</a></li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="reference.html">Reference</a></li>
|
||||
</ul>
|
||||
|
||||
|
@ -104,7 +103,7 @@
|
|||
©2019, 2020 Nguyễn Gia Phong et al.
|
||||
|
||||
|
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 2.4.3</a>
|
||||
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
|
||||
& <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|
||||
|
||||
</div>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,2 +1,2 @@
|
|||
palace
|
||||
sphinx
|
||||
sphinx >= 3.*
|
||||
|
|
|
@ -22,7 +22,7 @@ copyright = '2019, 2020 Nguyễn Gia Phong et al'
|
|||
author = 'Nguyễn Gia Phong et al.'
|
||||
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = '0.0.12'
|
||||
release = '0.1.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
Design Principles
|
||||
=================
|
||||
|
||||
In this section, we will discuss a few design principles in order to write
|
||||
a safe, efficient, easy-to-use and extendable 3D audio library for Python,
|
||||
by wrapping existing functionalities from the C++ API alure_.
|
||||
|
||||
This part of the documentation assumes its reader are at least familiar with
|
||||
Cython, Python and C++11.
|
||||
|
||||
.. _alure: https://github.com/kcat/alure
|
||||
|
||||
Project Overview
|
||||
----------------
|
||||
|
||||
Before diving into the design, here is a brief overview of the functionalities
|
||||
provided by palace:
|
||||
|
||||
#. Audio device creation and auxiliary functionalities:
|
||||
:py:const:`palace.device_names`, :py:func:`palace.query_extension`
|
||||
and :py:class:`palace.Device`
|
||||
#. Context creation and management: :py:func:`palace.current_context`,
|
||||
:py:func:`palace.use_context`, :py:class:`palace.Context`,
|
||||
:py:class:`palace.Listener` and :py:class:`palace.MessageHandler`
|
||||
#. Creation and caching of internal audio decoders and user-defined ones:
|
||||
:py:class:`palace.Decoder`, :py:class:`palace.BaseDecoder`,
|
||||
:py:data:`palace.decoder_factories`, :py:class:`palace.FileIO`,
|
||||
:py:func:`palace.current_fileio`, :py:func:`palace.use_fileio`
|
||||
and :py:class:`palace.Buffer`
|
||||
#. Source of audio playback: :py:class:`palace.Source`
|
||||
and :py:class:`palace.SourceGroup`
|
||||
#. Audio effect: :py:class:`palace.AuxiliaryEffectSlot`
|
||||
and :py:class:`palace.Effect`
|
||||
|
||||
.. _impl-idiom:
|
||||
|
||||
The Impl Idiom
|
||||
--------------
|
||||
|
||||
*Not to be confused with* `the pimpl idiom`_.
|
||||
|
||||
For memory-safety, whenever possible, we rely on Cython for allocation and
|
||||
deallocation of C++ objects. To do this, the nullary constructor needs to be
|
||||
(re-)declared in Cython, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef extern from 'foobar.h' namespace 'foobar':
|
||||
cdef cppclass Foo:
|
||||
Foo()
|
||||
float meth(size_t crack) except +
|
||||
...
|
||||
|
||||
The Cython extension type can then be declared as follows
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Bar:
|
||||
cdef Foo impl
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.impl = ...
|
||||
|
||||
@staticmethod
|
||||
def from_baz(baz: Baz) -> Bar:
|
||||
bar = Bar.__new__(Bar)
|
||||
bar.impl = ...
|
||||
return bar
|
||||
|
||||
def meth(self, crack: int) -> float:
|
||||
return self.impl.meth(crack)
|
||||
|
||||
.. _`the pimpl idiom`: https://wiki.c2.com/?PimplIdiom
|
||||
|
||||
The Modern Python
|
||||
-----------------
|
||||
|
||||
One of the goal of palace is to create a Pythonic, i.e. intuitive and concise,
|
||||
interface. To achieve this, we try to make use of some modern Python features,
|
||||
which not only allow users to adopt palace with ease, but also make their
|
||||
programs more readable and less error-prone.
|
||||
|
||||
.. _getter-setter:
|
||||
|
||||
Property Attributes
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
A large proportion of alure API are getters/setter methods. In Python,
|
||||
it is a good practice to use property_ to abstract these calls, and thus make
|
||||
the interface more natural with attribute-like referencing and assignments.
|
||||
|
||||
Due to implementation details, Cython has to hijack the ``@property`` decorator
|
||||
to make it work for read-write properties. Unfortunately, the Cython-generated
|
||||
descriptors do not play very well with other builtin decorators, thus in some
|
||||
cases, it is recommended to alias the call to ``property`` as follows
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
getter = property
|
||||
setter = lambda fset: property(fset=fset, doc=fset.__doc__)
|
||||
|
||||
Then ``@getter`` and ``@setter`` can be used to decorate read-only and
|
||||
write-only properties, respectively, without any trouble even if other
|
||||
decorators are used for the same extension type method.
|
||||
|
||||
.. _property: https://docs.python.org/3/library/functions.html#property
|
||||
|
||||
Context Managers
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The alure API defines many objects that need manual tear-down in
|
||||
a particular order. Instead of trying to be clever and perform automatic
|
||||
clean-ups at garbage collection, we should put the user in control.
|
||||
To quote *The Zen of Python*,
|
||||
|
||||
| If the implementation is hard to explain, it's a bad idea.
|
||||
| If the implementation is easy to explain, it may be a good idea.
|
||||
|
||||
With that being said, it does not mean we do not provide any level of
|
||||
abstraction. A simplified case in point would be
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class Device:
|
||||
cdef alure.Device impl
|
||||
|
||||
def __init__(self, name: str = '') -> None:
|
||||
self.impl = devmgr.open_playback(name)
|
||||
|
||||
def __enter__(self) -> Device:
|
||||
return self
|
||||
|
||||
def __exit__(self, *exc) -> Optional[bool]:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
self.impl.close()
|
||||
|
||||
Now if the ``with`` statement is used, it will make sure the device
|
||||
will be closed, regardless of whatever may happen within the inner block
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
with Device() as dev:
|
||||
...
|
||||
|
||||
as it is equivalent to
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dev = Device()
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
dev.close()
|
||||
|
||||
Other than closure/destruction of objects, typical uses of `context managers`__
|
||||
also include saving and restoring various kinds of global state (as seen in
|
||||
:py:class:`palace.Context`), locking and unlocking resources, etc.
|
||||
|
||||
__ https://docs.python.org/3/reference/datamodel.html#context-managers
|
||||
|
||||
The Double Reference
|
||||
--------------------
|
||||
|
||||
While wrapping C++ interfaces, :ref:`the impl idiom <impl-idiom>` might not
|
||||
be adequate, since the derived Python methods need to be callable from C++.
|
||||
Luckily, Cython can handle Python objects within C++ classes just fine,
|
||||
although we'll need to handle the reference count ourselves, e.g.
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef cppclass CppDecoder(alure.BaseDecoder):
|
||||
Decoder pyo
|
||||
|
||||
__init__(Decoder decoder):
|
||||
this.pyo = decoder
|
||||
Py_INCREF(pyo)
|
||||
|
||||
__dealloc__():
|
||||
Py_DECREF(pyo)
|
||||
|
||||
bool seek(uint64_t pos):
|
||||
return pyo.seek(pos)
|
||||
|
||||
With this being done, we can now write the wrapper as simply as
|
||||
|
||||
.. code-block:: cython
|
||||
|
||||
cdef class BaseDecoder:
|
||||
cdef shared_ptr[alure.Decoder] pimpl
|
||||
|
||||
def __cinit__(self, *args, **kwargs) -> None:
|
||||
self.pimpl = shared_ptr[alure.Decoder](new CppDecoder(self))
|
||||
|
||||
def seek(pos: int) -> bool:
|
||||
...
|
||||
|
||||
Because ``__cinit__`` is called by ``__new__``, any Python class derived
|
||||
from ``BaseDecoder`` will be exposed to C++ as an attribute of ``CppDecoder``.
|
||||
Effectively, this means the users can have the alure API calling their
|
||||
inherited Python object as naturally as if palace is implemented in pure Python.
|
||||
|
||||
In practice, :py:class:`palace.BaseDecoder` will also need to take into account
|
||||
other guarding mechanisms like :py:class:`abc.ABC`. Due to Cython limitations,
|
||||
implementation as a pure Python class and :ref:`aliasing <getter-setter>` of
|
||||
``@getter``/``@setter`` should be considered.
|
|
@ -1,16 +1,12 @@
|
|||
.. palace documentation master file, created by
|
||||
sphinx-quickstart on Sun Jan 5 19:56:04 2020.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to palace's documentation!
|
||||
==================================
|
||||
Welcome to our palace!
|
||||
======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:caption: Contents:
|
||||
|
||||
installation
|
||||
design
|
||||
reference
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
Reference
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
|
||||
.. automodule:: palace
|
||||
:members:
|
||||
|
|
Loading…
Reference in New Issue