feat: shortcut

This commit is contained in:
intelligence committed 2026-08-14 14:00:52 +02:00
1 parent 05a7c1cd83
commit 37fbe5d787
2 files changed
+15 -61

No files matched your search

+13 -59
View File
@@ -19,8 +19,6 @@ namespace {
constexpr const char* kAppId = "io.github.transui";
constexpr const char* kConfigGroup = "transui";
constexpr gint64 kSilenceTimeoutUs = 2 * G_USEC_PER_SEC;
constexpr double kSoundThresholdDb = -42.0;
const SecretSchema kSecretSchema = {
"io.github.transui.credentials", SECRET_SCHEMA_NONE,
@@ -45,10 +43,7 @@ struct App {
GtkWidget* processing = nullptr;
GtkWidget* elapsed_label = nullptr;
guint elapsed_source = 0;
guint silence_source = 0;
guint level_watch = 0;
guint elapsed_seconds = 0;
gint64 last_sound_us = 0;
GstElement* pipeline = nullptr;
std::string recording_path;
std::atomic<bool> cancelled{false};
@@ -290,53 +285,11 @@ gboolean tick_elapsed(gpointer data) {
return G_SOURCE_CONTINUE;
}
void stop_recording(GtkButton*, gpointer data);
gboolean monitor_audio_level(GstBus*, GstMessage* message, gpointer data) {
if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT) return G_SOURCE_CONTINUE;
const GstStructure* structure = gst_message_get_structure(message);
if (!structure || !gst_structure_has_name(structure, "level")) return G_SOURCE_CONTINUE;
const GValue* rms = gst_structure_get_value(structure, "rms");
if (!rms || !GST_VALUE_HOLDS_LIST(rms)) return G_SOURCE_CONTINUE;
auto* app = static_cast<App*>(data);
for (guint index = 0; index < gst_value_list_get_size(rms); ++index) {
const GValue* channel = gst_value_list_get_value(rms, index);
if (G_VALUE_HOLDS_DOUBLE(channel) && g_value_get_double(channel) > kSoundThresholdDb) {
app->last_sound_us = g_get_monotonic_time();
break;
}
}
return G_SOURCE_CONTINUE;
}
gboolean detect_silence(gpointer data) {
auto* app = static_cast<App*>(data);
if (!app->pipeline) {
app->silence_source = 0;
return G_SOURCE_REMOVE;
}
if (g_get_monotonic_time() - app->last_sound_us < kSilenceTimeoutUs)
return G_SOURCE_CONTINUE;
app->silence_source = 0;
stop_recording(nullptr, app);
return G_SOURCE_REMOVE;
}
void remove_recording_sources(App* app) {
void stop_elapsed_timer(App* app) {
if (app->elapsed_source) {
g_source_remove(app->elapsed_source);
app->elapsed_source = 0;
}
if (app->silence_source) {
g_source_remove(app->silence_source);
app->silence_source = 0;
}
if (app->level_watch) {
g_source_remove(app->level_watch);
app->level_watch = 0;
}
}
void remove_recording(App* app) {
@@ -347,7 +300,7 @@ void remove_recording(App* app) {
}
void close_recorder(App* app) {
remove_recording_sources(app);
stop_elapsed_timer(app);
if (app->pipeline) {
gst_element_set_state(app->pipeline, GST_STATE_NULL);
gst_object_unref(app->pipeline);
@@ -557,7 +510,7 @@ void show_processing(App* app) {
void stop_recording(GtkButton*, gpointer data) {
auto* app = static_cast<App*>(data);
if (!app->pipeline) return;
remove_recording_sources(app);
stop_elapsed_timer(app);
gst_element_send_event(app->pipeline, gst_event_new_eos());
GstBus* bus = gst_element_get_bus(app->pipeline);
GstMessage* message = gst_bus_timed_pop_filtered(bus, 3 * GST_SECOND,
@@ -585,7 +538,7 @@ void cancel_recording(GtkButton*, gpointer data) {
void recorder_closed(GtkWidget*, gpointer data) {
auto* app = static_cast<App*>(data);
app->recorder = nullptr;
remove_recording_sources(app);
stop_elapsed_timer(app);
if (app->pipeline) {
gst_element_set_state(app->pipeline, GST_STATE_NULL);
gst_object_unref(app->pipeline);
@@ -595,7 +548,14 @@ void recorder_closed(GtkWidget*, gpointer data) {
}
void start_recording(App* app) {
if (app->recorder) { gtk_window_present(GTK_WINDOW(app->recorder)); return; }
if (app->recorder) {
stop_recording(nullptr, app);
return;
}
if (app->processing) {
gtk_window_present(GTK_WINDOW(app->processing));
return;
}
if (app->config.local_url.empty() && (!app->config.hosted_first || app->config.hosted_url.empty())) {
open_settings(app);
show_error(GTK_WINDOW(app->settings), "Set an endpoint first.");
@@ -608,8 +568,7 @@ void start_recording(App* app) {
app->recording_path = template_path;
g_free(template_path);
gchar* pipeline_text = g_strdup_printf(
"autoaudiosrc ! audioconvert ! audioresample ! "
"level interval=100000000 post-messages=true ! wavenc ! filesink location=\"%s\"",
"autoaudiosrc ! audioconvert ! audioresample ! wavenc ! filesink location=\"%s\"",
app->recording_path.c_str());
GError* error = nullptr;
app->pipeline = gst_parse_launch(pipeline_text, &error);
@@ -620,9 +579,6 @@ void start_recording(App* app) {
remove_recording(app);
return;
}
GstBus* level_bus = gst_element_get_bus(app->pipeline);
app->level_watch = gst_bus_add_watch(level_bus, monitor_audio_level, app);
gst_object_unref(level_bus);
if (gst_element_set_state(app->pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
show_error(nullptr, "Could not access an audio input.");
close_recorder(app);
@@ -659,9 +615,7 @@ void start_recording(App* app) {
gtk_box_pack_start(GTK_BOX(actions), cancel, false, false, 0);
gtk_box_pack_start(GTK_BOX(actions), stop, false, false, 0);
app->elapsed_seconds = 0;
app->last_sound_us = g_get_monotonic_time();
app->elapsed_source = g_timeout_add_seconds(1, tick_elapsed, app);
app->silence_source = g_timeout_add(100, detect_silence, app);
g_signal_connect(cancel, "clicked", G_CALLBACK(cancel_recording), app);
g_signal_connect(stop, "clicked", G_CALLBACK(stop_recording), app);
g_signal_connect(window, "destroy", G_CALLBACK(recorder_closed), app);