#include <cstdio>
#include <string>
#include <thread>
+#include <utility>
#include <vector>
#include <cstring>
}
}
-static bool output_txt(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_txt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
const int n_segments = whisper_full_n_segments(ctx);
for (int i = 0; i < n_segments; ++i) {
const char * text = whisper_full_get_segment_text(ctx, i);
fout << speaker << text << "\n";
}
-
- return true;
}
-static bool output_vtt(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_vtt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
fout << "WEBVTT\n\n";
const int n_segments = whisper_full_n_segments(ctx);
fout << to_timestamp(t0) << " --> " << to_timestamp(t1) << "\n";
fout << speaker << text << "\n\n";
}
-
- return true;
}
-static bool output_srt(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_srt(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
const int n_segments = whisper_full_n_segments(ctx);
for (int i = 0; i < n_segments; ++i) {
const char * text = whisper_full_get_segment_text(ctx, i);
fout << to_timestamp(t0, true) << " --> " << to_timestamp(t1, true) << "\n";
fout << speaker << text << "\n\n";
}
-
- return true;
}
static char * escape_double_quotes_and_backslashes(const char * str) {
return escaped;
}
-static bool output_csv(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_csv(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
const int n_segments = whisper_full_n_segments(ctx);
fout << "start,end,";
if (params.diarize && pcmf32s.size() == 2)
}
fout << "\"" << text_escaped << "\"\n";
}
-
- return true;
}
-static bool output_score(struct whisper_context * ctx, const char * fname, const whisper_params & /*params*/, std::vector<std::vector<float>> /*pcmf32s*/) {
- std::ofstream fout(fname);
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_score(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & /*params*/, std::vector<std::vector<float>> /*pcmf32s*/) {
const int n_segments = whisper_full_n_segments(ctx);
// fprintf(stderr,"segments: %d\n",n_segments);
for (int i = 0; i < n_segments; ++i) {
// fprintf(stderr,"token: %s %f\n",token,probability);
}
}
- return true;
}
-static bool output_json(
+static void output_json(
struct whisper_context * ctx,
- const char * fname,
+ std::ofstream & fout,
const whisper_params & params,
- std::vector<std::vector<float>> pcmf32s,
- bool full) {
- std::ofstream fout(fname);
+ std::vector<std::vector<float>> pcmf32s) {
+ const bool full = params.output_jsn_full;
int indent = 0;
auto doindent = [&]() {
end_obj(end);
};
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
start_obj(nullptr);
value_s("systeminfo", whisper_print_system_info(), false);
start_obj("model");
end_arr(true);
end_obj(true);
- return true;
}
// karaoke video generation
// outputs a bash script that uses ffmpeg to generate a video with the subtitles
// TODO: font parameter adjustments
-static bool output_wts(struct whisper_context * ctx, const char * fname, const char * fname_inp, const whisper_params & params, float t_sec, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static bool output_wts(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s, const char * fname_inp, float t_sec, const char * fname_out) {
static const char * font = params.font_path.c_str();
std::ifstream fin(font);
fout.close();
- fprintf(stderr, "%s: run 'source %s' to generate karaoke video\n", __func__, fname);
+ fprintf(stderr, "# %s: run 'source %s' to generate karaoke video\n", __func__, fname_out);
return true;
}
-static bool output_lrc(struct whisper_context * ctx, const char * fname, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
- std::ofstream fout(fname);
- if (!fout.is_open()) {
- fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname);
- return false;
- }
-
- fprintf(stderr, "%s: saving output to '%s'\n", __func__, fname);
-
+static void output_lrc(struct whisper_context * ctx, std::ofstream & fout, const whisper_params & params, std::vector<std::vector<float>> pcmf32s) {
fout << "[by:whisper.cpp]\n";
const int n_segments = whisper_full_n_segments(ctx);
fout << '[' << timestamp_lrc << ']' << speaker << text << "\n";
}
-
- return true;
}
}
for (int f = 0; f < (int) params.fname_inp.size(); ++f) {
- const auto fname_inp = params.fname_inp[f];
- const auto fname_out = f < (int) params.fname_out.size() && !params.fname_out[f].empty() ? params.fname_out[f] : params.fname_inp[f];
+ const auto & fname_inp = params.fname_inp[f];
+ struct fout_factory {
+ std::string fname_out;
+ const size_t basename_length;
+ const bool is_stdout;
+ bool used_stdout;
+ decltype(whisper_print_segment_callback) * const print_segment_callback;
+ std::ofstream fout;
+
+ fout_factory (const std::string & fname_out_, const std::string & fname_inp, whisper_params & params) :
+ fname_out{!fname_out_.empty() ? fname_out_ : fname_inp},
+ basename_length{fname_out.size()},
+ is_stdout{fname_out == "-"},
+ used_stdout{},
+ print_segment_callback{is_stdout ? nullptr : whisper_print_segment_callback} {
+ if (!print_segment_callback) {
+ params.print_progress = false;
+ }
+ }
+
+ bool open(const char * ext, const char * function) {
+ if (is_stdout) {
+ if (std::exchange(used_stdout, true)) {
+ fprintf(stderr, "warning: Not appending multiple file formats to stdout\n");
+ return false;
+ }
+#ifdef _WIN32
+ fout = std::ofstream{"CON"};
+#else
+ fout = std::ofstream{"/dev/stdout"};
+#endif
+ // Not using fprintf stderr here because it might equal stdout
+ // Also assuming /dev is mounted
+ return true;
+ }
+ fname_out.resize(basename_length);
+ fname_out += ext;
+ fout = std::ofstream{fname_out};
+ if (!fout.is_open()) {
+ fprintf(stderr, "%s: failed to open '%s' for writing\n", __func__, fname_out.c_str());
+ return false;
+ }
+ fprintf(stderr, "%s: saving output to '%s'\n", function, fname_out.c_str());
+ return true;
+ }
+ } fout_factory{f < (int) params.fname_out.size() ? params.fname_out[f] : "", fname_inp, params};
std::vector<float> pcmf32; // mono-channel F32 PCM
std::vector<std::vector<float>> pcmf32s; // stereo-channel F32 PCM
// this callback is called on each new segment
if (!wparams.print_realtime) {
- wparams.new_segment_callback = whisper_print_segment_callback;
+ wparams.new_segment_callback = fout_factory.print_segment_callback;
wparams.new_segment_callback_user_data = &user_data;
}
// output stuff
{
- printf("\n");
-
- // output to text file
- if (params.output_txt) {
- const auto fname_txt = fname_out + ".txt";
- output_txt(ctx, fname_txt.c_str(), params, pcmf32s);
- }
-
- // output to VTT file
- if (params.output_vtt) {
- const auto fname_vtt = fname_out + ".vtt";
- output_vtt(ctx, fname_vtt.c_str(), params, pcmf32s);
- }
-
- // output to SRT file
- if (params.output_srt) {
- const auto fname_srt = fname_out + ".srt";
- output_srt(ctx, fname_srt.c_str(), params, pcmf32s);
- }
-
- // output to WTS file
- if (params.output_wts) {
- const auto fname_wts = fname_out + ".wts";
- output_wts(ctx, fname_wts.c_str(), fname_inp.c_str(), params, float(pcmf32.size() + 1000)/WHISPER_SAMPLE_RATE, pcmf32s);
- }
-
- // output to CSV file
- if (params.output_csv) {
- const auto fname_csv = fname_out + ".csv";
- output_csv(ctx, fname_csv.c_str(), params, pcmf32s);
- }
-
- // output to JSON file
- if (params.output_jsn) {
- const auto fname_jsn = fname_out + ".json";
- output_json(ctx, fname_jsn.c_str(), params, pcmf32s, params.output_jsn_full);
- }
-
- // output to LRC file
- if (params.output_lrc) {
- const auto fname_lrc = fname_out + ".lrc";
- output_lrc(ctx, fname_lrc.c_str(), params, pcmf32s);
- }
-
- // output to score file
- if (params.log_score) {
- const auto fname_score = fname_out + ".score.txt";
- output_score(ctx, fname_score.c_str(), params, pcmf32s);
+ // macros to stringify function name
+#define output_func(func, ext, param, ...) if (param && fout_factory.open(ext, #func)) {\
+ func(ctx, fout_factory.fout, params, __VA_ARGS__); \
+}
+#define output_ext(ext, ...) output_func(output_##ext, "." #ext, params.output_##ext, __VA_ARGS__)
+
+ output_ext(txt, pcmf32s);
+ output_ext(vtt, pcmf32s);
+ output_ext(srt, pcmf32s);
+ output_ext(wts, pcmf32s, fname_inp.c_str(), float(pcmf32.size() + 1000)/WHISPER_SAMPLE_RATE, fout_factory.fname_out.c_str());
+ output_ext(csv, pcmf32s);
+ output_func(output_json, ".json", params.output_jsn, pcmf32s);
+ output_ext(lrc, pcmf32s);
+ output_func(output_score, ".score.txt", params.log_score, pcmf32s);
+
+#undef output_ext
+#undef output_func
+
+ if (fout_factory.is_stdout && !fout_factory.used_stdout) {
+ fprintf(stderr, "warning: '--output-file -' used without any other '--output-*'");
}
}
}