CAudio.m 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. classdef CAudio < handle
  2. % a class managing audio interface
  3. % last modified by Strongway: genNoise with randn, normalize noise
  4. properties
  5. freq;
  6. channels;
  7. pins;
  8. latency;
  9. pHandle;
  10. hwName;
  11. hwType;
  12. end
  13. methods
  14. function obj = CAudio(varargin)
  15. %constructor: frequency, channels, latency
  16. p = inputParser;
  17. p.addParamValue('freq',96000,@isnumeric);
  18. p.addParamValue('channels',2,@isnumeric);
  19. p.addParamValue('latency', 0.001, @(x) x>0);
  20. p.addParamValue('pins',[1 2],@isnumeric); %define the pin number of the channels
  21. p.addParamValue('hardware','sndCard', @(x) any(strcmpi(x,{'sndCard','Aout','Ain'})));
  22. p.parse(varargin{:});
  23. obj.freq = p.Results.freq;
  24. obj.channels = p.Results.channels;
  25. obj.latency = p.Results.latency;
  26. obj.hwName = p.Results.hardware;
  27. obj.pins = p.Results.pins;
  28. switch obj.hwName
  29. case {'sndCard'}
  30. obj.hwType = 1;
  31. try
  32. InitializePsychSound(1);
  33. obj.pHandle = PsychPortAudio('Open', [],[],2,obj.freq,obj.channels);
  34. PsychPortAudio('LatencyBias', obj.pHandle, obj.latency);
  35. obj.latency = PsychPortAudio('LatencyBias', obj.pHandle);
  36. catch ME
  37. disp(ME.message);
  38. end
  39. case {'Aout','Ain'}
  40. obj.hwType = 2;
  41. obj.pHandle = analogoutput('nidaq',obj.hwName);
  42. achan = addchannel(obj.pHandle,obj.pins);
  43. obj.channels = size(achan,1);
  44. set(obj.pHandle,'SampleRate',obj.freq);
  45. set(obj.pHandle,'TriggerType','manual');
  46. otherwise
  47. obj.hwType = 1;
  48. end
  49. end
  50. function prepare(obj, data)
  51. switch obj.hwType
  52. case 1
  53. PsychPortAudio('FillBuffer', obj.pHandle, data);
  54. case 2
  55. putdata(obj.pHandle,data');
  56. start(obj.pHandle);
  57. end
  58. end
  59. function onsetTime = present(obj,startTime)
  60. if nargin < 2
  61. startTime = 0;
  62. end
  63. switch obj.hwType
  64. case 1
  65. PsychPortAudio('Start', obj.pHandle, 1, startTime, 0);
  66. active = 0;
  67. while active == 0
  68. s = PsychPortAudio('GetStatus',obj.pHandle);
  69. active = s.Active;
  70. end
  71. onsetTime = s.StartTime; %get true onset time
  72. case 2
  73. if startTime > 0
  74. waitTime = max(0,startTime - GetSecs);
  75. WaitSecs(waitTime);
  76. end
  77. trigger(obj.pHandle);
  78. onsetTime = getSecs;
  79. end
  80. end
  81. function stop(obj,afterFinish)
  82. if nargin < 2
  83. afterFinish = 0; %immediately stop
  84. end
  85. switch obj.hwType
  86. case 1
  87. PsychPortAudio('Stop', obj.pHandle, afterFinish);
  88. case 2
  89. stop(obj.pHandle);
  90. end
  91. end
  92. function open(obj)
  93. obj.pHandle = PsychPortAudio('Open', [],[],2,obj.freq,obj.channels);
  94. PsychPortAudio('LatencyBias', obj.pHandle, obj.latency);
  95. obj.latency = PsychPortAudio('LatencyBias', obj.pHandle);
  96. end
  97. function close(obj)
  98. switch obj.hwType
  99. case 1
  100. PsychPortAudio('Stop', obj.pHandle);
  101. % Close the audio device:
  102. PsychPortAudio('Close', obj.pHandle);
  103. end
  104. end
  105. function tone = genTone(obj, freq, duration)
  106. t = MakeBeep(freq, duration, obj.freq);
  107. tone = repmat(t(1:end-1),obj.channels(1),1);
  108. end
  109. function tone = genNoise(obj,duration)
  110. % change to randn (normal distribution) - Oct.14. 2013
  111. num_samp = obj.freq*duration;
  112. t = randn(1,num_samp);
  113. tone = repmat(t,obj.channels(1),1);
  114. % rescale
  115. tone = tone./3; % 3 sigma
  116. end
  117. function [tone, pn] = genPinkNoise(obj, f0, duration, dbc_per_hz, num_taps)
  118. % based on code phase_noise by Jeff Schenck 11/21/95
  119. % f0 reference frequency (must be in Hz.)
  120. % dbc_per_hz power per hertz relative to carrier at ref. freq.
  121. % num_taps number of filter taps in AR 1/f filter
  122. % (optional; default = 100)
  123. % pn phase-modulated 1/f process
  124. % theta 1/f process (before phase modulation)
  125. % Check input.
  126. if dbc_per_hz >= 0
  127. error('Power per Hz. must be negative.');
  128. elseif f0 <= 0
  129. error('Reference frequency must be positive.');
  130. end
  131. if nargin < 5
  132. num_taps = 100;
  133. end
  134. num_samp = obj.freq * duration;
  135. % Generate white noise. Apply gain for desired dBc/Hz. Warn user
  136. % if gain is too large (gain thresholds have been chosen somewhat
  137. % arbitrarily -- needs work).
  138. gain = sqrt(2*pi * f0 * 10^(dbc_per_hz/10));
  139. wn = gain * randn(1,num_samp);
  140. fprintf('Gain applied to white noise = %f.\n', gain);
  141. if gain >= 1
  142. fprintf('WARNING: Narrowband approximation no longer valid.\n');
  143. elseif gain >= .5
  144. fprintf('WARNING: Narrowband approximation on the verge of collapse.\n');
  145. end
  146. % Generate 1/f AR filter and apply to white noise to produce 1/f
  147. % noise.
  148. a = zeros(1,num_taps);
  149. a(1) = 1;
  150. for ii = 2:num_taps
  151. a(ii) = (ii - 2.5) * a(ii-1) / (ii-1);
  152. end
  153. theta = filter(1,a,wn);
  154. tone = repmat(theta,obj.channels(1),1);
  155. % Phase modulate.
  156. pn = exp(i*theta);
  157. % normalize
  158. sd3 = std(tone(1,:))*3; % 3 sigma
  159. tone = tone./sd3;
  160. end
  161. end
  162. end