streaming psychportaudio

hey mario-
playing with some streaming audio ideas. 2 issues -- if i specify a
device in psychportaudio('open'), then subsequent calls to
psychportaudio('getstatus') cause a silent crash. my function exits,
an indefinitely repeating sound will keep playing, etc. no error
output at all. this happens whether or not i've started output. if i
don't specify a device in the call to 'open,' 'getstatus' works fine.

second, i'm trying to use streaming with either directsound or mme --
no asio in this case cuz i don't care about latency. i do care about
sound continuity; i'm doing realtime synthesis. i'm not getting
underflows reported from 'fillbuffer,' but i get terrible sound
discontinuities throughout the sound with directsound. with mme,
everything's great. in the following demo code, i use the mouse x,y
to set frequency and amplitude. i precompute a full amplitude buffer
for each possible frequency, and then scan it for its first and last
upward going zero crossing, so that buffer transitions will be smooth.
this means that each of my calls to fillbuffer has a slightly
different length.

you recommend directsound over mme -- i expected that neither should
have problems with streaming, just absolute latency. any idea why
directsound has discontinuities?

'getstatus,' when it works, indicates i'm not taxing the cpu (CPULoad
= ~.005). the task manager shows i'm pegging one of my cores -- does
that mean that streaming 'fillbuffer' does not sleep the thread while
its waiting for room?

-erik


function streamTest
clc
clear all

ListenChar(2)
priority(maxpriority('kbcheck','getmouse'));
initializepsychsound

d=[];
hostapi='MME' ; %'Windows DirectSound' %vs. 'MME'
hostname='SoundMAX HD Audio O'; %'Primary Sound Driver' (wds only)
vs. 'SoundMAX HD Audio O' vs. 'Microsoft Sound Mapper - Output' (mme only)
devices = PsychPortAudio('GetDevices');

for i=1:length(devices)
if devices(i).NrOutputChannels>0 &&
strcmp(devices(i).HostAudioAPIName,hostapi) &&
strcmp(devices(i).DeviceName,hostname)
d=devices(i).DeviceIndex;
break
end
end
if isempty(d)
warning('host not found, using default')
end

pahandle = PsychPortAudio('Open',d); %if d==[], GetStatus works,
otherwise it silently crashes the program
numoctaves=8;
fundamental=27.5;
steps=12;
freqs=logspace(log10(fundamental),log10(fundamental*2^numoctaves),numoctaves*steps+1);
buffsize=2/fundamental;
amp=1;

%status = PsychPortAudio('GetStatus', pahandle) %GetStatus fails when
device specified
status.SampleRate=44100; %hardcoded cuz GetStatus fails when device
specified

for i=1:length(freqs)
f{i}=repmat(makebeep(freqs(i),buffsize,status.SampleRate),2,1);
zeroCrossings=find(diff(f{i}(1,:)>0)==1);
upwardZeroCrossings=[min(zeroCrossings) max(zeroCrossings)-1];
f{i}=f{i}(:,upwardZeroCrossings(1):upwardZeroCrossings(2));
f{i}=f{i}-repmat(mean(f{i}')',1,size(f{i},2));
f{i}=amp*f{i}/max(abs(f{i}(:)));
end

s=Screen('Resolution', 0);

numBuffs=2;

[x y]=getmouse;
ind=1+floor((length(freqs)-1)*x/s.width);
under=PsychPortAudio('FillBuffer', pahandle, repmat(f{ind},1,numBuffs),0);

done=false;

'hit s for status (crashes if audio device specified), space to quit'

PsychPortAudio('Start', pahandle, 0);
while ~done
[keyIsDown, secs, keyCode] = KbCheck;

if keyIsDown
if keyCode(kbname('space'))
done=true;
end
if keyCode(kbname('s'))
status = PsychPortAudio('GetStatus', pahandle) %GetStatus
fails when device specified
'hit s for status (crashes if audio device specified),
space to quit'
end
end

[x y]=getmouse;
if any([x y])<0 || any([x y]>[s.width s.height])
'mouse error'
[x y]
[s.width s.height]
end
ind=1+floor((length(freqs)-1)*x/s.width);

if PsychPortAudio('FillBuffer', pahandle, f{ind}*y/s.height ,1)
'got an underflow'
end
end

PsychPortAudio('Stop', pahandle);
PsychPortAudio('Close');
ListenChar(0)
priority(0);
--- In psychtoolbox@yahoogroups.com, "e_flister" <e_flister@...> wrote:
>
> hey mario-
> playing with some streaming audio ideas. 2 issues -- if i specify a
> device in psychportaudio('open'), then subsequent calls to
> psychportaudio('getstatus') cause a silent crash. my function exits,
> an indefinitely repeating sound will keep playing, etc. no error
> output at all. this happens whether or not i've started output. if i
> don't specify a device in the call to 'open,' 'getstatus' works fine.

Not reproducible on our machines, so likely some driver
bug for your soundcard. Did you try different deviceids?
At least if you specify the one that the driver auto-selects
by default, you shouldn't see that error.

>
> second, i'm trying to use streaming with either directsound or mme --
> no asio in this case cuz i don't care about latency. i do care about
> sound continuity; i'm doing realtime synthesis. i'm not getting
> underflows reported from 'fillbuffer,' but i get terrible sound
> discontinuities throughout the sound with directsound. with mme,
> everything's great. in the following demo code, i use the mouse x,y
> to set frequency and amplitude. i precompute a full amplitude buffer
> for each possible frequency, and then scan it for its first and last
> upward going zero crossing, so that buffer transitions will be smooth.
> this means that each of my calls to fillbuffer has a slightly
> different length.
>
> you recommend directsound over mme -- i expected that neither should
> have problems with streaming, just absolute latency. any idea why
> directsound has discontinuities?
>

Actually i recommend not using MS-Windows for any
serious work ;-)

Apart from that use ASIO whenever you can, not only
if you need low latency -- its so much less buggy and
deficient than Windows built-in sound subsystems.

For most studies, low latency is not what actually
matters, its more the accuracy of sound onset timing.
For accurately timed sound onset and proper timestamping
you'll also need ASIO mode, as the MME and DirectSound
drivers are terrible at that as well -- pretty much as good
as a random number generator.

The MME and DirectSound option is really only for
tasks where nearly nothing matters, like simple
streaming output or some beep tones etc.

DirectSound allows to achieve lower latency than MME
on some systems if you're lucky, albeit still much worse
than anything ASIO could offer. On many other systems
its not better than MME.

But for your specific case: You don't set the 'reqlatencyclass'
paramter in your 'Open' call, so the driver defaults to mode 1,
low-latency. In that case the driver requests a latency of
100 msecs for MME and 20 msecs for DirectSound -- values
that worked well in our testing.

Maybe your hardware doesn't cope with 20 msecs and
that's why DSound glitches and MME works with the
more generous 100 msecs. If you set 'reqlatencyclass'
to 0 as in Dons example, our driver will apply high-latency
settings which are somewhere in excess of 100 msecs,
so that may help.

In the end it doesn't matter if you choose MME or DSound
for streaming with no timing or latency requirements.

> 'getstatus,' when it works, indicates i'm not taxing the cpu (CPULoad
> = ~.005). the task manager shows i'm pegging one of my cores -- does
> that mean that streaming 'fillbuffer' does not sleep the thread while
> its waiting for room?

Depends on operating system. On good operating systems
like Linux or OS/X it does sleep, on Windows with its
unreliable scheduling it performs busy-waiting until enough
bufferspace is free.

-mario

>
> -erik
>
>
> function streamTest
> clc
> clear all
>
> ListenChar(2)
> priority(maxpriority('kbcheck','getmouse'));
> initializepsychsound
>
> d=[];
> hostapi='MME' ; %'Windows DirectSound' %vs. 'MME'
> hostname='SoundMAX HD Audio O'; %'Primary Sound Driver' (wds only)
> vs. 'SoundMAX HD Audio O' vs. 'Microsoft Sound Mapper - Output' (mme only)
> devices = PsychPortAudio('GetDevices');
>
> for i=1:length(devices)
> if devices(i).NrOutputChannels>0 &&
> strcmp(devices(i).HostAudioAPIName,hostapi) &&
> strcmp(devices(i).DeviceName,hostname)
> d=devices(i).DeviceIndex;
> break
> end
> end
> if isempty(d)
> warning('host not found, using default')
> end
>
> pahandle = PsychPortAudio('Open',d); %if d==[], GetStatus works,
> otherwise it silently crashes the program
> numoctaves=8;
> fundamental=27.5;
> steps=12;
>
freqs=logspace(log10(fundamental),log10(fundamental*2^numoctaves),numoctaves*steps
+1);
> buffsize=2/fundamental;
> amp=1;
>
> %status = PsychPortAudio('GetStatus', pahandle) %GetStatus fails when
> device specified
> status.SampleRate=44100; %hardcoded cuz GetStatus fails when device
> specified
>
> for i=1:length(freqs)
> f{i}=repmat(makebeep(freqs(i),buffsize,status.SampleRate),2,1);
> zeroCrossings=find(diff(f{i}(1,:)>0)==1);
> upwardZeroCrossings=[min(zeroCrossings) max(zeroCrossings)-1];
> f{i}=f{i}(:,upwardZeroCrossings(1):upwardZeroCrossings(2));
> f{i}=f{i}-repmat(mean(f{i}')',1,size(f{i},2));
> f{i}=amp*f{i}/max(abs(f{i}(:)));
> end
>
> s=Screen('Resolution', 0);
>
> numBuffs=2;
>
> [x y]=getmouse;
> ind=1+floor((length(freqs)-1)*x/s.width);
> under=PsychPortAudio('FillBuffer', pahandle, repmat(f{ind},1,numBuffs),0);
>
> done=false;
>
> 'hit s for status (crashes if audio device specified), space to quit'
>
> PsychPortAudio('Start', pahandle, 0);
> while ~done
> [keyIsDown, secs, keyCode] = KbCheck;
>
> if keyIsDown
> if keyCode(kbname('space'))
> done=true;
> end
> if keyCode(kbname('s'))
> status = PsychPortAudio('GetStatus', pahandle) %GetStatus
> fails when device specified
> 'hit s for status (crashes if audio device specified),
> space to quit'
> end
> end
>
> [x y]=getmouse;
> if any([x y])<0 || any([x y]>[s.width s.height])
> 'mouse error'
> [x y]
> [s.width s.height]
> end
> ind=1+floor((length(freqs)-1)*x/s.width);
>
> if PsychPortAudio('FillBuffer', pahandle, f{ind}*y/s.height ,1)
> 'got an underflow'
> end
> end
>
> PsychPortAudio('Stop', pahandle);
> PsychPortAudio('Close');
> ListenChar(0)
> priority(0);
>