Add design principles

This commit is contained in:
Nguyễn Gia Phong 2020-03-24 15:50:13 +07:00
parent 92f2454b96
commit a8e039a9f0
24 changed files with 1240 additions and 302 deletions

BIN
doctrees/design.doctree Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

@ -1,7 +1,5 @@
Reference
=========
.. toctree::
.. automodule:: palace
:members:

View File

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

View File

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

286
html/design.html Normal file
View File

@ -0,0 +1,286 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Design Principles &#8212; 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">&#39;foobar.h&#39;</span> <span class="n">namespace</span> <span class="s">&#39;foobar&#39;</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">-&gt;</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">-&gt;</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">&#64;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">&#64;getter</span></code> and <code class="docutils literal notranslate"><span class="pre">&#64;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, its 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">&#39;&#39;</span><span class="p">)</span> <span class="o">-&gt;</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">-&gt;</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">-&gt;</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">-&gt;</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 well 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">-&gt;</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">-&gt;</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">&#64;getter</span></code>/<code class="docutils literal notranslate"><span class="pre">&#64;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">
&copy;2019, 2020 Nguyễn Gia Phong et al.
|
Powered by <a href="http://sphinx-doc.org/">Sphinx 3.0.0+/4e6f8bc</a>
&amp; <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>

View File

@ -5,7 +5,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Index &#8212; palace 0.0.12 documentation</title>
<title>Index &#8212; 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 @@
&copy;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>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Welcome to palaces documentation! &#8212; palace 0.0.12 documentation</title>
<title>Welcome to our palace! &#8212; 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 palaces 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 @@
&copy;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>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Installation &#8212; palace 0.0.12 documentation</title>
<title>Installation &#8212; 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 palaces 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 palaces 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/
&copy;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>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
|

Binary file not shown.

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Python Module Index &#8212; palace 0.0.12 documentation</title>
<title>Python Module Index &#8212; 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 @@
&copy;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>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>Search &#8212; palace 0.0.12 documentation</title>
<title>Search &#8212; 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 @@
&copy;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>
&amp; <a href="https://github.com/bitprophet/alabaster">Alabaster 0.7.12</a>
</div>

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
palace
sphinx
sphinx >= 3.*

View File

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

207
src/design.rst Normal file
View File

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

View File

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

View File

@ -1,7 +1,5 @@
Reference
=========
.. toctree::
.. automodule:: palace
:members: