[0.1.10] Recording toolbar: a System sound button beside Mic
- System sound: on / off (red while on) turns what the computer plays on and off while recording, as Mic does the microphone; its state is saved as system_audio in [video]. - modules/Screen_Record: the microphone's switch is now one for either source: record_set_audio, record_audio_on and record_audio_failed take an Audio_Source_Kind, and the audio thread opens and closes each as it is turned on and off. - record-probe -system-toggle. With -sync-test, system sound on at 3 s and off at 7 s keeps only the beeps at 3 and 5 s (8-12 ms from their flashes), and the track runs the whole recording.
This commit is contained in:
parent
3f5d1ea124
commit
4acde4d7a6
13
README.md
13
README.md
@ -188,12 +188,12 @@ register the key, the Event Log says so; choose another, such as `Ctrl-Shift-S
|
|||||||
- Escape or a right click cancels.
|
- Escape or a right click cancels.
|
||||||
3. **Recording starts** at once, at up to 30 frames a second, cursor included. A frame is written
|
3. **Recording starts** at once, at up to 30 frames a second, cursor included. A frame is written
|
||||||
only when the screen changes, so a still screen costs almost nothing. **Sound:** what the
|
only when the screen changes, so a still screen costs almost nothing. **Sound:** what the
|
||||||
computer plays is recorded with it. The microphone is off until you turn it on with the
|
computer plays is recorded with it, and the microphone once you turn it on. The toolbar's
|
||||||
toolbar's **Mic** button, which works mid-recording. The button's state is kept for the next
|
**System sound** and **Mic** buttons turn each on and off, mid-recording too, and their state is
|
||||||
recording.
|
kept for the next recording.
|
||||||
- **A red border** is drawn just outside the region.
|
- **A red border** is drawn just outside the region.
|
||||||
- **A toolbar** beside it shows the time, the size and any frames dropped, with **Mic** (on or
|
- **A toolbar** beside it shows the time, the size and any frames dropped, with **System sound**
|
||||||
off), **Pause** (the time paused is left out of the video), **Stop**, and **Discard**, which asks
|
and **Mic** (each on or off), **Pause** (the time paused is left out of the video), **Stop**, and **Discard**, which asks
|
||||||
first.
|
first.
|
||||||
- **Neither is in the recording.** Neither takes the focus from the window being recorded.
|
- **Neither is in the recording.** Neither takes the focus from the window being recorded.
|
||||||
4. **Ctrl+PrintScreen again**, or Stop, stops it. The video goes into **Screen Captures** beside the stills,
|
4. **Ctrl+PrintScreen again**, or Stop, stops it. The video goes into **Screen Captures** beside the stills,
|
||||||
@ -213,7 +213,8 @@ register the key, the Event Log says so; choose another, such as `Ctrl-Shift-S
|
|||||||
- **Settings:** `[video]` in the settings file:
|
- **Settings:** `[video]` in the settings file:
|
||||||
- `fps` (30) and `cursor` (true).
|
- `fps` (30) and `cursor` (true).
|
||||||
- `capture`: `auto` (Desktop Duplication, else Windows.Graphics.Capture), `dxgi` or `wgc`.
|
- `capture`: `auto` (Desktop Duplication, else Windows.Graphics.Capture), `dxgi` or `wgc`.
|
||||||
- `system_audio` (true): what the computer plays, from the default output device.
|
- `system_audio` (true): what the computer plays, from the default output device. The toolbar's
|
||||||
|
System sound button sets it.
|
||||||
- `microphone` (false): the default input device. The toolbar's Mic button sets it.
|
- `microphone` (false): the default input device. The toolbar's Mic button sets it.
|
||||||
- Sound is AAC at 160 kb/s. A device unplugged mid-recording is picked up again from the new default.
|
- Sound is AAC at 160 kb/s. A device unplugged mid-recording is picked up again from the new default.
|
||||||
- **PrintScreen while recording** takes the whole screen under the cursor at once, without freezing
|
- **PrintScreen while recording** takes the whole screen under the cursor at once, without freezing
|
||||||
|
|||||||
@ -29,7 +29,7 @@ APP_NAME :: "mcap";
|
|||||||
APP_FOLDER_NAME :: "mcap";
|
APP_FOLDER_NAME :: "mcap";
|
||||||
GIT_BRANCH :: "check";
|
GIT_BRANCH :: "check";
|
||||||
GIT_HASH :: "-";
|
GIT_HASH :: "-";
|
||||||
VERSION :: "0.1.9";
|
VERSION :: "0.1.10";
|
||||||
BUILD_DATE :: "-";
|
BUILD_DATE :: "-";
|
||||||
DEBUG :: true;
|
DEBUG :: true;
|
||||||
COMPILE_TESTS :: true;
|
COMPILE_TESTS :: true;
|
||||||
|
|||||||
@ -8,7 +8,7 @@ APP_NAME :: "mcap";
|
|||||||
APP_FOLDER_NAME :: "mcap";
|
APP_FOLDER_NAME :: "mcap";
|
||||||
APP_BUNDLE_IDENTIFIER :: "com.example.mcap";
|
APP_BUNDLE_IDENTIFIER :: "com.example.mcap";
|
||||||
|
|
||||||
VERSION :: "0.1.9";
|
VERSION :: "0.1.10";
|
||||||
// Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated
|
// Where everything goes: bin/, or bin-headless/ for builds made with no_crash_dialog, so automated
|
||||||
// builds never write over the ones you run by hand.
|
// builds never write over the ones you run by hand.
|
||||||
build_directory := "bin";
|
build_directory := "bin";
|
||||||
|
|||||||
@ -16,9 +16,9 @@
|
|||||||
// Sound (system_audio, microphone) has a thread of its own (audio_source.jai's WASAPI): each packet is
|
// Sound (system_audio, microphone) has a thread of its own (audio_source.jai's WASAPI): each packet is
|
||||||
// put on the same timeline as the frames, by when it was heard, less the same pauses, into one mix,
|
// put on the same timeline as the frames, by when it was heard, less the same pauses, into one mix,
|
||||||
// and an AAC frame of it is encoded once every source has had its say up to there. A source that sends
|
// and an AAC frame of it is encoded once every source has had its say up to there. A source that sends
|
||||||
// nothing (the computer playing nothing) is silence. The microphone can be turned on and off while
|
// nothing (the computer playing nothing) is silence. Either source can be turned on and off while
|
||||||
// recording (record_set_microphone); with audio_track, the MP4 has a sound track even when no source
|
// recording (record_set_audio); with audio_track, the MP4 has a sound track even when no source is on
|
||||||
// is on at the start, so it can be.
|
// at the start, so one can be.
|
||||||
//
|
//
|
||||||
// recorder: Recorder;
|
// recorder: Recorder;
|
||||||
// ok, error, fall_back := record_start(*recorder, .{path = "out.mp4", screen = 0});
|
// ok, error, fall_back := record_start(*recorder, .{path = "out.mp4", screen = 0});
|
||||||
@ -74,8 +74,8 @@ Record_Options :: struct {
|
|||||||
legacy_duplicate: bool; // the plain DuplicateOutput, not DuplicateOutput1: for record-probe
|
legacy_duplicate: bool; // the plain DuplicateOutput, not DuplicateOutput1: for record-probe
|
||||||
testing: Record_Testing;
|
testing: Record_Testing;
|
||||||
system_audio: bool; // what the computer plays (the default output device)
|
system_audio: bool; // what the computer plays (the default output device)
|
||||||
microphone: bool; // the default input device, at the start: record_set_microphone turns it on and off
|
microphone: bool; // the default input device; both at the start: record_set_audio turns them on and off
|
||||||
audio_track: bool; // a sound track whatever is on at the start, so the microphone can be turned on later
|
audio_track: bool; // a sound track whatever is on at the start, so a source can be turned on later
|
||||||
}
|
}
|
||||||
|
|
||||||
Record_Stats :: struct {
|
Record_Stats :: struct {
|
||||||
@ -118,8 +118,8 @@ Recorder :: struct {
|
|||||||
audio_go: Semaphore; // the encoder is open: the audio thread may write
|
audio_go: Semaphore; // the encoder is open: the audio thread may write
|
||||||
audio_sources: [2] Audio_Source;
|
audio_sources: [2] Audio_Source;
|
||||||
audio_open: bool; // any source opened, or audio_track: the MP4 has sound
|
audio_open: bool; // any source opened, or audio_track: the MP4 has sound
|
||||||
microphone_on: bool; // the microphone is wanted: record_set_microphone; cleared when it cannot be opened
|
audio_on: [2] bool; // each source is wanted (by Audio_Source_Kind): record_set_audio; cleared when it cannot be opened
|
||||||
microphone_failed: bool; // it could not be opened after being turned on: record_microphone_failed says once
|
audio_failed: [2] bool; // it could not be opened after being turned on: record_audio_failed says once
|
||||||
zero_ticks: s64; // the recording's time 0, for frames and sound alike
|
zero_ticks: s64; // the recording's time 0, for frames and sound alike
|
||||||
paused_ticks: s64; // time paused so far, for the capture and audio threads; set by record_pause
|
paused_ticks: s64; // time paused so far, for the capture and audio threads; set by record_pause
|
||||||
pause_began: s64;
|
pause_began: s64;
|
||||||
@ -214,7 +214,7 @@ record_start :: (r: *Recorder, options: Record_Options) -> ok: bool, error: stri
|
|||||||
|
|
||||||
// The sound's sources are opened on their own thread (COM, for WASAPI), before the encoder, which
|
// The sound's sources are opened on their own thread (COM, for WASAPI), before the encoder, which
|
||||||
// has a sound stream only when one opened.
|
// has a sound stream only when one opened.
|
||||||
r.microphone_on = r.options.microphone;
|
r.audio_on = .[r.options.system_audio, r.options.microphone];
|
||||||
if r.options.system_audio || r.options.microphone || r.options.audio_track {
|
if r.options.system_audio || r.options.microphone || r.options.audio_track {
|
||||||
init(*r.audio_opened);
|
init(*r.audio_opened);
|
||||||
init(*r.audio_go);
|
init(*r.audio_go);
|
||||||
@ -276,22 +276,22 @@ record_pause :: (r: *Recorder, pause: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turns the microphone on or off while recording. It is opened only while on, so Windows shows it in
|
// Turns a source of sound on or off while recording. It is opened only while on, so Windows shows the
|
||||||
// use only then. False when the recording has no sound track to put it in.
|
// microphone in use only then. False when the recording has no sound track to put it in.
|
||||||
record_set_microphone :: (r: *Recorder, on: bool) -> bool {
|
record_set_audio :: (r: *Recorder, kind: Audio_Source_Kind, on: bool) -> bool {
|
||||||
if !r.running || !r.audio_open return false;
|
if !r.running || !r.audio_open return false;
|
||||||
atomic_swap(*r.microphone_on, on);
|
atomic_swap(*r.audio_on[kind], on);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
record_microphone :: (r: *Recorder) -> bool {
|
record_audio_on :: (r: *Recorder, kind: Audio_Source_Kind) -> bool {
|
||||||
return r.running && atomic_read(*r.microphone_on);
|
return r.running && atomic_read(*r.audio_on[kind]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether the microphone could not be opened when turned on (it is off again then), and why; once.
|
// Whether a source could not be opened when turned on (it is off again then), and why; once.
|
||||||
record_microphone_failed :: (r: *Recorder) -> failed: bool, error: string {
|
record_audio_failed :: (r: *Recorder, kind: Audio_Source_Kind) -> failed: bool, error: string {
|
||||||
if !r.running || !atomic_read(*r.microphone_failed) return false, "";
|
if !r.running || !atomic_read(*r.audio_failed[kind]) return false, "";
|
||||||
atomic_swap(*r.microphone_failed, false);
|
atomic_swap(*r.audio_failed[kind], false);
|
||||||
lock(*r.mutex);
|
lock(*r.mutex);
|
||||||
defer unlock(*r.mutex);
|
defer unlock(*r.mutex);
|
||||||
return true, copy_string(r.stats.audio_errors,, temp);
|
return true, copy_string(r.stats.audio_errors,, temp);
|
||||||
@ -617,7 +617,7 @@ record_audio_proc :: (thread: *Thread) -> s64 {
|
|||||||
if ok { r.audio_open = true; note(*names, s.name, *r.stats.audio); }
|
if ok { r.audio_open = true; note(*names, s.name, *r.stats.audio); }
|
||||||
else { audio_close(s); note(*errors, tprint("%: %", what, error), *r.stats.audio_errors); log_error("[record] No %: %", what, error); }
|
else { audio_close(s); note(*errors, tprint("%: %", what, error), *r.stats.audio_errors); log_error("[record] No %: %", what, error); }
|
||||||
}
|
}
|
||||||
if !r.audio_sources[1].open atomic_swap(*r.microphone_on, false);
|
for r.audio_sources if !it.open atomic_swap(*r.audio_on[it_index], false);
|
||||||
if r.options.audio_track r.audio_open = true;
|
if r.options.audio_track r.audio_open = true;
|
||||||
signal(*r.audio_opened);
|
signal(*r.audio_opened);
|
||||||
wait_for(*r.audio_go);
|
wait_for(*r.audio_go);
|
||||||
@ -631,28 +631,30 @@ record_audio_proc :: (thread: *Thread) -> s64 {
|
|||||||
defer array_free(mix.samples);
|
defer array_free(mix.samples);
|
||||||
while true {
|
while true {
|
||||||
stopping := atomic_read(*r.stopping);
|
stopping := atomic_read(*r.stopping);
|
||||||
// The microphone, turned on or off from the toolbar: opened only while on.
|
// Each source, turned on or off from the toolbar: opened only while on.
|
||||||
mic := *r.audio_sources[1];
|
for * source, index: r.audio_sources {
|
||||||
mic_wanted := atomic_read(*r.microphone_on) && !stopping;
|
on := atomic_read(*r.audio_on[index]) && !stopping;
|
||||||
if mic_wanted && !mic.open && !wanted[1] {
|
what := ifx kinds[index] == .SYSTEM then "System sound" else "Microphone";
|
||||||
ok, error := audio_open(mic, .MICROPHONE);
|
if on && !source.open && !wanted[index] {
|
||||||
if ok {
|
ok, error := audio_open(source, kinds[index]);
|
||||||
log("[record] Microphone on: %.", mic.name);
|
if ok {
|
||||||
note(*names, mic.name, *r.stats.audio);
|
log("[record] % on: %.", what, source.name);
|
||||||
mix.started[1] = false;
|
note(*names, source.name, *r.stats.audio);
|
||||||
} else {
|
mix.started[index] = false;
|
||||||
audio_close(mic);
|
} else {
|
||||||
log_error("[record] The microphone could not be opened: %", error);
|
audio_close(source);
|
||||||
note(*errors, tprint("microphone: %", error), *r.stats.audio_errors);
|
log_error("[record] % could not be opened: %", what, error);
|
||||||
atomic_swap(*r.microphone_on, false);
|
note(*errors, tprint("%: %", to_lower_copy(what,, temp), error), *r.stats.audio_errors);
|
||||||
atomic_swap(*r.microphone_failed, true);
|
atomic_swap(*r.audio_on[index], false);
|
||||||
|
atomic_swap(*r.audio_failed[index], true);
|
||||||
|
}
|
||||||
|
} else if !on && source.open {
|
||||||
|
log("[record] % off.", what);
|
||||||
|
audio_close(source);
|
||||||
|
source.lost_at = 0;
|
||||||
}
|
}
|
||||||
} else if !mic_wanted && mic.open {
|
wanted[index] = source.open || (wanted[index] && on);
|
||||||
log("[record] Microphone off.");
|
|
||||||
audio_close(mic);
|
|
||||||
mic.lost_at = 0;
|
|
||||||
}
|
}
|
||||||
wanted[1] = mic.open || (wanted[1] && mic_wanted);
|
|
||||||
|
|
||||||
for * source, index: r.audio_sources {
|
for * source, index: r.audio_sources {
|
||||||
if !wanted[index] continue;
|
if !wanted[index] continue;
|
||||||
|
|||||||
@ -11,8 +11,8 @@
|
|||||||
// time, Pause, Stop and Discard (app/screen_record_win32.jai); both are kept out of the recording.
|
// time, Pause, Stop and Discard (app/screen_record_win32.jai); both are kept out of the recording.
|
||||||
// Time paused is left out of the video. With sound: what the computer plays and the microphone, as
|
// Time paused is left out of the video. With sound: what the computer plays and the microphone, as
|
||||||
// [video] in the settings file says, as are the frame rate, the cursor and how the screen is taken.
|
// [video] in the settings file says, as are the frame rate, the cursor and how the screen is taken.
|
||||||
// The toolbar's Mic button turns the microphone on and off while recording, and is kept in [video]
|
// The toolbar's System sound and Mic buttons turn each on and off while recording, and are kept in
|
||||||
// for the next: every recording has a sound track for it, silent while nothing is on.
|
// [video] for the next: every recording has a sound track for them, silent while nothing is on.
|
||||||
//
|
//
|
||||||
// A recording that stops by itself (the screen went away, the encoder failed) keeps what it has, and
|
// A recording that stops by itself (the screen went away, the encoder failed) keeps what it has, and
|
||||||
// so does quitting while recording. One left in the temporary folder by a crash is offered at the next
|
// so does quitting while recording. One left in the temporary folder by a crash is offered at the next
|
||||||
@ -52,21 +52,25 @@ screen_record_frame :: () {
|
|||||||
screen_record_stop();
|
screen_record_stop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
failed_mic, mic_error := record_microphone_failed(*screen_record.recorder);
|
for kind: Audio_Source_Kind.[.SYSTEM, .MICROPHONE] {
|
||||||
if failed_mic {
|
failed, why := record_audio_failed(*screen_record.recorder, kind);
|
||||||
toast_show(.Error, "The microphone could not be turned on: %", mic_error);
|
if !failed continue;
|
||||||
screen_record_remember_microphone(false);
|
toast_show(.Error, "% could not be turned on: %", screen_record_audio_name(kind), why);
|
||||||
|
screen_record_remember_audio(kind, false);
|
||||||
record_ui_refresh();
|
record_ui_refresh();
|
||||||
}
|
}
|
||||||
if record_ui_take_ask() == {
|
ask := record_ui_take_ask();
|
||||||
case .MICROPHONE;
|
if ask == .SYSTEM_SOUND || ask == .MICROPHONE {
|
||||||
on := !record_microphone(*screen_record.recorder);
|
kind: Audio_Source_Kind = ifx ask == .SYSTEM_SOUND then .SYSTEM else .MICROPHONE;
|
||||||
if record_set_microphone(*screen_record.recorder, on) {
|
on := !record_audio_on(*screen_record.recorder, kind);
|
||||||
log("[record] Microphone turned %.", ifx on then "on" else "off");
|
if record_set_audio(*screen_record.recorder, kind, on) {
|
||||||
toast_show(.Success, "Microphone %", ifx on then "on" else "off");
|
log("[record] % turned %.", screen_record_audio_name(kind), ifx on then "on" else "off");
|
||||||
screen_record_remember_microphone(on);
|
toast_show(.Success, "% %", screen_record_audio_name(kind), ifx on then "on" else "off");
|
||||||
}
|
screen_record_remember_audio(kind, on);
|
||||||
record_ui_refresh();
|
}
|
||||||
|
record_ui_refresh();
|
||||||
|
}
|
||||||
|
if ask == {
|
||||||
case .PAUSE;
|
case .PAUSE;
|
||||||
pause := !record_paused(*screen_record.recorder);
|
pause := !record_paused(*screen_record.recorder);
|
||||||
record_pause(*screen_record.recorder, pause);
|
record_pause(*screen_record.recorder, pause);
|
||||||
@ -222,7 +226,7 @@ screen_record_start :: (immediate := false) {
|
|||||||
options.cursor = app_user_settings.video_cursor;
|
options.cursor = app_user_settings.video_cursor;
|
||||||
options.system_audio = app_user_settings.video_system_audio;
|
options.system_audio = app_user_settings.video_system_audio;
|
||||||
options.microphone = app_user_settings.video_microphone;
|
options.microphone = app_user_settings.video_microphone;
|
||||||
options.audio_track = true; // so the Mic button can turn the microphone on mid-recording
|
options.audio_track = true; // so the toolbar can turn a source on mid-recording
|
||||||
if app_user_settings.video_capture == {
|
if app_user_settings.video_capture == {
|
||||||
case "dxgi"; options.capture = .DXGI;
|
case "dxgi"; options.capture = .DXGI;
|
||||||
case "wgc"; options.capture = .WGC;
|
case "wgc"; options.capture = .WGC;
|
||||||
@ -253,13 +257,18 @@ screen_record_start :: (immediate := false) {
|
|||||||
record_ui_open(options.region, shot.whole_screen, stats.width, stats.height);
|
record_ui_open(options.region, shot.whole_screen, stats.width, stats.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Mic button's state, into [video] for the next recording.
|
// A sound button's state, into [video] for the next recording.
|
||||||
screen_record_remember_microphone :: (on: bool) {
|
screen_record_remember_audio :: (kind: Audio_Source_Kind, on: bool) {
|
||||||
if app_user_settings.video_microphone == on return;
|
setting := ifx kind == .SYSTEM then *app_user_settings.video_system_audio else *app_user_settings.video_microphone;
|
||||||
app_user_settings.video_microphone = on;
|
if setting.* == on return;
|
||||||
|
setting.* = on;
|
||||||
app_save_user_settings_file();
|
app_save_user_settings_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
screen_record_audio_name :: (kind: Audio_Source_Kind) -> string {
|
||||||
|
return ifx kind == .SYSTEM then "System sound" else "The microphone";
|
||||||
|
}
|
||||||
|
|
||||||
// Stops, and deletes what was recorded (the toolbar's Discard, which asked first).
|
// Stops, and deletes what was recorded (the toolbar's Discard, which asked first).
|
||||||
screen_record_discard :: () {
|
screen_record_discard :: () {
|
||||||
if !screen_record.recording return;
|
if !screen_record.recording return;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// While recording (app/screen_record.jai), on Windows: a thin red border just outside the region, and a
|
// While recording (app/screen_record.jai), on Windows: a thin red border just outside the region, and a
|
||||||
// toolbar beside it with the time, the size, frames dropped, and Mic, Pause, Stop and Discard. Both are
|
// toolbar beside it with the time, the size, frames dropped, and System sound, Mic, Pause, Stop and
|
||||||
|
// Discard. Both are
|
||||||
// plain GDI windows, as the overlay is, so they work while mcap is hidden in the tray, and both are
|
// plain GDI windows, as the overlay is, so they work while mcap is hidden in the tray, and both are
|
||||||
// kept out of every capture (WDA_EXCLUDEFROMCAPTURE), the recording's own included. The border is
|
// kept out of every capture (WDA_EXCLUDEFROMCAPTURE), the recording's own included. The border is
|
||||||
// outside the region anyway, so it could never be in the video.
|
// outside the region anyway, so it could never be in the video.
|
||||||
@ -9,7 +10,7 @@
|
|||||||
// message, as Stop and Discard close the toolbar. Discard asks first, in the toolbar itself: a
|
// message, as Stop and Discard close the toolbar. Discard asks first, in the toolbar itself: a
|
||||||
// message box would be in the recording.
|
// message box would be in the recording.
|
||||||
|
|
||||||
Record_UI_Ask :: enum u8 { NONE; MICROPHONE; PAUSE; STOP; DISCARD; }
|
Record_UI_Ask :: enum u8 { NONE; SYSTEM_SOUND; MICROPHONE; PAUSE; STOP; DISCARD; }
|
||||||
|
|
||||||
record_ui: struct {
|
record_ui: struct {
|
||||||
border, toolbar: HWND;
|
border, toolbar: HWND;
|
||||||
@ -19,7 +20,7 @@ record_ui: struct {
|
|||||||
asked: Record_UI_Ask; // a button pressed, for screen_record_frame
|
asked: Record_UI_Ask; // a button pressed, for screen_record_frame
|
||||||
confirming: bool; // Discard pressed once: "Discard it? Yes No"
|
confirming: bool; // Discard pressed once: "Discard it? Yes No"
|
||||||
hovered: s32 = -1; // the button under the mouse
|
hovered: s32 = -1; // the button under the mouse
|
||||||
buttons: [4] Record_UI_Button;
|
buttons: [5] Record_UI_Button;
|
||||||
button_count: s32;
|
button_count: s32;
|
||||||
started: Apollo_Time;
|
started: Apollo_Time;
|
||||||
size_text: string; // "1920 × 1080"; heap
|
size_text: string; // "1920 × 1080"; heap
|
||||||
@ -170,11 +171,13 @@ record_toolbar_buttons :: () -> [] Record_UI_Button {
|
|||||||
record_ui.buttons[1] = .{label = "No", confirm = true};
|
record_ui.buttons[1] = .{label = "No", confirm = true};
|
||||||
record_ui.button_count = 2;
|
record_ui.button_count = 2;
|
||||||
} else {
|
} else {
|
||||||
record_ui.buttons[0] = .{label = ifx record_microphone(*screen_record.recorder) then "Mic: on" else "Mic: off", ask = .MICROPHONE};
|
recorder := *screen_record.recorder;
|
||||||
record_ui.buttons[1] = .{label = ifx record_paused(*screen_record.recorder) then "Resume" else "Pause", ask = .PAUSE};
|
record_ui.buttons[0] = .{label = ifx record_audio_on(recorder, .SYSTEM) then "System sound: on" else "System sound: off", ask = .SYSTEM_SOUND};
|
||||||
record_ui.buttons[2] = .{label = "Stop", ask = .STOP};
|
record_ui.buttons[1] = .{label = ifx record_audio_on(recorder, .MICROPHONE) then "Mic: on" else "Mic: off", ask = .MICROPHONE};
|
||||||
record_ui.buttons[3] = .{label = "Discard", confirm = true};
|
record_ui.buttons[2] = .{label = ifx record_paused(recorder) then "Resume" else "Pause", ask = .PAUSE};
|
||||||
record_ui.button_count = 4;
|
record_ui.buttons[3] = .{label = "Stop", ask = .STOP};
|
||||||
|
record_ui.buttons[4] = .{label = "Discard", confirm = true};
|
||||||
|
record_ui.button_count = 5;
|
||||||
}
|
}
|
||||||
return array_view(record_ui.buttons, 0, record_ui.button_count);
|
return array_view(record_ui.buttons, 0, record_ui.button_count);
|
||||||
}
|
}
|
||||||
@ -197,7 +200,7 @@ record_toolbar_size :: () -> width: s32, height: s32 {
|
|||||||
padding, gap := record_ui_px(10), record_ui_px(8);
|
padding, gap := record_ui_px(10), record_ui_px(8);
|
||||||
status_width := text_width(dc, tprint("PAUSED 00:00:00 %", record_ui.size_text));
|
status_width := text_width(dc, tprint("PAUSED 00:00:00 %", record_ui.size_text));
|
||||||
dropped_width := text_width(dc, "999 dropped, screen lost 9 times");
|
dropped_width := text_width(dc, "999 dropped, screen lost 9 times");
|
||||||
buttons_width := text_width(dc, "Mic: off") + text_width(dc, "Resume") + text_width(dc, "Stop") + text_width(dc, "Yes, discard") + 4 * 2 * padding + 3 * gap;
|
buttons_width := text_width(dc, "System sound: off") + text_width(dc, "Mic: off") + text_width(dc, "Resume") + text_width(dc, "Stop") + text_width(dc, "Yes, discard") + 5 * 2 * padding + 4 * gap;
|
||||||
height := record_ui_px(34);
|
height := record_ui_px(34);
|
||||||
width := padding + record_ui_px(12) + gap + status_width + gap + dropped_width + gap + buttons_width + padding;
|
width := padding + record_ui_px(12) + gap + status_width + gap + dropped_width + gap + buttons_width + padding;
|
||||||
return width, height;
|
return width, height;
|
||||||
@ -314,7 +317,8 @@ record_toolbar_paint :: (hwnd: HWND) {
|
|||||||
label_width := text_width(dc, it.label);
|
label_width := text_width(dc, it.label);
|
||||||
r := RECT.{right - label_width - 2 * padding, (height - button_height) / 2, right, (height - button_height) / 2 + button_height};
|
r := RECT.{right - label_width - 2 * padding, (height - button_height) / 2, right, (height - button_height) / 2 + button_height};
|
||||||
record_ui.buttons[it_index].rect = r;
|
record_ui.buttons[it_index].rect = r;
|
||||||
lit := it.ask == .MICROPHONE && record_microphone(*screen_record.recorder); // on: in the record red
|
// A sound that is on, in the record red.
|
||||||
|
lit := (it.ask == .SYSTEM_SOUND && record_audio_on(*screen_record.recorder, .SYSTEM)) || (it.ask == .MICROPHONE && record_audio_on(*screen_record.recorder, .MICROPHONE));
|
||||||
fill(r, ifx lit then cast(u32) RECORD_UI_RED else ifx it_index == record_ui.hovered then cast(u32) RECORD_UI_HOVER else cast(u32) RECORD_UI_BUTTON);
|
fill(r, ifx lit then cast(u32) RECORD_UI_RED else ifx it_index == record_ui.hovered then cast(u32) RECORD_UI_HOVER else cast(u32) RECORD_UI_BUTTON);
|
||||||
SetTextColor(dc, ifx it.ask == .DISCARD then cast(u32) RECORD_UI_AMBER else cast(u32) RECORD_UI_TEXT);
|
SetTextColor(dc, ifx it.ask == .DISCARD then cast(u32) RECORD_UI_AMBER else cast(u32) RECORD_UI_TEXT);
|
||||||
wide := utf8_to_wide(it.label,, temp);
|
wide := utf8_to_wide(it.label,, temp);
|
||||||
|
|||||||
@ -670,8 +670,7 @@ app_save_user_settings_file :: () -> bool {
|
|||||||
if seen.event_log app_user_settings.event_log_capacity = on_disk.event_log_capacity;
|
if seen.event_log app_user_settings.event_log_capacity = on_disk.event_log_capacity;
|
||||||
if seen.video_fps app_user_settings.video_fps = on_disk.video_fps;
|
if seen.video_fps app_user_settings.video_fps = on_disk.video_fps;
|
||||||
if seen.video_cursor app_user_settings.video_cursor = on_disk.video_cursor;
|
if seen.video_cursor app_user_settings.video_cursor = on_disk.video_cursor;
|
||||||
if seen.video_system_audio app_user_settings.video_system_audio = on_disk.video_system_audio;
|
// video_system_audio and video_microphone are the app's: the recording toolbar's buttons set them.
|
||||||
// video_microphone is the app's: the recording toolbar's Mic button sets it.
|
|
||||||
if seen.video_capture {
|
if seen.video_capture {
|
||||||
free(app_user_settings.video_capture,, context.default_allocator);
|
free(app_user_settings.video_capture,, context.default_allocator);
|
||||||
app_user_settings.video_capture = copy_string(on_disk.video_capture,, context.default_allocator);
|
app_user_settings.video_capture = copy_string(on_disk.video_capture,, context.default_allocator);
|
||||||
|
|||||||
@ -22,7 +22,8 @@
|
|||||||
// frame: for measuring throughput.
|
// frame: for measuring throughput.
|
||||||
// -remux IN OUT a recording's fragmented MP4 made a regular one, as the app does, and nothing else.
|
// -remux IN OUT a recording's fragmented MP4 made a regular one, as the app does, and nothing else.
|
||||||
// -audio WHICH records sound too: system (what the computer plays), mic, or both.
|
// -audio WHICH records sound too: system (what the computer plays), mic, or both.
|
||||||
// -mic-toggle with -audio system: the microphone turned on 3 s in, and off at 7 s, as the toolbar does.
|
// -mic-toggle the microphone turned on 3 s in, and off at 7 s, as the toolbar does.
|
||||||
|
// -system-toggle likewise for what the computer plays.
|
||||||
// -sync-test every 2 s a white square flashes on the region and a 1 kHz beep plays; then the
|
// -sync-test every 2 s a white square flashes on the region and a 1 kHz beep plays; then the
|
||||||
// flashes and beeps are found in the file and their offsets given, and how they drift.
|
// flashes and beeps are found in the file and their offsets given, and how they drift.
|
||||||
// -check FILE reads FILE back with FFmpeg and says what it holds, and nothing else: for a
|
// -check FILE reads FILE back with FFmpeg and says what it holds, and nothing else: for a
|
||||||
@ -47,6 +48,7 @@ main :: () {
|
|||||||
pause_seconds: float64;
|
pause_seconds: float64;
|
||||||
sync_test := false;
|
sync_test := false;
|
||||||
mic_toggle := false;
|
mic_toggle := false;
|
||||||
|
system_toggle := false;
|
||||||
i := 1;
|
i := 1;
|
||||||
number :: (index: s64) -> s32 #expand { return cast(s32) string_to_int(`args[index]); }
|
number :: (index: s64) -> s32 #expand { return cast(s32) string_to_int(`args[index]); }
|
||||||
while i < args.count {
|
while i < args.count {
|
||||||
@ -77,6 +79,7 @@ main :: () {
|
|||||||
if a == "-lose" { options.testing |= .LOSE_DUPLICATION; i += 1; continue; }
|
if a == "-lose" { options.testing |= .LOSE_DUPLICATION; i += 1; continue; }
|
||||||
if a == "-sync-test" { sync_test = true; i += 1; continue; }
|
if a == "-sync-test" { sync_test = true; i += 1; continue; }
|
||||||
if a == "-mic-toggle" { mic_toggle = true; options.audio_track = true; i += 1; continue; }
|
if a == "-mic-toggle" { mic_toggle = true; options.audio_track = true; i += 1; continue; }
|
||||||
|
if a == "-system-toggle" { system_toggle = true; options.audio_track = true; i += 1; continue; }
|
||||||
if a == "-audio" && more >= 1 {
|
if a == "-audio" && more >= 1 {
|
||||||
which := args[i + 1];
|
which := args[i + 1];
|
||||||
options.system_audio = which == "system" || which == "both";
|
options.system_audio = which == "system" || which == "both";
|
||||||
@ -197,15 +200,16 @@ main :: () {
|
|||||||
print(" % s: % captured, % sent (% repeats), % written, % dropped, % lost, % KB\n",
|
print(" % s: % captured, % sent (% repeats), % written, % dropped, % lost, % KB\n",
|
||||||
FF(to_float64_seconds(last_print - start), 0), s.captured, s.emitted, s.repeats, s.written, s.dropped, s.lost, s.bytes / 1024);
|
FF(to_float64_seconds(last_print - start), 0), s.captured, s.emitted, s.repeats, s.written, s.dropped, s.lost, s.bytes / 1024);
|
||||||
}
|
}
|
||||||
if mic_toggle {
|
for kind: Audio_Source_Kind.[.SYSTEM, .MICROPHONE] {
|
||||||
|
if !(ifx kind == .SYSTEM then system_toggle else mic_toggle) continue;
|
||||||
elapsed := to_float64_seconds(current_time_monotonic() - start);
|
elapsed := to_float64_seconds(current_time_monotonic() - start);
|
||||||
should := elapsed >= 3 && elapsed < 7;
|
should := elapsed >= 3 && elapsed < 7;
|
||||||
if should != record_microphone(*recorder) && (elapsed < 3.5 || (elapsed >= 7 && elapsed < 7.5)) {
|
if should != record_audio_on(*recorder, kind) && (elapsed < 3.5 || (elapsed >= 7 && elapsed < 7.5)) {
|
||||||
record_set_microphone(*recorder, should);
|
record_set_audio(*recorder, kind, should);
|
||||||
print(" % s: microphone %\n", FF(elapsed, 1), ifx should then "on" else "off");
|
print(" % s: % %\n", FF(elapsed, 1), kind, ifx should then "on" else "off");
|
||||||
}
|
}
|
||||||
failed, why := record_microphone_failed(*recorder);
|
failed, why := record_audio_failed(*recorder, kind);
|
||||||
if failed print(" microphone failed: %\n", why);
|
if failed print(" % failed: %\n", kind, why);
|
||||||
}
|
}
|
||||||
if flash { // a flash and a beep together every 2 s, the flash for 200 ms
|
if flash { // a flash and a beep together every 2 s, the flash for 200 ms
|
||||||
elapsed := to_float64_seconds(current_time_monotonic() - start);
|
elapsed := to_float64_seconds(current_time_monotonic() - start);
|
||||||
|
|||||||
@ -506,7 +506,7 @@ Captured_Frame :: struct {
|
|||||||
<li>An MP4 can't gain a sound stream once its header is written, so the app's recordings always have one (<code>audio_track</code>), silent while nothing is on.</li>
|
<li>An MP4 can't gain a sound stream once its header is written, so the app's recordings always have one (<code>audio_track</code>), silent while nothing is on.</li>
|
||||||
<li>Checked with <code>record-probe -mic-toggle</code>, which turns the microphone on at 3 s and off at 7 s. With system sound, and with nothing else on, the sound track runs the whole recording without a break.</li>
|
<li>Checked with <code>record-probe -mic-toggle</code>, which turns the microphone on at 3 s and off at 7 s. With system sound, and with nothing else on, the sound track runs the whole recording without a break.</li>
|
||||||
</ul>
|
</ul>
|
||||||
What the computer plays is still chosen only in <code>[video]</code>.</li>
|
In mcap 0.1.10, <strong>System sound: on / off</strong> joined it and works the same way, saved as <code>system_audio</code>. With <code>record-probe -system-toggle -sync-test</code> (system sound on at 3 s, off at 7 s), only the beeps at 3 and 5 s are in the file, 8 to 12 ms from their flashes, and the track runs the whole recording.</li>
|
||||||
<li><strong>A lost device</strong> (a headset unplugged, the default changed) is opened again from the new default once a second.</li>
|
<li><strong>A lost device</strong> (a headset unplugged, the default changed) is opened again from the new default once a second.</li>
|
||||||
</ul></div>
|
</ul></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user