-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsample_classifier.cpp
More file actions
169 lines (151 loc) · 5.97 KB
/
Copy pathsample_classifier.cpp
File metadata and controls
169 lines (151 loc) · 5.97 KB
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
#include "sample_classifier.h"
#include "sample_library.h"
#include <string.h>
namespace {
int findExistingPathIndex(const SampleClassifier::ClassificationReport &report, const String &path) {
for (int i = 0; i < report.itemCount; i++) {
if (report.items[i].path == path) return i;
}
return -1;
}
void resetReport(SampleClassifier::ClassificationReport &report) {
for (int i = 0; i < SampleClassifier::ClassificationReport::kMaxItems; i++) {
report.items[i].note = 0;
report.items[i].path = "";
report.items[i].channelCount = 0;
report.items[i].bitsPerSample = 0;
report.items[i].sampleRate = 0;
report.items[i].dataBytes = 0;
report.items[i].dataOffset = 0;
report.items[i].durationSeconds = 0.0f;
report.items[i].mode = SampleClassifier::StorageMode::ReadError;
report.items[i].headBytes = 0;
}
report.itemCount = 0;
report.sampleRamBudgetBytes = 0;
report.sampleRamUsedBytes = 0;
report.ramSampleCount = 0;
report.streamSampleCount = 0;
report.missingFileCount = 0;
report.invalidFormatCount = 0;
report.readErrorCount = 0;
}
} // namespace
namespace SampleClassifier {
void classifyAssignedSamples(const SettingsStore::SamplerSettings &settings,
const SampleLibrary::Catalog &catalog,
ClassificationReport &report) {
resetReport(report);
report.sampleRamBudgetBytes = settings.sampleRamBudgetBytes;
// First occurrence of each path; later duplicates share its classification.
int firstIndex[ClassificationReport::kMaxItems];
// Playable first occurrences, packed into RAM shortest first.
int candidates[ClassificationReport::kMaxItems];
int candidateCount = 0;
for (int i = 0;
i < settings.assignmentCount && report.itemCount < ClassificationReport::kMaxItems;
i++) {
const SettingsStore::MidiAssignment &assignment = settings.assignments[i];
const int itemIndex = report.itemCount++;
AssignedSampleClassification &item = report.items[itemIndex];
item.note = assignment.note;
item.path = assignment.samplePath;
const int existingIndex = findExistingPathIndex(report, item.path);
firstIndex[itemIndex] = existingIndex;
if (existingIndex < itemIndex) continue;
const int index = SampleLibrary::findIndexByPath(catalog, item.path);
if (index < 0) {
item.mode = StorageMode::MissingFile;
} else if (!catalog.playable(index)) {
item.mode = catalog.validation[index].status == WavValidation::Status::ReadError ||
catalog.validation[index].status == WavValidation::Status::Unchecked
? StorageMode::ReadError : StorageMode::InvalidFormat;
} else {
const auto &info = catalog.validation[index];
item.channelCount = kRequiredChannelCount;
item.bitsPerSample = kRequiredBitsPerSample;
item.sampleRate = kRequiredSampleRate;
item.dataBytes = info.dataBytes;
item.dataOffset = info.dataOffset;
item.durationSeconds = static_cast<float>(info.dataBytes) / 88200.0f;
item.mode = StorageMode::Stream;
// Stable insertion keeps assignment order among equal sizes.
int pos = candidateCount++;
while (pos > 0 && report.items[candidates[pos - 1]].dataBytes > item.dataBytes) {
candidates[pos] = candidates[pos - 1];
pos--;
}
candidates[pos] = itemIndex;
}
}
// Reserve the start of every sample first: whichever of them end up
// streaming then start from RAM. Skip heads if even they do not fit.
uint32_t headTotal = 0;
for (int i = 0; i < candidateCount; i++) {
const AssignedSampleClassification &item = report.items[candidates[i]];
headTotal += item.dataBytes < kStreamHeadBytes ? item.dataBytes : kStreamHeadBytes;
}
const bool useHeads = headTotal <= report.sampleRamBudgetBytes;
if (useHeads) report.sampleRamUsedBytes = headTotal;
// Preload as many whole samples as the budget allows; each costs only what
// its reserved head does not cover. Shortest first leaves the fewest on SD.
for (int i = 0; i < candidateCount; i++) {
AssignedSampleClassification &item = report.items[candidates[i]];
const uint32_t head = useHeads ? (item.dataBytes < kStreamHeadBytes ? item.dataBytes
: kStreamHeadBytes)
: 0;
if (item.dataBytes - head <= report.sampleRamBudgetBytes - report.sampleRamUsedBytes) {
item.mode = StorageMode::Ram;
report.sampleRamUsedBytes += item.dataBytes - head;
} else {
item.headBytes = head;
}
}
for (int i = 0; i < report.itemCount; i++) {
AssignedSampleClassification &item = report.items[i];
if (firstIndex[i] < i) {
const AssignedSampleClassification &existing = report.items[firstIndex[i]];
item.channelCount = existing.channelCount;
item.bitsPerSample = existing.bitsPerSample;
item.sampleRate = existing.sampleRate;
item.dataBytes = existing.dataBytes;
item.dataOffset = existing.dataOffset;
item.durationSeconds = existing.durationSeconds;
item.mode = existing.mode;
item.headBytes = existing.headBytes;
}
switch (item.mode) {
case StorageMode::Ram:
report.ramSampleCount++;
break;
case StorageMode::Stream:
report.streamSampleCount++;
break;
case StorageMode::MissingFile:
report.missingFileCount++;
break;
case StorageMode::InvalidFormat:
report.invalidFormatCount++;
break;
case StorageMode::ReadError:
report.readErrorCount++;
break;
}
}
}
const char *storageModeLabel(StorageMode mode) {
switch (mode) {
case StorageMode::Ram:
return "RAM";
case StorageMode::Stream:
return "STREAM";
case StorageMode::MissingFile:
return "MISSING";
case StorageMode::InvalidFormat:
return "INVALID_FMT";
case StorageMode::ReadError:
return "READ_ERR";
}
return "UNKNOWN";
}
} // namespace SampleClassifier