-
Notifications
You must be signed in to change notification settings - Fork 3
/
Feature.m
171 lines (124 loc) · 5.09 KB
/
Feature.m
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
classdef Feature < dynamicprops
properties
type
range
color
end
properties (Transient)
reporter
end
properties (Dependent = true)
startTime
endTime
duration
lowFreq
highFreq
end
events
RangeChanged
end
methods
function obj = Feature(featureType, featureRange, varargin)
%% Create a new feature with the given type that exists in the given time and frequency ranges.
%
% The range can be a 1, 2 or 4 element vector:
% * 1 element = the feature exists at one point in time with no frequency limit.
% * 2 elements = the feature starts and ends at the given time points and has no frequency limit.
% * 4 elements = elements 1 and 2 are the start and stop time, elements 3 and 4 are the low and high frequencies.
% Additional properties can be assigned by adding name/value pairs of arguments.
%
% Examples:
% >> f1 = Feature('pulse', [72.6]);
% >> f2 = Feature('sine song', [12.34 13.56], 'fundamentalFrequency', 264.8);
% >> f3 = Feature('vocalization', [12.34 13.56 218.7 342.3]);
obj = obj@dynamicprops();
if mod(numel(varargin), 2) == 1
error 'Additional feature properties must be specified as pairs of names and values.'
end
obj.type = featureType;
if isscalar(featureRange)
obj.range = [featureRange featureRange -inf inf];
elseif size(featureRange, 2) == 2
obj.range = [featureRange -inf inf];
elseif size(featureRange, 2) == 4
obj.range = featureRange;
else
error('Feature ranges must have 1, 2 or 4 elements.')
end
%% Add any optional attributes.
% TODO: any value in having known attribute types like 'confidence'?
for argIndex = 1:numel(varargin) / 2;
propName = varargin{argIndex * 2 - 1};
propValue = varargin{argIndex * 2};
if strcmpi(propName, 'color')
obj.color = propValue;
else
addprop(obj, propName);
obj.(varargin{argIndex * 2 - 1}) = varargin{argIndex * 2};
end
end
end
function set.range(obj, range)
if isempty(obj.range) || isempty(range) || any(obj.range ~= range)
obj.range = range;
notify(obj, 'RangeChanged');
end
end
function t = get.startTime(obj)
t = obj.range(1);
end
function set.startTime(obj, time)
if obj.range(1) ~= time
obj.range(1) = time;
notify(obj, 'RangeChanged');
end
end
function t = get.endTime(obj)
t = obj.range(2);
end
function set.endTime(obj, time)
if obj.range(2) ~= time
obj.range(2) = time;
notify(obj, 'RangeChanged');
end
end
function t = get.duration(obj)
t = obj.range(2) - obj.range(1);
end
function t = get.lowFreq(obj)
t = obj.range(3);
end
function set.lowFreq(obj, freq)
if obj.range(3) ~= freq
obj.range(3) = freq;
notify(obj, 'RangeChanged');
end
end
function t = get.highFreq(obj)
t = obj.range(4);
end
function set.highFreq(obj, freq)
if obj.range(4) ~= freq
obj.range(4) = freq;
notify(obj, 'RangeChanged');
end
end
function c = get.color(obj)
if isempty(obj.color)
c = obj.reporter.featuresColor;
else
c = obj.color;
end
end
function [obj, idx] = sort(obj, varargin)
[~, idx] = sort([obj.startTime], varargin{:});
obj = obj(idx);
end
function m = matches(obj, otherFeature, timeThreshold, freqThreshold)
m = (obj.startTime == otherFeature.startTime || abs(obj.startTime - otherFeature.startTime) < timeThreshold) && ...
(obj.endTime == otherFeature.endTime || abs(obj.endTime - otherFeature.endTime) < timeThreshold) && ...
(obj.lowFreq == otherFeature.lowFreq || abs(obj.lowFreq - otherFeature.lowFreq) < freqThreshold) && ...
(obj.highFreq == otherFeature.highFreq || abs(obj.highFreq - otherFeature.highFreq) < freqThreshold);
end
end
end