/usr/share/psychtoolbox-3/PsychDemos/BasicAMAndMixScheduleDemo.m is in psychtoolbox-3-common 3.0.11.20131230.dfsg1-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | function BasicAMAndMixScheduleDemo
% Demonstrates basic use of sound schedules, volume controls, amplitude
% modulation and audio mixing of multiple voices.
%
% Usage: BasicAMAndMixScheduleDemo;
%
% For more advanced or different use of sound schedules see
% BasicSoundScheduleDemo. For more advanced use of mixing, modulators and
% other stuff see BasicMultiTrackAudioRecorderDemo.
%
% This demo shows how to use master-slave audio devices to playback with
% multiple voices, mixing multiple sounds together. It shows how to
% manually control the per-slave volume and master volume. It shows how to
% apply AM amplitude modulation to the different slave audio devices, and how
% to synchronize different devices and AM modulation. It also shows use of
% sound schedules to implement repetitive sound sequences with precise
% timing between different sounds.
%
% History:
% 25.04.2010 mk Written.
KbName('UnifyKeyNames');
leftArrow = KbName('LeftArrow');
rightArrow = KbName('RightArrow');
upArrow = KbName('UpArrow');
downArrow = KbName('DownArrow');
escape = KbName('ESCAPE');
% Initialize Sounddriver:
InitializePsychSound(1);
% Open real default [] soundcard as master device (+8) for playback only (+1), with
% standard low-latency, high timing precision mode, 2 channels, 48kHz:
nrchannels = 2;
freq = 48000;
% Add 15 msecs latency on Windows, to protect against shoddy drivers:
sugLat = [];
if IsWin
sugLat = 0.015;
end
pamaster = PsychPortAudio('Open', [], 1+8, 1, freq, nrchannels, [], sugLat);
% Start master immediately, wait for it to be started. We won't stop the
% master until the end of the session.
PsychPortAudio('Start', pamaster, 0, 0, 1);
% Set the masterVolume for the master: This volume setting affects all
% attached sound devices. We set this to 0.5, so it doesn't blow out the
% ears of our listeners...
PsychPortAudio('Volume', pamaster, 0.5);
% Create to slave audio devices for sound playback (+1), with same
% frequency, channel count et. as master. Attach them to master. As they're
% attached to the same sound channels of the master (actually the same
% single channel), their audio output will mix together:
pasound1 = PsychPortAudio('OpenSlave', pamaster, 1);
pasound2 = PsychPortAudio('OpenSlave', pamaster, 1);
% Attach some beep-tone to each of them. pasound1 gets a 500 Hz, 1 sec
% tone, pasound2 gets a 750 Hz, 1 sec tone:
sound1 = MakeBeep(500, 1.0, freq);
sound2 = MakeBeep(750, 1.0, freq);
sound3 = MakeBeep(1000, 1.0, freq);
% Make sure we have always 2 channels stereo output.
% Why? Because some low-end and embedded soundcards
% only support 2 channels, not 1 channel, and we want
% to be robust in our demos.
sound1 = [sound1 ; sound1];
sound2 = [sound2 ; sound2];
sound3 = [sound3 ; sound3];
% Create audio buffers prefilled with the 3 sounds:
pabuffer1 = PsychPortAudio('CreateBuffer', [], sound1);
pabuffer2 = PsychPortAudio('CreateBuffer', [], sound2);
pabuffer3 = PsychPortAudio('CreateBuffer', [], sound3);
% Demo of sound schedules: Build a sound schedule (a "playlist") that will
% play the three sound buffers infinitely repeating. Each sound is played 2
% seconds after start of playback of the previous one. As the sounds
% themselves are 1 second in duration, you should hear 1st sound for 1 sec,
% then 1 sec silence, then 2nd sound for a second, 1 sec silence, 3rd
% sound for a second, 1 sec silence, 1st sound ...
% The schedule shall contain exactly 6 slots which will repeat ad
% infinitum, or until stopped:
PsychPortAudio('UseSchedule', pasound1, 1, 6);
% This command code in a slot tells to take a break (+1) before processing
% the following of the following slot. The (+16) means to wait until the
% given amount of seconds has elapsed since start of playback of the most
% recent soundbuffer. Therefore it defines a relative spacing between
% playback of successive sound buffers.
% Note that there are more command codes available (see PsychPortAudio AddToSchedule?).
% These allow for other types of timing, just don't make sense in this
% demo.
cmdCode = 1 + 16;
% Add 1st sound buffer: The special '1' flag at the end tells not to delete
% this command from the schedule, but keep it for repeated execution on
% repetitions of the schedule:
PsychPortAudio('AddToSchedule', pasound1, pabuffer1, [], [], [], [], 1);
% Tell pasound1 to start playing the following buffer exactly 2.0 seconds
% after playback of the previous buffer has started:
PsychPortAudio('AddToSchedule', pasound1, -cmdCode, 2.0, [], [], [], 1);
% Add 2nd sound buffer:
PsychPortAudio('AddToSchedule', pasound1, pabuffer2, [], [], [], [], 1);
% Tell pasound1 to start playing the following buffer exactly 2.0 seconds
% after playback of the previous buffer has started:
PsychPortAudio('AddToSchedule', pasound1, -cmdCode, 2.0, [], [], [], 1);
% Add 3rd sound buffer:
PsychPortAudio('AddToSchedule', pasound1, pabuffer3, [], [], [], [], 1);
% Tell pasound1 to start playing the following buffer exactly 2.0 seconds
% after playback of the previous buffer has started:
PsychPortAudio('AddToSchedule', pasound1, -cmdCode, 2.0, [], [], [], 1);
fprintf('Starting sound playback 3 seconds from now...\n');
tStart = PsychPortAudio('Start', pasound1, 0, GetSecs + 3, 1);
fprintf('Started! Will add some AM modulation of first tone at start of 3rd repetition...\n');
% Create an AM modulator (+32) to gain-modulate the signal of pasound1, that is
% perform amplitude modulation. Modulation will only happen while
% pamodulator1 is active. No modulation will happen if it is stopped:
pamodulator1 = PsychPortAudio('OpenSlave', pasound1, 32);
% 'envelope1' sound is 1 second at 8 Hz frequency,
% built for a playback device with 'freq' Hz sampling
% rate:
envelope1 = (1 + MakeBeep(8, 1, freq)) / 2;
% Fill it into standard sound buffer of pamodulator1, instead of using
% 'CreateBuffer', just for a change...
PsychPortAudio('FillBuffer', pamodulator1, [envelope1; envelope1]);
% We define a repeating schedule for the pamodulator1 as well. This time
% only 2 slots. The first plays the 1 second AM modulation signal in our
% 'FillBuffer'ed buffer 0, then the 2nd slot pauses until 6 seconds after
% start of our modulation signal. This way we will get a 1 second modulation
% repeating every 6 seconds. Because the period of 6 seconds of
% pamodulator1 is identical to the 6 seconds period of the sound sequence
% of pasound1, they are phase-locked, so this will basically modulate the
% same 1 second piece of sound in pasound1:
PsychPortAudio('UseSchedule', pamodulator1, 1, 2);
PsychPortAudio('AddToSchedule', pamodulator1, 0, [], [], [], [], 1);
PsychPortAudio('AddToSchedule', pamodulator1, -cmdCode, 6.0, [], [], [], 1);
% Modulation schedule ready. We want to start modulation at the 3rd overall
% repetition of the three-tone sequence that pasound1 is playing. We know
% that one three-tone sequence takes 6 seconds and that the first iteration
% of the sequence started at 'tStart', so the 3rd repetition must therefore
% begin at tThirdRep = tStart + 2 * 6 seconds. Schedule start of our
% pamodulator1 for target time tThirdRep. This should cause modulation of
% the 1st tone, starting at the 3rd overall repetition, then repeating
% until stopped:
tThirdRep = tStart + 2 * 6;
% Schedule start of AM on infinite repeat. Wait for the actual start to happen:
PsychPortAudio('Start', pamodulator1, 0, tThirdRep, 1);
fprintf('Press any key to continue.\n');
KbStrokeWait;
% Stop the modulation immediately. This will just output the sequence at
% full constant volume again:
PsychPortAudio('Stop', pamodulator1);
fprintf('Back to full-blast unmodulated sequence!\n');
% Ok, this time we create a nice sin'e curve AM modulation to "soften" the
% onset and offset of each of the three tones - nicer to your ears :-)
%
% Each AM envelope is again 1 second duration and we choose a repeating
% pattern of 2 second period. This way for each 6 seconds playout of the
% 3-tone sequence, we will get 3 repetitions of the 2 seconds modulation
% sequence, phase-locked to the 3 seconds sequence, nicely modulating each
% of the three tones:
% Redefine our envelope, softly raising from 0.0 - 1.0, then falling from
% 1.0 to 0.0 again, all in 1 second:
envelope2 = sin((0:1/freq:1) * pi);
% Fill it into standard sound buffer of pamodulator1, instead of using
% 'CreateBuffer', just for a change...
PsychPortAudio('FillBuffer', pamodulator1, [envelope2; envelope2]);
% Clear and reset our 2-slot schedule:
PsychPortAudio('UseSchedule', pamodulator1, 2);
% Our 1 second envelope:
PsychPortAudio('AddToSchedule', pamodulator1, 0, [], [], [], [], 1);
% Restart playback every 2 seconds for a total period of 2 seconds:
PsychPortAudio('AddToSchedule', pamodulator1, -cmdCode, 2.0, [], [], [], 1);
% Ok, we need to start the new schedule in sync with start of a new 3-tone
% sequence for best results. We don't know where we are in the sequence,
% but we can query when the sequence started repeating the last time. It is
% stored in the status.StartTime field of pasound1 device.
% We need to wait until playout of the first tone started again to get the
% reference timestamp we want. Poll for this event:
while 1
status = PsychPortAudio('GetStatus', pasound1);
% Every 6th slot corresponds again to the 1st tone, and it needs to be
% playing that is status.Active:
if (mod(status.SchedulePosition, 6) == 0) && (status.Active)
% Got it! The status.startTime now corresponds to the start of the
% current 3-tone-sequence:
break;
end
% Nope. Wait a bit, then retry:
WaitSecs('YieldSecs', 0.1);
end
% Let's start our modulator offset by 2 repetitions, that is finish the
% current repetition of the 6 second cycle, then play another one, then
% start modulating. We do this here for simplicity, so we don't need to
% worry being already very close to the start of the next repetition by
% pure chance and thereby possibly missing the deadline:
tModStart = status.StartTime + 2 * 6;
PsychPortAudio('Start', pamodulator1, 0, tModStart);
fprintf('Will start soft modulation of each tone %f seconds from now at %f seconds.\n', tModStart - GetSecs, tModStart);
fprintf('This is %f seconds since start.\n', tModStart - tStart);
fprintf('Press any key to continue.\n');
KbStrokeWait;
% Ok, let's add the second pasound device into the mix. This one will play
% white noise, again envelope modulated at a low frequency:
% 5 Second worth of random noise in -1 ; +1 range:
noise = 2 * rand(1, 5 * freq) - 1;
% Into pasound2's standard buffer it goes...
PsychPortAudio('FillBuffer', pasound2, [noise ; noise]);
% Create a dedicated AM modulator for pasound2 as well:
pamodulator2 = PsychPortAudio('OpenSlave', pasound2, 32);
% Fill it with a slow version of the soft envelope of pamodulator1:
% Redefine our envelope, softly raising from 0.2 - 1.0, then falling from
% 1.0 to 0.2 again, all in 6 seconds:
envelope3 = 0.2 + 0.8 * sin((0:1/(freq*6):1) * pi);
% Fill it into standard sound buffer of pamodulator2, instead of using
% 'CreateBuffer', just for a change...
PsychPortAudio('FillBuffer', pamodulator2, [envelope3 ; envelope3]);
% Ok, we don't use any schedules for pasound2 and pamodulator2, we had that
% already. Instead we just start both of them, set to infinite repeat. We
% need to start them at exactly the same time so the modulation starts
% softly at the very first played out white-noise sample. We could pick any
% point in time in the future, e.g., GetSecs + 0.5 seconds, but for the fun
% of it, we will start it again synchronized to the sequence of pasound1,
% recycling the same trick we used before. Start it two repetitions after
% start of the most recent repetition of the whole 6-slot schedule.
% We need to wait until playout of the first tone started again to get the
% reference timestamp we want. Poll for this event:
while 1
status = PsychPortAudio('GetStatus', pasound1);
% Every 6th slot corresponds again to the 1st tone, and it needs to be
% playing that is status.Active:
if (mod(status.SchedulePosition, 6) == 0) && (status.Active)
% Got it! The status.startTime now corresponds to the start of the
% current 3-tone-sequence:
break;
end
% Nope. Wait a bit, then retry:
WaitSecs('YieldSecs', 0.1);
end
tModStart = status.StartTime + 2 * 6;
fprintf('Will start additional modulated random noise %f seconds from now.\n', tModStart - GetSecs);
fprintf('Should start at onset of first tone of the 3-tone sequence.\n');
% Schedule start of pamodulator2 at tModStart, set it to infinite repeat:
PsychPortAudio('Start', pamodulator2, 0, tModStart);
% Schedule start of pasound2 at tModStart, set it to infinite repeat.
% We wait until start has happened and retrieve its onset time, just
% because we can...
tNoiseStart = PsychPortAudio('Start', pasound2, 0, tModStart, 1);
fprintf('Started at system time %f seconds. Feel the breeze?\n\n', tNoiseStart);
fprintf('Press ESCAPE key to continue, arrow keys to manually change relative volumes.\n');
while 1
[secs, keyCode] = KbStrokeWait;
if keyCode(escape)
break;
end
if keyCode(leftArrow)
vs = max(PsychPortAudio('Volume', pasound1) - 0.1, 0);
PsychPortAudio('Volume', pasound1, vs);
end
if keyCode(rightArrow)
vs = min(PsychPortAudio('Volume', pasound1) + 0.1, 10);
PsychPortAudio('Volume', pasound1, vs);
end
if keyCode(downArrow)
vs = max(PsychPortAudio('Volume', pasound2) - 0.1, 0);
PsychPortAudio('Volume', pasound2, vs);
end
if keyCode(upArrow)
vs = min(PsychPortAudio('Volume', pasound2) + 0.1, 10);
PsychPortAudio('Volume', pasound2, vs);
end
end
% Let's stop pasound1 exactly at the end of one of its sound cycles. Find
% start of next cycle:
while 1
status = PsychPortAudio('GetStatus', pasound1);
% Every 6th slot corresponds again to the 1st tone, and it needs to be
% playing that is status.Active:
if (mod(status.SchedulePosition, 6) == 0) && (status.Active)
% Got it! The status.startTime now corresponds to the start of the
% current 3-tone-sequence:
break;
end
% Nope. Wait a bit, then retry:
WaitSecs('YieldSecs', 0.1);
end
% Compute stop time exactly 6 seconds after start:
tStop = status.StartTime + 6;
fprintf('Will stop tones at end of this 3-tone sequence.\n');
% Request (flag 3) a stop at time 'tStop'. Wait (flag 1) for stop to
% happen. Please note that a 'waitForEndOfPlayback' flag of 1 or use of the
% 'repetition' parameter wouldn't work here, as we've got a schedule
% attached and these parameters don't work with schedules...
PsychPortAudio('Stop', pasound1, 3, 1, [], tStop);
% We can immediately stop the modulator, as there ain't any sound anymore to
% modulate anyway:
PsychPortAudio('Stop', pamodulator1);
fprintf('3-Tone sequence over. Now softly fading out the breeze over about 10 seconds...\n');
% Fade out the breeze noise manually via pulling down the volume in 1000
% steps, 1 step every 0.01 seconds:
vs = PsychPortAudio('Volume', pasound2);
for v = vs:-0.001:0
% Assign new reduced masterVolume to pasound2:
PsychPortAudio('Volume', pasound2, v);
WaitSecs(0.01);
end
% Ok, we're fully muted. Let's immediately stop both the sound device and
% its modulator:
PsychPortAudio('Stop', pasound2);
PsychPortAudio('Stop', pamodulator2);
% That's it. Everything stopped and silent. Close all devices, release all
% ressources, shutdown the driver:
PsychPortAudio('Close');
fprintf('Finished. Bye!\n');
return;
|