From: yomguy <> Date: Tue, 19 Aug 2008 15:25:35 +0000 (+0000) Subject: * Add audiolab X-Git-Tag: 1.1~823 X-Git-Url: https://git.parisson.com/?a=commitdiff_plain;h=3caf3739463edbf1b26da1c6501a826b83cf1486;p=telemeta.git * Add audiolab --- diff --git a/telemeta/visualization/scikits/__init__.py b/telemeta/visualization/scikits/__init__.py new file mode 100644 index 00000000..de40ea7c --- /dev/null +++ b/telemeta/visualization/scikits/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/telemeta/visualization/scikits/audiolab/__init__.py b/telemeta/visualization/scikits/audiolab/__init__.py new file mode 100644 index 00000000..12911595 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/__init__.py @@ -0,0 +1,35 @@ +#! /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 + +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() diff --git a/telemeta/visualization/scikits/audiolab/docs/Makefile b/telemeta/visualization/scikits/audiolab/docs/Makefile new file mode 100644 index 00000000..5ea6c555 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/Makefile @@ -0,0 +1,54 @@ +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; diff --git a/telemeta/visualization/scikits/audiolab/docs/audiolab1.png b/telemeta/visualization/scikits/audiolab/docs/audiolab1.png new file mode 100644 index 00000000..c88e46f5 Binary files /dev/null and b/telemeta/visualization/scikits/audiolab/docs/audiolab1.png differ diff --git a/telemeta/visualization/scikits/audiolab/docs/base.tex b/telemeta/visualization/scikits/audiolab/docs/base.tex new file mode 100644 index 00000000..b10ea72c --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/base.tex @@ -0,0 +1,1182 @@ +% 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: Setup function (called at the bottom of this stylesheet). +% \DN{}: Handler for Docutils document tree node `node`; called by +% the Python module. +% \DEV: External variable, set by the Python module. +% \DEC: External command. It is called by the Python module and must be +% defined in this stylesheet. +% \DNA{}{}{}{}{}: +% Attribute handler for `attribute` set on nodes of type `nodename`. +% See below for a discussion of attribute handlers. +% \DA{}{}{}{}{}: +% Attribute handler for all `attribute`. Called only when no specific +% \DNA handler is defined. +% \DNC{}: +% Handler for `class`, when set on nodes of type `nodename`. +% \DC{}: +% Handler for `class`. Called only when no specific \DNC +% handler is defined. +% \D: 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 +% +% 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 \DNC. + \Difdefined{DN#4C#3}{% + % Pass only contents, nothing else! + \csname DN#4C#3\endcsname{#5}% + }{% + % Otherwise, dispatch to \DC. + \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 \DNA. + \csname DN#4A#2\endcsname{#1}{#2}{#3}{#4}{#5}% + }{\Difdefined{DA#2}{% + % Otherwise dispatch to \DA. + \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 \Dtitle. + \csname D\DEVparent title\endcsname{#1}% +} +\providecommand{\DNsubtitle}[1]{% + % Dispatch to \Dsubtitle. + \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} diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/format1.py b/telemeta/visualization/scikits/audiolab/docs/examples/format1.py new file mode 100644 index 00000000..f9166f98 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/format1.py @@ -0,0 +1,7 @@ +from scikits.audiolab import formatinfo as format + +f = format('aiff', 'ulaw') +print f + +f = format('ircam', 'float32') +print f diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/format2.py b/telemeta/visualization/scikits/audiolab/docs/examples/format2.py new file mode 100644 index 00000000..6b176a65 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/format2.py @@ -0,0 +1,6 @@ +from scikits.audiolab import supported_format, supported_encoding, \ + supported_endianness + +print supported_format() +print supported_encoding() +print supported_endianness() diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/matlab1.py b/telemeta/visualization/scikits/audiolab/docs/examples/matlab1.py new file mode 100644 index 00000000..fe1e8fac --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/matlab1.py @@ -0,0 +1,20 @@ +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) diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/quick1.py b/telemeta/visualization/scikits/audiolab/docs/examples/quick1.py new file mode 100644 index 00000000..1d3c4739 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/quick1.py @@ -0,0 +1,5 @@ +import scikits.audiolab as audiolab + +a = audiolab.sndfile('test.flac', 'read') +data = a.read_frames(1000) +a.close() diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/usage1.py b/telemeta/visualization/scikits/audiolab/docs/examples/usage1.py new file mode 100644 index 00000000..a689a01f --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/usage1.py @@ -0,0 +1,6 @@ +import scikits.audiolab as audiolab + +filename = 'test.flac' +a = audiolab.sndfile(filename, 'read') + +print a diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/usage2.py b/telemeta/visualization/scikits/audiolab/docs/examples/usage2.py new file mode 100644 index 00000000..0117e264 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/usage2.py @@ -0,0 +1,12 @@ +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[:]) diff --git a/telemeta/visualization/scikits/audiolab/docs/examples/write1.py b/telemeta/visualization/scikits/audiolab/docs/examples/write1.py new file mode 100644 index 00000000..b0fd1368 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/examples/write1.py @@ -0,0 +1,34 @@ +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) diff --git a/telemeta/visualization/scikits/audiolab/docs/index.txt b/telemeta/visualization/scikits/audiolab/docs/index.txt new file mode 100644 index 00000000..54110172 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/index.txt @@ -0,0 +1,264 @@ +.. + 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 `_ 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). + + ``_ + + +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) diff --git a/telemeta/visualization/scikits/audiolab/docs/user.tex b/telemeta/visualization/scikits/audiolab/docs/user.tex new file mode 100644 index 00000000..6ea70994 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/docs/user.tex @@ -0,0 +1,64 @@ +% 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}} + diff --git a/telemeta/visualization/scikits/audiolab/info.py b/telemeta/visualization/scikits/audiolab/info.py new file mode 100644 index 00000000..47dd67f6 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/info.py @@ -0,0 +1,2 @@ +VERSION = '0.8dev' +ignore = False diff --git a/telemeta/visualization/scikits/audiolab/matapi.py b/telemeta/visualization/scikits/audiolab/matapi.py new file mode 100644 index 00000000..1568fe9c --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/matapi.py @@ -0,0 +1,148 @@ +#! /usr/bin/env python +# Last Change: Mon Sep 10 07:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# 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 diff --git a/telemeta/visualization/scikits/audiolab/misc/Makefile b/telemeta/visualization/scikits/audiolab/misc/Makefile new file mode 100644 index 00000000..2fcbde26 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/misc/Makefile @@ -0,0 +1,23 @@ +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 diff --git a/telemeta/visualization/scikits/audiolab/misc/Sconstruct b/telemeta/visualization/scikits/audiolab/misc/Sconstruct new file mode 100644 index 00000000..6c29ab33 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/misc/Sconstruct @@ -0,0 +1,5 @@ +# vim:syntax=python +# Last Change: Fri Jun 01 12:00 PM 2007 J +# +mainobj = Object('winfdopen.c') +winfd = Program(mainobj, LIBS = ['sndfile']) diff --git a/telemeta/visualization/scikits/audiolab/misc/badflac.c b/telemeta/visualization/scikits/audiolab/misc/badflac.c new file mode 100644 index 00000000..906ad44f --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/misc/badflac.c @@ -0,0 +1,37 @@ +#include +#include + +#include + +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; +} diff --git a/telemeta/visualization/scikits/audiolab/misc/badflac.flac b/telemeta/visualization/scikits/audiolab/misc/badflac.flac new file mode 100644 index 00000000..70d0a657 Binary files /dev/null and b/telemeta/visualization/scikits/audiolab/misc/badflac.flac differ diff --git a/telemeta/visualization/scikits/audiolab/misc/winfdopen.c b/telemeta/visualization/scikits/audiolab/misc/winfdopen.c new file mode 100644 index 00000000..de95e453 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/misc/winfdopen.c @@ -0,0 +1,82 @@ +#include +#include +#include + +#include + +#ifdef _MSC_VER +#define __func__ __FUNCTION__ +#else +#include +#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; +} diff --git a/telemeta/visualization/scikits/audiolab/pyaudioio.py b/telemeta/visualization/scikits/audiolab/pyaudioio.py new file mode 100644 index 00000000..94c1e9fc --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/pyaudioio.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python +# Last Change: Tue May 22 10:00 AM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# 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) diff --git a/telemeta/visualization/scikits/audiolab/pysndfile.py.in b/telemeta/visualization/scikits/audiolab/pysndfile.py.in new file mode 100644 index 00000000..574e5bb1 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/pysndfile.py.in @@ -0,0 +1,886 @@ +#! /usr/bin/env python +# Last Change: Wed Oct 03 05:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# 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 diff --git a/telemeta/visualization/scikits/audiolab/soundio/SConstruct b/telemeta/visualization/scikits/audiolab/soundio/SConstruct new file mode 100644 index 00000000..1d1b221e --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/SConstruct @@ -0,0 +1,2 @@ +Program('main', source = ['simple.c'], LIBS = ['asound']) +Program('main2', source = ['simple2.c'], LIBS = ['asound']) diff --git a/telemeta/visualization/scikits/audiolab/soundio/_alsa.pyx b/telemeta/visualization/scikits/audiolab/soundio/_alsa.pyx new file mode 100644 index 00000000..5fb6a117 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/_alsa.pyx @@ -0,0 +1,198 @@ +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 = 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) diff --git a/telemeta/visualization/scikits/audiolab/soundio/alsa.py b/telemeta/visualization/scikits/audiolab/soundio/alsa.py new file mode 100644 index 00000000..15754deb --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/alsa.py @@ -0,0 +1,35 @@ +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) diff --git a/telemeta/visualization/scikits/audiolab/soundio/alsa_ctypes.py b/telemeta/visualization/scikits/audiolab/soundio/alsa_ctypes.py new file mode 100644 index 00000000..d1c65fc8 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/alsa_ctypes.py @@ -0,0 +1,440 @@ +#! /usr/bin/env python +# Last Change: Fri Sep 07 02:00 PM 2007 J + +# Copyright (C) 2006-2007 Cournapeau David +# +# 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) diff --git a/telemeta/visualization/scikits/audiolab/soundio/setup.py b/telemeta/visualization/scikits/audiolab/soundio/setup.py new file mode 100644 index 00000000..a2234513 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/setup.py @@ -0,0 +1,12 @@ +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}) + diff --git a/telemeta/visualization/scikits/audiolab/soundio/simple.c b/telemeta/visualization/scikits/audiolab/soundio/simple.c new file mode 100644 index 00000000..96e1c37f --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/simple.c @@ -0,0 +1,74 @@ +#include + +#include + +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; +} diff --git a/telemeta/visualization/scikits/audiolab/soundio/simple2.c b/telemeta/visualization/scikits/audiolab/soundio/simple2.c new file mode 100644 index 00000000..401d78dd --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/soundio/simple2.c @@ -0,0 +1,80 @@ +#include +#include + +#include + +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; +} diff --git a/telemeta/visualization/scikits/audiolab/tests/__init__.py b/telemeta/visualization/scikits/audiolab/tests/__init__.py new file mode 100644 index 00000000..7f2fbc0c --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/tests/__init__.py @@ -0,0 +1,2 @@ +#! /usr/bin/env python +# Last Change: Mon May 21 12:00 PM 2007 J diff --git a/telemeta/visualization/scikits/audiolab/tests/test_matapi.py b/telemeta/visualization/scikits/audiolab/tests/test_matapi.py new file mode 100644 index 00000000..fe17b8e9 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/tests/test_matapi.py @@ -0,0 +1,164 @@ +#! /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() diff --git a/telemeta/visualization/scikits/audiolab/tests/test_pysndfile.py b/telemeta/visualization/scikits/audiolab/tests/test_pysndfile.py new file mode 100644 index 00000000..632a4bc8 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/tests/test_pysndfile.py @@ -0,0 +1,396 @@ +#! /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() diff --git a/telemeta/visualization/scikits/audiolab/tests/testcommon.py b/telemeta/visualization/scikits/audiolab/tests/testcommon.py new file mode 100644 index 00000000..dd8c4a42 --- /dev/null +++ b/telemeta/visualization/scikits/audiolab/tests/testcommon.py @@ -0,0 +1,20 @@ +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) +