#include "engine/framework/audio/dsp.h" #include "engine/framework/audio/wav_reader.h" #include #include #include #include #include #include #include #include namespace { void write_f32_file(const std::filesystem::path & path, const std::vector & values) { std::ofstream out(path, std::ios::binary); if (!out) { throw std::runtime_error("failed to open output file: " + path.string()); } out.write(reinterpret_cast(values.data()), static_cast(values.size() * sizeof(float))); if (!out) { throw std::runtime_error("failed to write output file: " + path.string()); } } void write_i64_file(const std::filesystem::path & path, const std::vector & values) { std::ofstream out(path, std::ios::binary); if (!out) { throw std::runtime_error("failed to open output file: " + path.string()); } out.write(reinterpret_cast(values.data()), static_cast(values.size() * sizeof(int64_t))); if (!out) { throw std::runtime_error("failed to write output file: " + path.string()); } } } // namespace int main(int argc, char ** argv) { if (argc < 5) { std::cerr << "Usage: stft_parity_dump " " [--preemphasis VALUE] [--n-fft N] [--hop-length N] [--win-length N] [--pad-mode constant|reflect]\n"; return 1; } const std::filesystem::path wav_path(argv[1]); const std::string kind(argv[2]); const std::filesystem::path out_f32(argv[3]); const std::filesystem::path out_shape(argv[4]); float preemphasis = 0.0f; int64_t n_fft = 512; int64_t hop_length = 160; int64_t win_length = 400; engine::audio::STFTPadMode pad_mode = engine::audio::STFTPadMode::Constant; for (int i = 5; i < argc; i += 2) { if (i + 1 >= argc) { throw std::runtime_error("missing value for trailing option"); } const std::string option(argv[i]); const std::string value(argv[i + 1]); if (option == "--preemphasis") { preemphasis = std::strtof(value.c_str(), nullptr); } else if (option == "--n-fft") { n_fft = std::strtoll(value.c_str(), nullptr, 10); } else if (option == "--hop-length") { hop_length = std::strtoll(value.c_str(), nullptr, 10); } else if (option == "--win-length") { win_length = std::strtoll(value.c_str(), nullptr, 10); } else if (option == "--pad-mode") { if (value == "constant") { pad_mode = engine::audio::STFTPadMode::Constant; } else if (value == "reflect") { pad_mode = engine::audio::STFTPadMode::Reflect; } else { throw std::runtime_error("pad-mode must be 'constant' or 'reflect'"); } } else { throw std::runtime_error("unknown option: " + option); } } auto wav = engine::audio::read_wav_f32(wav_path); if (wav.channels != 1) { throw std::runtime_error("stft_parity_dump requires mono WAV"); } if (preemphasis != 0.0f && !wav.samples.empty()) { for (size_t i = wav.samples.size() - 1; i >= 1; --i) { wav.samples[i] = wav.samples[i] - preemphasis * wav.samples[i - 1]; } } const engine::audio::STFTConfig config{ n_fft, hop_length, win_length, true, pad_mode, }; std::vector window(static_cast(config.win_length), 0.0f); if (config.win_length == 1) { window[0] = 1.0f; } else { constexpr long double kPi = 3.14159265358979323846264338327950288L; for (int64_t i = 0; i < config.win_length; ++i) { window[static_cast(i)] = 0.5f - 0.5f * std::cos(2.0L * kPi * static_cast(i) / static_cast(config.win_length - 1)); } } engine::audio::AudioTensor result; if (kind == "stft") { result = engine::audio::STFT().compute_complex( wav.samples, window, 1, static_cast(wav.samples.size()), config); } else { throw std::runtime_error("kind must be 'stft'"); } write_f32_file(out_f32, result.values); write_i64_file(out_shape, result.shape); std::cout << "wrote " << result.values.size() << " floats\n"; std::cout << "preemphasis " << preemphasis << "\n"; return 0; }