Analog I/O Demo 6 – Generating audio with the digital to analog converter

This demo is very similar to Demo 4, except it plays the audio on the DAC. Playing the audio on the DAC allows the generation of precise audio stimuli. Using DAC eliminates variations between consumer electronics player, and also eliminates the delay between the different possible codecs.

Note

You can only use ADC/DAC with the full version of a device; the lite version does not have ADC/DAC capabilities.

To play back audio, we write the file in the DAC buffer. When we load the audio, we must make sure to obtain the frequency, otherwise the playback will be distorted. If audio is more than one channel, we also receive this information. A schedule is also created for proper playback.

Datapixx('SetDacSchedule', 0, freq, nTotalFrames*repetitions, [0: nChannels-1], 0, nTotalFrames);

The first zero is the delay, freq is the audio frequency acquired when loading the audio file. If you want to repeat the sound, you must know how many frames your sound file possesses. Here, there is no ‘mode’, since we are sending this to the DAC. The next argument sets up the ADC channel(s). The second zero is the address of the audio stimulus and the final argument is the number of frames.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
function DatapixxDacWaveDemo(wavFilename, repetitions)
% DatapixxDacWaveDemo([wavFilename=funk.wav] [, repetitions=0])
%
% Demonstrates how to play an audio waveform using the DATAPixx DAC
% (Digital to Analog Converter) subsystem.
%
% The precision DACs are capable of updating at up to 1 MHz, and can be used to
% generate arbitrary analog waveforms with an output swing of up to +-10V.
% One application of the DACs is for the generation of precise audio stimuli.
% The audio CODECs found in typical consumer electronics have serial interfaces.
% Internally, the CODEC processes the serial data with digital interpolation
% filters, a delta-sigma modulator, and an analog reconstruction filter.
% One result is that a single dataset played back on two different CODECs
% can result in slightly different analog waveforms. Using DACs eliminates this
% variation.
%
% A second issue is the CODEC group delay. CODECs introduce a delay between
% when they receive their serial data, and when their analog outputs update.
% This delay varies from one CODEC to another, and also varies within a single
% CODEC depending on waveform update rate. Using DACs eliminates this variation.
%
% Note that the DAC outputs are not intended to drive very heavy loads. It is
% possible to drive up to +-1V waveforms (like those used in PsychPortAudio)
% directly into 32 Ohm headphones, or up to +-10V waveforms into high impedance
% (eg: 400 Ohm) headphones like those used for studio quality sound. For higher
% power, or for driving 8 Ohm speaker loads, add an external headphone amplifier.
%
% Optional arguments:
%
% wavFilename = Name of a .wav sound file to load and play.
%               Otherwise the funk.wav provided with PsychToolbox is used.
%
% repetitions = Number of times to play the sound.
%               0 means play until the sun goes nova
%               (or a keypress, whichever comes first)
%
% Also see: DatapixxDacWaveStreamDemo
%
% History:
%
% Oct 1, 2009  paa     Written
% Oct 29, 2014 dml     Revised 

AssertOpenGL;   % We use PTB-3

% Get the .wav filename
if nargin < 1
    wavFilename = [];
end
if isempty(wavFilename)
    wavFilename = [PsychtoolboxRoot 'PsychDemos' filesep 'SoundFiles' filesep 'funk.wav'];
end

% Get the number of repetitions
if nargin < 2
    repetitions = [];
end
if isempty(repetitions)
    repetitions = 0;
end

% Load the .wav file
[waveData, freq] = audioread(wavFilename);
waveData = waveData';               % Transpose so that each row has 1 channel
nChannels = size(waveData, 1);
nTotalFrames = size(waveData, 2);

% Open Datapixx, and stop any schedules which might already be running
Datapixx('Open');
Datapixx('StopAllSchedules');
Datapixx('RegWrRd');    % Synchronize DATAPixx registers to local register cache

% Download the entire waveform to the DATAPixx default DAC address of 0.
Datapixx('WriteDacBuffer', waveData, 0);

% Configure the DATAPixx to play the buffer at the correct frequency.
% If the .wav file has a single channel, it will play on DAC channel 0.
% Additional .wav channels will play on increasing DAC channel numbers.
if (repetitions > 0)    % Play a fixed number of reps
    Datapixx('SetDacSchedule', 0, freq, nTotalFrames*repetitions, [0: nChannels-1], 0, nTotalFrames);
else                    % Play forever
    Datapixx('SetDacSchedule', 0, freq, 0, [0: nChannels-1], 0, nTotalFrames);
end

% Start the playback
Datapixx('StartDacSchedule');
Datapixx('RegWrRd');    % Synchronize DATAPixx registers to local register cache

% Wait until schedule stops, or until a key is pressed.
fprintf('\nWaveform playback starting, press any key to abort.\n');
if (exist('OCTAVE_VERSION'))
    fflush(stdout);
end
while 1
    Datapixx('RegWrRd');   % Update registers for GetDacStatus
    status = Datapixx('GetDacStatus');
    if ~status.scheduleRunning
        break;
    end
    if KbCheck
        Datapixx('StopDacSchedule');
        Datapixx('RegWrRd');    % Synchronize DATAPixx registers to local register cache
        break;
    end
end

% Show final status of DAC scheduler
fprintf('\nStatus information for DAC scheduler:\n');
Datapixx('RegWrRd');   % Update registers for GetDacStatus
disp(Datapixx('GetDacStatus'));

% Job done
Datapixx('Close');
fprintf('\nDemo completed\n\n');