Implement proper frame delay and VRR for IS Nitro Emulator

This commit is contained in:
Lorenzooone
2024-08-25 09:37:30 +02:00
parent 718294ba02
commit 6f56918f3f
17 changed files with 565 additions and 135 deletions

View File

@@ -0,0 +1,136 @@
#include "ISNitroMenu.hpp"
#define NUM_TOTAL_MENU_OPTIONS (sizeof(pollable_options)/sizeof(pollable_options[0]))
struct ISNitroMenuOptionInfo {
const std::string base_name;
const std::string false_name;
const bool is_selectable;
const bool is_inc;
const std::string dec_str;
const std::string inc_str;
const ISNitroMenuOutAction inc_out_action;
const ISNitroMenuOutAction out_action;
};
static const ISNitroMenuOptionInfo is_nitro_delay_option = {
.base_name = "Delay", .false_name = "", .is_selectable = false,
.is_inc = false, .dec_str = "", .inc_str = "", .inc_out_action = ISN_MENU_NO_ACTION,
.out_action = ISN_MENU_DELAY};
static const ISNitroMenuOptionInfo is_nitro_type_option = {
.base_name = "Capture", .false_name = "", .is_selectable = true,
.is_inc = true, .dec_str = "<", .inc_str = ">", .inc_out_action = ISN_MENU_TYPE_INC,
.out_action = ISN_MENU_TYPE_DEC};
static const ISNitroMenuOptionInfo* pollable_options[] = {
&is_nitro_delay_option,
&is_nitro_type_option,
};
ISNitroMenu::ISNitroMenu(bool font_load_success, sf::Font &text_font) : OptionSelectionMenu(){
this->options_indexes = new int[NUM_TOTAL_MENU_OPTIONS];
this->initialize(font_load_success, text_font);
this->num_enabled_options = 0;
}
ISNitroMenu::~ISNitroMenu() {
delete []this->options_indexes;
}
void ISNitroMenu::class_setup() {
this->num_options_per_screen = 5;
this->min_elements_text_scaling_factor = num_options_per_screen + 2;
this->width_factor_menu = 16;
this->width_divisor_menu = 9;
this->base_height_factor_menu = 12;
this->base_height_divisor_menu = 6;
this->min_text_size = 0.3;
this->max_width_slack = 1.1;
this->menu_color = sf::Color(30, 30, 60, 192);
this->title = "IS Nitro Settings";
this->show_back_x = true;
this->show_x = false;
this->show_title = true;
}
void ISNitroMenu::insert_data() {
this->num_enabled_options = 0;
for(int i = 0; i < NUM_TOTAL_MENU_OPTIONS; i++) {
this->options_indexes[this->num_enabled_options] = i;
this->num_enabled_options++;
}
this->prepare_options();
}
void ISNitroMenu::reset_output_option() {
this->selected_index = ISNitroMenuOutAction::ISN_MENU_NO_ACTION;
}
void ISNitroMenu::set_output_option(int index, int action) {
if(index == BACK_X_OUTPUT_OPTION)
this->selected_index = ISN_MENU_BACK;
else if((action == INC_ACTION) && this->is_option_inc_dec(index))
this->selected_index = pollable_options[this->options_indexes[index]]->inc_out_action;
else
this->selected_index = pollable_options[this->options_indexes[index]]->out_action;
}
int ISNitroMenu::get_num_options() {
return this->num_enabled_options;
}
std::string ISNitroMenu::get_string_option(int index, int action) {
if((action == INC_ACTION) && this->is_option_inc_dec(index))
return pollable_options[this->options_indexes[index]]->inc_str;
if((action == DEC_ACTION) && this->is_option_inc_dec(index))
return pollable_options[this->options_indexes[index]]->dec_str;
if(action == FALSE_ACTION)
return pollable_options[this->options_indexes[index]]->false_name;
return pollable_options[this->options_indexes[index]]->base_name;
}
bool ISNitroMenu::is_option_selectable(int index, int action) {
return pollable_options[this->options_indexes[index]]->is_selectable;
}
bool ISNitroMenu::is_option_inc_dec(int index) {
return pollable_options[this->options_indexes[index]]->is_inc;
}
static std::string get_capture_type_name(CaptureScreensType capture_type) {
switch(capture_type) {
case CAPTURE_SCREENS_TOP:
return "Top Screen";
case CAPTURE_SCREENS_BOTTOM:
return "Bottom Screen";
default:
return "Both Screens";
}
}
void ISNitroMenu::prepare(float menu_scaling_factor, int view_size_x, int view_size_y, CaptureStatus* capture_status) {
int num_pages = this->get_num_pages();
if(this->future_data.page >= num_pages)
this->future_data.page = num_pages - 1;
int start = this->future_data.page * this->num_options_per_screen;
for(int i = 0; i < this->num_options_per_screen + 1; i++) {
int index = (i * this->single_option_multiplier) + this->elements_start_id;
if(!this->future_enabled_labels[index])
continue;
int real_index = start + i;
int option_index = this->options_indexes[real_index];
switch(pollable_options[option_index]->out_action) {
case ISN_MENU_DELAY:
this->labels[index]->setText(this->setTextOptionInt(real_index, capture_status->curr_delay));
break;
case ISN_MENU_TYPE_DEC:
this->labels[index]->setText(this->setTextOptionString(real_index, get_capture_type_name(capture_status->capture_type)));
break;
default:
break;
}
}
this->base_prepare(menu_scaling_factor, view_size_x, view_size_y);
}

View File

@@ -12,6 +12,7 @@ struct MainMenuOptionInfo {
const bool active_bottom_screen;
const bool enabled_normal_mode;
const bool enabled_mono_mode;
const bool is_cc_specific;
const MainMenuOutAction out_action;
};
@@ -19,107 +20,114 @@ static const MainMenuOptionInfo connect_option = {
.base_name = "Disconnect", .false_name = "Connect",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_OPEN};
static const MainMenuOptionInfo windowed_option = {
.base_name = "Windowed Mode", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = false,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = false,
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
.out_action = MAIN_MENU_FULLSCREEN};
static const MainMenuOptionInfo fullscreen_option = {
.base_name = "Fullscreen Mode", .false_name = "",
.active_fullscreen = false, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = false,
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
.out_action = MAIN_MENU_FULLSCREEN};
static const MainMenuOptionInfo join_screens_option = {
.base_name = "Join Screens", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = false, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = false,
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
.out_action = MAIN_MENU_SPLIT};
static const MainMenuOptionInfo split_screens_option = {
.base_name = "Split Screens", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = false, .active_bottom_screen = false,
.enabled_normal_mode = true, .enabled_mono_mode = false,
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
.out_action = MAIN_MENU_SPLIT};
static const MainMenuOptionInfo video_settings_option = {
.base_name = "Video Settings", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_VIDEO_SETTINGS};
static const MainMenuOptionInfo quit_option = {
.base_name = "Quit Application", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = false,
.enabled_normal_mode = true, .enabled_mono_mode = false, .is_cc_specific = false,
.out_action = MAIN_MENU_QUIT_APPLICATION};
static const MainMenuOptionInfo audio_settings_option = {
.base_name = "Audio Settings", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_AUDIO_SETTINGS};
static const MainMenuOptionInfo save_profiles_option = {
.base_name = "Save Profile", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_SAVE_PROFILES};
static const MainMenuOptionInfo load_profiles_option = {
.base_name = "Load Profile", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_LOAD_PROFILES};
static const MainMenuOptionInfo status_option = {
.base_name = "Status", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_STATUS};
static const MainMenuOptionInfo licenses_option = {
.base_name = "Licenses", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_LICENSES};
static const MainMenuOptionInfo extra_settings_option = {
.base_name = "Extra Settings", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = false, .enabled_mono_mode = true,
.enabled_normal_mode = false, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_EXTRA_SETTINGS};
static const MainMenuOptionInfo shortcut_option = {
.base_name = "Shortcuts", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_SHORTCUT_SETTINGS};
static const MainMenuOptionInfo shutdown_option = {
.base_name = "Shutdown", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = false, .enabled_mono_mode = true,
.enabled_normal_mode = false, .enabled_mono_mode = true, .is_cc_specific = false,
.out_action = MAIN_MENU_SHUTDOWN};
static const MainMenuOptionInfo isn_settings_option = {
.base_name = "Is Nitro Settings", .false_name = "",
.active_fullscreen = true, .active_windowed_screen = true,
.active_joint_screen = true, .active_top_screen = true, .active_bottom_screen = true,
.enabled_normal_mode = true, .enabled_mono_mode = true, .is_cc_specific = true,
.out_action = MAIN_MENU_ISN_SETTINGS};
static const MainMenuOptionInfo* pollable_options[] = {
&connect_option,
&windowed_option,
@@ -132,6 +140,7 @@ static const MainMenuOptionInfo* pollable_options[] = {
&load_profiles_option,
&shortcut_option,
&status_option,
&isn_settings_option,
&licenses_option,
&extra_settings_option,
&quit_option,
@@ -164,7 +173,13 @@ void MainMenu::class_setup() {
this->show_title = true;
}
void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcut) {
static bool check_cc_specific_option(const MainMenuOptionInfo* option, CaptureConnectionType cc_type) {
if((option->out_action == MAIN_MENU_ISN_SETTINGS) && (cc_type == CAPTURE_CONN_IS_NITRO))
return true;
return false;
}
void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_mode, bool enable_shortcut, CaptureConnectionType cc_type, bool connected) {
this->num_enabled_options = 0;
for(int i = 0; i < NUM_TOTAL_MENU_OPTIONS; i++) {
bool valid = true;
@@ -182,6 +197,8 @@ void MainMenu::insert_data(ScreenType s_type, bool is_fullscreen, bool mono_app_
valid = valid && pollable_options[i]->enabled_mono_mode;
else
valid = valid && pollable_options[i]->enabled_normal_mode;
if(pollable_options[i]->is_cc_specific)
valid = valid && connected && check_cc_specific_option(pollable_options[i], cc_type);
if((pollable_options[i]->out_action == MAIN_MENU_SHORTCUT_SETTINGS) && (!enable_shortcut))
valid = false;
if(valid) {

View File

@@ -109,6 +109,7 @@ void WindowScreen::init_menus() {
this->shortcut_menu = new ShortcutMenu(this->font_load_success, this->text_font);
this->action_selection_menu = new ActionSelectionMenu(this->font_load_success, this->text_font);
this->scaling_ratio_menu = new ScalingRatioMenu(this->font_load_success, this->text_font);
this->is_nitro_menu = new ISNitroMenu(this->font_load_success, this->text_font);
}
void WindowScreen::destroy_menus() {
@@ -130,6 +131,7 @@ void WindowScreen::destroy_menus() {
delete this->shortcut_menu;
delete this->action_selection_menu;
delete this->scaling_ratio_menu;
delete this->is_nitro_menu;
}
void WindowScreen::set_close(int ret_val) {
@@ -174,6 +176,15 @@ void WindowScreen::fast_poll_change() {
this->print_notification_on_off("Slow Poll", this->display_data->fast_poll);
}
void WindowScreen::is_nitro_capture_type_change(bool positive) {
int new_value = (int)(this->capture_status->capture_type);
if(positive)
new_value += 1;
else
new_value += CAPTURE_SCREENS_ENUM_END - 1;
this->capture_status->capture_type = static_cast<CaptureScreensType>(new_value % CAPTURE_SCREENS_ENUM_END);
}
void WindowScreen::padding_change() {
if(this->m_info.is_fullscreen)
return;
@@ -615,14 +626,14 @@ void WindowScreen::setup_no_menu() {
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
}
void WindowScreen::setup_main_menu(bool reset_data) {
if(!this->can_setup_menu())
void WindowScreen::setup_main_menu(bool reset_data, bool skip_setup_check) {
if((!skip_setup_check) && (!this->can_setup_menu()))
return;
if(this->curr_menu != MAIN_MENU_TYPE) {
this->curr_menu = MAIN_MENU_TYPE;
if(reset_data)
this->main_menu->reset_data();
this->main_menu->insert_data(this->m_stype, this->m_info.is_fullscreen, this->display_data->mono_app_mode, is_shortcut_valid());
this->main_menu->insert_data(this->m_stype, this->m_info.is_fullscreen, this->display_data->mono_app_mode, is_shortcut_valid(), this->capture_status->device.cc_type, this->capture_status->connected);
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
}
}
@@ -900,6 +911,18 @@ void WindowScreen::setup_scaling_ratio_menu(bool reset_data) {
}
}
void WindowScreen::setup_is_nitro_menu(bool reset_data) {
if(!this->can_setup_menu())
return;
if(this->curr_menu != ISN_MENU_TYPE) {
this->curr_menu = ISN_MENU_TYPE;
if(reset_data)
this->is_nitro_menu->reset_data();
this->is_nitro_menu->insert_data();
this->last_menu_change_time = std::chrono::high_resolution_clock::now();
}
}
void WindowScreen::update_save_menu() {
if(this->curr_menu == SAVE_MENU_TYPE) {
this->curr_menu = DEFAULT_MENU_TYPE;
@@ -1234,6 +1257,10 @@ void WindowScreen::poll(bool do_everything) {
this->setup_shortcuts_menu();
done = true;
break;
case MAIN_MENU_ISN_SETTINGS:
this->setup_is_nitro_menu();
done = true;
break;
case MAIN_MENU_SHUTDOWN:
this->set_close(1);
this->setup_no_menu();
@@ -1771,6 +1798,30 @@ void WindowScreen::poll(bool do_everything) {
continue;
}
break;
case ISN_MENU_TYPE:
if(this->is_nitro_menu->poll(event_data)) {
switch(this->is_nitro_menu->selected_index) {
case ISN_MENU_BACK:
this->setup_main_menu(false);
done = true;
break;
case ISN_MENU_NO_ACTION:
break;
case ISN_MENU_DELAY:
break;
case ISN_MENU_TYPE_DEC:
this->is_nitro_capture_type_change(false);
break;
case ISN_MENU_TYPE_INC:
this->is_nitro_capture_type_change(true);
break;
default:
break;
}
this->is_nitro_menu->reset_output_option();
continue;
}
break;
default:
break;
}
@@ -1800,6 +1851,15 @@ void WindowScreen::update_ds_3ds_connection(bool changed_type) {
this->future_operations.call_crop = true;
}
void WindowScreen::update_capture_specific_settings() {
if(this->curr_menu == MAIN_MENU_TYPE) {
this->setup_no_menu();
this->setup_main_menu(true, true);
}
if(this->curr_menu == ISN_MENU_TYPE)
this->setup_no_menu();
}
int WindowScreen::load_data() {
int ret_val = this->m_prepare_load;
this->m_prepare_load = 0;
@@ -1985,6 +2045,9 @@ void WindowScreen::prepare_menu_draws(int view_size_x, int view_size_y) {
case SCALING_RATIO_MENU_TYPE:
this->scaling_ratio_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, &this->loaded_info);
break;
case ISN_MENU_TYPE:
this->is_nitro_menu->prepare(this->loaded_info.menu_scaling_factor, view_size_x, view_size_y, this->capture_status);
break;
default:
break;
}
@@ -2052,6 +2115,9 @@ void WindowScreen::execute_menu_draws() {
case SCALING_RATIO_MENU_TYPE:
this->scaling_ratio_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win);
break;
case ISN_MENU_TYPE:
this->is_nitro_menu->draw(this->loaded_info.menu_scaling_factor, this->m_win);
break;
default:
break;
}

View File

@@ -57,7 +57,7 @@ static void SuccessConnectionOutTextGenerator(OutTextData &out_text_data, Captur
}
}
static bool load(const std::string path, const std::string name, ScreenInfo &top_info, ScreenInfo &bottom_info, ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts) {
static bool load(const std::string path, const std::string name, ScreenInfo &top_info, ScreenInfo &bottom_info, ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
std::ifstream file(path + name);
std::string line;
@@ -110,6 +110,11 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top
continue;
}
if(key == "is_screen_capture_type") {
capture_status->capture_type = static_cast<CaptureScreensType>(std::stoi(value) % CaptureScreensType::CAPTURE_SCREENS_ENUM_END);
continue;
}
if(audio_data->load_audio_data(key, value))
continue;
}
@@ -125,7 +130,9 @@ static bool load(const std::string path, const std::string name, ScreenInfo &top
return result;
}
static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts) {
static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
capture_status->enabled_3d = false;
capture_status->capture_type = CAPTURE_SCREENS_BOTH;
reset_screen_info(frontend_data->top_screen->m_info);
reset_screen_info(frontend_data->bot_screen->m_info);
reset_screen_info(frontend_data->joint_screen->m_info);
@@ -138,11 +145,11 @@ static void defaults_reload(FrontendData *frontend_data, AudioData* audio_data,
frontend_data->reload = true;
}
static void load_layout_file(int load_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, bool skip_io, bool do_print) {
static void load_layout_file(int load_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status, bool skip_io, bool do_print) {
if(skip_io)
return;
defaults_reload(frontend_data, audio_data, extra_button_shortcuts);
defaults_reload(frontend_data, audio_data, extra_button_shortcuts, capture_status);
if(load_index == SIMPLE_RESET_DATA_INDEX) {
UpdateOutText(out_text_data, "Reset detected. Defaults re-loaded", "Reset detected\nDefaults re-loaded", TEXT_KIND_WARNING);
@@ -152,16 +159,16 @@ static void load_layout_file(int load_index, FrontendData *frontend_data, AudioD
bool name_load_success = false;
std::string layout_name = LayoutNameGenerator(load_index);
std::string layout_path = LayoutPathGenerator(load_index);
bool op_success = load(layout_path, layout_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts);
bool op_success = load(layout_path, layout_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts, capture_status);
if(do_print && op_success) {
std::string load_name = load_layout_name(load_index, name_load_success);
UpdateOutText(out_text_data, "Layout loaded from: " + layout_path + layout_name, "Layout " + load_name + " loaded", TEXT_KIND_SUCCESS);
}
else if(!op_success)
defaults_reload(frontend_data, audio_data, extra_button_shortcuts);
defaults_reload(frontend_data, audio_data, extra_button_shortcuts, capture_status);
}
static bool save(const std::string path, const std::string name, const std::string save_name, const ScreenInfo &top_info, const ScreenInfo &bottom_info, const ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts) {
static bool save(const std::string path, const std::string name, const std::string save_name, const ScreenInfo &top_info, const ScreenInfo &bottom_info, const ScreenInfo &joint_info, DisplayData &display_data, AudioData *audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status) {
#if (!defined(_MSC_VER)) || (_MSC_VER > 1916)
std::filesystem::create_directories(path);
#else
@@ -183,13 +190,14 @@ static bool save(const std::string path, const std::string name, const std::stri
file << "last_connected_ds=" << display_data.last_connected_ds << std::endl;
file << "extra_button_enter_short=" << extra_button_shortcuts->enter_shortcut->cmd << std::endl;
file << "extra_button_page_up_short=" << extra_button_shortcuts->page_up_shortcut->cmd << std::endl;
file << "is_screen_capture_type=" << capture_status->capture_type << std::endl;
file << audio_data->save_audio_data();
file.close();
return true;
}
static void save_layout_file(int save_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, bool skip_io, bool do_print) {
static void save_layout_file(int save_index, FrontendData *frontend_data, AudioData* audio_data, OutTextData &out_text_data, ExtraButtonShortcuts* extra_button_shortcuts, CaptureStatus* capture_status, bool skip_io, bool do_print) {
if(skip_io)
return;
@@ -200,7 +208,7 @@ static void save_layout_file(int save_index, FrontendData *frontend_data, AudioD
std::string save_name = load_layout_name(save_index, name_load_success);
std::string layout_name = LayoutNameGenerator(save_index);
std::string layout_path = LayoutPathGenerator(save_index);
bool op_success = save(layout_path, layout_name, save_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts);
bool op_success = save(layout_path, layout_name, save_name, frontend_data->top_screen->m_info, frontend_data->bot_screen->m_info, frontend_data->joint_screen->m_info, frontend_data->display_data, audio_data, out_text_data, extra_button_shortcuts, capture_status);
if(do_print && op_success) {
UpdateOutText(out_text_data, "Layout saved to: " + layout_path + layout_name, "Layout " + save_name + " saved", TEXT_KIND_SUCCESS);
}
@@ -223,7 +231,7 @@ static void soundCall(AudioData *audio_data, CaptureData* capture_data) {
if((capture_data->read[curr_out] > get_video_in_size(capture_data)) && (loaded_samples < MAX_MAX_AUDIO_LATENCY)) {
int n_samples = get_audio_n_samples(capture_data, capture_data->read[curr_out]);
double out_time = capture_data->time_in_buf[curr_out];
convertAudioToOutput(&capture_data->capture_buf[curr_out], out_buf[audio_buf_counter], n_samples, endianness, capture_data);
convertAudioToOutput(curr_out, out_buf[audio_buf_counter], n_samples, endianness, capture_data);
audio.samples.emplace(out_buf[audio_buf_counter], n_samples, out_time);
if(++audio_buf_counter == NUM_CONCURRENT_AUDIO_BUFFERS) {
audio_buf_counter = 0;
@@ -307,7 +315,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
frontend_data.bot_screen = bot_screen;
frontend_data.joint_screen = joint_screen;
load_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, false);
load_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, false);
// Due to the risk for seizures, at the start of the program, set BFI to false!
top_screen->m_info.bfi = false;
bot_screen->m_info.bfi = false;
@@ -322,6 +330,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
std::thread joint_thread(screen_display_thread, joint_screen);
capture_data->status.connected = connect(true, capture_data, &frontend_data);
bool last_connected = capture_data->status.connected;
SuccessConnectionOutTextGenerator(out_text_data, capture_data);
while(capture_data->status.running) {
@@ -337,13 +346,16 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
VideoOutputData *chosen_buf = out_buf;
bool blank_out = false;
if(capture_data->status.connected) {
if(!last_connected)
update_connected_specific_settings(&frontend_data, capture_data->status.device);
last_connected = true;
bool timed_out = !capture_data->status.video_wait.timed_lock();
curr_out = (capture_data->status.curr_in - 1 + NUM_CONCURRENT_DATA_BUFFERS) % NUM_CONCURRENT_DATA_BUFFERS;
if((!capture_data->status.cooldown_curr_in) && (curr_out != prev_out)) {
last_frame_time = capture_data->time_in_buf[curr_out];
if(capture_data->read[curr_out] >= get_video_in_size(capture_data)) {
convertVideoToOutput(&capture_data->capture_buf[curr_out], out_buf, capture_data);
convertVideoToOutput(curr_out, out_buf, capture_data);
num_allowed_blanks = MAX_ALLOWED_BLANKS;
}
else {
@@ -355,11 +367,14 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
}
}
}
else if(capture_data->status.cooldown_curr_in)
else if((capture_data->status.cooldown_curr_in) || timed_out)
blank_out = true;
prev_out = curr_out;
}
else {
if(last_connected)
update_connected_specific_settings(&frontend_data, capture_data->status.device);
last_connected = false;
default_sleep();
blank_out = true;
}
@@ -392,12 +407,12 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
if((load_index = top_screen->load_data()) || (load_index = bot_screen->load_data()) || (load_index = joint_screen->load_data())) {
// This value should only be loaded when starting the program...
bool previous_last_connected_ds = frontend_data.display_data.last_connected_ds;
load_layout_file(load_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, true);
load_layout_file(load_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, true);
frontend_data.display_data.last_connected_ds = previous_last_connected_ds;
}
if((save_index = top_screen->save_data()) || (save_index = bot_screen->save_data()) || (save_index = joint_screen->save_data())) {
save_layout_file(save_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, true);
save_layout_file(save_index, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, true);
top_screen->update_save_menu();
bot_screen->update_save_menu();
joint_screen->update_save_menu();
@@ -433,7 +448,7 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data,
bot_screen->after_thread_join();
joint_screen->after_thread_join();
save_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, skip_io, false);
save_layout_file(STARTUP_FILE_INDEX, &frontend_data, audio_data, out_text_data, &extra_button_shortcuts, &capture_data->status, skip_io, false);
if(!out_text_data.consumed) {
ConsoleOutText(out_text_data.full_text);

View File

@@ -7,7 +7,8 @@
#include <cstring>
void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureData* capture_data) {
void convertVideoToOutput(int index, VideoOutputData *p_out, CaptureData* capture_data) {
CaptureReceived *p_in = &capture_data->capture_buf[index];
#ifdef USE_FTD3
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3)
ftd3_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d);
@@ -22,13 +23,14 @@ void convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, Capture
#endif
#ifdef USE_IS_NITRO_USB
if(capture_data->status.device.cc_type == CAPTURE_CONN_IS_NITRO)
usb_is_nitro_convertVideoToOutput(p_in, p_out, &capture_data->status.device);
usb_is_nitro_convertVideoToOutput(p_in, p_out, capture_data->capture_type[index]);
#endif
}
void convertAudioToOutput(CaptureReceived *p_in, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
void convertAudioToOutput(int index, sf::Int16 *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) {
if(!capture_data->status.device.has_audio)
return;
CaptureReceived *p_in = &capture_data->capture_buf[index];
uint8_t* base_ptr = NULL;
#ifdef USE_FTD3
if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3) {

View File

@@ -786,9 +786,17 @@ void update_connected_3ds_ds(FrontendData* frontend_data, const CaptureDevice &o
}
}
void default_sleep(int wanted_ms) {
void update_connected_specific_settings(FrontendData* frontend_data, const CaptureDevice &cc_device) {
if(cc_device.cc_type == CAPTURE_CONN_IS_NITRO) {
frontend_data->top_screen->update_capture_specific_settings();
frontend_data->bot_screen->update_capture_specific_settings();
frontend_data->joint_screen->update_capture_specific_settings();
}
}
void default_sleep(float wanted_ms) {
if(wanted_ms < 0)
sf::sleep(sf::milliseconds(1000/USB_CHECKS_PER_SECOND));
sf::sleep(sf::milliseconds(1000.0/USB_CHECKS_PER_SECOND));
else
sf::sleep(sf::milliseconds(wanted_ms));
}

View File

@@ -247,7 +247,7 @@ int DisableLca2(libusb_device_handle *handle) {
ret = WriteNecMemU16(handle, 0xF84000A, 1);
if(ret < 0)
return ret;
default_sleep(2000);
//default_sleep(2000);
return WriteNecMemU16(handle, 0xF84000A, 0);
}
@@ -290,6 +290,7 @@ int UpdateFrameForwardEnable(libusb_device_handle *handle, uint8_t value) {
}
int ReadFrame(libusb_device_handle *handle, uint8_t* buf, int length) {
// Maybe making this async would be better for lower end hardware...
int num_bytes = 0;
int ret = bulk_in(handle, &usb_is_nitro_desc, buf, length, &num_bytes);
if(num_bytes != length)

View File

@@ -30,7 +30,7 @@
#define SERIAL_NUMBER_SIZE (IS_NITRO_REAL_SERIAL_NUMBER_SIZE + 1)
#define NUM_CONSECUTIVE_FRAMES 32
#define FRAME_BUFFER_SIZE 32
enum usb_capture_status {
USB_CAPTURE_SUCCESS = 0,
@@ -40,9 +40,9 @@ enum usb_capture_status {
USB_CAPTURE_ERROR
};
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames);
static int StartCapture(libusb_device_handle *handle, int *out_frame_count);
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames);
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type);
static int StartCapture(libusb_device_handle *handle, uint16_t *out_frame_count, float* single_frame_time, CaptureScreensType capture_type);
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type);
static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb_device_handle *handle, int &curr_serial_extra_id) {
uint8_t data[SERIAL_NUMBER_SIZE];
@@ -50,13 +50,18 @@ static std::string get_serial(const is_nitro_usb_device* usb_device_desc, libusb
bool conn_success = true;
if(libusb_set_configuration(handle, usb_device_desc->default_config) != LIBUSB_SUCCESS)
conn_success = false;
//if(libusb_reset_device(handle) != LIBUSB_SUCCESS)
// conn_success = false;
if(conn_success && libusb_claim_interface(handle, usb_device_desc->default_interface) != LIBUSB_SUCCESS)
conn_success = false;
if(conn_success && EndCapture(handle, false, 0) != LIBUSB_SUCCESS)
if(conn_success && EndCapture(handle, false, 0, CAPTURE_SCREENS_BOTH) != LIBUSB_SUCCESS)
conn_success = false;
if(conn_success && (GetDeviceSerial(handle, data) == LIBUSB_SUCCESS)) {
if(conn_success && (GetDeviceSerial(handle, data) != LIBUSB_SUCCESS)) {
int ret = 0;
while(ret >= 0)
ret = drain_frames(handle, FRAME_BUFFER_SIZE * 2, 0, CAPTURE_SCREENS_TOP);
if((GetDeviceSerial(handle, data) != LIBUSB_SUCCESS))
conn_success = false;
}
if(conn_success) {
data[IS_NITRO_REAL_SERIAL_NUMBER_SIZE] = '\0';
serial_str = std::string((const char*)data);
}
@@ -165,71 +170,120 @@ bool is_nitro_connect_usb(bool print_failed, CaptureData* capture_data, CaptureD
return true;
}
static uint64_t _is_nitro_emulator_get_video_in_size() {
static uint64_t _is_nitro_emulator_get_video_in_size(CaptureScreensType capture_type) {
if((capture_type == CAPTURE_SCREENS_TOP) || (capture_type == CAPTURE_SCREENS_BOTTOM))
return sizeof(ISNitroEmulatorVideoInputData) / 2;
return sizeof(ISNitroEmulatorVideoInputData);
}
static uint64_t get_capture_size() {
return sizeof(ISNitroCaptureReceived);
}
uint64_t usb_is_nitro_emulator_get_video_in_size(CaptureData* capture_data) {
return _is_nitro_emulator_get_video_in_size();
return _is_nitro_emulator_get_video_in_size(capture_data->status.capture_type);
}
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames) {
static int drain_frames(libusb_device_handle *handle, int num_frames, int start_frames, CaptureScreensType capture_type) {
ISNitroEmulatorVideoInputData video_in_buffer;
for (int i = start_frames; i < num_frames; i++) {
int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size());
int ret = ReadFrame(handle, (uint8_t*)&video_in_buffer, _is_nitro_emulator_get_video_in_size(capture_type));
if(ret < 0)
return ret;
}
return LIBUSB_SUCCESS;
}
static int StartCapture(libusb_device_handle *handle, int *out_frame_count) {
static int set_capture_mode(libusb_device_handle *handle, CaptureScreensType capture_type) {
uint8_t capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTH;
if(capture_type == CAPTURE_SCREENS_TOP)
capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_TOP;
if(capture_type == CAPTURE_SCREENS_BOTTOM)
capture_mode_flag = IS_NITRO_FORWARD_CONFIG_MODE_BOTTOM;
return UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | capture_mode_flag | IS_NITRO_FORWARD_CONFIG_RATE_FULL);
}
static int StartCapture(libusb_device_handle *handle, uint16_t &out_frame_count, float &single_frame_time, CaptureScreensType capture_type) {
int ret = DisableLca2(handle);
if(ret < 0)
return ret;
ret = UpdateFrameForwardConfig(handle, IS_NITRO_FORWARD_CONFIG_COLOR_RGB24 | IS_NITRO_FORWARD_CONFIG_MODE_BOTH | IS_NITRO_FORWARD_CONFIG_RATE_FULL);
ret = set_capture_mode(handle, capture_type);
if(ret < 0)
return ret;
ret = SetForwardFrameCount(handle, NUM_CONSECUTIVE_FRAMES);
ret = SetForwardFrameCount(handle, FRAME_BUFFER_SIZE);
if(ret < 0)
return ret;
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
// Reset this in case it's high. At around 0xFFFF, reading from the USB DMA seems to fail...
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE);
if(ret < 0)
return ret;
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE);
if(ret < 0)
return ret;
// Get to the closest next frame
auto clock_start = std::chrono::high_resolution_clock::now();
uint16_t oldFrameCount;
uint16_t newFrameCount;
ret = GetFrameCounter(handle, &oldFrameCount);
if(ret < 0)
return ret;
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE | IS_NITRO_FORWARD_ENABLE_RESTART);
if(ret < 0)
return ret;
uint16_t newFrameCount;
while (true) {
newFrameCount = oldFrameCount;
while(newFrameCount == oldFrameCount) {
ret = GetFrameCounter(handle, &newFrameCount);
if(ret < 0)
return ret;
if (newFrameCount < 64 && newFrameCount != oldFrameCount && newFrameCount != oldFrameCount + 1) {
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
if(diff.count() > 0.2)
break;
}
}
ret = StartUsbCaptureDma(handle);
// Get to the next modulo 32 frame.
// We also do this to measure the time that is needed for each frame...
// To do so, a minimum of 4 frames is required (FRAME_BUFFER_SIZE - 1 + 4)
clock_start = std::chrono::high_resolution_clock::now();
ret = GetFrameCounter(handle, &oldFrameCount);
if(ret < 0)
return ret;
ret = drain_frames(handle, newFrameCount, 0);
*out_frame_count = newFrameCount;
uint16_t targetFrameCount = (newFrameCount + FRAME_BUFFER_SIZE + 3) & (~(FRAME_BUFFER_SIZE - 1));
while(oldFrameCount != targetFrameCount) {
ret = GetFrameCounter(handle, &oldFrameCount);
if(ret < 0)
return ret;
// Placing a sleep of some kind here would be much better...
// Though this is only executed for a small time when first connecting...
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
if(diff.count() > 1.0)
break;
}
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
// Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits.
newFrameCount &= 0xFF;
oldFrameCount &= 0xFF;
int frame_diff = ((int)oldFrameCount) - ((int)newFrameCount);
if(frame_diff < 0)
frame_diff += 1 << 8;
out_frame_count = oldFrameCount;
// Determine how much time a single frame takes. We'll use it for sleeps
if(frame_diff == 0)
single_frame_time = 0;
else
single_frame_time = diff.count() / frame_diff;
// Start the actual DMA
if(single_frame_time > 0) {
ret = StartUsbCaptureDma(handle);
if(ret < 0)
return ret;
}
return ret;
}
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames) {
static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int start_frames, CaptureScreensType capture_type) {
int ret = 0;
if(do_drain_frames)
ret = drain_frames(handle, NUM_CONSECUTIVE_FRAMES, start_frames);
ret = drain_frames(handle, FRAME_BUFFER_SIZE, start_frames, capture_type);
if(ret < 0)
return ret;
ret = StopUsbCaptureDma(handle);
@@ -238,38 +292,98 @@ static int EndCapture(libusb_device_handle *handle, bool do_drain_frames, int st
return UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_DISABLE);
}
int reset_capture_frames(libusb_device_handle* handle, int &frame_counter, uint16_t &total_frame_counter) {
total_frame_counter += 1;
frame_counter += 1;
static void frame_wait(float single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> clock_last_reset, int curr_frame_counter, int last_frame_counter) {
if(curr_frame_counter == 0)
return;
auto curr_time = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = curr_time - clock_last_reset;
float expected_time = single_frame_time * curr_frame_counter;
// If the current time is too low, sleep a bit to make sure we don't overrun the framerate counter
// Don't do it regardless of the situation, and only in small increments...
// Otherwise there is the risk of sleeping too much
while((diff.count() < expected_time) && ((expected_time - diff.count()) > (single_frame_time / 4)) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1)))) {
default_sleep((expected_time - diff.count()) / 4);
curr_time = std::chrono::high_resolution_clock::now();
diff = curr_time - clock_last_reset;
}
}
if(frame_counter == NUM_CONSECUTIVE_FRAMES) {
int reset_capture_frames(libusb_device_handle* handle, uint16_t &curr_frame_counter, uint16_t &last_frame_counter, float &single_frame_time, std::chrono::time_point<std::chrono::high_resolution_clock> &clock_last_reset, CaptureScreensType &curr_capture_type, CaptureScreensType wanted_capture_type, int multiplier) {
curr_frame_counter += 1;
if(curr_frame_counter == FRAME_BUFFER_SIZE) {
int ret = StopUsbCaptureDma(handle);
if(ret < 0)
return ret;
/*
uint16_t internalFrameCount = -1;
int diff;
do {
if (internalFrameCount != -1)
default_sleep(8000);
ret = GetFrameCounter(handle, &internalFrameCount);
// If the user requests a mode change, accomodate them.
// Though it may lag for a bit...
if(wanted_capture_type != curr_capture_type) {
curr_capture_type = wanted_capture_type;
ret = set_capture_mode(handle, curr_capture_type);
if(ret < 0)
return ret;
}
diff = internalFrameCount - total_frame_counter;
if(diff > 32768)
diff -= 1 << 16;
} while(diff <= 0);
*/
uint16_t internalFrameCount = 0;
uint16_t full_internalFrameCount = 0;
int frame_diff = 0;
int diff_target = FRAME_BUFFER_SIZE * multiplier;
do {
// Check how many frames have passed...
ret = GetFrameCounter(handle, &internalFrameCount);
full_internalFrameCount = internalFrameCount;
// Sometimes the upper 8 bits aren't updated... Use only the lower 8 bits.
internalFrameCount &= 0xFF;
if(ret < 0)
return ret;
frame_diff = internalFrameCount - last_frame_counter;
if(frame_diff < 0)
frame_diff += 1 << 8;
// If the frames haven't advanced, the DS is either turned off or sleeping. If so, avoid locking up
if(frame_diff == 0)
break;
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_last_reset;
// If too much time has passed, the DS is probably either turned off or sleeping. If so, avoid locking up
if(diff.count() > (1.0 * multiplier)) {
frame_diff = 0;
break;
}
// Exit if enough frames have passed, or if there currently is some delay.
// Exiting early makes it possible to catch up to the DMA, if we're behind.
} while((frame_diff < diff_target) && (!(last_frame_counter & (FRAME_BUFFER_SIZE - 1))));
// Determine how much time a single frame takes. We'll use it for sleeps
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_last_reset;
if(frame_diff == 0)
single_frame_time = 0;
else
single_frame_time = diff.count() / (frame_diff / ((float)multiplier));
clock_last_reset = curr_time;
// Save the current frame counter's 8 LSB
last_frame_counter = internalFrameCount;
// If we're nearing 0xFFFF for the frame counter, reset it.
// It's a problematic value for DMA reading
if(frame_diff && (full_internalFrameCount >= 0xF000)) {
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_RESTART | IS_NITRO_FORWARD_ENABLE_ENABLE);
if(ret < 0)
return ret;
clock_last_reset = std::chrono::high_resolution_clock::now();
}
ret = UpdateFrameForwardEnable(handle, IS_NITRO_FORWARD_ENABLE_ENABLE);
if(ret < 0)
return ret;
frame_counter = 0;
ret = StartUsbCaptureDma(handle);
if(ret < 0)
return ret;
curr_frame_counter = 0;
// Start the actual DMA
if(single_frame_time > 0) {
ret = StartUsbCaptureDma(handle);
if(ret < 0)
return ret;
}
}
return LIBUSB_SUCCESS;
}
@@ -278,42 +392,55 @@ void is_nitro_capture_main_loop(CaptureData* capture_data) {
if(!usb_is_initialized())
return;
libusb_device_handle *handle = (libusb_device_handle*)capture_data->handle;
int frame_counter = 0;
int ret = StartCapture(handle, &frame_counter);
uint16_t total_frame_counter = frame_counter;
uint16_t last_frame_counter = 0;
float single_frame_time = 0;
uint16_t curr_frame_counter = 0;
CaptureScreensType curr_capture_type = capture_data->status.capture_type;
int ret = StartCapture(handle, last_frame_counter, single_frame_time, curr_capture_type);
if(ret < 0) {
capture_error_print(true, capture_data, "Capture Start: Failed");
return;
}
int inner_curr_in = 0;
auto clock_start = std::chrono::high_resolution_clock::now();
auto clock_last_reset = std::chrono::high_resolution_clock::now();
while(capture_data->status.connected && capture_data->status.running) {
ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size());
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Read error");
break;
}
frame_wait(single_frame_time, clock_last_reset, curr_frame_counter, last_frame_counter);
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
ret = reset_capture_frames(handle, frame_counter, total_frame_counter);
if(single_frame_time > 0) {
ret = ReadFrame(handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_emulator_get_video_in_size(curr_capture_type));
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Read error");
break;
}
// Output to the other threads...
const auto curr_time = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> diff = curr_time - clock_start;
clock_start = curr_time;
capture_data->time_in_buf[inner_curr_in] = diff.count();
capture_data->read[inner_curr_in] = _is_nitro_emulator_get_video_in_size(curr_capture_type);
capture_data->capture_type[inner_curr_in] = curr_capture_type;
inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
if(capture_data->status.cooldown_curr_in)
capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
capture_data->status.curr_in = inner_curr_in;
capture_data->status.video_wait.unlock();
capture_data->status.audio_wait.unlock();
}
else {
capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM;
default_sleep(20);
}
capture_data->status.curr_delay = last_frame_counter % FRAME_BUFFER_SIZE;
ret = reset_capture_frames(handle, curr_frame_counter, last_frame_counter, single_frame_time, clock_last_reset, curr_capture_type, capture_data->status.capture_type, 1);
if(ret < 0) {
capture_error_print(true, capture_data, "Disconnected: Frame counter reset error");
break;
}
clock_start = curr_time;
capture_data->time_in_buf[inner_curr_in] = diff.count();
capture_data->read[inner_curr_in] = _is_nitro_emulator_get_video_in_size();
inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS;
if(capture_data->status.cooldown_curr_in)
capture_data->status.cooldown_curr_in = capture_data->status.cooldown_curr_in - 1;
capture_data->status.curr_in = inner_curr_in;
capture_data->status.video_wait.unlock();
capture_data->status.audio_wait.unlock();
}
EndCapture(handle, true, frame_counter);
EndCapture(handle, true, curr_frame_counter, curr_capture_type);
}
void usb_is_nitro_capture_cleanup(CaptureData* capture_data) {
@@ -322,16 +449,23 @@ void usb_is_nitro_capture_cleanup(CaptureData* capture_data) {
is_nitro_connection_end((libusb_device_handle*)capture_data->handle);
}
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureDevice* capture_device) {
void usb_is_nitro_convertVideoToOutput(CaptureReceived *p_in, VideoOutputData *p_out, CaptureScreensType capture_type) {
if(!usb_is_initialized())
return;
for(int i = 0; i < IN_VIDEO_HEIGHT_DS; i++)
for(int j = 0; j < IN_VIDEO_WIDTH_DS; j++) {
int pixel = (i * IN_VIDEO_WIDTH_DS) + j;
p_out->screen_data[pixel][0] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][2];
p_out->screen_data[pixel][1] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][1];
p_out->screen_data[pixel][2] = p_in->is_nitro_capture_received.video_in.screen_data[pixel][0];
}
int num_pixels = _is_nitro_emulator_get_video_in_size(capture_type) / 3;
int out_start_pos = 0;
int out_clear_pos = num_pixels;
if(capture_type == CAPTURE_SCREENS_BOTTOM) {
out_start_pos = num_pixels;
out_clear_pos = 0;
}
if((capture_type == CAPTURE_SCREENS_BOTTOM) || (capture_type == CAPTURE_SCREENS_TOP))
memset(p_out->screen_data[out_clear_pos], 0, num_pixels * 3);
for(int i = 0; i < num_pixels; i++) {
p_out->screen_data[i + out_start_pos][0] = p_in->is_nitro_capture_received.video_in.screen_data[i][2];
p_out->screen_data[i + out_start_pos][1] = p_in->is_nitro_capture_received.video_in.screen_data[i][1];
p_out->screen_data[i + out_start_pos][2] = p_in->is_nitro_capture_received.video_in.screen_data[i][0];
}
}
void usb_is_nitro_init() {