Commit 79e33cc2 authored by Mark Hymers's avatar Mark Hymers
Browse files

Add first pywavplay octave/linux version


Signed-off-by: Mark Hymers's avatarMark Hymers <mark.hymers@hankel.co.uk>
parents
*.mex
*.o
pawavplay_alsa.mex: pawavplay_alsa.cpp
mkoctfile --mex pawavplay_alsa.cpp -lportaudio
.PHONY: clean
clean:
rm -f pawavplay_alsa.mex
PAWavPlay for Octave on Linux
=============================
This is a modified version of the original pawavplay (found at
https://github.com/jgdsens/pa-wavplay/) which has been modified
to support playing audio on Linux when using Octave rather
than MATLAB (although it should work with MATLAB too, it is
just untested). The license is as-per the original code.
This has been tested using an built-in Intel HDA sound chipset
on the developer's Thinkpad X260 as well as with a
USB-attached Komplete Audio 6 sound card (Only the first
four channels could be tested by the author due to lack
of a SPDIF device to receive channels 5 and 6), but all 6
should work.
For any queries, please contact:
Mark Hymers `<mark.hymers@ynic.york.ac.uk>`
Compiling
---------
To compile for Octave on Linux, simply run:
```
make
```
This will produce the file `pawavplay_alsa.mex`.
To build, you will require the `mkoctfile` command and the
Portaudio library and development headers.
On Debian (and derived distributions), you can ensure
that you have these dependencies by running:
```
apt-get install liboctave-dev portaudio19-dev
```
Note that you may need to run this as root (using `sudo` or
by changing to root using `su`).
Usage
-----
It works as the original, except that the only supported
device type is 'alsa'. So, to list your available devices,
use:
```
pa_wavplay('alsa')
```
This will list your devices and their IDs.
To play audio, use:
```
pa_wavplay(buffer, [samplerate], [deviceid], [devicetype]);
```
To record audio (currently untested), use:
```
inputbuffer = pa_wavrecord(channels, nsamples, [samplerate], [deviceid], [devicetype])
```
To play and record audio simultaneously (currently untested):
```
inputbuffer = pa_wavplayrecord(playbuffer,[playdevice],[samplerate], [recnsamples], [recchannels], [recdevice],[devicetype])
```
see help for each of these commands for much more information.
function pa_wavplay(varargin)
% pa_wavplay: playback a matrix to multichannel sound hardware
%
% Usage: pa_wavplay([devicetype])
% pa_wavplay(buffer, [samplerate], [deviceid], [devicetype])
%
% pa_wavplay is a tool for playing multi-channel audio through your ASIO
% device. Arguments in [] are optional
%
% pa_wavplay([devicetype]) will list all your audio devices for that
% device type then exit.
%
% - buffer is the matrix to play
% - samplerate is the sampling frequency. Default: 44100
% - deviceid is the device id to use for output. Default: 0
% - devicetype determines which sound driver to use
% 'win' Windows Multimedia Device
% 'dx' DirectX DirectSound driver
% 'wasapi' Windows WASAPI Device
% 'asio' ASIO Driver (default)
%
% For stereo playback, buffer should be an N-by-2 matrix. The number of
% audio channels supported is hardware dependent.
%
% samplerate should be a valid integer sample rate, eg. 22050 44100 etc.
%
% As of pa_wavplay 0.2, buffer should be either of type double or
% single. pa_wavplay uses 32-bit floats (single) internally.
%
% SEE ALSO: pa_wavrecord, pa_wavplayrecord
%
% Copyright 2014 Sensimetrics Corp.
% For additional Matlab tools for audio and video, consult www.sens.com
% check right num of args
error(nargchk(0,4,nargin));
% defaults
device_opt = 1;
device = 0;
fs = 44100;
% no args, print devices for asio
if (nargin==0)
pawavplay_alsa;
return;
end
% if devtype specified
if ischar(varargin{end}),
s=varargin{end};
varargin(end)=[];
if (nargin==1)
pawavplay_alsa;
return;
end
end
% data buffer
y = varargin{1};
% sample rate
if length(varargin)>=2,
fs=varargin{2};
end
% device id
if length(varargin) >= 3,
if (~(ischar(varargin{3})))
device = varargin{3};
end
end
fprintf('Using ALSA driver\n');
pawavplay_alsa(y, device, fs, 0, 0, 0, -1);
% [EOF] pa_wavplay.m
function y = pa_wavplayrecord(varargin)
% pa_wavplayrecord: simultaneous playback & record of multichannel sound
%
% Usage:
% inputbuffer = pa_wavplayrecord(playbuffer,[playdevice],[samplerate],
% [recnsamples], [recfirstchannel], [reclastchannel],
% [recdevice], [devicetype])
%
% pa_wavplayrecord is a tool for playing and recording multi-channel
% audio through your ASIO device. Arguments in [] are optional
%
% pa_wavplayrecord([devicetype]) will list all your audio devices for
% that device type then exit.
%
% - playbuffer the matrix to play
% - playdevice the device to play it on. Default: 0
% - samplerate the sampling frequency. Default: 44100
% - recnsamples the number of samples to record. Default: 0
% if 0 then we'll record for the duration of playbuffer
% - recfirstchannel the first channel to record from Default: 1
% - reclastchannel the last channel to record from Default: 1
% - recdevice the device to record from. Default: 0
% - devicetype determines which sound driver to use
% 'win' Windows Multimedia Device
% 'dx' DirectX DirectSound driver
% 'wasapi' Windows WASAPI Device
% 'asio' ASIO Driver (default)
%
% See the help for pa_wavplay for a list of some of these arguments,
% and the formatting of them.
%
% SEE ALSO: pa_wavrecord, pa_wavplayrecord
%
% Copyright 2014 Sensimetrics Corp.
% For additional Matlab tools for audio and video, consult www.sens.com
% check right num of args
error(nargchk(1,8,nargin));
% defaults
device_opt = 1;
playdevice = 0;
fs = 44100;
recnsamples = 0;
recfirstchannel = 1;
reclastchannel = 1;
recdevice = 0;
% if devtype specified
if ischar(varargin{end}),
s=varargin{end};
varargin(end)=[];
device_opt = find(strcmp(s,{'asio','win', 'dx', 'wasapi'})==1);
if isempty(device_opt),
error(['Unrecognized DEVICETYPE: ' s]);
end
if (nargin==1)
if device_opt==1, % asio
pawavplaya;
end
if device_opt==2, % win
pawavplayw;
end
if device_opt==3, % dx
pawavplayx;
end
if device_opt==4, % wasapi
pawavplaywasapi;
end
return;
end
end
% data buffer
playbuffer = varargin{1};
% play device
if length(varargin)>=2,
playdevice=varargin{2};
end
% sample rate
if length(varargin)>=3,
fs=varargin{3};
end
% recnsamples
if length(varargin)>=4,
recnsamples=varargin{4};
end
% recfirstchannel
if length(varargin)>=5,
recfirstchannel=varargin{5};
end
% reclastchannel
if length(varargin)>=6,
reclastchannel=varargin{6};
end
% recdevice
if length(varargin)>=7,
recdevice=varargin{7};
end
if device_opt==1, % asio
fprintf('Using ASIO driver\n');
y = pawavplaya(playbuffer, playdevice, fs, recfirstchannel, reclastchannel, recnsamples, recdevice);
elseif device_opt==2, % win
fprintf('Using WMME driver\n');
y = pawavplayw(playbuffer, playdevice, fs, recfirstchannel, reclastchannel, recnsamples, recdevice);
elseif device_opt==3, % dx
fprintf('Using DirectX driver\n');
y = pawavplayx(playbuffer, playdevice, fs, recfirstchannel, reclastchannel, recnsamples, recdevice);
elseif device_opt==4, % wasapi
fprintf('Using WASAPI driver\n');
y = pawavplaywasapi(playbuffer, playdevice, fs, recfirstchannel, reclastchannel, recnsamples, recdevice);
end
fprintf('Converting result to doubles\n');
y = double(y);
% [EOF] pa_wavplayrecord.m
\ No newline at end of file
function y = pa_wavrecord(varargin)
% pa_wavrecord: record from multichannel sound hardware
%
% Usage: pa_wavrecord([devicetype])
% inputbuffer = pa_wavrecord(firstchannel, lastchannel, nsamples, [samplerate],
% [deviceid], [devicetype])
%
% pa_wavrecord is a tool for recording multi-channel audio through your
% ASIO device. Arguments in [] are optional
%
% - firstchannel the first input channel to record from
% - lastchannel the last input channel to record from
% - nsamples the number of samples to record from each channel
% - samplerate the sampling frequency. Default: 44100
% - deviceid the device id to use for INPUT. Default: 0
% - informat the desired data type of inputbuffer. Valid types
% and the number of bits per sample are as follows:
% - devicetype determines which sound driver to use
% 'win' Windows Multimedia Device
% 'dx' DirectX DirectSound driver
% 'wasapi' Windows WASAPI Device
% 'asio' ASIO Driver (default)
% - inputbuffer is a variable that will hold the recorded audio,
% running along rows, with a seperate column for
% each channel
%
% SEE ALSO: pa_wavplay, pa_wavplayrecord
%
% Copyright 2014 Sensimetrics Corp.
% For additional Matlab tools for audio and video, consult www.sens.com
% check right num of args
error(nargchk(0,6,nargin));
% defaults
device_opt = 1;
device = 0;
fs = 44100;
%in_opt = 2;
% no args, print devices for asio
if (nargin==0)
pawavplaya;
return;
end
% if devtype specified
if ischar(varargin{end})
s=varargin{end};
varargin(end)=[];
device_opt = find(strcmp(s,{'asio','win', 'dx', 'wasapi'})==1);
if isempty(device_opt),
error(['Unrecognized DEVICETYPE: ' s]);
end
if (nargin==1)
if device_opt==1, % asio
pawavplaya;
end
if device_opt==2, % win
pawavplayw;
end
if device_opt==3, % dx
pawavplayx;
end
if device_opt==4, % wasapi
pawavplaywasapi;
end
return;
end
end
%{
% if informat specified
if ischar(varargin{end})
s=varargin{end};
varargin(end)=[];
in_opt = strmatch(s,{'int16','double'});
if isempty(in_opt),
error(['Unrecognized informat: ' s]);
end
end
%}
% channels
firstchannel = varargin{1};
lastchannel = varargin{2};
% samples
nsamples = varargin{3};
% sample rate
if length(varargin)>=4,
fs=varargin{4};
end
% device id
if length(varargin) >= 5,
device = varargin{5};
end
if device_opt==1, % asio
fprintf('Using ASIO driver\n');
y = pawavplaya(0, -1, fs, firstchannel, lastchannel, nsamples, device);
elseif device_opt==2, % win
fprintf('Using WMME driver\n');
y = pawavplayw(0, -1, fs, firstchannel, lastchannel, nsamples, device);
elseif device_opt==3, % dx
fprintf('Using DirectX driver\n');
y = pawavplayx(0, -1, fs, firstchannel, lastchannel, nsamples, device);
elseif device_opt==4, % wasapi
fprintf('Using WASAPI driver\n');
y = pawavplaywasapi(0, -1, fs, firstchannel, lastchannel, nsamples, device);
end
fprintf('Converting result to doubles\n');
y = double(y);
% [EOF] pa_wavrecord.m
\ No newline at end of file
/*
pa_wavplay
A matlab function for playing an audio buffer on multi-channel hardware.
Matt Frear, MARCS Auditory Lab, Sydney, Australia
m.frear@uws.edu.au
with help with the recording code from:
Paul Henderson, Rensselaer Polytechnic Institute, USA, Department of Architectural Acoustics
hendep2@rpi.edu
Modified July 2014 by Joseph Desloge, Sensimetrics Corp., Malden, MA USA
desloge@sens.com
mexFunction is the entry point from Matlab - parses arguments and converts input
pa_wavplay opens the portaudio stream
paWavCallback is the callback function for portaudio, plays the audio
CHANGES:
1.0 separated ASIO, DirectSound, and Win Audio into separate files. Added WASAPI capability.
0.21 User can specify range of channels to record from.
0.20 Added recording and simultaneous recording and playback functionality
Now uses floats internally instead of int16s
Much of the command parsing done in external .m files now.
0.1 initial release
*/
#define API_NAME "ALSA"
#define BLOCK 2048
#include <math.h>
#include <string.h>
#include "mex.h"
#include "portaudio.h"
/////////// my data types
typedef float SAMPLE; // format for portaudio - float = 32 bit
enum recordmode // play, or record, or both
{
play,
record,
playrecord
};
// My struct for holding the audiobuffer and info about it. Received by the paWavCallback
typedef struct
{
SAMPLE *buffer; // PLAYBACK audio buffer //pdh
SAMPLE *recbuffer; // RECORDING audio buffer //pdh
int bufpos; // current play pos in the buffer
int buflen; // total length of the buffer
int bufchannels; // number of audio channels
int recbuffirstchan; // first RECORDING audio channel
int recbufflastchan; // last recording channel
int recbufpos; // current rec pos in the buffer
int recbuflen; // total length of the RECORDING buffer
recordmode recmode; // what we're doing - playing, recording, or both
}
paWavData;
/////////// function prototypes
static int paWavCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, const PaStreamCallbackFlags status, void *userData );
int pa_wavplayrec(SAMPLE *buffer, int buflen, int bufchannels, SAMPLE *recbuffer, int recbuflen, int firstrecchannel, int lastrecchannel, int deviceID, int recdevID, int samplerate);
void pa_printdevinfo();
void printdevice(int api_idx, int device);
// Convert functions - to convert an input buffer to our SAMPLE buffer
SAMPLE* convDouble(double *buffer, int buflen);
SAMPLE* convFloat(float *buffer, int buflen);
SAMPLE* convUchar(unsigned char *buffer, int buflen);
// Entry point from Matlab
// Parse arguments, convert buffer to my SAMPLE (float), then call pa_wavplay with parsed args
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
bool recording = true;
bool playing = true;
// Check for proper number of arguments
/* char *usage = "Usage: recordbuffer = pa_wavplay(playbuffer, playdevice, samplerate, recfirstchannel, reclastchannel, recnsamples, recdevice)\n pa_wavplayrecord with no arguments will list all the available devices\n";
if (nrhs != 0 && nrhs != 6)
mexErrMsgTxt(usage);
*/
if (nrhs == 0)
{
// mexPrintf(usage);
pa_printdevinfo();
return;
}
////// get deviceids. If deviceid < 0 don't play. if recdeviceid < 0 don't record ///////
// get playback device
int playdeviceid = 0;
double* devptr = mxGetPr(prhs[1]);
if (devptr != NULL)
playdeviceid = (int)*devptr;
if (playdeviceid < 0)
playing = false;
// get record device
int recdeviceid = 0;
devptr = mxGetPr(prhs[6]);
if (devptr != NULL)
recdeviceid = (int)*devptr;
if (recdeviceid < 0)
recording = false;
if (!recording && !playing)
{
mexPrintf("playdevice < 0 and recdevice < 0. Nothing to be done");
// mexErrMsgTxt(usage);
}
////// Next step: convert input buffer to SAMPLE buffer /////////
if (mxIsComplex(prhs[0]))
mexErrMsgTxt("audiobuffer must be noncomplex.");
int bufrows = mxGetM(prhs[0]); // get number of rows in the buffer
int bufcols = mxGetN(prhs[0]); // get number of columns
SAMPLE *myBuf = NULL;
if (playing)
{
// Float32 - sweet, same as SAMPLE, no conversion necessary
if (mxIsSingle(prhs[0]))
{
SAMPLE *bufptr = (SAMPLE *)mxGetData(prhs[0]);
if (bufptr == NULL)
mexErrMsgTxt("audiobuffer is NULL");
myBuf = bufptr;
}
// double
else if (mxIsDouble(prhs[0]))
{
double *bufptr = mxGetPr(prhs[0]);
if (bufptr == NULL)
mexErrMsgTxt("audiobuffer is NULL");
myBuf = convDouble(bufptr, bufrows*bufcols);
}
else
{
mexErrMsgTxt("audiobuffer is an invalid data type");
}
}
// get samplerate
double* sampleptr = mxGetPr(prhs[2]);
if (sampleptr == NULL)
mexErrMsgTxt("samplerate is NULL");
double samplerate = sampleptr[0];
int recordsamples = 0, firstrecordchannel = 0, lastrecordchannel = 0, Inputbufrows = 0, Inputbufcols = 0;
SAMPLE *myInputBuf = NULL;
if (recording)
{
// get number of record channels
double* sptr = mxGetPr(prhs[3]);
if (sptr == NULL)
mexErrMsgTxt("firstrecordchannel is NULL");
firstrecordchannel = (int)sptr[0];
if (firstrecordchannel <=0)
mexErrMsgTxt("invalid firstrecordchannel: <= 0");
sptr = mxGetPr(prhs[4]);
if (sptr == NULL)
mexErrMsgTxt("lastrecchannel is NULL");
lastrecordchannel = (int)sptr[0];
if (lastrecordchannel <=0)
mexErrMsgTxt("invalid lastrecordchannel <= 0");
if (lastrecordchannel < firstrecordchannel)
mexErrMsgTxt("invalid lastrecordchannel, < firstrecordchannel");
Inputbufcols = lastrecordchannel - firstrecordchannel + 1;
// get number of record samples
sptr = mxGetPr(prhs[5]);
if (sptr == NULL)
mexErrMsgTxt("recsamples is NULL");
recordsamples = (int)sptr[0];
if (recordsamples <= 0)
Inputbufrows = bufrows; // get number of rows in the buffer
else
Inputbufrows = recordsamples;
///// allocate memory for the output matrix
plhs[0] = mxCreateNumericMatrix(Inputbufrows,Inputbufcols, mxSINGLE_CLASS, mxREAL);