--- /dev/null
+__import__('pkg_resources').declare_namespace(__name__)
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Mon Sep 10 07:00 PM 2007 J
+"""
+audiolab: a small toolbox to read, write and play audio to and from
+numpy arrays.
+
+audiolab provides two API:
+ - one similar to matlab: this gives you wavread, wavwrite functions really
+ similar to matlab's functions.
+ - a more complete API, which can be used to read, write to many audio file
+ (including wav, aiff, flac, au, IRCAM, htk, etc...), with IO capabilities
+ not available to matlab (seek, append data, etc...)
+
+It is a thin wrapper around libsndfile from Erik Castro Lopo.
+
+Copyright (C) 2006-2007 Cournapeau David <cournape@gmail.com>
+
+LICENSE: audiolab is licensed under the LGPL, as is libsndfile itself. See
+COPYING.txt for details. """
+
+from info import VERSION
+__version__ = VERSION
+
+from pysndfile import formatinfo, sndfile
+from pysndfile import supported_format, supported_endianness, \
+ supported_encoding
+#from scikits.audiolab.matapi import wavread, aiffread, flacread, auread, \
+# sdifread, wavwrite, aiffwrite, flacwrite, auwrite, sdifwrite
+from matapi import *
+
+__all__ = filter(lambda s:not s.startswith('_'),dir())
+
+from numpy.testing import NumpyTest
+def test():
+ return NumpyTest().test()
--- /dev/null
+py2tex = PYTHONPATH=/home/david/local/lib/python2.5/site-packages pygmentize -l python -f tex
+rst2tex = PYTHONPATH=/home/david/local/lib/python2.5/site-packages rst2newlatex.py \
+ --stylesheet-path base.tex --user-stylesheet user.tex
+
+pytexfiles = audiolab.tex quick1.tex \
+ usage1.tex \
+ usage2.tex \
+ format1.tex \
+ format2.tex \
+ write1.tex \
+ matlab1.tex
+
+SOURCEPATH = $(PWD)
+
+EXTTOCLEAN=.chk .dvi .log .aux .bbl .blg .blig .ilg .toc .lof .lot .idx .ind .out .bak .ps .pdf .bm
+
+audiolab.pdf: $(pytexfiles)
+ pdflatex $<
+ pdflatex $<
+ pdflatex $<
+
+audiolab.tex: index.txt
+ $(rst2tex) $< > $@
+
+quick1.tex: examples/quick1.py
+ $(py2tex) $< > $@
+
+usage1.tex: examples/usage1.py
+ $(py2tex) $< > $@
+
+usage2.tex: examples/usage2.py
+ $(py2tex) $< > $@
+
+format1.tex: examples/format1.py
+ $(py2tex) $< > $@
+
+format2.tex: examples/format2.py
+ $(py2tex) $< > $@
+
+write1.tex: examples/write1.py
+ $(py2tex) $< > $@
+
+matlab1.tex: examples/matlab1.py
+ $(py2tex) $< > $@
+
+clean:
+ for i in $(pytexfiles); do \
+ rm -f `echo $$i`; \
+ done;
+ for i in $(SOURCEPATH); do \
+ for j in $(EXTTOCLEAN); do \
+ rm -f `echo $$i/*$$j`; \
+ done; \
+ done;
--- /dev/null
+% System stylesheet for the new LaTeX writer, newlatex2e.
+
+% Major parts of the rendering are done in this stylesheet and not in the
+% Python module.
+
+% For development notes, see notes.txt.
+
+% User documentation (in the stylesheet for now; that may change though):
+
+% Naming conventions:
+% All uppercase letters in macro names have a specific meaning.
+% \D...: All macros introduced by the Docutils LaTeX writer start with "D".
+% \DS<name>: Setup function (called at the bottom of this stylesheet).
+% \DN<nodename>{<contents>}: Handler for Docutils document tree node `node`; called by
+% the Python module.
+% \DEV<name>: External variable, set by the Python module.
+% \DEC<name>: External command. It is called by the Python module and must be
+% defined in this stylesheet.
+% \DN<nodename>A<attribute>{<number>}{<attribute>}{<value>}{<nodename>}{<contents>}:
+% Attribute handler for `attribute` set on nodes of type `nodename`.
+% See below for a discussion of attribute handlers.
+% \DA<attribute>{<number>}{<attribute>}{<value>}{<nodename>}{<contents>}:
+% Attribute handler for all `attribute`. Called only when no specific
+% \DN<nodename>A<attribute> handler is defined.
+% \DN<nodename>C<class>{<contents>}:
+% Handler for `class`, when set on nodes of type `nodename`.
+% \DC<class>{<contents>}:
+% Handler for `class`. Called only when no specific \DN<nodename>C<class>
+% handler is defined.
+% \D<name>: Generic variable or function.
+
+% Attribute handlers:
+% TODO
+
+% ---------------------------------------------------------------------------
+
+% Having to intersperse code with \makeatletter-\makeatother pairs is very
+% annoying, so we call \makeatletter at the top and \makeatother at the
+% bottom. Just be aware that you cannot use "@" as a text character inside
+% this stylesheet.
+\makeatletter
+
+% Print-mode (as opposed to online mode e.g. with Adobe Reader).
+% This causes for example blue hyperlinks.
+\providecommand{\Dprinting}{false}
+
+% \DSearly is called right after \documentclass.
+\providecommand{\DSearly}{}
+% \DSlate is called at the end of the stylesheet (right before the document
+% tree).
+\providecommand{\DSlate}{}
+
+% Use the KOMA script article class.
+\providecommand{\Ddocumentclass}{scrartcl}
+\providecommand{\Ddocumentoptions}{a4paper}
+\providecommand{\DSdocumentclass}{
+ \documentclass[\Ddocumentoptions]{\Ddocumentclass} }
+
+% Todo: This should be movable to the bottom, but it isn't as long as
+% we use \usepackage commands at the top level of this stylesheet
+% (which we shouldn't).
+\DSdocumentclass
+
+\providecommand{\DSpackages}{
+ % Load miscellaneous packages.
+ % Note 1: Many of the packages loaded here are used throughout this stylesheet.
+ % If one of these packages does not work on your system or in your scenario,
+ % please let us know, so we can consider making the package optional.
+ % Note 2: It would appear cleaner to load packages where they are used.
+ % However, since using a wrong package loading order can lead to *very*
+ % subtle bugs, we centralize the loading of most packages here.
+ \DSfontencoding % load font encoding packages
+ \DSlanguage % load babel
+ % Using \ifthenelse conditionals.
+ \usepackage{ifthen} % before hyperref (really!)
+ % There is not support for *not* using hyperref because it's used in many
+ % places. If this is a problem (e.g. because hyperref doesn't work on your
+ % system), please let us know.
+ \usepackage[colorlinks=false,pdfborder={0 0 0}]{hyperref}
+ % Get color, e.g. for links and system messages.
+ \usepackage{color}
+ % Get \textnhtt macro (non-hyphenating type writer).
+ \usepackage{hyphenat}
+ % For sidebars.
+ \usepackage{picins}
+ % We use longtable to create tables.
+ \usepackage{longtable}
+ % Images.
+ \usepackage{graphicx}
+ % These packages might be useful (some just add magic pixie dust), so
+ % evaluate them:
+ %\usepackage{fixmath}
+ %\usepackage{amsmath}
+ % Add some missing symbols like \textonehalf.
+ \usepackage{textcomp}
+}
+
+\providecommand{\DSfontencoding}{
+ % Set up font encoding. Called by \DSpackages.
+ % AE is a T1 emulation. It provides mostly the same characters and
+ % features as T1-encoded fonts but doesn't use bitmap fonts (which are
+ % unsuitable for online reading and subtle for printers).
+ \usepackage{ae}
+ % Provide the characters not contained in AE from EC bitmap fonts.
+ \usepackage{aecompl}
+ % Guillemets ("<<", ">>") in AE.
+ \usepackage{aeguill}
+}
+
+\providecommand{\DSsymbols}{%
+ % Fix up symbols.
+ % The Euro symbol in Computer Modern looks, um, funny. Let's get a
+ % proper Euro symbol.
+ \usepackage{eurosym}%
+ \renewcommand{\texteuro}{\euro}%
+}
+
+% Taken from
+% <http://groups.google.de/groups?selm=1i0n5tgtplti420e1omp4pctlv19jpuhbb%404ax.com>
+% and modified. Used with permission.
+\providecommand{\Dprovidelength}[2]{%
+ \begingroup%
+ \escapechar\m@ne%
+ \xdef\@gtempa{{\string#1}}%
+ \endgroup%
+ \expandafter\@ifundefined\@gtempa%
+ {\newlength{#1}\setlength{#1}{#2}}%
+ {}%
+}
+
+\providecommand{\Dprovidecounter}[2]{%
+ % Like \newcounter except that it doesn't crash if the counter
+ % already exists.
+ \@ifundefined{c@#1}{\newcounter{#1}\setcounter{#1}{#2}}{}
+}
+
+\Dprovidelength{\Dboxparindent}{\parindent}
+
+\providecommand{\Dmakebox}[1]{%
+ % Make a centered, frameless box. Useful e.g. for block quotes.
+ % Do not use minipages here, but create pseudo-lists to allow
+ % page-breaking. (Don't use KOMA-script's addmargin environment
+ % because it messes up bullet lists.)
+ \Dmakelistenvironment{}{}{%
+ \setlength{\parskip}{0pt}%
+ \setlength{\parindent}{\Dboxparindent}%
+ \item{#1}%
+ }%
+}
+
+\providecommand{\Dmakefbox}[1]{%
+ % Make a centered, framed box. Useful e.g. for admonitions.
+ \vspace{0.4\baselineskip}%
+ \begin{center}%
+ \fbox{%
+ \begin{minipage}[t]{0.9\linewidth}%
+ \setlength{\parindent}{\Dboxparindent}%
+ #1%
+ \end{minipage}%
+ }%
+ \end{center}%
+ \vspace{0.4\baselineskip}%
+}
+
+% We do not currently recognize the difference between an end-sentence and a
+% mid-sentence period (". " vs. ". " in plain text). So \frenchspacing is
+% appropriate.
+\providecommand{\DSfrenchspacing}{\frenchspacing}
+
+
+\Dprovidelength{\Dblocklevelvspace}{%
+ % Space between block-level elements other than paragraphs.
+ 0.7\baselineskip plus 0.3\baselineskip minus 0.2\baselineskip%
+}
+\providecommand{\DECauxiliaryspace}{%
+ \ifthenelse{\equal{\Dneedvspace}{true}}{\vspace{\Dblocklevelvspace}}{}%
+ \par\noindent%
+}
+\providecommand{\DECparagraphspace}{\par}
+\providecommand{\Dneedvspace}{true}
+
+\providecommand{\DSlanguage}{%
+ % Set up babel.
+ \usepackage[\DEVlanguagebabel]{babel}
+}
+
+\providecommand{\Difdefined}[3]{\@ifundefined{#1}{#3}{#2}}
+
+% Handler for 'classes' attribute (called for each class attribute).
+\providecommand{\DAclasses}[5]{%
+ % Dispatch to \DN<nodename>C<class>.
+ \Difdefined{DN#4C#3}{%
+ % Pass only contents, nothing else!
+ \csname DN#4C#3\endcsname{#5}%
+ }{%
+ % Otherwise, dispatch to \DC<class>.
+ \Difdefined{DC#3}{%
+ \csname DC#3\endcsname{#5}%
+ }{%
+ #5%
+ }%
+ }%
+}
+
+\providecommand{\DECattr}[5]{%
+ % Global attribute dispatcher, called inside the document tree.
+ % Parameters:
+ % 1. Attribute number.
+ % 2. Attribute name.
+ % 3. Attribute value.
+ % 4. Node name.
+ % 5. Node contents.
+ \Difdefined{DN#4A#2}{%
+ % Dispatch to \DN<nodename>A<attribute>.
+ \csname DN#4A#2\endcsname{#1}{#2}{#3}{#4}{#5}%
+ }{\Difdefined{DA#2}{%
+ % Otherwise dispatch to \DA<attribute>.
+ \csname DA#2\endcsname{#1}{#2}{#3}{#4}{#5}%
+ }{%
+ % Otherwise simply run the contents without calling a handler.
+ #5%
+ }}%
+}
+
+% ---------- Link handling ----------
+% Targets and references.
+
+\providecommand{\Draisedlink}[1]{%
+ % Anchors are placed on the base line by default. This is a bad thing for
+ % inline context, so we raise the anchor (normally by \baselineskip).
+ \Hy@raisedlink{#1}%
+}
+
+% References.
+% We're assuming here that the "refid" and "refuri" attributes occur
+% only in inline context (in TextElements).
+\providecommand{\DArefid}[5]{%
+ \ifthenelse{\equal{#4}{reference}}{%
+ \Dexplicitreference{\##3}{#5}%
+ }{%
+ % If this is not a target node (targets with refids are
+ % uninteresting and should be silently dropped).
+ \ifthenelse{\not\equal{#4}{target}}{%
+ % If this is a footnote reference, call special macro.
+ \ifthenelse{\equal{#4}{footnotereference}}{%
+ \Dimplicitfootnotereference{\##3}{#5}%
+ }{%
+ \ifthenelse{\equal{#4}{citationreference}}{%
+ \Dimplicitcitationreference{\##3}{#5}%
+ }{%
+ \Dimplicitreference{\##3}{#5}%
+ }%
+ }%
+ }{}%
+ }%
+}
+\providecommand{\DArefuri}[5]{%
+ \ifthenelse{\equal{#4}{target}}{%
+ % The node name is 'target', so this is a hyperlink target, like this:
+ % .. _mytarget: URI
+ % Hyperlink targets are ignored because they are invisible.
+ }{%
+ % If a non-target node has a refuri attribute, it must be an explicit URI
+ % reference (i.e. node name is 'reference').
+ \Durireference{#3}{#5}%
+ }%
+}
+% Targets.
+\providecommand{\DAids}[5]{%
+ \label{#3}%
+ \ifthenelse{\equal{#4}{footnotereference}}{%
+ {%
+ \renewcommand{\HyperRaiseLinkDefault}{%
+ % Dirty hack to make backrefs to footnote references work.
+ % For some reason, \baselineskip is 0pt in fn references.
+ 0.5\Doriginalbaselineskip%
+ }%
+ \Draisedlink{\hypertarget{#3}{}}#5%
+ }%
+ }{%
+ \Draisedlink{\hypertarget{#3}{}}#5%
+ }%
+}
+\providecommand{\Dimplicitreference}[2]{%
+ % Create implicit reference to ID. Implicit references occur
+ % e.g. in TOC-backlinks of section titles. Parameters:
+ % 1. Target.
+ % 2. Link text.
+ \href{#1}{#2}%
+}
+\providecommand{\Dimplicitfootnotereference}[2]{%
+ % Ditto, but for the special case of footnotes.
+ % We want them to be rendered like explicit references.
+ \Dexplicitreference{#1}{#2}%
+}
+\providecommand{\Dimplicitcitationreference}[2]{%
+ % Ditto for citation references.
+ \Dimplicitfootnotereference{#1}{#2}%
+}
+\providecommand{\Dcolorexplicitreference}{%
+ \ifthenelse{\equal{\Dprinting}{true}}{\color{black}}{\color{blue}}%
+}
+\providecommand{\Dexplicitreference}[2]{%
+ % Create explicit reference to ID, e.g. created with "foo_".
+ % Parameters:
+ % 1. Target.
+ % 2. Link text.
+ \href{#1}{{\Dcolorexplicitreference#2}}%
+}
+\providecommand{\Dcolorurireference}{\Dcolorexplicitreference}
+\providecommand{\Durireference}[2]{%
+ % Create reference to URI. Parameters:
+ % 1. Target.
+ % 2. Link text.
+ \href{#1}{{\Dcolorurireference#2}}%
+}
+
+\Dprovidecounter{Dpdfbookmarkid}{0}%
+\providecommand{\Dpdfbookmark}[1]{%
+ % Temporarily decrement Desctionlevel counter.
+ \addtocounter{Dsectionlevel}{-1}%
+ %\typeout{\arabic{Dsectionlevel}}%
+ %\typeout{#1}%
+ %\typeout{docutils\roman{Dpdfbookmarkid}}%
+ %\typeout{}%
+ \pdfbookmark[\arabic{Dsectionlevel}]{#1}{docutils\arabic{Dpdfbookmarkid}}%
+ \addtocounter{Dsectionlevel}{1}%
+ \addtocounter{Dpdfbookmarkid}{1}%
+}
+% ---------- End of Link Handling ----------
+
+\providecommand{\DNparagraph}[1]{%
+ \ifthenelse{\equal{\DEVparagraphindented}{true}}{\indent}{\noindent}%
+ #1%
+}
+\providecommand{\Dformatboxtitle}[1]{{\Large\textbf{#1}}}
+\providecommand{\Dformatboxsubtitle}[1]{{\large\textbf{#1}}}
+\providecommand{\Dtopictitle}[1]{%
+ \Difinsidetoc{\vspace{1em}\par}{}%
+ \noindent\Dformatboxtitle{#1}%
+ \ifthenelse{\equal{\DEVhassubtitle}{false}}{\vspace{1em}}{\vspace{0.5em}}%
+ \par%
+}
+\providecommand{\Dadmonitiontitle}[1]{%
+ \Dtopictitle{#1}%
+}
+\providecommand{\Dtopicsubtitle}[1]{%
+ \noindent\Dformatboxsubtitle{#1}%
+ \vspace{1em}%
+ \par%
+}
+\providecommand{\Dsidebartitle}[1]{\Dtopictitle{#1}}
+\providecommand{\Dsidebarsubtitle}[1]{\Dtopicsubtitle{#1}}
+\providecommand{\Ddocumenttitle}[1]{%
+ \begin{center}{\Huge#1}\end{center}%
+ \ifthenelse{\equal{\DEVhassubtitle}{true}}{\vspace{0.1cm}}{\vspace{1cm}}%
+}
+\providecommand{\Ddocumentsubtitle}[1]{%
+ \begin{center}{\huge#1}\end{center}%
+ \vspace{1cm}%
+}
+% Can be overwritten by user stylesheet.
+\providecommand{\Dformatsectiontitle}[1]{#1}
+\providecommand{\Dformatsectionsubtitle}[1]{\Dformatsectiontitle{#1}}
+\providecommand{\Dbookmarksectiontitle}[1]{%
+ % Return text suitable for use in \section*, \subsection*, etc.,
+ % containing a PDF bookmark. Parameter: The title (as node tree).
+ \Draisedlink{\Dpdfbookmark{\DEVtitleastext}}%
+ #1%
+}
+\providecommand{\Dsectiontitlehook}[1]{#1}
+\providecommand{\Dsectiontitle}[1]{%
+ \Dsectiontitlehook{%
+ \Ddispatchsectiontitle{\Dbookmarksectiontitle{\Dformatsectiontitle{#1}}}%
+ }%
+}
+\providecommand{\Ddispatchsectiontitle}[1]{%
+ \@ifundefined{Dsectiontitle\roman{Dsectionlevel}}{%
+ \Ddeepsectiontitle{#1}%
+ }{%
+ \csname Dsectiontitle\roman{Dsectionlevel}\endcsname{#1}%
+ }%
+}
+\providecommand{\Ddispatchsectionsubtitle}[1]{%
+ \Ddispatchsectiontitle{#1}%
+}
+\providecommand{\Dsectiontitlei}[1]{\section*{#1}}
+\providecommand{\Dsectiontitleii}[1]{\subsection*{#1}}
+\providecommand{\Ddeepsectiontitle}[1]{%
+ % Anything below \subsubsection (like \paragraph or \subparagraph)
+ % is useless because it uses the same font. The only way to
+ % (visually) distinguish such deeply nested sections is to use
+ % section numbering.
+ \subsubsection*{#1}%
+}
+\providecommand{\Dsectionsubtitlehook}[1]{#1}
+\Dprovidelength{\Dsectionsubtitleraisedistance}{0.7em}
+\providecommand{\Dsectionsubtitlescaling}{0.85}
+\providecommand{\Dsectionsubtitle}[1]{%
+ \Dsectionsubtitlehook{%
+ % Move the subtitle nearer to the title.
+ \vspace{-\Dsectionsubtitleraisedistance}%
+ % Don't create a PDF bookmark.
+ \Ddispatchsectionsubtitle{%
+ \Dformatsectionsubtitle{\scalebox{\Dsectionsubtitlescaling}{#1}}%
+ }%
+ }%
+}
+\providecommand{\DNtitle}[1]{%
+ % Dispatch to \D<parent>title.
+ \csname D\DEVparent title\endcsname{#1}%
+}
+\providecommand{\DNsubtitle}[1]{%
+ % Dispatch to \D<parent>subtitle.
+ \csname D\DEVparent subtitle\endcsname{#1}%
+}
+
+\providecommand{\DNliteralblock}[1]{%
+ \Dmakelistenvironment{}{%
+ \ifthenelse{\equal{\Dinsidetabular}{true}}{%
+ \setlength{\leftmargin}{0pt}%
+ }{}%
+ \setlength{\rightmargin}{0pt}%
+ }{%
+ \raggedright\item\noindent\nohyphens{\textnhtt{#1\Dfinalstrut}}%
+ }%
+}
+\providecommand{\DNdoctestblock}[1]{\DNliteralblock{#1}}
+\providecommand{\DNliteral}[1]{\textnhtt{#1}}
+\providecommand{\DNemphasis}[1]{\emph{#1}}
+\providecommand{\DNstrong}[1]{\textbf{#1}}
+\providecommand{\DECvisitdocument}{\begin{document}\noindent}
+\providecommand{\DECdepartdocument}{\end{document}}
+\providecommand{\DNtopic}[1]{%
+ \ifthenelse{\equal{\DEVcurrentNtopicAcontents}{1}}{%
+ \addtocounter{Dtoclevel}{1}%
+ \par\noindent%
+ #1%
+ \addtocounter{Dtoclevel}{-1}%
+ }{%
+ \par\noindent%
+ \Dmakebox{#1}%
+ }%
+}
+\providecommand{\DNadmonition}[1]{%
+ \DNtopic{#1}%
+}
+\providecommand{\Dformatrubric}[1]{\textbf{#1}}
+\Dprovidelength{\Dprerubricspace}{0.3em}
+\providecommand{\DNrubric}[1]{%
+ \vspace{\Dprerubricspace}\par\noindent\Dformatrubric{#1}\par%
+}
+
+\providecommand{\Dbullet}{}
+\providecommand{\DECsetbullet}[1]{\renewcommand{\Dbullet}{#1}}
+\providecommand{\DNbulletlist}[1]{%
+ \Difinsidetoc{%
+ \Dtocbulletlist{#1}%
+ }{%
+ \Dmakelistenvironment{\Dbullet}{}{#1}%
+ }%
+}
+% Todo: So what on earth is @pnumwidth?
+\renewcommand{\@pnumwidth}{2.2em}
+\providecommand{\DNlistitem}[1]{%
+ \Difinsidetoc{%
+ \ifthenelse{\equal{\theDtoclevel}{1}\and\equal{\Dlocaltoc}{false}}{%
+ {%
+ \par\addvspace{1em}\noindent%
+ \sectfont%
+ #1\hfill\pageref{\DEVcurrentNlistitemAtocrefid}%
+ }%
+ }{%
+ \@dottedtocline{0}{\Dtocindent}{0em}{#1}{%
+ \pageref{\DEVcurrentNlistitemAtocrefid}%
+ }%
+ }%
+ }{%
+ \item{#1}%
+ }%
+}
+\providecommand{\DNenumeratedlist}[1]{#1}
+\Dprovidecounter{Dsectionlevel}{0}
+\providecommand{\Dvisitsectionhook}{}
+\providecommand{\Ddepartsectionhook}{}
+\providecommand{\DECvisitsection}{%
+ \addtocounter{Dsectionlevel}{1}%
+ \Dvisitsectionhook%
+}
+\providecommand{\DECdepartsection}{%
+ \Ddepartsectionhook%
+ \addtocounter{Dsectionlevel}{-1}%
+}
+
+% Using \_ will cause hyphenation after _ even in \textnhtt-typewriter
+% because the hyphenat package redefines \_. So we use
+% \textunderscore here.
+\providecommand{\DECtextunderscore}{\textunderscore}
+
+\providecommand{\Dtextinlineliteralfirstspace}{{ }}
+\providecommand{\Dtextinlineliteralsecondspace}{{~}}
+
+\Dprovidelength{\Dlistspacing}{0.8\baselineskip}
+
+\providecommand{\Dsetlistrightmargin}{%
+ \ifthenelse{\lengthtest{\linewidth>12em}}{%
+ % Equal margins.
+ \setlength{\rightmargin}{\leftmargin}%
+ }{%
+ % If the line is narrower than 10em, we don't remove any further
+ % space from the right.
+ \setlength{\rightmargin}{0pt}%
+ }%
+}
+\providecommand{\Dresetlistdepth}{false}
+\Dprovidelength{\Doriginallabelsep}{\labelsep}
+\providecommand{\Dmakelistenvironment}[3]{%
+ % Make list environment with support for unlimited nesting and with
+ % reasonable default lengths. Parameters:
+ % 1. Label (same as in list environment).
+ % 2. Spacing (same as in list environment).
+ % 3. List contents (contents of list environment).
+ \ifthenelse{\equal{\Dinsidetabular}{true}}{%
+ % Unfortunately, vertical spacing doesn't work correctly when
+ % using lists inside tabular environments, so we use a minipage.
+ \begin{minipage}[t]{\linewidth}%
+ }{}%
+ {%
+ \renewcommand{\Dneedvspace}{false}%
+ % \parsep0.5\baselineskip
+ \renewcommand{\Dresetlistdepth}{false}%
+ \ifnum \@listdepth>5%
+ \protect\renewcommand{\Dresetlistdepth}{true}%
+ \@listdepth=5%
+ \fi%
+ \begin{list}{%
+ #1%
+ }{%
+ \setlength{\itemsep}{0pt}%
+ \setlength{\partopsep}{0pt}%
+ \setlength{\topsep}{0pt}%
+ % List should take 90% of total width.
+ \setlength{\leftmargin}{0.05\linewidth}%
+ \ifthenelse{\lengthtest{\leftmargin<1.8em}}{%
+ \setlength{\leftmargin}{1.8em}%
+ }{}%
+ \setlength{\labelsep}{\Doriginallabelsep}%
+ \Dsetlistrightmargin%
+ #2%
+ }{%
+ #3%
+ }%
+ \end{list}%
+ \ifthenelse{\equal{\Dresetlistdepth}{true}}{\@listdepth=5}{}%
+ }%
+ \ifthenelse{\equal{\Dinsidetabular}{true}}{\end{minipage}}{}%
+}
+\providecommand{\Dfinalstrut}{\@finalstrut\@arstrutbox}
+\providecommand{\DAlastitem}[5]{#5\Dfinalstrut}
+
+\Dprovidelength{\Ditemsep}{0pt}
+\providecommand{\DECmakeenumeratedlist}[6]{%
+ % Make enumerated list.
+ % Parameters:
+ % - prefix
+ % - type (\arabic, \roman, ...)
+ % - suffix
+ % - suggested counter name
+ % - start number - 1
+ % - list contents
+ \newcounter{#4}%
+ \Dmakelistenvironment{#1#2{#4}#3}{%
+ % Use as much space as needed for the label.
+ \setlength{\labelwidth}{10em}%
+ % Reserve enough space so that the label doesn't go beyond the
+ % left margin of preceding paragraphs. Like that:
+ %
+ % A paragraph.
+ %
+ % 1. First item.
+ \setlength{\leftmargin}{2.5em}%
+ \Dsetlistrightmargin%
+ \setlength{\itemsep}{\Ditemsep}%
+ % Use counter recommended by Python module.
+ \usecounter{#4}%
+ % Set start value.
+ \addtocounter{#4}{#5}%
+ }{%
+ % The list contents.
+ #6%
+ }%
+}
+
+
+% Single quote in literal mode. \textquotesingle from package
+% textcomp has wrong width when using package ae, so we use a normal
+% single curly quote here.
+\providecommand{\DECtextliteralsinglequote}{'}
+
+
+% "Tabular lists" are field lists and options lists (not definition
+% lists because there the term always appears on its own line). We'll
+% use the terminology of field lists now ("field", "field name",
+% "field body"), but the same is also analogously applicable to option
+% lists.
+%
+% We want these lists to be breakable across pages. We cannot
+% automatically get the narrowest possible size for the left column
+% (i.e. the field names or option groups) because tabularx does not
+% support multi-page tables, ltxtable needs to have the table in an
+% external file and we don't want to clutter the user's directories
+% with auxiliary files created by the filecontents environment, and
+% ltablex is not included in teTeX.
+%
+% Thus we set a fixed length for the left column and use list
+% environments. This also has the nice side effect that breaking is
+% now possible anywhere, not just between fields.
+%
+% Note that we are creating a distinct list environment for each
+% field. There is no macro for a whole tabular list!
+\Dprovidelength{\Dtabularlistfieldnamewidth}{6em}
+\Dprovidelength{\Dtabularlistfieldnamesep}{0.5em}
+\providecommand{\Dinsidetabular}{false}
+\providecommand{\Dsavefieldname}{}
+\providecommand{\Dsavefieldbody}{}
+\Dprovidelength{\Dusedfieldnamewidth}{0pt}
+\Dprovidelength{\Drealfieldnamewidth}{0pt}
+\providecommand{\Dtabularlistfieldname}[1]{\renewcommand{\Dsavefieldname}{#1}}
+\providecommand{\Dtabularlistfieldbody}[1]{\renewcommand{\Dsavefieldbody}{#1}}
+\Dprovidelength{\Dparskiptemp}{0pt}
+\providecommand{\Dtabularlistfield}[1]{%
+ {%
+ % This only saves field name and field body in \Dsavefieldname and
+ % \Dsavefieldbody, resp. It does not insert any text into the
+ % document.
+ #1%
+ % Recalculate the real field name width everytime we encounter a
+ % tabular list field because it may have been changed using a
+ % "raw" node.
+ \setlength{\Drealfieldnamewidth}{\Dtabularlistfieldnamewidth}%
+ \addtolength{\Drealfieldnamewidth}{\Dtabularlistfieldnamesep}%
+ \Dmakelistenvironment{%
+ \makebox[\Drealfieldnamewidth][l]{\Dsavefieldname}%
+ }{%
+ \setlength{\labelwidth}{\Drealfieldnamewidth}%
+ \setlength{\leftmargin}{\Drealfieldnamewidth}%
+ \setlength{\rightmargin}{0pt}%
+ \setlength{\labelsep}{0pt}%
+ }{%
+ \item%
+ \settowidth{\Dusedfieldnamewidth}{\Dsavefieldname}%
+ \setlength{\Dparskiptemp}{\parskip}%
+ \ifthenelse{%
+ \lengthtest{\Dusedfieldnamewidth>\Dtabularlistfieldnamewidth}%
+ }{%
+ \mbox{}\par%
+ \setlength{\parskip}{0pt}%
+ }{}%
+ \Dsavefieldbody%
+ \setlength{\parskip}{\Dparskiptemp}%
+ %XXX Why did we need this?
+ %\@finalstrut\@arstrutbox%
+ }%
+ \par%
+ }%
+}
+
+\providecommand{\Dformatfieldname}[1]{\textbf{#1:}}
+\providecommand{\DNfieldlist}[1]{#1}
+\providecommand{\DNfield}[1]{\Dtabularlistfield{#1}}
+\providecommand{\DNfieldname}[1]{%
+ \Dtabularlistfieldname{%
+ \Dformatfieldname{#1}%
+ }%
+}
+\providecommand{\DNfieldbody}[1]{\Dtabularlistfieldbody{#1}}
+
+\providecommand{\Dformatoptiongroup}[1]{%
+ % Format option group, e.g. "-f file, --input file".
+ \texttt{#1}%
+}
+\providecommand{\Dformatoption}[1]{%
+ % Format option, e.g. "-f file".
+ % Put into mbox to avoid line-breaking at spaces.
+ \mbox{#1}%
+}
+\providecommand{\Dformatoptionstring}[1]{%
+ % Format option string, e.g. "-f".
+ #1%
+}
+\providecommand{\Dformatoptionargument}[1]{%
+ % Format option argument, e.g. "file".
+ \textsl{#1}%
+}
+\providecommand{\Dformatoptiondescription}[1]{%
+ % Format option description, e.g.
+ % "\DNparagraph{Read input data from file.}"
+ #1%
+}
+\providecommand{\DNoptionlist}[1]{#1}
+\providecommand{\Doptiongroupjoiner}{,{ }}
+\providecommand{\Disfirstoption}{%
+ % Auxiliary macro indicating if a given option is the first child
+ % of its option group (if it's not, it has to preceded by
+ % \Doptiongroupjoiner).
+ false%
+}
+\providecommand{\DNoptionlistitem}[1]{%
+ \Dtabularlistfield{#1}%
+}
+\providecommand{\DNoptiongroup}[1]{%
+ \renewcommand{\Disfirstoption}{true}%
+ \Dtabularlistfieldname{\Dformatoptiongroup{#1}}%
+}
+\providecommand{\DNoption}[1]{%
+ % If this is not the first option in this option group, add a
+ % joiner.
+ \ifthenelse{\equal{\Disfirstoption}{true}}{%
+ \renewcommand{\Disfirstoption}{false}%
+ }{%
+ \Doptiongroupjoiner%
+ }%
+ \Dformatoption{#1}%
+}
+\providecommand{\DNoptionstring}[1]{\Dformatoptionstring{#1}}
+\providecommand{\DNoptionargument}[1]{{ }\Dformatoptionargument{#1}}
+\providecommand{\DNdescription}[1]{%
+ \Dtabularlistfieldbody{\Dformatoptiondescription{#1}}%
+}
+
+\providecommand{\DNdefinitionlist}[1]{%
+ \begin{description}%
+ \parskip0pt%
+ #1%
+ \end{description}%
+}
+\providecommand{\DNdefinitionlistitem}[1]{%
+ % LaTeX expects the label in square brackets; we provide an empty
+ % label.
+ \item[]#1%
+}
+\providecommand{\Dformatterm}[1]{#1}
+\providecommand{\DNterm}[1]{\hspace{-5pt}\Dformatterm{#1}}
+% I'm still not sure what's the best rendering for classifiers. The
+% colon syntax is used by reStructuredText, so it's at least WYSIWYG.
+% Use slanted text because italic would cause too much emphasis.
+\providecommand{\Dformatclassifier}[1]{\textsl{#1}}
+\providecommand{\DNclassifier}[1]{~:~\Dformatclassifier{#1}}
+\providecommand{\Dformatdefinition}[1]{#1}
+\providecommand{\DNdefinition}[1]{\par\Dformatdefinition{#1}}
+
+\providecommand{\Dlineblockindentation}{2.5em}
+\providecommand{\DNlineblock}[1]{%
+ \Dmakelistenvironment{}{%
+ \ifthenelse{\equal{\DEVparent}{lineblock}}{%
+ % Parent is a line block, so indent.
+ \setlength{\leftmargin}{\Dlineblockindentation}%
+ }{%
+ % At top level; don't indent.
+ \setlength{\leftmargin}{0pt}%
+ }%
+ \setlength{\rightmargin}{0pt}%
+ \setlength{\parsep}{0pt}%
+ }{%
+ #1%
+ }%
+}
+\providecommand{\DNline}[1]{\item#1}
+
+\providecommand{\DNtransition}{%
+ \raisebox{0.25em}{\parbox{\linewidth}{\hspace*{\fill}\hrulefill\hrulefill\hspace*{\fill}}}%
+}
+
+\providecommand{\Dformatblockquote}[1]{%
+ % Format contents of block quote.
+ % This occurs in block-level context, so we cannot use \textsl.
+ {\slshape#1}%
+}
+\providecommand{\Dformatattribution}[1]{---\textup{#1}}
+\providecommand{\DNblockquote}[1]{%
+ \Dmakebox{%
+ \Dformatblockquote{#1}
+ }%
+}
+\providecommand{\DNattribution}[1]{%
+ \par%
+ \begin{flushright}\Dformatattribution{#1}\end{flushright}%
+}
+
+
+% Sidebars:
+% Vertical and horizontal margins.
+\Dprovidelength{\Dsidebarvmargin}{0.5em}
+\Dprovidelength{\Dsidebarhmargin}{1em}
+% Padding (space between contents and frame).
+\Dprovidelength{\Dsidebarpadding}{1em}
+% Frame width.
+\Dprovidelength{\Dsidebarframewidth}{2\fboxrule}
+% Position ("l" or "r").
+\providecommand{\Dsidebarposition}{r}
+% Width.
+\Dprovidelength{\Dsidebarwidth}{0.45\linewidth}
+\providecommand{\DNsidebar}[1]{
+ \parpic[\Dsidebarposition]{%
+ \begin{minipage}[t]{\Dsidebarwidth}%
+ % Doing this with nested minipages is ugly, but I haven't found
+ % another way to place vertical space before and after the fbox.
+ \vspace{\Dsidebarvmargin}%
+ {%
+ \setlength{\fboxrule}{\Dsidebarframewidth}%
+ \setlength{\fboxsep}{\Dsidebarpadding}%
+ \fbox{%
+ \begin{minipage}[t]{\linewidth}%
+ \setlength{\parindent}{\Dboxparindent}%
+ #1%
+ \end{minipage}%
+ }%
+ }%
+ \vspace{\Dsidebarvmargin}%
+ \end{minipage}%
+ }%
+}
+
+
+% Citations and footnotes.
+\providecommand{\Dformatfootnote}[1]{%
+ % Format footnote.
+ {%
+ \footnotesize#1%
+ % \par is necessary for LaTeX to adjust baselineskip to the
+ % changed font size.
+ \par%
+ }%
+}
+\providecommand{\Dformatcitation}[1]{\Dformatfootnote{#1}}
+\Dprovidelength{\Doriginalbaselineskip}{0pt}
+\providecommand{\DNfootnotereference}[1]{%
+ {%
+ % \baselineskip is 0pt in \textsuperscript, so we save it here.
+ \setlength{\Doriginalbaselineskip}{\baselineskip}%
+ \textsuperscript{#1}%
+ }%
+}
+\providecommand{\DNcitationreference}[1]{{[}#1{]}}
+\Dprovidelength{\Dfootnotesep}{3.5pt}
+\providecommand{\Dsetfootnotespacing}{%
+ % Spacing commands executed at the beginning of footnotes.
+ \setlength{\parindent}{0pt}%
+ \hspace{1em}%
+}
+\providecommand{\DNfootnote}[1]{%
+ % See ltfloat.dtx for details.
+ {%
+ \insert\footins{%
+ % BUG: This is too small if the user adds
+ % \onehalfspacing or \doublespace.
+ \vspace{\Dfootnotesep}%
+ \Dsetfootnotespacing%
+ \Dformatfootnote{#1}%
+ }%
+ }%
+}
+\providecommand{\DNcitation}[1]{\DNfootnote{#1}}
+\providecommand{\Dformatfootnotelabel}[1]{%
+ % Keep \footnotesize in footnote labels (\textsuperscript would
+ % reduce the font size even more).
+ \textsuperscript{\footnotesize#1{ }}%
+}
+\providecommand{\Dformatcitationlabel}[1]{{[}#1{]}{ }}
+\providecommand{\Dformatmultiplebackrefs}[1]{%
+ % If in printing mode, do not write out multiple backrefs.
+ \ifthenelse{\equal{\Dprinting}{true}}{}{\textsl{#1}}%
+}
+\providecommand{\Dthislabel}{}
+\providecommand{\DNlabel}[1]{%
+ % Footnote or citatation label.
+ \renewcommand{\Dthislabel}{#1}%
+ \ifthenelse{\not\equal{\DEVsinglebackref}{}}{%
+ \let\Doriginallabel=\Dthislabel%
+ \def\Dthislabel{%
+ \Dsinglefootnotebacklink{\DEVsinglebackref}{\Doriginallabel}%
+ }%
+ }{}%
+ \ifthenelse{\equal{\DEVparent}{footnote}}{%
+ % Footnote label.
+ \Dformatfootnotelabel{\Dthislabel}%
+ }{%
+ \ifthenelse{\equal{\DEVparent}{citation}}{%
+ % Citation label.
+ \Dformatcitationlabel{\Dthislabel}%
+ }{}%
+ }%
+ % If there are multiple backrefs, add them now.
+ \Dformatmultiplebackrefs{\DEVmultiplebackrefs}%
+}
+\providecommand{\Dsinglefootnotebacklink}[2]{%
+ % Create normal backlink of a footnote label. Parameters:
+ % 1. ID.
+ % 2. Link text.
+ % Treat like a footnote reference.
+ \Dimplicitfootnotereference{\##1}{#2}%
+}
+\providecommand{\DECmultifootnotebacklink}[2]{%
+ % Create generated backlink, as in (1, 2). Parameters:
+ % 1. ID.
+ % 2. Link text.
+ % Treat like a footnote reference.
+ \Dimplicitfootnotereference{\##1}{#2}%
+}
+\providecommand{\Dsinglecitationbacklink}[2]{\Dsinglefootnotebacklink{#1}{#2}}
+\providecommand{\DECmulticitationbacklink}[2]{\DECmultifootnotebacklink{#1}{#2}}
+
+
+\providecommand{\DECmaketable}[2]{%
+ % Make table. Parameters:
+ % 1. Table spec (like "|p|p|").
+ % 2. Table contents.
+ {%
+ \ifthenelse{\equal{\Dinsidetabular}{true}}{%
+ % Inside longtable; we cannot have nested longtables.
+ \begin{tabular}{#1}%
+ \hline%
+ #2%
+ \end{tabular}%
+ }{%
+ \renewcommand{\Dinsidetabular}{true}%
+ \begin{longtable}{#1}%
+ \hline%
+ #2%
+ \end{longtable}%
+ }%
+ }%
+}
+\providecommand{\DNthead}[1]{%
+ #1%
+ \endhead%
+}
+\providecommand{\DNrow}[1]{%
+ #1\tabularnewline%
+ \hline%
+}
+\providecommand{\Dinsidemulticolumn}{false}
+\providecommand{\Dcompensatingmulticol}[3]{%
+ \multicolumn{#1}{#2}{%
+ {%
+ \renewcommand{\Dinsidemulticolumn}{true}%
+ % Compensate for weird missing vertical space at top of paragraph.
+ \raisebox{-2.5pt}{#3}%
+ }%
+ }%
+}
+\providecommand{\DECcolspan}[2]{%
+ % Take care of the morecols attribute (but incremented by 1).
+ &%
+ \Dcompensatingmulticol{#1}{l|}{#2}%
+}
+\providecommand{\DECcolspanleft}[2]{%
+ % Like \Dmorecols, but called for the leftmost entries in a table
+ % row.
+ \Dcompensatingmulticol{#1}{|l|}{#2}%
+}
+\providecommand{\DECsubsequententry}[1]{%
+ %
+}
+\providecommand{\DNentry}[1]{%
+ % The following sequence adds minimal vertical space above the top
+ % lines of the first cell paragraph, so that vertical space is
+ % balanced at the top and bottom of table cells.
+ \ifthenelse{\equal{\Dinsidemulticolumn}{false}}{%
+ \vspace{-1em}\vspace{-\parskip}\par%
+ }{}%
+ #1%
+ % No need to add an ampersand ("&"); that's done by \DECsubsequententry.
+}
+\providecommand{\DAtableheaderentry}[5]{\Dformattableheaderentry{#5}}
+\providecommand{\Dformattableheaderentry}[1]{{\bfseries#1}}
+
+
+\providecommand{\DNsystemmessage}[1]{%
+ {%
+ \ifthenelse{\equal{\Dprinting}{false}}{\color{red}}{}%
+ \bfseries%
+ #1%
+ }%
+}
+
+
+\providecommand{\Dinsidehalign}{false}
+\newsavebox{\Dalignedimagebox}
+\Dprovidelength{\Dalignedimagewidth}{0pt}
+\providecommand{\Dhalign}[2]{%
+ % Horizontally align the contents to the left or right so that the
+ % text flows around it.
+ % Parameters:
+ % 1. l or r
+ % 2. Contents.
+ \renewcommand{\Dinsidehalign}{true}%
+ % For some obscure reason \parpic consumes some vertical space.
+ \vspace{-3pt}%
+ % Now we do something *really* ugly, but this enables us to wrap the
+ % image in a minipage while still allowing tight frames when
+ % class=border (see \DNimageCborder).
+ \sbox{\Dalignedimagebox}{#2}%
+ \settowidth{\Dalignedimagewidth}{\usebox{\Dalignedimagebox}}%
+ \parpic[#1]{%
+ \begin{minipage}[b]{\Dalignedimagewidth}%
+ % Compensate for previously added space, but not entirely.
+ \vspace*{2.0pt}%
+ \vspace*{\Dfloatimagetopmargin}%
+ \usebox{\Dalignedimagebox}%
+ \vspace*{1.5pt}%
+ \vspace*{\Dfloatimagebottommargin}%
+ \end{minipage}%
+ }%
+ \renewcommand{\Dinsidehalign}{false}%
+}
+
+
+% Maximum width of an image.
+\providecommand{\Dimagemaxwidth}{\linewidth}
+\providecommand{\Dfloatimagemaxwidth}{0.5\linewidth}
+% Auxiliary variable.
+\Dprovidelength{\Dcurrentimagewidth}{0pt}
+\providecommand{\DNimageAalign}[5]{%
+ \ifthenelse{\equal{#3}{left}}{%
+ \Dhalign{l}{#5}%
+ }{%
+ \ifthenelse{\equal{#3}{right}}{%
+ \Dhalign{r}{#5}%
+ }{%
+ \ifthenelse{\equal{#3}{center}}{%
+ % Text floating around centered figures is a bad idea. Thus
+ % we use a center environment. Note that no extra space is
+ % added by the writer, so the space added by the center
+ % environment is fine.
+ \begin{center}#5\end{center}%
+ }{%
+ #5%
+ }%
+ }%
+ }%
+}
+% Base path for images.
+\providecommand{\Dimagebase}{}
+% Auxiliary command. Current image path.
+\providecommand{\Dimagepath}{}
+\providecommand{\DNimageAuri}[5]{%
+ % Insert image. We treat the URI like a path here.
+ \renewcommand{\Dimagepath}{\Dimagebase#3}%
+ \Difdefined{DcurrentNimageAwidth}{%
+ \Dwidthimage{\DEVcurrentNimageAwidth}{\Dimagepath}%
+ }{%
+ \Dsimpleimage{\Dimagepath}%
+ }%
+}
+\Dprovidelength{\Dfloatimagevmargin}{0pt}
+\providecommand{\Dfloatimagetopmargin}{\Dfloatimagevmargin}
+\providecommand{\Dfloatimagebottommargin}{\Dfloatimagevmargin}
+\providecommand{\Dwidthimage}[2]{%
+ % Image with specified width.
+ % Parameters:
+ % 1. Image width.
+ % 2. Image path.
+ % Need to make bottom-alignment dependent on align attribute (add
+ % functional test first). Need to observe height attribute.
+ %\begin{minipage}[b]{#1}%
+ \includegraphics[width=#1,height=\textheight,keepaspectratio]{#2}%
+ %\end{minipage}%
+}
+\providecommand{\Dcurrentimagemaxwidth}{}
+\providecommand{\Dsimpleimage}[1]{%
+ % Insert image, without much parametrization.
+ \settowidth{\Dcurrentimagewidth}{\includegraphics{#1}}%
+ \ifthenelse{\equal{\Dinsidehalign}{true}}{%
+ \renewcommand{\Dcurrentimagemaxwidth}{\Dfloatimagemaxwidth}%
+ }{%
+ \renewcommand{\Dcurrentimagemaxwidth}{\Dimagemaxwidth}%
+ }%
+ \ifthenelse{\lengthtest{\Dcurrentimagewidth>\Dcurrentimagemaxwidth}}{%
+ \Dwidthimage{\Dcurrentimagemaxwidth}{#1}%
+ }{%
+ \Dwidthimage{\Dcurrentimagewidth}{#1}%
+ }%
+}
+\providecommand{\Dwidthimage}[2]{%
+ % Image with specified width.
+ % Parameters:
+ % 1. Image width.
+ % 2. Image path.
+ \Dwidthimage{#1}{#2}%
+}
+
+% Figures.
+\providecommand{\DNfigureAalign}[5]{%
+ % Hack to make it work Right Now.
+ %\def\DEVcurrentNimageAwidth{\DEVcurrentNfigureAwidth}%
+ %
+ %\def\DEVcurrentNimageAwidth{\linewidth}%
+ \DNimageAalign{#1}{#2}{#3}{#4}{%
+ \begin{minipage}[b]{0.4\linewidth}#5\end{minipage}}%
+ %\let\DEVcurrentNimageAwidth=\relax%
+ %
+ %\let\DEVcurrentNimageAwidth=\relax%
+}
+\providecommand{\DNcaption}[1]{\par\noindent{\slshape#1}}
+\providecommand{\DNlegend}[1]{\DECauxiliaryspace#1}
+
+\providecommand{\DCborder}[1]{\fbox{#1}}
+% No padding between image and border.
+\providecommand{\DNimageCborder}[1]{\frame{#1}}
+
+
+% Need to replace with language-specific stuff. Maybe look at
+% csquotes.sty and ask the author for permission to use parts of it.
+\providecommand{\DECtextleftdblquote}{``}
+\providecommand{\DECtextrightdblquote}{''}
+
+% Table of contents:
+\Dprovidelength{\Dtocininitialsectnumwidth}{2.4em}
+\Dprovidelength{\Dtocadditionalsectnumwidth}{0.7em}
+% Level inside a table of contents. While this is at -1, we are not
+% inside a TOC.
+\Dprovidecounter{Dtoclevel}{-1}%
+\providecommand{\Dlocaltoc}{false}%
+\providecommand{\DNtopicClocal}[1]{%
+ \renewcommand{\Dlocaltoc}{true}%
+ \addtolength{\Dtocsectnumwidth}{2\Dtocadditionalsectnumwidth}%
+ \addtolength{\Dtocindent}{-2\Dtocadditionalsectnumwidth}%
+ #1%
+ \addtolength{\Dtocindent}{2\Dtocadditionalsectnumwidth}%
+ \addtolength{\Dtocsectnumwidth}{-2\Dtocadditionalsectnumwidth}%
+ \renewcommand{\Dlocaltoc}{false}%
+}
+\Dprovidelength{\Dtocindent}{0pt}%
+\Dprovidelength{\Dtocsectnumwidth}{\Dtocininitialsectnumwidth}
+% Compensate for one additional TOC indentation space so that the
+% top-level is unindented.
+\addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth}
+\addtolength{\Dtocindent}{-\Dtocsectnumwidth}
+\providecommand{\Difinsidetoc}[2]{%
+ \ifthenelse{\not\equal{\theDtoclevel}{-1}}{#1}{#2}%
+}
+\providecommand{\DNgeneratedCsectnum}[1]{%
+ \Difinsidetoc{%
+ % Section number inside TOC.
+ \makebox[\Dtocsectnumwidth][l]{#1}%
+ }{%
+ % Section number inside section title.
+ #1\quad%
+ }%
+}
+\providecommand{\Dtocbulletlist}[1]{%
+ \addtocounter{Dtoclevel}{1}%
+ \addtolength{\Dtocindent}{\Dtocsectnumwidth}%
+ \addtolength{\Dtocsectnumwidth}{\Dtocadditionalsectnumwidth}%
+ #1%
+ \addtolength{\Dtocsectnumwidth}{-\Dtocadditionalsectnumwidth}%
+ \addtolength{\Dtocindent}{-\Dtocsectnumwidth}%
+ \addtocounter{Dtoclevel}{-1}%
+}
+
+
+% For \DECpixelunit, the length value is pre-multiplied with 0.75, so by
+% specifying "pt" we get the same notion of "pixel" as graphicx.
+\providecommand{\DECpixelunit}{pt}
+% Normally lengths are relative to the current linewidth.
+\providecommand{\DECrelativeunit}{\linewidth}
+
+
+% ACTION: These commands actually *do* something.
+% Ultimately, everything should be done here, and no active content should be
+% above (not even \usepackage).
+
+\DSearly
+\DSpackages
+\DSfrenchspacing
+\DSsymbols
+\DSlate
+
+\makeatother
+
+ \usepackage{fancyvrb}
--- /dev/null
+from scikits.audiolab import formatinfo as format
+
+f = format('aiff', 'ulaw')
+print f
+
+f = format('ircam', 'float32')
+print f
--- /dev/null
+from scikits.audiolab import supported_format, supported_encoding, \
+ supported_endianness
+
+print supported_format()
+print supported_encoding()
+print supported_endianness()
--- /dev/null
+from tempfile import mkstemp
+from os.path import join, dirname
+from os import remove
+
+from scikits.audiolab import wavread, wavwrite
+
+(tmp, fs, enc) = wavread('test.wav')
+if tmp.ndim < 2:
+ nc = 1
+else:
+ nc = tmp.shape[1]
+
+print "The file has %d frames, %d channel(s)" % (tmp.shape[0], nc)
+print "FS is %f, encoding is %s" % (fs, enc)
+
+fd, cfilename = mkstemp('pysndfiletest.wav')
+try:
+ wavwrite(tmp, cfilename, fs = 16000, enc = 'pcm24')
+finally:
+ remove(cfilename)
--- /dev/null
+import scikits.audiolab as audiolab
+
+a = audiolab.sndfile('test.flac', 'read')
+data = a.read_frames(1000)
+a.close()
--- /dev/null
+import scikits.audiolab as audiolab
+
+filename = 'test.flac'
+a = audiolab.sndfile(filename, 'read')
+
+print a
--- /dev/null
+import numpy as N
+
+import scikits.audiolab as audiolab
+
+filename = 'test.flac'
+a = audiolab.sndfile(filename, 'read')
+
+tmp = a.read_frames(1e4)
+float_tmp = a.read_frames(1e4, dtype = N.float32)
+
+import pylab as P
+P.plot(tmp[:])
--- /dev/null
+from tempfile import mkstemp
+from os import remove
+
+import numpy as N
+from scikits.audiolab import formatinfo as format
+import scikits.audiolab as audiolab
+
+# Create a temp file in the system temporary dir, and always remove
+# it at the end
+cd, filename = mkstemp('tmptest.wav')
+try:
+ fmt = format('wav', 'pcm24')
+ nchannels = 2
+ fs = 44100
+
+ afile = audiolab.sndfile(filename, 'write', fmt, nchannels, fs)
+
+ # Create a stereo white noise, with Gaussian distribution
+ tmp = 0.1 * N.random.randn(1000, nchannels)
+
+ # Write the first 500 frames of the signal
+ # Note that the write_frames method uses tmp's numpy dtype to determine how
+ # to write to the file; sndfile also converts the data on the fly if necessary
+ afile.write_frames(tmp, 500)
+
+ afile.close()
+
+ # Let's check that the written file has the expected meta data
+ afile = audiolab.sndfile(filename, 'read')
+ assert(afile.get_samplerate() == fs)
+ assert(afile.get_channels() == nchannels)
+ assert(afile.get_nframes() == 500)
+finally:
+ remove(filename)
--- /dev/null
+..
+ restindex
+ page-title: audiolab
+ crumb: audiolab
+ link-title: audiolab
+ encoding: utf-8
+ output-encoding: None
+ file: audiolab1.png
+ file: quick1.py
+ file: usage1.py
+ file: usage2.py
+ file: format1.py
+ file: format2.py
+ file: write1.py
+ file: matlab1.py
+ file: audiolab.pdf
+ /restindex
+
+.. vim:syntax=rest
+.. Last Change: Tue Jul 17 11:00 AM 2007 J
+
+===============================================================
+ Pyaudiolab, a python package to make noise with numpy arrays
+===============================================================
+
+Introduction
+============
+
+.. _scipy: http://www.scipy.org
+.. _libsndfile: http://www.mega-nerd.com/libsndfile/
+
+For people doing audio processing, it is useful to be able to import data from
+audio files, and export them back, as well as listening to the results of some
+processing; matlab have functions such as wavread, wavwrite, soundsc, etc...
+for that purposes. The goal of audiolab is to give those capabilities to the
+`scipy`_ environment by wrapping the excellent library `libsndfile`_ from Erik
+Castro de Lopo. Pyaudio supports all format supported by libsndfile, including
+wav, aiff, ircam files, and flac (an open source lossless compressed format);
+see `here <http://www.mega-nerd.com/libsndfile/#Features">`_ for a complete
+list.
+
+ **Note**: The library is still in beta stage: reading and writing
+ data is possible, but only in frames, not per item.
+ Also, the ability to play data on the system's soundcard is not there yet.
+ I have never encountered any data corruption, except when using the buggy
+ ctypes included in Ubuntu's python 2.5 (bug which was solved recently).
+
+ **Note**: The online version of this document is not always up to date. The
+ pdf included in the package is the reference, and always in sync with the
+ package. If something does not work, please refer first to the pdf included in
+ the package.
+
+.. contents:: Tables of contents
+
+Download and installation
+=========================
+
+Download
+--------
+
+audiolab is part of scikits: its source can be downloaded directly from the
+scikits svn repository: svn co http://svn.scipy.org/svn/scikits/trunk/audiolab
+
+Requirements
+------------
+
+audiolab requires the following softwares:
+
+ - a python interpreter.
+ - libsndfile (including the header sndfile.h, which means linux users should
+ download the libsndfile-dev package).
+ - numpy (any version >= 1.0 should work).
+ - ctypes (version >= 1.0.1)
+
+Starting with version 2.5, python include ctypes in its standart library, so you
+don't need to install ctypes separately in this case.
+
+It has been run succesfully on the following platforms:
+
+ - linux ubuntu (32 and 64 bits)
+ - windows XP
+
+I would be interested to hear anyone who succeesfully used it on other
+plateforms (Mac Os X, solaris, etc...).
+
+ **Note**: the ctypes used in python2.5 package in ubuntu (and maybe debian
+ as well) *had* a nasty bug which makes it impossible to use 64 bits integers. You
+ should NOT use this package with audiolab (importing audiolab should fail,
+ but if the version is not correctly detected, you will have file corruption when
+ writing data to audio files). Run the test to check everything is working (a
+ test case tests this issue).
+
+ `<https://launchpad.net/ubuntu/+source/python2.5/+bug/71914>`_
+
+
+Installation
+------------
+
+For unix users, if libsndfile is installed in standart location (eg /usr/lib,
+/usr/local/lib), the installer should be able to find them automatically, and
+you only need to do a "python setup.py install". In other cases, you need to
+create a file site.cfg to set the location of libsndfile and its header (there
+are site.cfg examples which should give you an idea how to use them on your
+platform).
+
+For windows users: the library distributed by Erik Castro de Lopo cannot be
+used directly; you need to follow the instructions given in libsndfile
+distribution in the file README-precompiled-dll.txt. See also site.cfg.win32.
+
+License
+-------
+
+audiolab is released under the LGPL, which forces you to release back the
+modifications you may make in the version of audiolab you are distributing,
+but you can still use it in closed softwares.
+
+Quick view
+==========
+
+The following code shows you how to open a file for read, reading the first
+1000 frames, and closing it:
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/quick1.py}
+
+.. raw:: latex
+
+ \input{quick1.tex}
+
+Usage
+=====
+
+Opening a file and getting its parameters
+-----------------------------------------
+
+Once imported, audiolab gives you access the sndfile class, which is the
+class of audiolab use to open audio files.
+You create a sndfile instance when you want
+to open a file for reading or writing (the file test.flac is included
+in the audiolab package, in the test_data directory):
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/usage1.py}
+
+.. raw:: latex
+
+ \input{usage1.tex}
+
+Prints you the informations related to the file, like its sampling rate,
+the number of frames, etc... You can of course get each parameter
+individually by using the corresponding sndfile.get* accessors.
+
+Importing audio data
+--------------------
+
+Now that we've opened a file, we would like to read its audio content,
+right ? For now, you can only import the data as floating point data,
+float (32 bits) or double (64 bits). The function
+sndfile.read_frames read n frames,
+where a frame contains a sample of each channel (one in mono, 2 in stereo,
+etc...):
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/usage2.py}
+
+.. raw:: latex
+
+ \input{usage2.tex}
+
+The above code import 10000 frames, and plot the first channel using matplotlib
+(see below). A frame holds one sample from each channel: 1000 frames of a stereo
+file is 2000 samples. Each channel is one column of the numpy array. The read
+functions follow numpy conventions, that is by default, the data are read as
+double, but you can give a dtype argument to the function.
+
+.. image:: audiolab1.png
+ :width: 500
+ :height: 400
+
+The format class
+----------------
+
+When opening a file for writing, you need to give various parameters related to
+the format such as the file format, the encoding. The format class is used to
+create valid formats from those parameters By default, the format class creates
+a format object with file type wav, and 16 bits pcm encoding:
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/format1.py}
+
+.. raw:: latex
+
+ \input{format1.tex}
+
+prints back "Major Format: AIFF (Apple/SGI), Encoding Format: U-Law"
+and "Major Format: SF (Berkeley/IRCAM/CARL), Encoding Format: 32 bit float".
+
+To get a list of all possible file format and encoding, the function
+supported_* are available:
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/format2.py}
+
+.. raw:: latex
+
+ \input{format2.tex}
+
+ **Note**: not all combination of encoding, endianness and format are possible.
+ If you try to create a format with incompatible values, you will get an error
+ while creating an instance of format.
+
+Writing data to a file
+----------------------
+
+Opening a file for writing is a bit more complicated than reading; you need to
+say which format you are requesting, the number of channels and the sampling
+rate (in Hz) you are requesting; all thoses information are mandatory ! The
+class format is used to build a format understable by libsndfile from
+'user-friendly' values. Let's see how it works.
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/write1.py}
+
+.. raw:: latex
+
+ \input{write1.tex}
+
+Matlab-like API
+---------------
+
+audiolab also have a matlab-like API for audio IO. Its usage is as similar as it
+can get using python:
+
+.. raw:: html
+
+ {mycolorize;input/softwares/audiolab/matlab1.py}
+
+.. raw:: latex
+
+ \input{matlab1.tex}
+
+Known bugs:
+===========
+
+ - there seems to be a problem when using libsndfile fseek facilities with flac
+ files (which are necessary for the functions flacread/flacwrite). The
+ problem seems to be with libFLAC; for this reason, seek in flac files is not
+ enabled by default for now. See FLAC_SUPPORT.txt for more informations.
+
+TODO
+====
+
+audiolab is still in early stages. Before a release, I would like to implement the
+follwings:
+
+ - support (at least some) meta-data embedded in some audio files format.
+ - support the libsndfile's error system
+ - player on all major plateforms (at least linux/windows/max OS X)
--- /dev/null
+% Last Change: Wed Jan 31 08:00 PM 2007 J
+% vim:syntax=tex
+
+\newcommand\at{@}
+\newcommand\lb{[}
+\newcommand\rb{]}
+\newcommand\Cba[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\Caz[1]{\textcolor[rgb]{0.00,0.25,0.82}{#1}}
+\newcommand\Cay[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}}
+\newcommand\Cax[1]{\textcolor[rgb]{0.00,0.63,0.00}{#1}}
+\newcommand\Cbc[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\Cas[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\Car[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}}
+\newcommand\Caq[1]{\textcolor[rgb]{0.73,0.27,0.27}{\textit{#1}}}
+\newcommand\Cap[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}}
+\newcommand\Caw[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\Cav[1]{\textcolor[rgb]{0.60,0.60,0.60}{\textbf{#1}}}
+\newcommand\Cau[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\Cat[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\Cak[1]{\textbf{#1}}
+\newcommand\Caj[1]{\textcolor[rgb]{0.73,0.40,0.53}{#1}}
+\newcommand\Cai[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}}
+\newcommand\Cah[1]{\textcolor[rgb]{0.63,0.63,0.00}{#1}}
+\newcommand\Cao[1]{\textcolor[rgb]{0.53,0.00,0.00}{#1}}
+\newcommand\Can[1]{\textcolor[rgb]{0.00,0.50,0.00}{#1}}
+\newcommand\Cam[1]{\textcolor[rgb]{0.73,0.40,0.13}{\textbf{#1}}}
+\newcommand\Cal[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\Cac[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\Cab[1]{\textit{#1}}
+\newcommand\Caa[1]{\textcolor[rgb]{0.50,0.50,0.50}{#1}}
+\newcommand\Cag[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\Caf[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}}
+\newcommand\Cae[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\Cad[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\Cbb[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\CaZ[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\CaY[1]{\textcolor[rgb]{0.00,0.00,0.50}{\textbf{#1}}}
+\newcommand\CaX[1]{\textcolor[rgb]{0.00,0.50,0.00}{\textbf{#1}}}
+\newcommand\Cbd[1]{\textcolor[rgb]{0.73,0.40,0.53}{\textbf{#1}}}
+\newcommand\Cbe[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\CaS[1]{\textcolor[rgb]{0.50,0.00,0.50}{\textbf{#1}}}
+\newcommand\CaR[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}}
+\newcommand\CaQ[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}}
+\newcommand\CaP[1]{\textcolor[rgb]{0.40,0.40,0.40}{#1}}
+\newcommand\CaW[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\CaV[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}}
+\newcommand\CaU[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\CaT[1]{\textcolor[rgb]{0.00,0.00,1.00}{\textbf{#1}}}
+\newcommand\CaK[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}}
+\newcommand\CaJ[1]{\textcolor[rgb]{0.00,0.63,0.00}{#1}}
+\newcommand\CaI[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\CaH[1]{\textcolor[rgb]{0.67,0.13,1.00}{\textbf{#1}}}
+\newcommand\CaO[1]{\textcolor[rgb]{0.73,0.27,0.27}{#1}}
+\newcommand\CaN[1]{\textcolor[rgb]{0.00,0.00,0.50}{\textbf{#1}}}
+\newcommand\CaM[1]{\textcolor[rgb]{0.00,0.00,1.00}{#1}}
+\newcommand\CaL[1]{\textcolor[rgb]{0.00,0.53,0.00}{#1}}
+\newcommand\CaC[1]{\textcolor[rgb]{0.00,0.53,0.00}{\textit{#1}}}
+\newcommand\CaB[1]{\textcolor[rgb]{0.82,0.25,0.23}{\textbf{#1}}}
+\newcommand\CaA[1]{\textcolor[rgb]{0.67,0.13,1.00}{#1}}
+\newcommand\CaG[1]{\fcolorbox[rgb]{1.00,0.00,0.00}{1,1,1}{#1}}
+\newcommand\CaF[1]{\textcolor[rgb]{0.72,0.53,0.04}{#1}}
+\newcommand\CaE[1]{\textcolor[rgb]{1.00,0.00,0.00}{#1}}
+\newcommand\CaD[1]{\textcolor[rgb]{0.63,0.00,0.00}{#1}}
+
--- /dev/null
+VERSION = '0.8dev'
+ignore = False
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Mon Sep 10 07:00 PM 2007 J
+
+# Copyright (C) 2006-2007 Cournapeau David <cournape@gmail.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+"""This module implements functions to read and write to audio files easily
+(ala matlab: wavread, etc...)."""
+
+import numpy as N
+
+from pysndfile import formatinfo, sndfile
+from pysndfile import PyaudioException, FlacUnsupported
+
+__all__ = []
+_MATAPI_FORMAT = ['wav', 'aiff', 'au', 'sdif', 'flac']
+for i in _MATAPI_FORMAT:
+ __all__.extend(['%sread' % i, '%swrite' % i])
+
+# writer function factory
+def _writer_factory(name, format, def_fs, descr):
+ """ Create a writer function with fileformat described by format, default
+ sampling rate def_fs, and docstring descr."""
+ def basic_writer(data, filename, fs = def_fs, enc = format.get_encoding()):
+ """Common "template" to all write functions."""
+ if N.ndim(data) <= 1:
+ nc = 1
+ nframes = N.size(data)
+ elif N.ndim(data) == 2:
+ nc = data.shape[1]
+ nframes = data.shape[0]
+ else:
+ RuntimeError("Only rank 0, 1, and 2 arrays supported as audio data")
+
+ hdl = sndfile(filename, 'write', format, nc, fs)
+ try:
+ hdl.write_frames(data, nframes)
+ finally:
+ hdl.close()
+ doc = \
+ """ wrapper around pysndfile to write %s file,
+ in a similar manner to matlab's wavwrite/auwrite and the likes.
+
+ OVERWRITES EXISTING FILE !
+
+ Args:
+ - data: a rank 0, 1 (mono) or 2 (one channel per col) numpy array
+ - filename: a string for the audio file name
+ - fs: the sampling rate in Hz (%d Hz by default).
+ - enc: a string for the encoding such as 'pcm16', etc...(%s by
+ default). Not supported yet !
+
+ For a total control over options, such as endianness, append data to an
+ existing file, etc... you should use sndfile class instances instead""" \
+ % (str(descr), def_fs, format.get_encoding())
+ basic_writer.__doc__ = doc
+ basic_writer.__name__ = name
+ return basic_writer
+
+# template for reader functions
+def _reader_factory(name, filetype, descr):
+ """Factory for reader functions ala matlab."""
+ def basic_reader(filename, last = None, first = 0):
+ """Common "template" to all read functions."""
+ hdl = sndfile(filename, 'read')
+ try:
+ if not hdl.get_file_format() == filetype:
+ raise PyaudioException("%s is not a %s file (is %s)" \
+ % (filename, filetype, hdl.get_file_format()))
+
+ fs = hdl.get_samplerate()
+ enc = hdl.get_encoding()
+ # Set the pointer to start position
+ nf = hdl.seek(first, 1)
+ if not nf == first:
+ raise IOError("Error while seeking at starting position")
+
+ if last is None:
+ nframes = hdl.get_nframes() - first
+ data = hdl.read_frames(nframes)
+ else:
+ data = hdl.read_frames(last)
+ finally:
+ hdl.close()
+
+ return data, fs, enc
+ doc = \
+ """ wrapper around pysndfile to read a %s file in float64,
+ in a similar manner to matlab wavread/auread/etc...
+
+ Returns a tuple (data, fs, enc), where :
+ - data are the read data (one column per channel)
+ - fs, the sampling rate
+ - enc, a string which is the encoding of the file, such as 'pcm16',
+ 'float32', etc...
+
+ For a total control over options, such as output's dtype, etc...,
+ you should use sndfile class instances instead""" % (str(descr),)
+ basic_reader.__doc__ = doc
+ basic_reader.__name__ = name
+ return basic_reader
+
+wavread = _reader_factory('wavread', 'wav',
+ formatinfo('wav', 'pcm16').get_major_str())
+auread = _reader_factory('auread', 'au',
+ formatinfo('au', 'pcm16').get_major_str())
+aiffread = _reader_factory('aiffread', 'aiff',
+ formatinfo('aiff', 'pcm16').get_major_str())
+sdifread = _reader_factory('sdifread', 'ircam',
+ formatinfo('ircam', 'pcm16').get_major_str())
+
+_f1 = formatinfo('wav', 'pcm16')
+wavwrite = _writer_factory('wavwrite', _f1, 8000, _f1.get_major_str())
+
+_f2 = formatinfo('au', 'ulaw')
+auwrite = _writer_factory('auwrite', _f2, 8000, _f2.get_major_str())
+
+_f3 = formatinfo('aiff', 'pcm16')
+aiffwrite = _writer_factory('aiffwrite', _f3, 8000, _f3.get_major_str())
+
+_f4 = formatinfo('ircam', 'pcm16')
+sdifwrite = _writer_factory('sdifwrite', _f4, 44100, _f4.get_major_str())
+
+try:
+ flacread = _reader_factory('flacread', 'flac',
+ formatinfo('flac', 'pcm16').get_major_str())
+ _f5 = formatinfo('flac', 'pcm16')
+ flacwrite = _writer_factory('flacwrite', _f5, 44100, _f5.get_major_str())
+except FlacUnsupported,e:
+ print e
+ print "Matlab API for FLAC is disabled"
+ def missing_flacread(*args):
+ raise UnimplementedError("Matlab API for FLAC is disabled on your "\
+ "installation")
+ flacread = missing_flacread
+ flacwrite = missing_flacread
--- /dev/null
+CC = colorgcc
+LD = gcc
+
+CFLAGS = -Wall -W
+
+winfdopen: winfdopen.o
+ $(LD) $< -o $@ -lsndfile
+
+winfdopen.o: winfdopen.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+badflac: badflac.o
+ $(LD) $< -o $@ -lsndfile
+
+badflac.o: badflac.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+test_badflac: badflac badflac.flac
+ ./badflac badflac.flac
+
+clean:
+ rm -f *.o
+ rm -f badflac
--- /dev/null
+# vim:syntax=python
+# Last Change: Fri Jun 01 12:00 PM 2007 J
+#
+mainobj = Object('winfdopen.c')
+winfd = Program(mainobj, LIBS = ['sndfile'])
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sndfile.h>
+
+int main(int argc, char *argv[])
+{
+ SF_INFO info;
+ SNDFILE* file;
+ sf_count_t nf;
+
+ char buffer [2048] ;
+ if (argc < 2) {
+ fprintf(stderr, "usage: badflac filename \n");
+ exit(EXIT_FAILURE);
+ }
+
+ info.format = 0;
+ file = sf_open(argv[1], SFM_READ, &info);
+ if (file == NULL) {
+ fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, argv[1]);
+ sf_command (file, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ;
+ fprintf(stderr, "sndfile error is %s:\n", buffer);
+ exit(EXIT_FAILURE);
+ }
+
+ fprintf(stderr, "Values of seek are on this platform: SET %d, CUR %d, END %d\n",
+ SEEK_SET, SEEK_CUR, SEEK_END);
+
+ fprintf(stderr, "trying to seek %lld frames\n", (long long)1);
+ nf = sf_seek(file, 1, SEEK_CUR);
+ fprintf(stderr, "seeked through %lld frames\n", nf);
+
+ sf_close(file);
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <sndfile.h>
+
+#ifdef _MSC_VER
+#define __func__ __FUNCTION__
+#else
+#include <unistd.h>
+#endif
+
+int test(const char* filename, int byfd);
+
+int main(int argc, char *argv[])
+{
+ int st;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s filename \n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ st = test(argv[1], 0);
+ if (st) {
+ fprintf(stderr, "Error while opening directly\n");
+ } else {
+ fprintf(stderr, "Opening directly is fine\n");
+ }
+
+ st = test(argv[1], 1);
+ if (st) {
+ fprintf(stderr, "Error while opening by fd\n");
+ } else {
+ fprintf(stderr, "Opening by fd is fine\n");
+ }
+
+ return 0;
+}
+
+/* If byfd is true, try to open the file with sf_open_fd */
+int test(const char* filename, int byfd)
+{
+ SF_INFO info;
+ SNDFILE* file;
+ int fid, flags, st;
+ char buffer [2048];
+
+ st = 0;
+
+ flags = O_RDONLY;
+#if (defined (WIN32) || defined (_WIN32))
+ flags |= O_BINARY;
+#endif
+
+ info.format = 0;
+ if (byfd) {
+ fid = open(filename, flags);
+ if (fid < 0) {
+ fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, filename);
+ return -1;
+ }
+
+ file = sf_open_fd(fid, SFM_READ, &info, SF_TRUE);
+ } else {
+ file = sf_open(filename, SFM_READ, &info);
+ }
+
+ if (file == NULL) {
+ fprintf(stderr, "%s:%s failed opening file %s\n", __FILE__, __func__, filename);
+ sf_command (file, SFC_GET_LOG_INFO, buffer, sizeof (buffer)) ;
+ fprintf(stderr, "sndfile error is %s:\n", buffer);
+ close(fid);
+ exit(EXIT_FAILURE);
+ } else {
+ fprintf(stderr, "%s:%s file %s has %d frames \n", __FILE__, __func__, filename, info.frames);
+ }
+
+ sf_close(file);
+
+ return st;
+}
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Tue May 22 10:00 AM 2007 J
+
+# Copyright (C) 2006-2007 Cournapeau David <cournape@gmail.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option) any
+# later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this library; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# TODO:
+# - find out why finally does not work with KeyboardInterrupt instances
+
+from tempfile import mkstemp
+from os import remove, popen
+
+from pysndfile import sndfile, formatinfo as format
+
+def play(input, sr = 22050):
+ """play(input, sr = 22050): "play" a numpy array input
+ to the audio device using aplay command, @ sampling rate
+ sr.
+
+ Warning: This is really crude, as it copies the numpy array
+ into an audio file before writing: I am too lazy to write
+ interfaces to also, windows and co..."""
+ # Check inputs are OK
+ if input.ndim == 1:
+ nc = 1
+ nframes = input.size
+ else:
+ (nframes, nc) = input.shape
+
+ # Create tmp file
+ fd, filename = mkstemp('py_player')
+
+ # Copy the data into it
+ b = sndfile(filename, 'write', format('wav', 'pcm16'), nc, sr)
+ b.write_frames(input, nframes)
+ b.close()
+
+ # Play using an audio command
+ try:
+ cmd = "aplay %s" % filename
+ popen(cmd)
+ remove(filename)
+ except KeyboardInterrupt, inst:
+ remove(filename)
+ raise inst
+
+if __name__ == '__main__':
+ # Read the content of a file into numpy array, and play the numpy
+ # array using the play command
+ import numpy as N
+ sr = 22050
+ # Play a really small noise to avoid screaming in loudspeakers
+ # or headphones.
+ noise = 0.0001 * N.random.randn((sr))
+ play(noise, sr)
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Wed Oct 03 05:00 PM 2007 J
+
+# Copyright (C) 2006-2007 Cournapeau David <cournape@gmail.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# vim:syntax=python
+
+# TODO:
+# - import format classes so that we get meaningful information from an
+# existing format
+# - better API for reader/writer, including integer formats and partial
+# reading
+# - ability to get log of sndfile ?
+# - check how to play sound under windows, OS X and other UNIX
+
+"""This module implements the wrappers around libsndfile."""
+
+__docformat__ = 'restructuredtext'
+
+#__all__ = ['sndfile', 'formatinfo']
+
+import copy
+import warnings
+
+#================
+# Load libsndfile
+#================
+import ctypes
+from ctypes import cdll, Structure, c_int, pointer, POINTER, \
+ create_string_buffer, c_char_p, sizeof, string_at
+try:
+ from ctypes import c_int64
+except ImportError, e:
+ print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\
+ " this is likely because ctypes was compiled with libffi. see" +\
+ " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914"
+ raise e
+
+from numpy.ctypeslib import ndpointer
+CTYPES_MAJOR = int(ctypes.__version__.split('.')[0])
+CTYPES_MINOR = int(ctypes.__version__.split('.')[1])
+CTYPES_MICRO = int(ctypes.__version__.split('.')[2])
+if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1):
+ raise ImportError("version of ctypes is %s, expected at least %s" \
+ % (ctypes.__version__, '1.0.1'))
+import numpy as N
+
+_SND = cdll.LoadLibrary('%SHARED_LOCATION%')
+
+#=========================
+# Definition of constants
+#=========================
+# READ/WRITE Mode
+%SFM%
+
+# SF BOOL
+%SF_BOOL%
+
+# Format
+%SF_FORMAT%
+
+# ENDIANESS
+%SF_ENDIAN%
+
+# Commands
+%SF_COMMAND%
+
+%SF_ERR%
+
+# format equivalence: dic used to create internally
+# the right enum values from user friendly strings
+py_to_snd_encoding_dic = {
+ 'pcms8' : SF_FORMAT['SF_FORMAT_PCM_S8'],
+ 'pcm16' : SF_FORMAT['SF_FORMAT_PCM_16'],
+ 'pcm24' : SF_FORMAT['SF_FORMAT_PCM_24'],
+ 'pcm32' : SF_FORMAT['SF_FORMAT_PCM_32'],
+
+ 'pcmu8' : SF_FORMAT['SF_FORMAT_PCM_U8'],
+
+ 'float32' : SF_FORMAT['SF_FORMAT_FLOAT'],
+ 'float64' : SF_FORMAT['SF_FORMAT_DOUBLE'],
+
+ 'ulaw' : SF_FORMAT['SF_FORMAT_ULAW'],
+ 'alaw' : SF_FORMAT['SF_FORMAT_ALAW'],
+ 'ima_adpcm' : SF_FORMAT['SF_FORMAT_IMA_ADPCM'],
+ 'ms_adpcm' : SF_FORMAT['SF_FORMAT_MS_ADPCM'],
+
+ 'gsm610' : SF_FORMAT['SF_FORMAT_GSM610'],
+ 'vox_adpcm' : SF_FORMAT['SF_FORMAT_VOX_ADPCM'],
+
+ 'g721_32' : SF_FORMAT['SF_FORMAT_G721_32'],
+ 'g723_24' : SF_FORMAT['SF_FORMAT_G723_24'],
+ 'g723_40' : SF_FORMAT['SF_FORMAT_G723_40'],
+
+ 'dww12' : SF_FORMAT['SF_FORMAT_DWVW_12'],
+ 'dww16' : SF_FORMAT['SF_FORMAT_DWVW_16'],
+ 'dww24' : SF_FORMAT['SF_FORMAT_DWVW_24'],
+ 'dwwN' : SF_FORMAT['SF_FORMAT_DWVW_N'],
+
+ 'dpcm8' : SF_FORMAT['SF_FORMAT_DPCM_8'],
+ 'dpcm16': SF_FORMAT['SF_FORMAT_DPCM_16']
+}
+
+py_to_snd_file_format_dic = {
+ 'wav' : SF_FORMAT['SF_FORMAT_WAV'],
+ 'aiff' : SF_FORMAT['SF_FORMAT_AIFF'],
+ 'au' : SF_FORMAT['SF_FORMAT_AU'],
+ 'raw' : SF_FORMAT['SF_FORMAT_RAW'],
+ 'paf' : SF_FORMAT['SF_FORMAT_PAF'],
+ 'svx' : SF_FORMAT['SF_FORMAT_SVX'],
+ 'nist' : SF_FORMAT['SF_FORMAT_NIST'],
+ 'voc' : SF_FORMAT['SF_FORMAT_VOC'],
+ 'ircam' : SF_FORMAT['SF_FORMAT_IRCAM'],
+ 'wav64' : SF_FORMAT['SF_FORMAT_W64'],
+ 'mat4' : SF_FORMAT['SF_FORMAT_MAT4'],
+ 'mat5' : SF_FORMAT['SF_FORMAT_MAT5'],
+ 'pvf' : SF_FORMAT['SF_FORMAT_PVF'],
+ 'xi' : SF_FORMAT['SF_FORMAT_XI'],
+ 'htk' : SF_FORMAT['SF_FORMAT_HTK'],
+ 'sds' : SF_FORMAT['SF_FORMAT_SDS'],
+ 'avr' : SF_FORMAT['SF_FORMAT_AVR'],
+ 'wavex' : SF_FORMAT['SF_FORMAT_WAVEX'],
+ 'sd2' : SF_FORMAT['SF_FORMAT_SD2'],
+ 'flac' : SF_FORMAT['SF_FORMAT_FLAC'],
+ 'caf' : SF_FORMAT['SF_FORMAT_CAF']
+}
+
+py_to_snd_endianness_dic = {
+ 'file' : SF_ENDIAN['SF_ENDIAN_FILE'],
+ 'little' : SF_ENDIAN['SF_ENDIAN_LITTLE'],
+ 'big' : SF_ENDIAN['SF_ENDIAN_BIG'],
+ 'cpu' : SF_ENDIAN['SF_ENDIAN_CPU']
+}
+
+# Those following dic are used internally to get user-friendly values from
+# sndfile enum
+SND_TO_PY_ENCODING = \
+ dict([(i, j) for j, i in py_to_snd_encoding_dic.items()])
+SND_TO_PY_FILE_FORMAT = \
+ dict([(i, j) for j, i in py_to_snd_file_format_dic.items()])
+SND_TO_PY_ENDIANNESS = \
+ dict([(i, j) for j, i in py_to_snd_endianness_dic.items()])
+
+#==========================================
+# Check that libsndfile is expected version
+#==========================================
+def get_libsndfile_version():
+ nverbuff = 256
+ verbuff = create_string_buffer(nverbuff)
+ n = _SND.sf_command(c_int(0), c_int(SF_COMMAND['SFC_GET_LIB_VERSION']),
+ verbuff, nverbuff)
+ if n < 1:
+ raise Exception("Error while getting version of libsndfile")
+
+ # Transform the buffer into a string
+ ver = ""
+ for i in range(n):
+ ver += verbuff[i]
+
+ # Get major, minor and micro from version
+ version = ver.split('-')[1]
+ prerelease = 0
+ major, minor, micro = [i for i in version.split('.')]
+ try:
+ micro = int(micro)
+ except ValueError,e:
+ print "micro is " + str(micro)
+ micro, prerelease = micro.split('pre')
+
+ return int(major), int(minor), int(micro), prerelease
+
+MAJOR, MINOR, MICRO, PRERELEASE = get_libsndfile_version()
+if not(MAJOR == 1):
+ raise Exception("audiolab expects major version %d of libsndfile" % 1)
+if not(MICRO == 17):
+ if PRERELEASE == 0:
+ prestr = "No"
+ else:
+ prestr = "%s" % PRERELEASE
+ print "WARNING libsndfile-%d.%d.%d (prerelease: %s) "\
+ "this has only been tested with libsndfile 1.0.17 for now, "\
+ "use at your own risk !" % (MAJOR, MINOR, MICRO, prestr)
+
+#================
+# Python wrappers
+#================
+
+#+++++++++++++++++
+# Public exception
+#+++++++++++++++++
+class PyaudioException(Exception):
+ pass
+
+class InvalidFormat(PyaudioException):
+ pass
+
+class PyaudioIOError(PyaudioException, IOError):
+ pass
+
+class WrappingError(PyaudioException):
+ pass
+
+class FlacUnsupported(RuntimeError, PyaudioException):
+ pass
+
+#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+# Private classes/function (Should not be used outside this file)
+#++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+class _sf_info(Structure):
+ """Structure representing C structure SF_INFO"""
+ _fields_ = [('frames', c_int64),
+ ('samplerate', c_int),
+ ('channels', c_int),
+ ('format', c_int),
+ ('sections', c_int),
+ ('seekable', c_int)]
+ def __str__(self):
+ return "%d frames, sr = %d Hz, %d channels, format is %d" % \
+ (self.frames, self.samplerate, self.channels, self.format)
+
+class _sf_format_info(Structure):
+ """Structure representing C structure SF_FORMAT_INFO (useful for
+ sf_command )"""
+ _fields_ = [('format', c_int),
+ ('name', c_char_p),
+ ('extension', c_char_p)]
+ def __str__(self):
+ return "format hex is %#010x, name is %s, extension is %s" % \
+ (self.format, self.name, self.extension)
+
+ def __repr__(self):
+ print self.__str__()
+
+class _sndfile(Structure):
+ pass
+
+sf_info_p = POINTER(_sf_info)
+sndfile_p = POINTER(_sndfile)
+
+# functions args
+# TODO: is there a way to ensure that arg1 is the right kind of pointer ?
+arg1 = c_char_p
+arg2 = c_int
+arg3 = sf_info_p
+_SND.sf_open.argtypes = [arg1, arg2, arg3]
+_SND.sf_open.restype = sndfile_p
+
+arg1 = sndfile_p
+_SND.sf_close.argtypes = [arg1]
+_SND.sf_close.restype = c_int
+
+arg1 = c_int
+arg2 = c_int
+arg3 = sf_info_p
+arg4 = c_int
+_SND.sf_open_fd.argtypes = [arg1, arg2, arg3, arg4]
+_SND.sf_open_fd.restype = sndfile_p
+
+arg1 = sndfile_p
+arg2 = ndpointer(dtype=N.float64)
+arg3 = c_int64
+
+# double function
+_SND.sf_readf_double.argtypes = [arg1, arg2, arg3]
+_SND.sf_readf_double.restype = c_int64
+
+_SND.sf_writef_double.argtypes = [arg1, arg2, arg3]
+_SND.sf_writef_double.restype = c_int64
+
+# float function
+arg1 = sndfile_p
+arg2 = ndpointer(dtype=N.float32)
+arg3 = c_int64
+_SND.sf_readf_float.argtypes = [arg1, arg2, arg3]
+_SND.sf_readf_float.restype = c_int64
+
+_SND.sf_writef_float.argtypes = [arg1, arg2, arg3]
+_SND.sf_writef_float.restype = c_int64
+
+# int function
+arg1 = sndfile_p
+arg2 = ndpointer(dtype=N.int32)
+arg3 = c_int64
+_SND.sf_readf_int.argtypes = [arg1, arg2, arg3]
+_SND.sf_readf_int.restype = c_int64
+
+_SND.sf_writef_int.argtypes = [arg1, arg2, arg3]
+_SND.sf_writef_int.restype = c_int64
+
+# short function
+arg1 = sndfile_p
+arg2 = ndpointer(dtype=N.int16)
+arg3 = c_int64
+_SND.sf_readf_short.argtypes = [arg1, arg2, arg3]
+_SND.sf_readf_short.restype = c_int64
+
+_SND.sf_writef_short.argtypes = [arg1, arg2, arg3]
+_SND.sf_writef_short.restype = c_int64
+
+# Error functions
+arg1 = sndfile_p
+_SND.sf_strerror.argtypes = [arg1]
+_SND.sf_strerror.restype = c_char_p
+
+# Function to sync data to file
+arg1 = sndfile_p
+_SND.sf_write_sync.argtypes = [arg1]
+
+# Function to seek
+arg1 = sndfile_p
+arg2 = c_int64
+arg3 = c_int
+_SND.sf_seek.argtypes = [arg1, arg2, arg3]
+_SND.sf_seek.restype = c_int64
+
+# To pass when a C function needs a NULL arg
+_cNULL = POINTER(c_int)()
+
+class _format_from_internal:
+ """Class to handle audio format with sndfile.
+
+ DO NOT USE THIS CLASS OUTSIDE pysndfile.py MODULE: YOU MAY CRASH YOUR
+ INTERPRETER !
+
+ Basically, we have 3 classes of parameters:
+ - the main format: (major format), like wav, aiff, etc...
+ - the subtype format: pcm, bits resolution
+ - endianness: little, big, as the cpu, default of the format
+
+ This class encapsulates those parameters, and can build a representation of
+ them from the format integer of sf_info. This should *NOT* be used, use
+ format instead, which inherits this class to build a valid format from user
+ friendly arguments. """
+ def __init__(self, format_integer):
+ # Get the internal values which corresponds to the values libsndfile
+ # can understand
+ self._int_type = format_integer & SF_FORMAT['SF_FORMAT_TYPEMASK']
+ self._int_encoding = format_integer & SF_FORMAT['SF_FORMAT_SUBMASK']
+ self._int_endianness = format_integer & SF_FORMAT['SF_FORMAT_ENDMASK']
+
+ assert format_integer == self._int_type | self._int_encoding |\
+ self._int_endianness
+ self._format = format_integer
+
+ # Now, we need to test if the combination of format, encoding and
+ # endianness is valid. sf_format_check needs also samplerate and
+ # channel information, so just give a fake samplerate and channel
+ # number. Looking at sndfile.c, it looks like samplerate is never
+ # actually checked, and that when channels is checked, it is only
+ # checked against values different than 1 or 2, so giving a value of
+ # 1 to channel should be ok.
+ self._sfinfo = _sf_info()
+ self._sfinfo.channels = 1
+ self._sfinfo.samplerate = 8000
+ self._sfinfo.format = self._format
+
+ ret = _SND.sf_format_check(pointer(self._sfinfo))
+ if ret is not SF_BOOL['SF_TRUE']:
+ raise InvalidFormat()
+
+ # Get the sndfile string description of the format type
+ blop = _sf_format_info()
+ blop.format = self._int_type
+ st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \
+ pointer(blop), sizeof(blop))
+ if st is not 0:
+ if SND_TO_PY_FILE_FORMAT[self._int_type] == 'flac':
+ raise FlacUnsupported("Flac is not supported by your version"\
+ " of libsndfile")
+ else:
+ raise WrappingError("Could not get format string for format "\
+ "%d, " % blop.format + "please report this problem "\
+ "to the maintainer")
+
+ self.format_str = blop.name
+
+ # Get the sndfile string description of the format subtype
+ blop.format = self._int_encoding
+ st = _SND.sf_command(_cNULL, SF_COMMAND['SFC_GET_FORMAT_INFO'], \
+ pointer(blop), sizeof(blop))
+ if st is not 0:
+ raise WrappingError()
+
+ self.encoding_str = blop.name
+
+ def get_format_raw(self):
+ """Do not use this function !"""
+ return self._format
+
+ def get_major_str(self):
+ """Do not use this function !"""
+ return self.format_str
+
+ def get_encoding_str(self):
+ """Do not use this function !"""
+ return self.encoding_str
+
+ def get_file_format(self):
+ """return user friendly file format string"""
+ return SND_TO_PY_FILE_FORMAT[self._int_type]
+
+ def get_encoding(self):
+ """return user friendly encoding string"""
+ return SND_TO_PY_ENCODING[self._int_encoding]
+
+ def get_endianness(self):
+ """return user friendly file format string"""
+ return SND_TO_PY_ENDIANNESS[self._int_endianness]
+
+ # Various function
+ def is_type(self, t):
+ return (self._format & SF_FORMAT['SF_FORMAT_TYPEMASK']) \
+ == py_to_snd_file_format_dic[t]
+
+ # Syntactic sugar
+ def __str__(self):
+ return "Major Format: %s, Encoding Format: %s" % \
+ (self.format_str, self.encoding_str)
+
+ def __repr__(self):
+ return self.__str__()
+
+#+++++++++++
+# Public API
+#+++++++++++
+
+class formatinfo(_format_from_internal):
+ def __init__(self, type = 'wav', encoding = 'pcm16', endianness = 'file'):
+ """Build a valid format usable by the sndfile class when opening an
+ audio file for writing.
+
+ Blah blah
+
+ :Parameters:
+ type : string
+ represents the major file format (wav, etc...).
+ encoding : string
+ represents the encoding (pcm16, etc..).
+ endianness : string
+ represents the endianess.
+
+ Notes
+ -----
+
+ Valid type strings are listed by file_format_dic.keys() Valid encoding
+ strings are listed by encoding_dic.keys() Valid endianness strings are
+ listed by endianness_dic.keys() """
+ # Keep the arguments
+ self.type = type
+ self.encoding = encoding
+ self.endianness = endianness
+
+ # Get the internal values which corresponds to the values libsndfile
+ # can understand
+ self._int_type = py_to_snd_file_format_dic[type]
+ self._int_encoding = py_to_snd_encoding_dic[encoding]
+ self._int_endianness = py_to_snd_endianness_dic[endianness]
+
+ # Build the internal integer from parameters, and pass it to the super
+ # class, which will do all the work
+ format = self._int_type | self._int_encoding | self._int_endianness
+
+ _format_from_internal.__init__(self, format)
+
+class sndfile:
+ """Main class to open, read and write audio files"""
+ def __init__(self, filename, mode = 'read', format = None, channels = 0, \
+ samplerate = 0):
+ """Create an instance of sndfile.
+
+ :Parameters:
+ filename : string or int
+ name of the file to open (string), or file descriptor (integer)
+ mode : string
+ 'read' for read, 'write' for write, or 'rwrite' for read and
+ write.
+ format : formatinfo
+ when opening a new file for writing, give the format to write
+ in.
+ channels : int
+ number of channels.
+ samplerate : int
+ sampling rate.
+
+ :Returns:
+ sndfile: a valid sndfile object
+
+ Notes
+ -----
+
+ format, channels and samplerate need to be given only in the write
+ modes and for raw files. """
+ # Check the mode is one of the expected values
+ if mode == 'read':
+ sfmode = SFM['SFM_READ']
+ elif mode == 'write':
+ sfmode = SFM['SFM_WRITE']
+ if format == None:
+ raise Exception("For write mode, you should provide"\
+ "a format argument !")
+ elif mode == 'rwrite':
+ sfmode = SFM['SFM_RDWR']
+ if format == None:
+ raise Exception("For write mode, you should provide"\
+ "a format argument !")
+ else:
+ raise Exception("mode %s not recognized" % str(mode))
+
+ sfinfo = _sf_info()
+ sfinfo_p = pointer(sfinfo)
+
+ # Fill the sfinfo struct
+ sfinfo.frames = c_int64(0)
+ if type(channels) is not int:
+ print "Warning, channels is converted to int, was %s" % \
+ str(type(channels))
+ sfinfo.channels = int(channels)
+ else:
+ sfinfo.channels = channels
+
+ if type(samplerate) is not int:
+ print "Warning, sampling rate is converted to int, was %s" % \
+ str(type(samplerate))
+ sfinfo.samplerate = int(samplerate)
+ else:
+ sfinfo.samplerate = samplerate
+
+ sfinfo.sections = 0
+ sfinfo.seekable = False
+ if mode == 'read' and format == None:
+ sfinfo.format = 0
+ else:
+ if sfinfo.channels > 256 or sfinfo.channels < 1:
+ msg = "number of channels is %d, expected " \
+ "between 1 and 256" % sfinfo.channels
+ raise RuntimeError(msg)
+ sfinfo.format = format.get_format_raw()
+ if not _SND.sf_format_check(sfinfo_p):
+ msg = "unknown error in format specification ?" +\
+ " Please report this to the author"
+ raise WrappingError()
+
+ sfinfo_p = pointer(sfinfo)
+ self._sfmode = sfmode
+ self.hdl = 0
+
+ if type(filename) == int:
+ res = _SND.sf_open_fd(filename, self._sfmode, sfinfo_p,
+ SF_BOOL['SF_FALSE'])
+ self._byfd = True
+ self.fd = filename
+ self.filename = ""
+ else:
+ res = _SND.sf_open(filename, self._sfmode, sfinfo_p)
+ self._byfd = False
+ self.filename = filename
+
+ try:
+ # If res is NULL, this statement will raise a ValueError exception
+ a = res[0]
+ except ValueError:
+ if self._byfd:
+ msg = "error while opening file descriptor %d\n\t->" % self.fd
+ else:
+ msg = "error while opening file %s\n\t-> " % self.filename
+ msg += _SND.sf_strerror(res)
+ if self._byfd:
+ msg += """
+(Check that the mode argument passed to sndfile is the same than the one used
+when getting the file descriptor, eg do not pass 'read' to sndfile if you
+passed 'write' to open to get the file descriptor. If you are on win32, you are
+out of luck, because its implementation of POSIX open is broken)"""
+ raise IOError("error while opening %s\n\t->%s" % (filename, msg))
+
+ if mode == 'read':
+ tmp = _format_from_internal(sfinfo.format)
+ self._format = formatinfo(tmp.get_file_format(), \
+ tmp.get_encoding(), tmp.get_endianness())
+ else:
+ self._format = format
+
+ self._sfinfo = sfinfo
+ self.hdl = res
+
+ if self.get_file_format() == 'flac':
+ def SeekNotEnabled(self, *args):
+ raise FlacUnsupported("seek not supported on Flac by default,"\
+ " because\n some version of FLAC libraries are buggy."\
+ " Read FLAC_SUPPORT.txt")
+ self.seek = SeekNotEnabled
+ else:
+ self.seek = self._seek
+
+ def __del__(self, close_func = _SND.sf_close):
+ # Stupid python needs the close_func, otherwise
+ # it may clean ctypes before calling here
+ if hasattr(self,'hdl'):
+ if not(self.hdl == 0):
+ close_func(self.hdl)
+ self.hdl = 0
+
+ def close(self):
+ """close the file."""
+ self.__del__()
+
+ def sync(self):
+ """call the operating system's function to force the writing of all
+ file cache buffers to disk the file.
+
+ No effect if file is open as read"""
+ _SND.sf_write_sync(self.hdl)
+
+ def _seek(self, offset, whence = 0, mode = 'rw'):
+ """similar to python seek function, taking only in account audio data.
+
+ :Parameters:
+ offset : int
+ the number of frames (eg two samples for stereo files) to move
+ relatively to position set by whence.
+ whence : int
+ only 0 (beginning), 1 (current) and 2 (end of the file) are
+ valid.
+ mode : string
+ If set to 'rw', both read and write pointers are updated. If
+ 'r' is given, only read pointer is updated, if 'w', only the
+ write one is (this may of course make sense only if you open
+ the file in a certain mode).
+
+ Notes
+ -----
+
+ - one only takes into accound audio data.
+ - if an invalid seek is given (beyond or before the file), a
+ PyaudioIOError is launched."""
+ c_offset = _num2int64(offset)
+ if mode == 'rw':
+ # Update both read and write pointers
+ st = _SND.sf_seek(self.hdl, c_offset, whence)
+ elif mode == 'r':
+ whence = whence | SFM['SFM_READ']
+ st = _SND.sf_seek(self.hdl, c_offset, whence)
+ elif mode == 'w':
+ whence = whence | SFM['SFM_WRITE']
+ st = _SND.sf_seek(self.hdl, c_offset, whence)
+ else:
+ raise ValueError("mode should be one of 'r', 'w' or 'rw' only")
+
+ if st == -1:
+ msg = "Error while seeking, libsndfile error is %s" \
+ % (_SND.sf_strerror(self.hdl))
+ raise PyaudioIOError(msg)
+ return st
+
+ # Functions to get informations about the file
+ def get_nframes(self):
+ """ Return the number of frames of the file"""
+ if self._sfmode == SFM['SFM_READ']:
+ # XXX: is this reliable for any file (think pipe and co ?)
+ return self._sfinfo.frames
+ else:
+ # In write/rwrite mode, the only reliable way to get the number of
+ # frames is to use seek.
+ raise NotImplementedError("Sorry, getting the current number of"
+ "frames in write modes is not supported yet")
+
+ def get_samplerate(self):
+ """ Return the samplerate in Hz of the file"""
+ return self._sfinfo.samplerate
+
+ def get_channels(self):
+ """ Return the number of channels of the file"""
+ return self._sfinfo.channels
+
+ def get_file_format(self):
+ """return user friendly file format string"""
+ return SND_TO_PY_FILE_FORMAT[self._format._int_type]
+
+ def get_encoding(self):
+ """return user friendly encoding string"""
+ return SND_TO_PY_ENCODING[self._format._int_encoding]
+
+ def get_endianness(self):
+ """return user friendly file format string"""
+ return SND_TO_PY_ENDIANNESS[self._format._int_endianness]
+
+ #------------------
+ # Functions to read
+ #------------------
+ def read_frames(self, nframes, dtype = N.float64):
+ """Read nframes frames of the file.
+
+ :Parameters:
+ nframes : int
+ number of frames to read.
+ dtype : numpy dtype
+ dtype of the returned array containing read data (see note).
+
+ Notes
+ -----
+
+ - read_frames updates the read pointer.
+ - One column is one channel.
+ - if float are requested when the file contains integer data, you will
+ get normalized data (that is the max possible integer will be 1.0,
+ and the minimal possible value -1.0).
+ - if integers are requested when the file contains floating point data,
+ it may give wrong results because there is an ambiguity: if the
+ floating data are normalized, you can get a file with only 0 !
+ Getting integer data from files encoded in normalized floating point
+ is not supported (yet: sndfile supports it)."""
+ c_nframes = _num2int64(nframes)
+ if c_nframes < 0:
+ raise ValueError("number of frames has to be >= 0")
+
+ # XXX: inout argument
+ if self._sfinfo.channels > 1:
+ y = N.zeros((nframes, self._sfinfo.channels), dtype)
+ else:
+ y = N.zeros(nframes, dtype)
+
+ if dtype == N.float64:
+ res = _SND.sf_readf_double(self.hdl, y, c_nframes)
+ elif dtype == N.float32:
+ res = _SND.sf_readf_float(self.hdl, y, c_nframes)
+ elif dtype == N.int32:
+ res = _SND.sf_readf_int(self.hdl, y, c_nframes)
+ elif dtype == N.int16:
+ res = _SND.sf_readf_short(self.hdl, y, c_nframes)
+ else:
+ RuntimeError("Sorry, only float, double, int and short read " + \
+ "supported for now")
+
+ if not(res == nframes):
+ msg = "Read %d frames, expected to read %d" % (res, nframes)
+ msg += ", libsndfile last msg is \n\t%s" \
+ % _SND.sf_strerror(self.hdl)
+ raise IOError(msg)
+
+ return y
+
+ #-------------------
+ # Functions to write
+ #-------------------
+ # TODO: Think about overflow vs type of input, etc...
+ def write_frames(self, input, nframes = -1):
+ """write data to file.
+
+ :Parameters:
+ input : ndarray
+ array containing data to write.
+ nframes : int
+ number of frames to write.
+
+ Notes
+ -----
+
+ - one channel is one column
+ - updates the write pointer.
+ - if float are given when the file contains integer data, you should
+ put normalized data (that is the range [-1..1] will be written as the
+ maximum range allowed by the integer bitwidth)."""
+ # First, get the number of channels and frames from input
+ if input.ndim == 1:
+ nc = 1
+ else:
+ if input.ndim > 2:
+ raise Exception("Expect array of rank <= 2, got %d" \
+ % input.ndim)
+ nc = input.shape[1]
+
+ if nframes == -1:
+ nframes = N.size(input)
+ # Number of channels should be the one expected
+ if not(nc == self._sfinfo.channels):
+ raise Exception("Expected %d channels, got %d" % \
+ (self._sfinfo.channels, nc))
+
+ # Writing to the file
+ c_nframes = _num2int64(nframes)
+ if c_nframes < 0:
+ raise ValueError("number of frames has to be >= 0")
+
+ input = N.require(input, requirements = 'C')
+
+ if input.dtype == N.float32:
+ if self._check_overflow(input):
+ warnings.warn("Warning, overflow detected when writing.")
+ res = _SND.sf_writef_float(self.hdl, input, c_nframes)
+ elif input.dtype == N.float64:
+ self._check_overflow(input)
+ if self._check_overflow(input):
+ warnings.warn("Warning, overflow detected when writing.")
+ res = _SND.sf_writef_double(self.hdl, input, c_nframes)
+ elif input.dtype == N.int32:
+ res = _SND.sf_writef_int(self.hdl, input, c_nframes)
+ elif input.dtype == N.int16:
+ res = _SND.sf_writef_short(self.hdl, input, c_nframes)
+ else:
+ raise Exception("type of input not understood: input should"
+ " be float64 or float32""")
+
+ if not(res == nframes):
+ raise IOError("write %d frames, expected to write %d" \
+ % res, nframes)
+
+ def _check_overflow(self, data):
+ if N.max(data ** 2) >= 1.:
+ return True
+ return False
+
+ # Syntactic sugar
+ def __repr__(self):
+ return self.__str__()
+
+ def __str__(self):
+ repstr = "----------------------------------------\n"
+ if self._byfd:
+ repstr += "File : %d (opened by file descriptor)\n" % self.fd
+ else:
+ repstr += "File : %s\n" % self.filename
+ repstr += "Channels : %d\n" % self._sfinfo.channels
+ repstr += "Sample rate : %d\n" % self._sfinfo.samplerate
+ repstr += "Frames : %d\n" % self._sfinfo.frames
+ repstr += "Raw Format : %#010x -> %s\n" % \
+ (self._format.get_format_raw(), self._format.get_major_str())
+ repstr += "File format : %s\n" % self.get_file_format()
+ repstr += "Encoding : %s\n" % self.get_encoding()
+ repstr += "Endianness : %s\n" % self.get_endianness()
+ repstr += "Sections : %d\n" % self._sfinfo.sections
+ if self._sfinfo.seekable:
+ seek = 'True'
+ else:
+ seek = 'False'
+ repstr += "Seekable : %s\n" % seek
+ repstr += "Duration : %s\n" % self._generate_duration_str()
+ return repstr
+
+ def _generate_duration_str(self):
+ if self._sfinfo.samplerate < 1:
+ return None
+ tsec = self._sfinfo.frames / self._sfinfo.samplerate
+ hrs = tsec / 60 / 60
+ tsec = tsec % (60 ** 2)
+ mins = tsec / 60
+ tsec = tsec % 60
+ secs = tsec
+ ms = 1000 * self._sfinfo.frames / self._sfinfo.samplerate % 1000
+
+ return "%02d:%02d:%02d.%3d" % (hrs, mins, secs, ms)
+
+def supported_format():
+ # XXX: broken
+ return py_to_snd_file_format_dic.keys()
+
+def supported_endianness():
+ # XXX: broken
+ return py_to_snd_endianness_dic.keys()
+
+def supported_encoding():
+ # XXX: broken
+ return py_to_snd_encoding_dic.keys()
+
+def _num2int64(value):
+ """ Convert a python objet to a c_int64, safely."""
+ if not (type(value) == int or type(value) == long):
+ value = long(value)
+ print "Warning, converting %s to long" % str(value)
+ c_value = c_int64(value)
+ if not c_value.value == value:
+ raise RuntimeError("Error while converting %s to a c_int64"\
+ ", maybe %s is too big ?" % str(value))
+ return c_value
--- /dev/null
+Program('main', source = ['simple.c'], LIBS = ['asound'])
+Program('main2', source = ['simple2.c'], LIBS = ['asound'])
--- /dev/null
+cdef extern from "alsa/asoundlib.h":
+ ctypedef enum snd_pcm_stream_t:
+ SND_PCM_STREAM_PLAYBACK
+ SND_PCM_STREAM_CAPTURE
+ ctypedef enum snd_pcm_access_t :
+ SND_PCM_ACCESS_MMAP_INTERLEAVED
+ SND_PCM_ACCESS_MMAP_NONINTERLEAVED
+ SND_PCM_ACCESS_MMAP_COMPLEX
+ SND_PCM_ACCESS_RW_INTERLEAVED
+ SND_PCM_ACCESS_RW_NONINTERLEAVED
+ ctypedef enum snd_pcm_format_t :
+ SND_PCM_FORMAT_UNKNOWN
+ SND_PCM_FORMAT_S8
+ SND_PCM_FORMAT_U8
+ SND_PCM_FORMAT_S16_LE
+ SND_PCM_FORMAT_S16_BE
+ SND_PCM_FORMAT_U16_LE
+ SND_PCM_FORMAT_U16_BE
+ SND_PCM_FORMAT_S24_LE
+ SND_PCM_FORMAT_S24_BE
+ SND_PCM_FORMAT_U24_LE
+ SND_PCM_FORMAT_U24_BE
+ SND_PCM_FORMAT_S32_LE
+ SND_PCM_FORMAT_S32_BE
+ SND_PCM_FORMAT_U32_LE
+ SND_PCM_FORMAT_U32_BE
+ SND_PCM_FORMAT_FLOAT_LE
+ SND_PCM_FORMAT_FLOAT_BE
+ SND_PCM_FORMAT_FLOAT64_LE
+ SND_PCM_FORMAT_FLOAT64_BE
+ SND_PCM_FORMAT_IEC958_SUBFRAME_LE
+ SND_PCM_FORMAT_IEC958_SUBFRAME_BE
+ SND_PCM_FORMAT_MU_LAW
+ SND_PCM_FORMAT_A_LAW
+ SND_PCM_FORMAT_IMA_ADPCM
+ SND_PCM_FORMAT_MPEG
+ SND_PCM_FORMAT_GSM
+ SND_PCM_FORMAT_SPECIAL
+ SND_PCM_FORMAT_S24_3LE
+ SND_PCM_FORMAT_S24_3BE
+ SND_PCM_FORMAT_U24_3LE
+ SND_PCM_FORMAT_U24_3BE
+ SND_PCM_FORMAT_S20_3LE
+ SND_PCM_FORMAT_S20_3BE
+ SND_PCM_FORMAT_U20_3LE
+ SND_PCM_FORMAT_U20_3BE
+ SND_PCM_FORMAT_S18_3LE
+ SND_PCM_FORMAT_S18_3BE
+ SND_PCM_FORMAT_U18_3LE
+ SND_PCM_FORMAT_U18_3BE
+ SND_PCM_FORMAT_S16
+ SND_PCM_FORMAT_U16
+ SND_PCM_FORMAT_S24
+ SND_PCM_FORMAT_U24
+ SND_PCM_FORMAT_S32
+ SND_PCM_FORMAT_U32
+ SND_PCM_FORMAT_FLOAT
+ SND_PCM_FORMAT_FLOAT64
+ SND_PCM_FORMAT_IEC958_SUBFRAME
+
+ ctypedef struct snd_pcm_t
+ # XXX: how to make sure the typedef is OK ?
+ ctypedef unsigned long snd_pcm_uframes_t
+
+ int snd_pcm_open(snd_pcm_t **, char*, int, int)
+ int snd_pcm_close(snd_pcm_t *)
+ int snd_pcm_drain(snd_pcm_t *)
+
+ int snd_pcm_set_params(snd_pcm_t *, snd_pcm_format_t,
+ snd_pcm_access_t, unsigned int,
+ unsigned int, int, unsigned int)
+
+ int snd_pcm_writei(snd_pcm_t *, void*, snd_pcm_uframes_t)
+
+ char* snd_strerror(int error)
+
+ int snd_card_next(int *icard)
+ int snd_card_get_name(int icard, char** name)
+ char* snd_asoundlib_version()
+
+cdef extern from "numpy/arrayobject.h":
+ ctypedef int intp
+ ctypedef extern class numpy.ndarray [object PyArrayObject]:
+ cdef char *data
+ cdef int nd
+ cdef intp *dimensions
+ cdef intp *strides
+ cdef int flags
+
+cdef extern from "stdlib.h":
+ ctypedef unsigned long size_t
+ void free(void *ptr)
+ void *malloc(size_t size)
+ void *realloc(void *ptr, size_t size)
+ size_t strlen(char *s)
+ char *strcpy(char *dest, char *src)
+
+cdef extern from "stdint.h":
+ ctypedef unsigned short int16_t
+
+cdef extern from "Python.h":
+ object PyString_FromStringAndSize(char *v, int len)
+
+class AlsaException(Exception):
+ pass
+
+def asoundlib_version():
+ return snd_asoundlib_version()
+
+def card_indexes():
+ """Returns a list containing index of cards recognized by alsa."""
+ cdef int icur = -1
+
+ cards = []
+ while 1:
+ st = snd_card_next(&icur)
+ if st < 0:
+ raise AlsaException("Could not get next card")
+ if icur < 0:
+ break
+ cards.append(icur)
+ return tuple(cards)
+
+def card_name(index):
+ """Get the name of the card corresponding to the given index."""
+ cdef char* sptr
+ st = snd_card_get_name(index, &sptr)
+ if st < 0:
+ raise AlsaException("Error while getting card name %d: alsa error "\
+ "was %s" % (index, snd_strerror(st)))
+ else:
+ cardname = PyString_FromStringAndSize(sptr, len(sptr))
+ free(sptr)
+ return cardname
+
+cdef class _PCM:
+ cdef snd_pcm_t* pcmhdl
+ cdef public char* name
+
+ def __new__(self, device = "default", stream = SND_PCM_STREAM_PLAYBACK):
+ self.pcmhdl = NULL
+
+ st = snd_pcm_open(&self.pcmhdl, device, stream, 0)
+ if st < 0:
+ raise AlsaException("Cannot open device %s: %s" % (device, snd_strerror(st)))
+
+ def __init__(self, device = "default", stream = SND_PCM_STREAM_PLAYBACK):
+ self.name = device
+
+ def __dealloc__(self):
+ if self.pcmhdl:
+ snd_pcm_close(self.pcmhdl)
+
+cdef class Device:
+ cdef _PCM pcm
+ cdef unsigned int samplerate
+ # XXX: set property instead
+ cdef public unsigned int channels
+ cdef snd_pcm_format_t format
+ cdef snd_pcm_access_t access
+ def __init__(self, samplerate = 48000, channels = 1,
+ format = SND_PCM_FORMAT_S16,
+ access = SND_PCM_ACCESS_RW_INTERLEAVED):
+ self.pcm = _PCM()
+
+ self.samplerate = samplerate
+ self.channels = channels
+
+ self.access = access
+ self.format = format
+
+ st = snd_pcm_set_params(self.pcm.pcmhdl, format, access, channels,
+ samplerate, 1, 1000000)
+ if st < 0:
+ raise AlsaException()
+
+ def play_short(self, ndarray input):
+ cdef int16_t* p = <int16_t*>input.data
+ cdef int16_t* buff = NULL
+ #cdef int n = input.dimensions[0]
+ cdef int n = input.dimensions[0]
+ cdef int nb = n / 1024 / 16
+ cdef snd_pcm_uframes_t bufsz = 1024 * 16
+
+ assert self.format == SND_PCM_FORMAT_S16
+ assert self.access == SND_PCM_ACCESS_RW_INTERLEAVED
+
+ for i from 0 <= i < nb:
+ st = snd_pcm_writei(self.pcm.pcmhdl, &p[i * bufsz], bufsz)
+ if not st == bufsz:
+ print "Error while writing to device", st
+ print snd_strerror(st)
+
+ snd_pcm_drain(self.pcm.pcmhdl)
+
+ def _get_name(self):
+ return self.pcm.name
+ name = property(_get_name)
--- /dev/null
+import numpy as np
+
+from _alsa import card_name, card_indexes, asoundlib_version
+from _alsa import Device, AlsaException
+
+def play(input, samplerate = 48000):
+ if input.ndim == 1:
+ n = input.size
+ nc = 1
+ elif input.ndim == 2:
+ n, nc = input.shape
+ else:
+ raise ValueError("Only ndim 1 or 2 supported")
+
+ if not input.dtype in (np.float32, np.float64):
+ raise ValueError("input should be array of float32 or float64 !")
+
+ try:
+ dev = Device(samplerate = samplerate, channels = nc)
+ dev.play_short((16384 * input).astype(np.int16))
+ except AlsaException, e:
+ raise IOError(str(e))
+
+if __name__ == '__main__':
+ print "Asoundlib version is", asoundlib_version()
+ for i in card_indexes():
+ print card_name(i)
+
+ dev = Device()
+ print "Device name:", dev.name
+
+ a = 0.2 * np.random.randn(4e4)
+ play(a, 16000)
+ play(a, 8000)
+ play(a, 22050)
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Fri Sep 07 02:00 PM 2007 J
+
+# Copyright (C) 2006-2007 Cournapeau David <cournape@gmail.com>
+#
+# This library is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# This library is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+# vim:syntax=python
+
+"""This is a small module to wrap basic functionalities of alsa: it uses ctypes
+for the wrapping."""
+
+__docformat__ = 'restructuredtext'
+
+__all__ = ['asoundlib_version', 'asoundlib_version_numbers', 'card_indexes', \
+ 'card_name', 'card_longname']
+#================
+# Load alsa lib
+#================
+import ctypes
+from ctypes import cdll, Structure, c_int, pointer, POINTER, c_uint,\
+ create_string_buffer, c_char_p, sizeof, byref, string_at, c_size_t,\
+ c_void_p, c_ulong, c_uint16, c_long, c_uint8, c_int16, c_float
+try:
+ from ctypes import c_int64
+except ImportError, e:
+ print "Cannot import c_int64 from ctypes: if you are on ubuntu/debian," +\
+ " this is likely because ctypes was compiled with libffi. see" +\
+ " https://launchpad.net/ubuntu/+source/python2.5/+bug/71914"
+ raise e
+
+from numpy.ctypeslib import ndpointer
+CTYPES_MAJOR = int(ctypes.__version__.split('.')[0])
+CTYPES_MINOR = int(ctypes.__version__.split('.')[1])
+CTYPES_MICRO = int(ctypes.__version__.split('.')[2])
+if CTYPES_MAJOR < 1 or (CTYPES_MINOR == 0 and CTYPES_MICRO < 1):
+ raise ImportError("version of ctypes is %s, expected at least %s" \
+ % (ctypes.__version__, '1.0.1'))
+import numpy as N
+
+ALSALOC = 'libasound.so.2'
+_ALSA = cdll.LoadLibrary(ALSALOC)
+# XXX: alsa means linux, normally. How to locate the glibc ?
+_C = cdll.LoadLibrary("libc.so.6")
+
+# Check version
+_ALSA.snd_asoundlib_version.args = []
+_ALSA.snd_asoundlib_version.restype = c_char_p
+
+def asoundlib_version():
+ """Return the version of asoundlib as a string"""
+ version = _ALSA.snd_asoundlib_version()
+ return version
+
+def asoundlib_version_numbers():
+ """Return the version of asoundlib as a tuple(major, minor, micro)."""
+ version = asoundlib_version()
+ return tuple(int(i) for i in version.split('.'))
+
+major, minor, micro = asoundlib_version_numbers()
+if not (major == 1 and minor == 0):
+ raise RuntimeError("Expected 1.0.x version, got %s instead" % asoundlib_version())
+
+# Define pointers to opaque structures
+class snd_pcm_t(Structure):
+ pass
+
+class snd_pcm_hw_params_t(Structure):
+ pass
+
+class snd_pcm_sw_params_t(Structure):
+ pass
+
+class snd_output_t(Structure):
+ pass
+
+snd_pcm_t_p = POINTER(snd_pcm_t)
+snd_pcm_hw_params_t_p = POINTER(snd_pcm_hw_params_t)
+snd_pcm_sw_params_t_p = POINTER(snd_pcm_sw_params_t)
+snd_output_t_p = POINTER(snd_output_t)
+
+#------------
+# Define enum
+#------------
+SND_PCM_STREAM = {'SND_PCM_STREAM_PLAYBACK' : 0,
+ 'SND_PCM_STREAM_CAPTURE' : 1}
+
+#-----------------------------------------------
+# Define all the function we need from asoundlib
+#-----------------------------------------------
+arg1 = c_int
+arg2 = POINTER(c_char_p)
+_ALSA.snd_card_get_name.argtypes = [arg1, arg2]
+_ALSA.snd_card_get_name.restype = c_int
+
+arg1 = c_int
+arg2 = POINTER(c_char_p)
+_ALSA.snd_card_get_longname.argtypes = [arg1, arg2]
+_ALSA.snd_card_get_longname.restype = c_int
+
+arg1 = POINTER(c_int)
+_ALSA.snd_card_next.argtypes = [arg1]
+_ALSA.snd_card_next.restype = c_int
+
+arg1 = c_int
+_ALSA.snd_strerror.argtypes = [arg1]
+_ALSA.snd_strerror.restype = c_char_p
+
+# output related functions
+arg1 = POINTER(snd_output_t_p)
+_ALSA.snd_output_buffer_open.argtypes = [arg1]
+_ALSA.snd_output_buffer_open.restype = c_int
+
+arg1 = snd_output_t_p
+_ALSA.snd_output_close.argtypes = [arg1]
+_ALSA.snd_output_close.restype = c_int
+
+arg1 = snd_output_t_p
+arg2 = POINTER(c_char_p)
+_ALSA.snd_output_buffer_string.argtypes = [arg1, arg2]
+_ALSA.snd_output_buffer_string.restype = c_size_t
+
+arg1 = snd_pcm_t_p
+arg2 = snd_output_t_p
+_ALSA.snd_pcm_dump.argtypes = [arg1, arg2]
+_ALSA.snd_pcm_dump.restype = c_int
+
+# pcm related functions
+arg1 = POINTER(POINTER(snd_pcm_t))
+arg2 = c_char_p
+arg3 = c_int
+arg4 = c_int
+_ALSA.snd_pcm_open.argtypes = [arg1, arg2, arg3, arg4]
+_ALSA.snd_pcm_open.restype = c_int
+
+arg1 = POINTER(snd_pcm_t)
+_ALSA.snd_pcm_close.argtypes = [arg1]
+_ALSA.snd_pcm_close.restype = c_int
+
+arg1 = POINTER(snd_pcm_t)
+_ALSA.snd_pcm_reset.argtypes = [arg1]
+_ALSA.snd_pcm_reset.restype = c_int
+
+arg1 = POINTER(snd_pcm_t)
+_ALSA.snd_pcm_drain.argtypes = [arg1]
+_ALSA.snd_pcm_drain.restype = c_int
+
+arg1 = POINTER(snd_pcm_hw_params_t_p)
+_ALSA.snd_pcm_hw_params_malloc.argtypes = [arg1]
+_ALSA.snd_pcm_hw_params_malloc.restype = c_int
+
+arg1 = snd_pcm_hw_params_t_p
+_ALSA.snd_pcm_hw_params_free.argtypes = [arg1]
+_ALSA.snd_pcm_hw_params_free.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+_ALSA.snd_pcm_hw_params_any.argtypes = [arg1, arg2]
+_ALSA.snd_pcm_hw_params_any.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+_ALSA.snd_pcm_hw_params.argtypes = [arg1, arg2]
+_ALSA.snd_pcm_hw_params.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = c_int
+_ALSA.snd_pcm_hw_params_set_access.argtypes = [arg1, arg2, arg3]
+_ALSA.snd_pcm_hw_params_set_access.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = POINTER(c_uint)
+arg4 = c_int
+_ALSA.snd_pcm_hw_params_set_rate_near.argtypes = [arg1, arg2, arg3, arg4]
+_ALSA.snd_pcm_hw_params_set_rate_near.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = c_uint
+_ALSA.snd_pcm_hw_params_set_channels.argtypes = [arg1, arg2, arg3]
+_ALSA.snd_pcm_hw_params_set_channels.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = c_int
+_ALSA.snd_pcm_hw_params_set_format.argtypes = [arg1, arg2, arg3]
+_ALSA.snd_pcm_hw_params_set_format.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = POINTER(c_ulong)
+_ALSA.snd_pcm_hw_params_set_buffer_size_near.argtypes = [arg1, arg2, arg3]
+_ALSA.snd_pcm_hw_params_set_buffer_size_near.restype = c_int
+
+arg1 = snd_pcm_t_p
+arg2 = snd_pcm_hw_params_t_p
+arg3 = POINTER(c_ulong)
+arg4 = c_int
+_ALSA.snd_pcm_hw_params_set_period_size_near.argtypes = [arg1, arg2, arg3, arg4]
+_ALSA.snd_pcm_hw_params_set_period_size_near.restype = c_int
+
+# write
+arg1 = snd_pcm_t_p
+arg2 = c_void_p
+arg3 = c_ulong
+_ALSA.snd_pcm_writei.argtypes = [arg1, arg2, arg3]
+_ALSA.snd_pcm_writei.restype = c_ulong
+
+#==================
+# General functions
+#==================
+class AlsaException(Exception):
+ pass
+
+class OutputBuffer():
+ def __init__(self):
+ ohdl = snd_output_t_p()
+ st = _ALSA.snd_output_buffer_open(byref(ohdl))
+ if st:
+ raise AlsaException("Error creating output buffer")
+ self._hdl = ohdl
+
+ def __del__(self, close_func = _ALSA.snd_output_close):
+ if hasattr(self, '_hdl'):
+ if not(self._hdl == 0):
+ close_func(self._hdl)
+ self._hdl = 0
+
+
+OHDL = OutputBuffer()
+
+#========================
+# Cards related functions
+#========================
+def card_indexes():
+ """Returns a list containing index of cards recognized by alsa."""
+ cards = []
+ cur = c_int(-1)
+ while 1:
+ st = _ALSA.snd_card_next(byref(cur))
+ if st < 0:
+ raise AlsaException("Could not get next card")
+ if cur.value < 0:
+ break
+ cards.append(cur.value)
+ return tuple(cards)
+
+def card_name(index):
+ """Get the name of the card corresponding to the given index."""
+ sptr = c_char_p(0)
+ st = _ALSA.snd_card_get_name(index, byref(sptr))
+ if st < 0:
+ raise AlsaException("Error while getting card name %d: alsa error "\
+ "was %s" % (index, _ALSA.snd_strerror(st)))
+ cardname = string_at(sptr)
+ _C.free(sptr)
+ return cardname
+
+def card_longname(index):
+ """Get the long name of the card corresponding to the given index."""
+ sptr = c_char_p(0)
+ st = _ALSA.snd_card_get_longname(index, byref(sptr))
+ if st < 0:
+ raise AlsaException("Error while getting card longname %d: alsa error "\
+ "was %s" % (index, _ALSA.snd_strerror(st)))
+ cardname = string_at(sptr)
+ _C.free(sptr)
+ return cardname
+
+#=======================
+# Pcm related functions
+#=======================
+class _HwParams:
+ """Small class to assure that the hw parmas are always freed."""
+ def __init__(self):
+ self._hdl = snd_pcm_hw_params_t_p()
+ st = _ALSA.snd_pcm_hw_params_malloc(byref(self._hdl))
+ if st:
+ raise AlsaException("error while creating hw params %s" \
+ % _ALSA.snd_strerror(st))
+
+ def __del__(self, close_func = _ALSA.snd_pcm_hw_params_free):
+ if hasattr(self, '_hdl'):
+ if not(self._hdl == 0):
+ close_func(self._hdl)
+ self._hdl = 0
+
+class Pcm:
+ def __init__(self, device = 'default', samplerate = 48000, channels = 1):
+ self._pcmhdl = POINTER(snd_pcm_t)()
+
+ # Open the pcm device
+ st = _ALSA.snd_pcm_open(byref(self._pcmhdl), device,
+ SND_PCM_STREAM['SND_PCM_STREAM_PLAYBACK'], 0)
+ if st:
+ raise AlsaException("error while opening pcm device %s; alsa error is %s"\
+ % (device, _ALSA.snd_strerror(st)))
+
+ # Set hw params
+ self._set_hw_params(samplerate, channels)
+
+ # Reset the pcm device
+ st = _ALSA.snd_pcm_reset(self._pcmhdl)
+ if st:
+ raise AlsaException("error while resetting pcm : %s" \
+ % _ALSA.snd_strerror(st))
+
+ self.nc = channels
+ self.samplerate = samplerate
+
+ def _set_hw_params(self, samplerate, channels):
+ # XXX: Parameters copied from sndfile-play.c from libsndfile. Check the
+ # meaning
+ alsa_period_size = c_ulong(1024)
+ alsa_buffer_frames = c_ulong(alsa_period_size.value * 4)
+
+ hw = _HwParams()
+ hwhdl = hw._hdl
+ st = _ALSA.snd_pcm_hw_params_any(self._pcmhdl, hwhdl)
+ if st < 0:
+ raise AlsaException("cannot initialize hw st: %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params_set_access(self._pcmhdl, hwhdl, 3)
+ if st < 0:
+ raise AlsaException("cannot initialize hw st: %s" \
+ % _ALSA.snd_strerror(st))
+
+ rrate = c_uint(samplerate)
+ st = _ALSA.snd_pcm_hw_params_set_rate_near(self._pcmhdl, hwhdl, byref(rrate), 0)
+ if st < 0:
+ raise AlsaException("cannot set samplerate : %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params_set_format(self._pcmhdl, hwhdl, 14)
+ if st < 0:
+ raise AlsaException("cannot set format : %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params_set_channels(self._pcmhdl, hwhdl, channels)
+ if st < 0:
+ raise AlsaException("cannot set number of channels : %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params_set_buffer_size_near(self._pcmhdl, hwhdl,
+ byref(alsa_buffer_frames))
+ if st < 0:
+ raise AlsaException("cannot set buffer size: %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params_set_period_size_near(self._pcmhdl, hwhdl,
+ byref(alsa_period_size), 0)
+ if st < 0:
+ raise AlsaException("cannot set period size: %s" \
+ % _ALSA.snd_strerror(st))
+
+ st = _ALSA.snd_pcm_hw_params(self._pcmhdl, hwhdl)
+ if st < 0:
+ raise AlsaException("cannot set params : %s" \
+ % _ALSA.snd_strerror(st))
+
+ def __del__(self, close_func = _ALSA.snd_pcm_close):
+ if hasattr(self, '_pcmhdl'):
+ try:
+ self._pcmhdl[0]
+ close_func(self._pcmhdl)
+ self._pcmhdl = POINTER(snd_pcm_t)()
+ except ValueError:
+ pass
+
+ def __str__(self):
+ buf = c_char_p()
+ msg = "Pcm device: "
+ if self._pcmhdl > 0:
+ msg += " Opened\n"
+ # XXX error checking
+ st = _ALSA.snd_pcm_dump(self._pcmhdl, OHDL._hdl)
+ st = _ALSA.snd_output_buffer_string(OHDL._hdl, byref(buf))
+ if st > 0:
+ msg += string_at(buf)
+ return msg
+
+ def __repr__(self):
+ return self.__str__()
+
+ def write_float(self, data):
+ bufsz = 1024 * 2
+ nb = data.size / bufsz / self.nc
+ for i in range(nb):
+ b = a[i * bufsz: (i+1) * bufsz]
+ st = _ALSA.snd_pcm_writei(self._pcmhdl,
+ b.ctypes.data_as(POINTER(c_float)), bufsz)
+ if not st == bufsz:
+ print "Error while writing to device"
+ b = a[nb * bufsz: -1]
+ reframes = b.size / self.nc
+ st = _ALSA.snd_pcm_writei(self._pcmhdl,
+ b.ctypes.data_as(POINTER(c_float)), reframes)
+ if not st == reframes:
+ print "Error while writing to device"
+
+ _ALSA.snd_pcm_drain(self._pcmhdl)
+#class AlsaPlayer:
+# def __init__(self, device = "default"):
+# pcm = _Pcm(device)
+# hw = _HwParams()
+# err = _ALSA.snd_pcm_hw_params_any(pcm.pcmhdl, hw.hwparamshdl)
+# if err:
+# raise AlsaException("
+
+if __name__ == '__main__':
+ print card_indexes()
+ print [card_name(i) for i in card_indexes()]
+ print [card_longname(i) for i in card_indexes()]
+ print asoundlib_version()
+ print asoundlib_version_numbers()
+ from scikits.audiolab import wavread
+ a, fs = wavread('test.wav')[:2]
+ if a.ndim > 1:
+ nc = a.shape[1]
+ else:
+ nc = 1
+ a = a.astype(N.float32)
+ pcm = Pcm("default:1", 22050, channels = nc)
+ print pcm
+ pcm.write_float(a)
--- /dev/null
+from distutils.core import setup
+from distutils.extension import Extension
+from Cython.Distutils import build_ext
+from numpy.distutils.misc_util import get_numpy_include_dirs
+
+setup(name = "PyrexGuide",
+ ext_modules=[
+ Extension("_alsa", ["_alsa.pyx"],
+ libraries = ['asound'],
+ include_dirs = get_numpy_include_dirs())],
+ cmdclass = {'build_ext': build_ext})
+
--- /dev/null
+#include <stdio.h>
+
+#include <alsa/asoundlib.h>
+
+int main()
+{
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hw;
+
+ int st;
+ unsigned int rrate = 44100;
+ snd_output_t *output;
+
+ st = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);
+ if (st) {
+ fprintf(stderr, "Error opening pcm\n");
+ return -1;
+ }
+
+ st = snd_pcm_hw_params_malloc(&hw);
+ if (st) {
+ fprintf(stderr, "Error while allocating hw\n");
+ return -1;
+ }
+
+ st = snd_pcm_hw_params_any(pcm, hw);
+ if (st) {
+ fprintf(stderr, "Error while allocating hw\n");
+ return -1;
+ }
+
+ st = snd_pcm_hw_params_set_access(pcm, hw, SND_PCM_ACCESS_RW_INTERLEAVED);
+ if (st) {
+ fprintf(stderr, "Error while allocating hw\n");
+ return -1;
+ }
+
+ st = snd_pcm_hw_params_set_rate_near(pcm, hw, &rrate, 0);
+ if (st) {
+ fprintf(stderr, "Error while allocating hw\n");
+ return -1;
+ }
+ fprintf(stdout, "rate is %d\n", rrate);
+
+ st = snd_pcm_hw_params(pcm, hw);
+ if (st) {
+ fprintf(stderr, "Error while allocating hw\n");
+ return -1;
+ }
+
+ //st = snd_output_stdio_attach(&output, stdout, 0);
+ //if (st < 0) {
+ // printf("Output failed: %s\n", snd_strerror(st));
+ // return 0;
+ //}
+ st = snd_output_buffer_open(&output);
+ if (st < 0) {
+ printf("Output buffer open failed: %s\n", snd_strerror(st));
+ return 0;
+ }
+ char* buf;
+ snd_pcm_dump(pcm, output);
+ st = snd_output_buffer_string(output, &buf);
+ fprintf(stdout, "%d chars\n", st);
+ fprintf(stdout, "%s", buf);
+
+ //snd_output_close(output);
+ //snd_pcm_hw_params_free(hw);
+ snd_pcm_close(pcm);
+
+ snd_config_update_free_global();
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdint.h>
+
+#include <alsa/asoundlib.h>
+
+int play(snd_pcm_t *pcm)
+{
+ int16_t buff[1024 * 16];
+ int i, j;
+ snd_pcm_uframes_t frames;
+
+ for (i = 0; i < 4; ++i) {
+ for (j = 0; j < 1024 * 16; ++j) {
+ buff[j] = random() & 0xff;
+ }
+ frames = snd_pcm_writei(pcm, buff, 1024 * 16);
+ fprintf(stderr, "%lu\n", frames);
+ }
+
+ return 0;
+}
+
+int set(snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access,
+ unsigned int channels, unsigned int samplerate)
+{
+ int st;
+
+ st = snd_pcm_set_params(pcm, format, access, channels, samplerate,
+ 1, 1000000);
+ if (st < 0) {
+ printf("Output failed: %s\n", snd_strerror(st));
+ return st;
+ }
+
+ return 0;
+}
+
+int main()
+{
+ snd_pcm_t *pcm;
+ snd_pcm_hw_params_t *hw;
+
+ int st;
+ unsigned int rrate = 48000;
+ unsigned int nc = 1;
+ snd_output_t *output;
+
+ int i, j;
+ snd_pcm_sframes_t frames;
+
+ st = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0);
+ if (st) {
+ fprintf(stderr, "Error opening pcm\n");
+ return -1;
+ }
+
+ st = snd_output_stdio_attach(&output, stdout, 0);
+ if (st < 0) {
+ printf("Output failed: %s\n", snd_strerror(st));
+ return -1;
+ }
+
+ set(pcm, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 44100);
+
+ play(pcm);
+ snd_pcm_drain(pcm);
+
+ set(pcm, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 8000);
+
+ play(pcm);
+
+ //snd_pcm_dump(pcm, output);
+
+ snd_output_close(output);
+ snd_pcm_close(pcm);
+
+ snd_config_update_free_global();
+
+ return 0;
+}
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Mon May 21 12:00 PM 2007 J
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Tue Jul 17 11:00 AM 2007 J
+from os.path import join, dirname
+from os import remove
+from tempfile import mkstemp
+
+from numpy.testing import *
+import numpy as N
+
+set_package_path()
+from audiolab.matapi import wavread, auread, aiffread, sdifread, flacread
+from audiolab.matapi import wavwrite, auwrite, aiffwrite, sdifwrite, flacwrite
+from audiolab.pysndfile import PyaudioException, sndfile, formatinfo as audio_format
+from audiolab.pysndfile import FlacUnsupported
+restore_path()
+
+#Optional:
+set_local_path()
+# import modules that are located in the same directory as this file.
+from testcommon import open_tmp_file, close_tmp_file
+restore_path()
+
+class test_audiolab(NumpyTestCase):
+ def _test_read(self, func, format, filext):
+ # Create a tmp audio file, write some random data into it, and check it
+ # is the expected data when read from a function from the matapi.
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.' + filext)
+ try:
+ nbuff = 22050
+ noise = 0.1 * N.random.randn(nbuff)
+
+ # Open the copy file for writing
+ b = sndfile(cfilename, 'write', format, 1, nbuff)
+ b.write_frames(noise, nbuff)
+ b.close()
+
+ # Reread the data
+ b = sndfile(cfilename, 'read')
+ rcnoise = b.read_frames(nbuff)
+ b.close()
+
+ rnoise = func(cfilename)[0]
+
+ assert_array_equal(rnoise, rcnoise)
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_wavread(self):
+ """ Check wavread """
+ self._test_read(wavread, audio_format('wav', 'pcm16', 'file'), 'wav')
+
+ def test_flacread(self):
+ """ Check flacread """
+ try:
+ self._test_read(flacread, audio_format('flac', 'pcm16', 'file'), 'flac')
+ except FlacUnsupported:
+ print "Flac unsupported, flacread not tested"
+
+ def test_auread(self):
+ """ Check auread """
+ self._test_read(auread, audio_format('au', 'ulaw', 'file'), 'au')
+
+ def test_aiffread(self):
+ """ Check aiffread """
+ self._test_read(aiffread, audio_format('aiff', 'pcm16', 'file'), 'aiff')
+
+ def test_sdifread(self):
+ """ Check sdifread (ircam format) """
+ self._test_read(sdifread, audio_format('ircam', 'pcm16', 'file'), 'sdif')
+
+ def test_bad_wavread(self):
+ """ Check wavread on bad file"""
+ # Create a tmp audio file with non wav format, write some random data into it,
+ # and check it can not be opened by wavread
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nbuff = 22050
+ noise = 0.1 * N.random.randn(nbuff)
+
+ # Open the copy file for writing
+ format = audio_format('aiff', 'pcm16')
+ b = sndfile(cfilename, 'write', format, 1, nbuff)
+
+ b.write_frames(noise, nbuff)
+
+ b.close()
+
+ b = sndfile(cfilename, 'read')
+ rcnoise = b.read_frames(nbuff)
+ b.close()
+
+ try:
+ rnoise = wavread(cfilename)[0]
+ raise Exception("wavread on non wav file succeded, expected to fail")
+ except PyaudioException, e:
+ pass
+ #print str(e) + ", as expected"
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def _test_write(self, func, format, filext):
+ """ Check *write functions from matpi """
+ rfd1, fd1, cfilename1 = open_tmp_file('pysndfiletest.' + filext)
+ rfd2, fd2, cfilename2 = open_tmp_file('pysndfiletest.' + filext)
+ try:
+ nbuff = 22050
+ fs = nbuff
+ noise = 0.1 * N.random.randn(nbuff)
+
+ # Open the first file for writing with sndfile
+ b = sndfile(cfilename1, 'write', format, 1, fs)
+
+ b.write_frames(noise, nbuff)
+
+ b.close()
+
+ # Write same data with wavwrite
+ func(noise, cfilename2, fs)
+
+ # Compare if both files have same hash
+ f1 = open(cfilename1)
+ f2 = open(cfilename2)
+
+ import md5
+
+ m1 = md5.new()
+ m2 = md5.new()
+
+ m1.update(f1.read())
+ m2.update(f2.read())
+
+ f1.close()
+ f2.close()
+ assert m1.hexdigest() == m2.hexdigest()
+ finally:
+ close_tmp_file(rfd1, cfilename1)
+ close_tmp_file(rfd2, cfilename2)
+
+ def test_wavwrite(self):
+ """ Check wavwrite """
+ self._test_write(wavwrite, audio_format('wav', 'pcm16', 'file'), 'wav')
+
+ def test_aiffwrite(self):
+ """ Check aiffwrite """
+ self._test_write(aiffwrite, audio_format('aiff', 'pcm16', 'file'), 'aiff')
+
+ def test_auwrite(self):
+ """ Check wavwrite """
+ self._test_write(auwrite, audio_format('au', 'ulaw', 'file'), 'au')
+
+ def test_sdifwrite(self):
+ """ Check wavwrite """
+ self._test_write(sdifwrite, audio_format('ircam', 'pcm16', 'file'), 'sdif')
+
+ def test_flacwrite(self):
+ """ Check flacwrite """
+ try:
+ self._test_write(flacwrite, audio_format('flac', 'pcm16', 'file'), 'flac')
+ except FlacUnsupported:
+ print "Flac unsupported, flacwrite not tested"
+
+if __name__ == "__main__":
+ NumpyTest().run()
--- /dev/null
+#! /usr/bin/env python
+# Last Change: Tue Jul 17 11:00 AM 2007 J
+"""Test for the sndfile class."""
+from os.path import join, dirname
+import os
+import sys
+
+from numpy.testing import NumpyTestCase, assert_array_equal, NumpyTest, \
+ assert_array_almost_equal, set_package_path, restore_path, set_local_path
+import numpy as N
+
+set_package_path()
+from audiolab import pysndfile
+from audiolab.pysndfile import sndfile, formatinfo as audio_format
+restore_path()
+
+set_local_path()
+from testcommon import open_tmp_file, close_tmp_file
+restore_path()
+
+# XXX: there is a lot to refactor here
+class test_pysndfile(NumpyTestCase):
+ def test_basic_io(self):
+ """ Check open, close and basic read/write"""
+ # dirty !
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nbuff = 22050
+
+ # Open the test file for reading
+ a = sndfile(ofilename, 'read')
+ nframes = a.get_nframes()
+
+ # Open the copy file for writing
+ format = audio_format('wav', 'pcm16')
+ b = sndfile(fd, 'write', format, a.get_channels(),
+ a.get_samplerate())
+
+ # Copy the data
+ for i in range(nframes / nbuff):
+ tmpa = a.read_frames(nbuff)
+ assert tmpa.dtype == N.float
+ b.write_frames(tmpa, nbuff)
+ nrem = nframes % nbuff
+ tmpa = a.read_frames(nrem)
+ assert tmpa.dtype == N.float
+ b.write_frames(tmpa, nrem)
+
+ a.close()
+ b.close()
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+
+ def test_basic_io_fd(self):
+ """ Check open from fd works"""
+ # dirty !
+ if sys.platform == 'win32':
+ print "Not testing opening by fd because does not work on win32"
+ else:
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ fd = os.open(ofilename, os.O_RDONLY)
+ hdl = sndfile(fd, 'read')
+ hdl.close()
+
+ def test_raw(self):
+ rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.raw')
+ format = audio_format('raw', 'pcm16', 'little')
+ a = sndfile(rawname, 'read', format, 1, 11025)
+ assert a.get_nframes() == 11290
+ a.close()
+
+ def test_float64(self):
+ """Check float64 write/read works"""
+ # dirty !
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nbuff = 22050
+
+ # Open the test file for reading
+ a = sndfile(ofilename, 'read')
+ nframes = a.get_nframes()
+
+ # Open the copy file for writing
+ format = audio_format('wav', 'float64')
+ b = sndfile(fd, 'write', format, a.get_channels(),
+ a.get_samplerate())
+
+ # Copy the data in the wav file
+ for i in range(nframes / nbuff):
+ tmpa = a.read_frames(nbuff, dtype = N.float64)
+ assert tmpa.dtype == N.float64
+ b.write_frames(tmpa, nbuff)
+ nrem = nframes % nbuff
+ tmpa = a.read_frames(nrem)
+ b.write_frames(tmpa, nrem)
+
+ a.close()
+ b.close()
+
+ # Now, reopen both files in for reading, and check data are
+ # the same
+ a = sndfile(ofilename, 'read')
+ b = sndfile(cfilename, 'read')
+ for i in range(nframes / nbuff):
+ tmpa = a.read_frames(nbuff, dtype = N.float64)
+ tmpb = b.read_frames(nbuff, dtype = N.float64)
+ assert_array_equal(tmpa, tmpb)
+
+ a.close()
+ b.close()
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_float32(self):
+ """Check float write/read works"""
+ # dirty !
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nbuff = 22050
+
+ # Open the test file for reading
+ a = sndfile(ofilename, 'read')
+ nframes = a.get_nframes()
+
+ # Open the copy file for writing
+ format = audio_format('wav', 'float32')
+ b = sndfile(fd, 'write', format, a.get_channels(),
+ a.get_samplerate())
+
+ # Copy the data in the wav file
+ for i in range(nframes / nbuff):
+ tmpa = a.read_frames(nbuff, dtype = N.float32)
+ assert tmpa.dtype == N.float32
+ b.write_frames(tmpa, nbuff)
+ nrem = nframes % nbuff
+ tmpa = a.read_frames(nrem)
+ b.write_frames(tmpa, nrem)
+
+ a.close()
+ b.close()
+
+ # Now, reopen both files in for reading, and check data are
+ # the same
+ a = sndfile(ofilename, 'read')
+ b = sndfile(cfilename, 'read')
+ for i in range(nframes / nbuff):
+ tmpa = a.read_frames(nbuff, dtype = N.float32)
+ tmpb = b.read_frames(nbuff, dtype = N.float32)
+ assert_array_equal(tmpa, tmpb)
+
+ a.close()
+ b.close()
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_supported_features(self):
+ msg = "\nsupported file format are : this test is broken FIXME"
+ #for i in pysndfile.supported_format():
+ # msg += str(i) + ', '
+ #print msg
+ #msg = "supported encoding format are : "
+ #for i in pysndfile.supported_encoding():
+ # msg += str(i) + ', '
+ #print msg
+ #msg = "supported endianness are : "
+ #for i in pysndfile.supported_endianness():
+ # msg += str(i) + ', '
+ print msg
+
+ def test_short_io(self):
+ # TODO: check if neg or pos value is the highest in abs
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nb = 2 ** 14
+ nbuff = 22050
+ fs = 22050
+ a = N.random.random_integers(-nb, nb, nbuff)
+ a = a.astype(N.short)
+
+ # Open the file for writing
+ format = audio_format('wav', 'pcm16')
+ b = sndfile(fd, 'write', format, 1, fs)
+
+ b.write_frames(a, nbuff)
+ b.close()
+
+ b = sndfile(cfilename, 'read')
+
+ read_a = b.read_frames(nbuff, dtype = N.short)
+ b.close()
+
+ assert_array_equal(a, read_a)
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_int_io(self):
+ # TODO: check if neg or pos value is the highest in abs
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ nb = 2 ** 25
+ nbuff = 22050
+ fs = 22050
+ a = N.random.random_integers(-nb, nb, nbuff)
+ a = a.astype(N.int32)
+
+ # Open the file for writing
+ format = audio_format('wav', 'pcm32')
+ b = sndfile(fd, 'write', format, 1, fs)
+
+ b.write_frames(a, nbuff)
+ b.close()
+
+ b = sndfile(cfilename, 'read')
+
+ read_a = b.read_frames(nbuff, dtype = N.int32)
+ b.close()
+
+ assert_array_equal(a, read_a)
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_mismatch(self):
+ # This test open a file for writing, but with bad args (channels and
+ # nframes inverted)
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ # Open the file for writing
+ format = audio_format('wav', 'pcm16')
+ try:
+ b = sndfile(fd, 'write', \
+ format, channels = 22000, samplerate = 1)
+ raise Exception("Try to open a file with more than 256 "\
+ "channels, this should not succeed !")
+ except RuntimeError, e:
+ #print "Gave %d channels, error detected is \"%s\"" % (22000, e)
+ pass
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_bigframes(self):
+ """ Try to seek really far"""
+ rawname = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ a = sndfile(rawname, 'read')
+ try:
+ try:
+ a.seek(2 ** 60)
+ raise Exception("Seek really succeded ! This should not happen")
+ except pysndfile.PyaudioIOError, e:
+ pass
+ finally:
+ a.close()
+
+ def test_float_frames(self):
+ """ Check nframes can be a float"""
+ rfd, fd, cfilename = open_tmp_file('pysndfiletest.wav')
+ try:
+ # Open the file for writing
+ format = audio_format('wav', 'pcm16')
+ a = sndfile(fd, 'rwrite', format, channels = 1,
+ samplerate = 22050)
+ tmp = N.random.random_integers(-100, 100, 1000)
+ tmp = tmp.astype(N.short)
+ a.write_frames(tmp, tmp.size)
+ a.seek(0)
+ a.sync()
+ ctmp = a.read_frames(1e2, dtype = N.short)
+ a.close()
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+ def test_nofile(self):
+ """ Check the failure when opening a non existing file."""
+ try:
+ f = sndfile("floupi.wav", "read")
+ raise AssertionError("call to non existing file should not succeed")
+ except IOError:
+ pass
+ except Exception, e:
+ raise AssertionError("opening non existing file should raise a IOError exception, got %s instead" % e.__class__)
+
+class test_seek(NumpyTestCase):
+ def test_simple(self):
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ # Open the test file for reading
+ a = sndfile(ofilename, 'read')
+ nframes = a.get_nframes()
+
+ buffsize = 1024
+ buffsize = min(nframes, buffsize)
+
+ # First, read some frames, go back, and compare buffers
+ buff = a.read_frames(buffsize)
+ a.seek(0)
+ buff2 = a.read_frames(buffsize)
+ assert_array_equal(buff, buff2)
+
+ a.close()
+
+ # Now, read some frames, go back, and compare buffers
+ # (check whence == 1 == SEEK_CUR)
+ a = sndfile(ofilename, 'read')
+ a.read_frames(buffsize)
+ buff = a.read_frames(buffsize)
+ a.seek(-buffsize, 1)
+ buff2 = a.read_frames(buffsize)
+ assert_array_equal(buff, buff2)
+
+ a.close()
+
+ # Now, read some frames, go back, and compare buffers
+ # (check whence == 2 == SEEK_END)
+ a = sndfile(ofilename, 'read')
+ buff = a.read_frames(nframes)
+ a.seek(-buffsize, 2)
+ buff2 = a.read_frames(buffsize)
+ assert_array_equal(buff[-buffsize:], buff2)
+
+ def test_rw(self):
+ """Test read/write pointers for seek."""
+ ofilename = join(dirname(pysndfile.__file__), 'test_data', 'test.wav')
+ rfd, fd, cfilename = open_tmp_file('rwseektest.wav')
+ try:
+ ref = sndfile(ofilename, 'read')
+ test = sndfile(fd, 'rwrite', format = ref._format, channels =
+ ref.get_channels(), samplerate = ref.get_samplerate())
+ n = 1024
+
+ rbuff = ref.read_frames(n, dtype = N.int16)
+ test.write_frames(rbuff)
+ tbuff = test.read_frames(n, dtype = N.int16)
+
+ assert_array_equal(rbuff, tbuff)
+
+ # Test seeking both read and write pointers
+ test.seek(0, 0)
+ test.write_frames(rbuff)
+ tbuff = test.read_frames(n, dtype = N.int16)
+ assert_array_equal(rbuff, tbuff)
+
+ # Test seeking only read pointer
+ rbuff1 = rbuff.copy()
+ rbuff2 = rbuff1 * 2 + 1
+ rbuff2.clip(-30000, 30000)
+ test.seek(0, 0, 'r')
+ test.write_frames(rbuff2)
+ tbuff1 = test.read_frames(n, dtype = N.int16)
+ try:
+ tbuff2 = test.read_frames(n, dtype = N.int16)
+ except IOError, e:
+ msg = "write pointer was updated in read seek !"
+ msg += "\n(msg is %s)" % e
+ raise AssertionError(msg)
+
+ assert_array_equal(rbuff1, tbuff1)
+ assert_array_equal(rbuff2, tbuff2)
+ if N.all(rbuff2 == tbuff1):
+ raise AssertionError("write pointer was updated"\
+ " in read seek !")
+
+ # Test seeking only write pointer
+ rbuff3 = rbuff1 * 2 - 1
+ rbuff3.clip(-30000, 30000)
+ test.seek(0, 0, 'rw')
+ test.seek(n, 0, 'w')
+ test.write_frames(rbuff3)
+ tbuff1 = test.read_frames(n, N.int16)
+ try:
+ assert_array_equal(tbuff1, rbuff1)
+ except AssertionError:
+ raise AssertionError("read pointer was updated in write seek !")
+
+ try:
+ tbuff3 = test.read_frames(n, N.int16)
+ except IOError, e:
+ msg = "read pointer was updated in write seek !"
+ msg += "\n(msg is %s)" % e
+ raise AssertionError(msg)
+
+ assert_array_equal(tbuff3, rbuff3)
+ test.close()
+
+ finally:
+ close_tmp_file(rfd, cfilename)
+
+if __name__ == "__main__":
+ NumpyTest().run()
--- /dev/null
+import os
+from tempfile import mkstemp
+import sys
+
+def open_tmp_file(name):
+ """On any sane platforms, return a fd on a tmp file. On windows, returns
+ the filename, and as such, is not secure (someone else can reopen the file
+ in between)."""
+ fd, cfilename = mkstemp('pysndfiletest.wav')
+ if sys.platform == 'win32':
+ return fd, cfilename, cfilename
+ else:
+ return fd, fd, cfilename
+
+def close_tmp_file(fd, filename):
+ """On any sane platforms, remove the file . On windows, only close the
+ file."""
+ os.close(fd)
+ os.remove(filename)
+