+void find_kmeans(const std::vector<pulse> &pulses, double calibration_factor, const std::vector<float> &initial_centers)
+{
+ std::vector<float> last_centers = initial_centers;
+ std::vector<float> sums;
+ std::vector<float> num;
+ sums.resize(initial_centers.size());
+ num.resize(initial_centers.size());
+ for ( ;; ) {
+ for (unsigned i = 0; i < initial_centers.size(); ++i) {
+ sums[i] = 0.0f;
+ num[i] = 0;
+ }
+ for (unsigned i = 0; i < pulses.size(); ++i) {
+ double cycles = pulses[i].len * calibration_factor * C64_FREQUENCY;
+ // Ignore heavy outliers, which are almost always long pauses.
+ if (cycles > 2000.0) {
+ continue;
+ }
+ std::pair<int, double> selected_point_and_sq_dist = find_closest_point(cycles, last_centers);
+ int p = selected_point_and_sq_dist.first;
+ sums[p] += cycles;
+ ++num[p];
+ }
+ bool any_moved = false;
+ for (unsigned i = 0; i < initial_centers.size(); ++i) {
+ if (num[i] == 0) {
+ fprintf(stderr, "K-means broke down, can't output new reference training points\n");
+ return;
+ }
+ float new_center = sums[i] / num[i];
+ if (fabs(new_center - last_centers[i]) > 1e-3) {
+ any_moved = true;
+ }
+ last_centers[i] = new_center;
+ }
+ if (!any_moved) {
+ break;
+ }
+ }
+ fprintf(stderr, "New reference training points:");
+ for (unsigned i = 0; i < last_centers.size(); ++i) {
+ fprintf(stderr, " %.3f", last_centers[i]);
+ }
+ fprintf(stderr, "\n");
+}
+
+void spsa_train(const std::vector<float> &pcm, int sample_rate)