ita_NI_daq_run.m 7.49 KB
Newer Older
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
function varargout = ita_NI_daq_run(varargin)
% ITA_NI_DAQ_RUN play and record sound with NI card and DAQ toolbox (adapted from ita_portaudio_run)
% see ita_portaudio_run for options and help

% Autor: Markus Mueller-Trapet -- Email: markus.mueller-trapet@nrc.ca
% Created:  13-Apr-2017

%% ITA Toolbox preferences for verbose level
verboseMode = ita_preferences('verboseMode');

%% Input checks
if isa(varargin{1},'itaAudio')
    % if a signal is the first input, then playback that signal
    sArgs.pos1_data = 'itaAudioTime';
    playback = true;
else
    % otherwise just record for the given number of samples
    sArgs.pos1_data = 'numeric';
    playback = false;
end

% second argument has to be the DAQ NI session
sArgs.pos2_niSession = 'anything';

% only do recording if requested
if nargout > 0
    record = true;
else
    record = false;
end

%% Init Settings
sArgs.normalizeinput    = false;
sArgs.normalizeoutput    = false;

sArgs.inputchannels     = [];
sArgs.outputchannels    = [];
sArgs.recsamples        = -1;
sArgs.repeats           = 1;
sArgs.samplingRate      = -1; % will always be taken from NI session
% cancel button and monitor not supported currently, but kept for compatibility
sArgs.cancelbutton      = ita_preferences('portAudioMonitor'); % -1:automatic; 0:off; 1:on
sArgs.latencysamples    = 0;
sArgs.singleprecision   = false;

[data, niSession, sArgs] = ita_parse_arguments(sArgs,varargin);

if ~playback && sArgs.recsamples == -1
    sArgs.recsamples = data;
end

sArgs.samplingRate = round(niSession.Rate); % NI rate is not exact

in_channel_vec   = sArgs.inputchannels;
out_channel_vec  = sArgs.outputchannels;
normalize_input  = sArgs.normalizeinput;
normalize_output = sArgs.normalizeoutput;

if ~playback && ~record
    error('ITA_NI_DAQ_RUN:What do you want? Play or Record, or both? You need to specify an input and/or an output!')
end

if playback
    % Extract channels to play
    % are there enough channels to play
    if data.nChannels == 1 && (length(out_channel_vec) > 1) %playback the same on alle channels activated
        ita_verbose_info('Oh Lord. I will playback the same on all channels.', 2)
        nChannelsToPlay = length(out_channel_vec);
        data = data.ch(ones(1,nChannelsToPlay)); %duplicated data to all channels
    elseif data.nChannels < length(out_channel_vec) %too many output channels for this input data
        ita_verbose_info('Not enough channels in data to play.',0)
        out_channel_vec = out_channel_vec(1:data.nChannels);
    elseif data.nChannels > length(out_channel_vec)
        ita_verbose_info('Too many channels in data file, I will only play the first ones',1);
        data = ita_split(data,1:length(out_channel_vec));
    end
    
    % Show channel data if requested
    if verboseMode==2
        ita_metainfo_show_channelnames(data);
    end
    
    % Check levels - Normalizing
    peak_value = max(max(abs(data.timeData)));
    if (peak_value > 1) || (normalize_output)
        ita_verbose_info('Oh Lord! Levels too high for playback. Normalizing...',0)
        data = ita_normalize_dat(data);
    end
end

%% Extend excitation to compensate soundcard latency
if playback && record && ~isempty(sArgs.latencysamples) && (sArgs.latencysamples > 0)
    latencysamples = sArgs.latencysamples;
    data = ita_extend_dat(data,data.nSamples+latencysamples,'forcesamples');
end

% record as many samples as are in the playback signal
if playback
    sArgs.recsamples = data.nSamples;
end

% Full (double) precision
recordDatadat = zeros(sArgs.recsamples,numel(in_channel_vec),sArgs.repeats);
if sArgs.singleprecision % only single precision
    recordDatadat = single(recordDatadat);
end

% run measurement, possibly repeated
for idrep = 1:sArgs.repeats
    if playback && record
        niSession.queueOutputData(double(data.time));
        ita_verbose_info('start playback and record',1)
    elseif record
        niSession.queueOutputData(zeros(sArgs.recsamples,1));
        ita_verbose_info('start record',1)
    elseif playback
        niSession.queueOutputData(double(data.time));
        ita_verbose_info('start playback',1)
    else
        error('ITA_NI_DAQ_run:No input and no output, what should I do?')
    end
    pause(0.01)
    % do the measurement
    if ~sArgs.singleprecision % Full (double) precision
        recordDatadat(:,:,idrep) = niSession.startForeground();
    else
        recordDatadat(:,:,idrep) = single(niSession.startForeground());
    end
end % loop for repeats

ita_verbose_info('playback/record finished ',1);

recordData = itaAudio();
recordData.dataType = class(recordDatadat);
recordData.dataTypeOutput = class(recordDatadat);
% Check if we need to average multiple measurements:
if size(recordDatadat,3) > 1
    % average:
    recordData.timeData = mean(recordDatadat,3);
else
    % no average: (This saves memory!)
    recordData.timeData = recordDatadat;
end
recordData.samplingRate = sArgs.samplingRate;

for idx = 1:numel(in_channel_vec)
    recordData.channelNames{idx} = ['Ch ' int2str(in_channel_vec(idx))];
end

%% Remove Latency
if playback && record && ~isempty(sArgs.latencysamples) && (sArgs.latencysamples > 0)
    recordData = ita_extract_dat(recordData,recordData.nSamples-latencysamples,'firstsample',latencysamples+1);
end

%% Check for clipping and other errors
clipping = false;
if record
    if any(any(abs(recordData.timeData)>=10)) % Check for clipping (NI card actually handles up to 10Vpk)
        ita_verbose_info('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!',0);
        ita_verbose_info('!ITA_NI_DAQ_RUN:Careful, Clipping!',0);
        ita_verbose_info('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!',0);
        clipping = true;
    end
    % This check for singularities needs a lot of memory! TODO: find a
    % better solution!:
    if any(isnan(recordData.timeData(:))) || any(isinf(recordData.timeData(:))) %Check for singularities
        ita_verbose_info('There are singularities in the audio signal! You''d better check your settings!',0)
        clipping = true;
    end
    if any(all(recordData.timeData == 0,1)) && record
        ita_verbose_info('There are empty channels in the audio signal! You''d better check your settings!',0)
    end
    % maximum for each channel
    maxData = max(abs(recordData.timeData),[],1);
    [channelMax, indexMax] = max(maxData);
    
    % jri: to detect non working microphones etc, the minimum of the
    % maximums on the channels is also outputted
    if length(in_channel_vec) > 1
        [channelMin, indexMin] = min(maxData);
        ita_verbose_info(['Minimum digital level: ' int2str(20*log10(channelMin)) ' dBFS on channel: ' int2str(in_channel_vec(indexMin))],0);
    end
    ita_verbose_info(['Maximum digital level: ' int2str(20*log10(channelMax)) ' dBFS on channel: ' int2str(in_channel_vec(indexMax))],0);
end

%% Add history line
infosforhistory = struct('PlayDevice','NI','Play_Channels',out_channel_vec,'RecDevice','NI','Rec_Channels',in_channel_vec,'Sampling_Rate',niSession.Rate,'Normalize_Input',normalize_input,'Normalize_Output',0,'Rec_Samples',sArgs.recsamples,'Block',1,'Repeats',sArgs.repeats);
recordData = ita_metainfo_add_historyline(recordData,'ita_NI_daq_run',[{data}; ita_struct2arguments(infosforhistory)],'withsubs');

if clipping
    recordData = ita_metainfo_add_historyline(recordData,'!!!ITA_NI_DAQ_RUN:Careful, clipping or something else went wrong!!!');
    recordData = ita_errorlog_add(recordData,'!!!ITA_NI_DAQ_RUN:Careful, clipping or something else went wrong!!!');
end

%% Find output parameters
if nargout ~= 0
    if normalize_input
        ita_normalize_dat(recordData)
    end
    varargout{1} = recordData;
end

end % function