PsychPortAudio('Fillbuffer') for continous stream paradigm - please help

  • PsychtoolboxVersion - 3.0.16
  • Ubuntu 20.04 LTS , MATLAB 2020a & Ubuntu 18.04, MATLAB 2019a
  • XPS with intel i7-3537u, intel GPU

Hello Community,

I can’t figure out how to append to the buffer while in playback. From what I understood from the Documentation it should be as easy as

pahandle = PsychPortAudio('Open', 0, 1, 4, 44100, 1); 
x = []; % a sound of several seconds
buffer= PsychPortAudio('CreateBuffer', [], x );
PsychPortAudio('FillBuffer', pahandle, buffer );
PsychPortAudio('Start', pahandle, [], 0, 1);
PsychPortAudio('FillBuffer', pahandle, buffer, 1 );

However, Once the playback of the first fill finishes I get the following error

Error in function FillBuffer: 	Usage error
Audiodevice no longer in playback mode (Auto stopped?!?)! Can't continue a streaming buffer refill while stopped. Check your code!

even when I’m sure the sound in the buffer is long enough that it shouldn’t finish before the refill. Do I need to explicitly pass to PTB that it should continue with the script after PsychPortAudio('Start')?

I’ll be very thankful for any hints on how to get it going.

PS I looked at the code from BasicSoundScheduleDemo() and I think mine should work given the demo works on my computer, what am I not seeing?

The main difference is that you try to “append” the same buffer, whereas our demo tries to append a shorter buffer.

The initial buffer as set with ‘FillBuffer’ defines how much capacity there is for sound. If you use the streamingrefill flag 1 to “refill” the audio stream while it is playing, you can only add new buffers smaller than the original one. This is because new buffers are not appended, but if you, e.g., try to refill with a buffer of 1 second duration, the driver will wait until at least 1 second of the original sound buffer has been played out and consumed, to make up space for the new buffer. Trying to append the same buffer will fail, because before the new (= same = old) buffer could be used, the old buffer would have to play out completely to make enough space, and if the old buffer has played out completely, that means all original sound was consumed, and playback stopped, and game over…

Adding a smaller buffer for the refill should work. Or the approach from DelayedSoundFeedbackDemo.m.

Or you use sound schedules as shown in the first part of BasicSoundScheduleDemo.m Here adding new buffers to a running playback schedule will be like adding new songs to a playlist, while the playlist is playing: You append new buffers, and they will play out, once the preceding buffers have been played.

Thanks for your reply Mario, it really helped me to understand the issue. However, now that I managed to ‘append’ to the buffer while in playback, the appended segments don’t actually get played. I attach a minimal example:

pahandle = PsychPortAudio('Open', 0, 1, 1, 44100, 1); %deviceID, mode, latency mode,  freq, chann, buffersize, suddestedLate, select
PsychPortAudio('RunMode', pahandle, 1);%

   
% Make a beep which we will play back to the user
myBeep = MakeBeep(500, 1, 44100);
silence= zeros(1, 22050) ;
PsychPortAudio('FillBuffer', pahandle, [myBeep silence myBeep silence myBeep silence]); 
PsychPortAudio('Start', pahandle);
PsychPortAudio('FillBuffer', pahandle, myBeep , 1);

and the output:

PTB-INFO: Closing handle 0.
PTB-INFO: Trying to resume potentially suspended PulseAudio server... ... status 0
PTB-INFO: Trying to suspend potentially running PulseAudio server... ... status 0
PTB-INFO: Using modified PortAudio V19.6.0-devel, revision 396fe4b6699ae929d3a685b3ef8a7e97396139a4
PTB-INFO: New audio device 0 with handle 0 opened as PortAudio stream:
PTB-INFO: For 1 channels Playback: Audio subsystem is ALSA, Audio device name is HDA Intel PCH: ALC3260 Analog (hw:0,0)
PTB-INFO: Real samplerate 44100.000000 Hz. Input latency 0.000000 msecs, Output latency 9.977324 msecs.

Tested on two machines.

EDIT: to clarify, it doesn’t throw an error for ‘FillBuffer’ , it also returns the values for ETA and so on, but the beep never gets played (I can hear 3 beeps instead of 4). If I define 2 repetitions when starting the device, I can hear 6 beeps, indicating the first fill gets played 2 times.

EDIT2:
(changed frequency of the second fill to differentiate between the intial fill vs the one during playback)
I tried several ‘repetition’ values, and it looks like the value 0 is the problem. It only plays the initial fill. For example here

%-----------
% Setup and cleanup
%-----------
AssertOpenGL;
InitializePsychSound(1);
PsychPortAudio('Close'); %make sure all audio devices are closed
PsychPortAudio('Verbosity', 12); %how much info to print out
%-----------
% Audio
%-----------
%find ID of your device 
x  = PsychPortAudio('GetDevices'); % run to choose ID - usually many devices
pahandle = PsychPortAudio('Open', 5, 1, 1, 44100, 1); %deviceID, mode, latency mode,  freq, chann, buffersize, suddestedLate, select

PsychPortAudio('RunMode', pahandle, 1);%

   
% Make a beep which we will play back to the user
myBeep = MakeBeep(500, 1, 44100);
myBeep2 = MakeBeep(600, 1, 44100);
silence = zeros(1, 0.5*44100);
bigBuffer = PsychPortAudio('CreateBuffer', [], [myBeep silence myBeep silence myBeep silence]);
smallBuffer = PsychPortAudio('CreateBuffer', [], [myBeep2 silence]);

PsychPortAudio('FillBuffer', pahandle, bigBuffer); 
PsychPortAudio('Start', pahandle, 0);
[x, y, z] = PsychPortAudio('FillBuffer', pahandle, smallBuffer , 1);

[x, y, z] = PsychPortAudio('FillBuffer', pahandle, smallBuffer , 1);

[x, y, z] = PsychPortAudio('FillBuffer', pahandle, smallBuffer , 1);

PsychPortAudio('Stop', pahandle, 1);

The sound myBeep2 never gets played. When I set the repetitions to 2, it correctly playes for the length of the buffer initiated with the initial fill times 2, and I can hear myBeep2.

Scheduling is not an option because it doesn’t give me access to the timing(unless I’m missing something), and I need it (at least the ETA).

The reason a repetition value of zero seems to not work, is because of your

call. You specify the ‘1’ flag to make it wait for sound playback to stop by itself, but a repetition value of 0 means to “repeat indefinitely” ie. never stop by itself, but only when playback is stopped manually via, e.g., a ‘Stop’ call. Both contradict each other, so to resolve the conflict, your ‘Stop’ command turns into a PsychPortAudio('Stop', pahandle) without the wait flag. Therefore sound will stop as soon as all three ‘FillBuffer’ commands are done and ‘Stop’ is executed. Which happens to be exactly after your bigBuffer has played out once, so there’s enough space to complete all three ‘FillBuffer’ commands for smallBuffer. So playback stops at exactly the moment where the smallBuffer’s were about to get played.

If you add KbStrokeWait before the ‘Stop’ call, to wait for a key press before 'Stop’ing, you’ll see it suddenly works.

1 Like

Thanks a lot! Works like a charm now.