/* Generate matched raw/smoothed previews for human listening; never plays audio. * Usage: quality-preview /absolute/path/prefix (writes prefix-{raw,smooth}.wav). */ #include "xtc/xtc_audio.h" #include "xtc/xtc_midi.h" #include "xtc/xtc_dsp.h" #include #include #include #include static xtc_result preview(const char *prefix, int smooth) { xtc_project *project = NULL; xtc_midi_plan *midi = NULL; xtc_recorder *recorder = NULL; xtc_gain_ramp ramp; uint64_t track, clip, note, position; char path[1024]; xtc_result result; int n = snprintf(path, sizeof(path), "%s-%s.wav", prefix, smooth ? "smooth" : "raw"); if (n < 0 || (size_t)n >= sizeof(path)) return XTC_ERR_CAPACITY; result = xtc_project_create(&project); if (result == XTC_OK) result = xtc_project_add_track(project, "Listening phrase", &track); if (result == XTC_OK) result = xtc_project_add_midi_clip(project, track, 0, 7680, &clip); if (result == XTC_OK) result = xtc_project_add_note(project, clip, 0, 480, 60, 100, 0, ¬e); if (result == XTC_OK) result = xtc_project_add_note(project, clip, 720, 480, 64, 100, 0, ¬e); if (result == XTC_OK) result = xtc_project_add_note(project, clip, 1440, 1440, 67, 100, 0, ¬e); if (result == XTC_OK) result = xtc_midi_plan_create(project, 48000, &midi); if (result == XTC_OK) result = xtc_midi_plan_set_fade_frames(midi, smooth ? 240U : 0U); if (result == XTC_OK) result = xtc_recorder_create_replace(path, 48000, 256, 1, &recorder); (void)xtc_gain_ramp_reset(&ramp, smooth ? 0 : 1); (void)xtc_gain_ramp_target(&ramp, 1, smooth ? 240U : 0U); for (position = 0; result == XTC_OK && position < 192000; position += 256) { float samples[256]; size_t i; result = xtc_midi_plan_render(midi, position, NULL, 0, samples, 256); if (result != XTC_OK) break; for (i = 0; i < 256; ++i) { uint64_t sample = position + i; /* Deliberately abrupt constant clips with 5 ms fades in the smooth * version, followed by a tone stopped mid-period at three seconds. */ if (sample >= 96000 && sample < 108000) { uint64_t age = sample - 96000, tail = 107999 - sample; uint64_t distance = age < tail ? age : tail; float envelope = smooth && distance < 240 ? (float)distance / 240.0f : 1; samples[i] += 0.2f * envelope; } if (sample >= 120000 && sample < 144000) samples[i] += 0.2f * (float)sin(6.283185307179586 * 443.0 * (double)(sample - 120000) / 48000.0); if (sample == 143760) (void)xtc_gain_ramp_target(&ramp, 0, smooth ? 240U : 0U); (void)xtc_gain_ramp_process(&ramp, samples + i, NULL, 1); } xtc_recorder_push(recorder, samples, 256); result = xtc_recorder_drain(recorder); } if (result == XTC_OK) result = xtc_recorder_finish(recorder); xtc_recorder_destroy(recorder); xtc_midi_plan_destroy(midi); xtc_project_destroy(project); if (result == XTC_OK) printf("Wrote %s\n", path); return result; } int main(int argc, char **argv) { xtc_result result; if (argc != 2) { fprintf(stderr, "Usage: %s OUTPUT_PREFIX\n", argv[0]); return EXIT_FAILURE; } result = preview(argv[1], 0); if (result == XTC_OK) result = preview(argv[1], 1); if (result != XTC_OK) { fprintf(stderr, "%s\n", xtc_result_string(result)); return EXIT_FAILURE; } return EXIT_SUCCESS; }