diff --git a/CMakeLists.txt b/CMakeLists.txt index 1291a64..3295644 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,7 +394,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} --build ${TOOLS_DATA_DIR}) set(OUTPUT_NAME cc3dsfs) -add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/Menus/ISNitroMenu.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp ${SOURCE_CPP_EXTRA_FILES}) +add_executable(${OUTPUT_NAME} source/cc3dsfs.cpp source/utils.cpp source/audio_data.cpp source/audio.cpp source/frontend.cpp source/TextRectangle.cpp source/WindowScreen.cpp source/WindowScreen_Menu.cpp source/devicecapture.cpp source/conversions.cpp source/ExtraButtons.cpp source/Menus/ConnectionMenu.cpp source/Menus/OptionSelectionMenu.cpp source/Menus/MainMenu.cpp source/Menus/VideoMenu.cpp source/Menus/CropMenu.cpp source/Menus/PARMenu.cpp source/Menus/RotationMenu.cpp source/Menus/OffsetMenu.cpp source/Menus/AudioMenu.cpp source/Menus/BFIMenu.cpp source/Menus/RelativePositionMenu.cpp source/Menus/ResolutionMenu.cpp source/Menus/FileConfigMenu.cpp source/Menus/ExtraSettingsMenu.cpp source/Menus/StatusMenu.cpp source/Menus/LicenseMenu.cpp source/WindowCommands.cpp source/Menus/ShortcutMenu.cpp source/Menus/ActionSelectionMenu.cpp source/Menus/ScalingRatioMenu.cpp source/Menus/ISNitroMenu.cpp source/CaptureDataBuffers.cpp ${TOOLS_DATA_DIR}/font_ttf.cpp ${SOURCE_CPP_EXTRA_FILES}) if(NOT ("${EXTRA_DEPENDENCIES}" STREQUAL "")) add_dependencies(${OUTPUT_NAME} ${EXTRA_DEPENDENCIES}) endif() diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp index 226803a..d2b35f3 100644 --- a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.hpp @@ -6,6 +6,6 @@ int initial_cleanup_capture(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers); int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb_device* usb_device_desc); -void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data); +void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf); #endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp index f503d1c..54c1a2d 100644 --- a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.hpp @@ -6,6 +6,6 @@ int initial_cleanup_emulator(const is_nitro_usb_device* usb_device_desc, is_nitro_device_handlers* handlers); int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); -void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data); +void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf); #endif diff --git a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp index f675c8a..e922a7f 100644 --- a/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp +++ b/include/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_general.hpp @@ -8,6 +8,6 @@ uint64_t _is_nitro_get_video_in_size(CaptureScreensType capture_type); int set_acquisition_mode(is_nitro_device_handlers* handlers, CaptureScreensType capture_type, CaptureSpeedsType capture_speed, const is_nitro_usb_device* usb_device_desc); int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int start_frames, CaptureScreensType capture_type, const is_nitro_usb_device* usb_device_desc); -int is_nitro_read_frame_and_output(CaptureData* capture_data, int& inner_curr_in, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start); +int is_nitro_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start); #endif diff --git a/include/capture_structs.hpp b/include/capture_structs.hpp index a7ed171..f1747c7 100755 --- a/include/capture_structs.hpp +++ b/include/capture_structs.hpp @@ -3,11 +3,9 @@ #include "utils.hpp" #include "hw_defs.hpp" +#include #include -// Max value (Due to support of old FTD3XX versions...) -#define NUM_CONCURRENT_DATA_BUFFERS 8 - // It may happen that a frame is lost. // This value prevents showing a black frame for that. // Shouldn't happen with recent updates, though... @@ -23,6 +21,14 @@ enum InputVideoDataType { VIDEO_DATA_RGB, VIDEO_DATA_BGR, VIDEO_DATA_RGB16 }; enum CaptureScreensType { CAPTURE_SCREENS_BOTH, CAPTURE_SCREENS_TOP, CAPTURE_SCREENS_BOTTOM, CAPTURE_SCREENS_ENUM_END }; enum CaptureSpeedsType { CAPTURE_SPEEDS_FULL, CAPTURE_SPEEDS_HALF, CAPTURE_SPEEDS_THIRD, CAPTURE_SPEEDS_QUARTER, CAPTURE_SPEEDS_ENUM_END }; +// Readers are Audio and Video. So 2. +// Use 2 extra buffers. One for writing in case the other 2 are busy, +// and the other for writing without overwriting the latest stuff in +// a worst case scenario... +enum CaptureReaderType { CAPTURE_READER_VIDEO, CAPTURE_READER_AUDIO, CAPTURE_READER_ENUM_END }; +#define NUM_CONCURRENT_DATA_BUFFER_READERS ((int)CAPTURE_READER_ENUM_END) +#define NUM_CONCURRENT_DATA_BUFFERS (NUM_CONCURRENT_DATA_BUFFER_READERS + 2) + #pragma pack(push, 1) struct PACKED RGB83DSVideoInputData { @@ -132,7 +138,6 @@ struct CaptureStatus { std::string graphical_error_text; std::string detailed_error_text; bool new_error_text; - volatile int curr_in = 0; volatile int cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM; volatile bool connected = false; volatile bool running = true; @@ -146,12 +151,35 @@ struct CaptureStatus { ConsumerMutex audio_wait; }; +struct CaptureDataSingleBuffer { + CaptureScreensType capture_type; + uint64_t read; + CaptureReceived capture_buf; + double time_in_buf; +}; + +class CaptureDataBuffers { +public: + CaptureDataBuffers(); + CaptureDataSingleBuffer* GetReaderBuffer(CaptureReaderType reader_type); + void ReleaseReaderBuffer(CaptureReaderType reader_type); + void WriteToBuffer(CaptureReceived* buffer, uint64_t read, double time_in_buf, CaptureDevice* device, CaptureScreensType capture_type = CAPTURE_SCREENS_BOTH); +private: + std::mutex access_mutex; + int last_curr_in; + int curr_writer_pos; + int curr_reader_pos[NUM_CONCURRENT_DATA_BUFFER_READERS]; + int num_readers[NUM_CONCURRENT_DATA_BUFFERS]; + bool has_read_data[NUM_CONCURRENT_DATA_BUFFERS][NUM_CONCURRENT_DATA_BUFFER_READERS]; + CaptureDataSingleBuffer buffers[NUM_CONCURRENT_DATA_BUFFERS]; + + CaptureDataSingleBuffer* GetWriterBuffer(); + void ReleaseWriterBuffer(); +}; + struct CaptureData { void* handle; - CaptureScreensType capture_type[NUM_CONCURRENT_DATA_BUFFERS]; - uint64_t read[NUM_CONCURRENT_DATA_BUFFERS]; - CaptureReceived capture_buf[NUM_CONCURRENT_DATA_BUFFERS]; - double time_in_buf[NUM_CONCURRENT_DATA_BUFFERS]; + CaptureDataBuffers data_buffers; CaptureStatus status; }; #endif diff --git a/include/conversions.hpp b/include/conversions.hpp index 1125f89..36cbaad 100755 --- a/include/conversions.hpp +++ b/include/conversions.hpp @@ -4,6 +4,6 @@ #include "capture_structs.hpp" #include "display_structs.hpp" -bool convertVideoToOutput(int index, VideoOutputData *p_out, CaptureData* capture_data); -bool convertAudioToOutput(int index, std::int16_t *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data); +bool convertVideoToOutput(VideoOutputData *p_out, CaptureDataSingleBuffer* data_buffer, CaptureStatus* status); +bool convertAudioToOutput(std::int16_t *p_out, uint64_t n_samples, const bool is_big_endian, CaptureDataSingleBuffer* data_buffer, CaptureStatus* status); #endif diff --git a/source/CaptureDataBuffers.cpp b/source/CaptureDataBuffers.cpp new file mode 100644 index 0000000..42b8c09 --- /dev/null +++ b/source/CaptureDataBuffers.cpp @@ -0,0 +1,93 @@ +#include "capture_structs.hpp" +#include + +CaptureDataBuffers::CaptureDataBuffers() { + last_curr_in = 0; + curr_writer_pos = -1; + for(int i = 0; i < NUM_CONCURRENT_DATA_BUFFER_READERS; i++) + curr_reader_pos[i] = -1; + for(int i = 0; i < NUM_CONCURRENT_DATA_BUFFERS; i++) { + num_readers[i] = 0; + for(int j = 0; j < NUM_CONCURRENT_DATA_BUFFER_READERS; j++) + has_read_data[i][j] = true; + } +} + +static int reader_to_index(CaptureReaderType reader_type) { + switch(reader_type) { + case CAPTURE_READER_VIDEO: + return 0; + case CAPTURE_READER_AUDIO: + return 1; + default: + return 0; + } +} + +CaptureDataSingleBuffer* CaptureDataBuffers::GetReaderBuffer(CaptureReaderType reader_type) { + int index = reader_to_index(reader_type); + CaptureDataSingleBuffer* retval = NULL; + access_mutex.lock(); + if(curr_reader_pos[index] != -1) + retval = &buffers[curr_reader_pos[index]]; + else if(!has_read_data[last_curr_in][index]) { + has_read_data[last_curr_in][index] = true; + num_readers[last_curr_in] += 1; + retval = &buffers[last_curr_in]; + curr_reader_pos[index] = last_curr_in; + } + access_mutex.unlock(); + return retval; +} + +CaptureDataSingleBuffer* CaptureDataBuffers::GetWriterBuffer() { + CaptureDataSingleBuffer* retval = NULL; + access_mutex.lock(); + if(curr_writer_pos != -1) + retval = &buffers[curr_writer_pos]; + else + for(int i = 0; i < NUM_CONCURRENT_DATA_BUFFERS; i++) + if((num_readers[i] == 0) && (i != last_curr_in)) { + retval = &buffers[i]; + curr_writer_pos = i; + for(int j = 0; j < NUM_CONCURRENT_DATA_BUFFER_READERS; j++) + has_read_data[i][j] = false; + break; + } + access_mutex.unlock(); + return retval; +} + +void CaptureDataBuffers::ReleaseReaderBuffer(CaptureReaderType reader_type) { + int index = reader_to_index(reader_type); + access_mutex.lock(); + if(curr_reader_pos[index] != -1) { + num_readers[curr_reader_pos[index]] -= 1; + curr_reader_pos[index] = -1; + } + access_mutex.unlock(); +} + +void CaptureDataBuffers::ReleaseWriterBuffer() { + access_mutex.lock(); + if(curr_writer_pos != -1) { + last_curr_in = curr_writer_pos; + curr_writer_pos = -1; + } + access_mutex.unlock(); +} + +void CaptureDataBuffers::WriteToBuffer(CaptureReceived* buffer, uint64_t read, double time_in_buf, CaptureDevice* device, CaptureScreensType capture_type) { + CaptureDataSingleBuffer* target = this->GetWriterBuffer(); + // How did we end here?! + if(target == NULL) + return; + memcpy(&target->capture_buf, buffer, read); + // Make sure to also copy the extra needed data, if any + if((device->cc_type == CAPTURE_CONN_USB) && (!device->is_3ds)) + memcpy(&target->capture_buf.usb_received_old_ds.frameinfo, &buffer->usb_received_old_ds.frameinfo, sizeof(buffer->usb_received_old_ds.frameinfo)); + target->read = read; + target->time_in_buf = time_in_buf; + target->capture_type = capture_type; + this->ReleaseWriterBuffer(); +} diff --git a/source/CaptureDeviceSpecific/3dscapture_ftd3.cpp b/source/CaptureDeviceSpecific/3dscapture_ftd3.cpp index 8fc481f..6f6b88e 100644 --- a/source/CaptureDeviceSpecific/3dscapture_ftd3.cpp +++ b/source/CaptureDeviceSpecific/3dscapture_ftd3.cpp @@ -31,16 +31,22 @@ #define REAL_SERIAL_NUMBER_SIZE 16 #define SERIAL_NUMBER_SIZE (REAL_SERIAL_NUMBER_SIZE+1) +#define FTD3XX_CONCURRENT_BUFFERS 4 + +struct FTD3XXReceivedDataBuffer { + CaptureReceived capture_buf; + OVERLAPPED overlap; + ULONG read_buffer; +}; + const uint16_t ftd3xx_valid_vids[] = {FTD3XX_VID}; const uint16_t ftd3xx_valid_pids[] = {0x601e, 0x601f, 0x602a, 0x602b, 0x602c, 0x602d, 0x602f}; static bool get_is_bad_ftd3xx(); static bool get_skip_initial_pipe_abort(); -static OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS]; -static bool is_bad_ftd3xx = get_is_bad_ftd3xx(); -static bool skip_initial_pipe_abort = get_skip_initial_pipe_abort(); -static ULONG read_buffers[NUM_CONCURRENT_DATA_BUFFERS]; +const bool is_bad_ftd3xx = get_is_bad_ftd3xx(); +const bool skip_initial_pipe_abort = get_skip_initial_pipe_abort(); static bool get_is_bad_ftd3xx() { #ifdef _WIN32 @@ -189,106 +195,115 @@ bool connect_ftd3(bool print_failed, CaptureData* capture_data, CaptureDevice* d return true; } -static inline void data_output_update(CaptureData* capture_data, int &inner_curr_in, std::chrono::time_point &base_time, bool inc_inner_curr_in) { +static inline void data_output_update(FTD3XXReceivedDataBuffer* received_buffer, CaptureData* capture_data, std::chrono::time_point &base_time) { const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration diff = curr_time - base_time; base_time = curr_time; - capture_data->time_in_buf[inner_curr_in] = diff.count(); - capture_data->read[inner_curr_in] = read_buffers[inner_curr_in]; + capture_data->data_buffers.WriteToBuffer(&received_buffer->capture_buf, received_buffer->read_buffer, diff.count(), &capture_data->status.device); 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 + 1) % NUM_CONCURRENT_DATA_BUFFERS; - if(inc_inner_curr_in) - inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS; // Signal that there is data available capture_data->status.video_wait.unlock(); capture_data->status.audio_wait.unlock(); } -static void fast_capture_call(CaptureData* capture_data, OVERLAPPED overlap[NUM_CONCURRENT_DATA_BUFFERS]) { +static void fast_capture_call(FTD3XXReceivedDataBuffer* received_buffer, CaptureData* capture_data) { int inner_curr_in = 0; FT_STATUS ftStatus; - for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) { - ftStatus = FT_InitializeOverlapped(capture_data->handle, &overlap[inner_curr_in]); + for (inner_curr_in = 0; inner_curr_in < FTD3XX_CONCURRENT_BUFFERS; ++inner_curr_in) { + ftStatus = FT_InitializeOverlapped(capture_data->handle, &received_buffer[inner_curr_in].overlap); if (ftStatus) { capture_error_print(true, capture_data, "Disconnected: Initialize failed"); return; } } - for (inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS - 1; ++inner_curr_in) { - ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], &overlap[inner_curr_in]); + for (inner_curr_in = 0; inner_curr_in < FTD3XX_CONCURRENT_BUFFERS - 1; ++inner_curr_in) { + ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&received_buffer[inner_curr_in].capture_buf, get_capture_size(capture_data), &received_buffer[inner_curr_in].read_buffer, &received_buffer[inner_curr_in].overlap); if (ftStatus != FT_IO_PENDING) { capture_error_print(true, capture_data, "Disconnected: Read failed"); return; } } - inner_curr_in = NUM_CONCURRENT_DATA_BUFFERS - 1; + inner_curr_in = FTD3XX_CONCURRENT_BUFFERS - 1; auto clock_start = std::chrono::high_resolution_clock::now(); while (capture_data->status.connected && capture_data->status.running) { - ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], &overlap[inner_curr_in]); + ftStatus = FT_ASYNC_CALL(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&received_buffer[inner_curr_in].capture_buf, get_capture_size(capture_data), &received_buffer[inner_curr_in].read_buffer, &received_buffer[inner_curr_in].overlap); if (ftStatus != FT_IO_PENDING) { capture_error_print(true, capture_data, "Disconnected: Read failed"); return; } - inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS; + inner_curr_in = (inner_curr_in + 1) % FTD3XX_CONCURRENT_BUFFERS; - ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &read_buffers[inner_curr_in], true); + ftStatus = FT_GetOverlappedResult(capture_data->handle, &received_buffer[inner_curr_in].overlap, &received_buffer[inner_curr_in].read_buffer, true); if(FT_FAILED(ftStatus)) { capture_error_print(true, capture_data, "Disconnected: USB error"); return; } - data_output_update(capture_data, inner_curr_in, clock_start, false); + data_output_update(&received_buffer[inner_curr_in], capture_data, clock_start); } } -static bool safe_capture_call(CaptureData* capture_data) { - int inner_curr_in = 0; +static bool safe_capture_call(FTD3XXReceivedDataBuffer* received_buffer, CaptureData* capture_data) { auto clock_start = std::chrono::high_resolution_clock::now(); while(capture_data->status.connected && capture_data->status.running) { #ifdef _WIN32 - FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], NULL); + FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&received_buffer->capture_buf, get_capture_size(capture_data), &received_buffer->read_buffer, NULL); #else - FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&capture_data->capture_buf[inner_curr_in], get_capture_size(capture_data), &read_buffers[inner_curr_in], 1000); + FT_STATUS ftStatus = FT_ReadPipeEx(capture_data->handle, FIFO_CHANNEL, (UCHAR*)&received_buffer->capture_buf, get_capture_size(capture_data), &received_buffer->read_buffer, 1000); #endif if(FT_FAILED(ftStatus)) { capture_error_print(true, capture_data, "Disconnected: Read failed"); return true; } - data_output_update(capture_data, inner_curr_in, clock_start, true); + data_output_update(received_buffer, capture_data, clock_start); } return false; } +static FTD3XXReceivedDataBuffer* init_received_buffer() { + if(is_bad_ftd3xx) + return new FTD3XXReceivedDataBuffer; + + return new FTD3XXReceivedDataBuffer[FTD3XX_CONCURRENT_BUFFERS]; +} + +static void close_received_buffer(FTD3XXReceivedDataBuffer* received_buffer, CaptureData* capture_data) { + if(is_bad_ftd3xx) { + delete received_buffer; + return; + } + + for(int inner_curr_in = 0; inner_curr_in < FTD3XX_CONCURRENT_BUFFERS; ++inner_curr_in) { + FT_STATUS ftStatus = FT_GetOverlappedResult(capture_data->handle, &received_buffer[inner_curr_in].overlap, &received_buffer[inner_curr_in].read_buffer, true); + if(FT_ReleaseOverlapped(capture_data->handle, &received_buffer[inner_curr_in].overlap)) { + capture_error_print(true, capture_data, "Disconnected: Release failed"); + } + } + delete []received_buffer; +} + void ftd3_capture_main_loop(CaptureData* capture_data) { + FTD3XXReceivedDataBuffer* received_buffer = init_received_buffer(); if(!is_bad_ftd3xx) - fast_capture_call(capture_data, overlap); + fast_capture_call(received_buffer, capture_data); else - safe_capture_call(capture_data); + safe_capture_call(received_buffer, capture_data); + close_received_buffer(received_buffer, capture_data); } void ftd3_capture_cleanup(CaptureData* capture_data) { - FT_STATUS ftStatus; - if(!is_bad_ftd3xx) { - for(int inner_curr_in = 0; inner_curr_in < NUM_CONCURRENT_DATA_BUFFERS; ++inner_curr_in) { - ftStatus = FT_GetOverlappedResult(capture_data->handle, &overlap[inner_curr_in], &read_buffers[inner_curr_in], true); - if(FT_ReleaseOverlapped(capture_data->handle, &overlap[inner_curr_in])) { - capture_error_print(true, capture_data, "Disconnected: Release failed"); - } - } - } - if(FT_AbortPipe(capture_data->handle, BULK_IN)) { capture_error_print(true, capture_data, "Disconnected: Abort failed"); } diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp index a5ff379..ae23ac9 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition.cpp @@ -172,22 +172,18 @@ int EndAcquisition(is_nitro_device_handlers* handlers, bool do_drain_frames, int return EndAcquisitionEmulator(handlers, do_drain_frames, start_frames, capture_type, usb_device_desc); } -int is_nitro_read_frame_and_output(CaptureData* capture_data, int &inner_curr_in, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start) { - int ret = ReadFrame((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)&capture_data->capture_buf[inner_curr_in], _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor); +int is_nitro_read_frame_and_output(CaptureData* capture_data, CaptureReceived* capture_buf, CaptureScreensType curr_capture_type, std::chrono::time_point &clock_start) { + int ret = ReadFrame((is_nitro_device_handlers*)capture_data->handle, (uint8_t*)capture_buf, _is_nitro_get_video_in_size(curr_capture_type), (const is_nitro_usb_device*)capture_data->status.device.descriptor); if (ret < 0) return ret; // Output to the other threads... const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration 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_get_video_in_size(curr_capture_type); - capture_data->capture_type[inner_curr_in] = curr_capture_type; + capture_data->data_buffers.WriteToBuffer(capture_buf, _is_nitro_get_video_in_size(curr_capture_type), diff.count(), &capture_data->status.device, 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(); return ret; @@ -196,11 +192,13 @@ int is_nitro_read_frame_and_output(CaptureData* capture_data, int &inner_curr_in void is_nitro_acquisition_main_loop(CaptureData* capture_data) { if(!usb_is_initialized()) return; + CaptureReceived* capture_buf = new CaptureReceived; capture_data->status.reset_hardware = false; if(((const is_nitro_usb_device*)(capture_data->status.device.descriptor))->is_capture) - is_nitro_acquisition_capture_main_loop(capture_data); + is_nitro_acquisition_capture_main_loop(capture_data, capture_buf); else - is_nitro_acquisition_emulator_main_loop(capture_data); + is_nitro_acquisition_emulator_main_loop(capture_data, capture_buf); + delete capture_buf; } void usb_is_nitro_acquisition_cleanup(CaptureData* capture_data) { diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp index 869361c..960e846 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_capture.cpp @@ -88,7 +88,7 @@ int EndAcquisitionCapture(is_nitro_device_handlers* handlers, const is_nitro_usb return StopUsbCaptureDma(handlers, usb_device_desc); } -void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { +void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf) { is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; bool is_acquisition_off = true; @@ -119,7 +119,7 @@ void is_nitro_acquisition_capture_main_loop(CaptureData* capture_data) { } if(is_acquisition_off) continue; - ret = is_nitro_read_frame_and_output(capture_data, inner_curr_in, curr_capture_type, clock_start); + ret = is_nitro_read_frame_and_output(capture_data, capture_buf, curr_capture_type, clock_start); if(ret < 0) { ret = EndAcquisition(handlers, true, 0, curr_capture_type, usb_device_desc); if(ret < 0) { diff --git a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp index 9d7056f..2d71cab 100644 --- a/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp +++ b/source/CaptureDeviceSpecific/ISNitro/usb_is_nitro_acquisition_emulator.cpp @@ -294,7 +294,7 @@ int EndAcquisitionEmulator(is_nitro_device_handlers* handlers, bool do_drain_fra return UpdateFrameForwardEnable(handlers, false, false, usb_device_desc); } -void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { +void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data, CaptureReceived* capture_buf) { is_nitro_device_handlers* handlers = (is_nitro_device_handlers*)capture_data->handle; const is_nitro_usb_device* usb_device_desc = (const is_nitro_usb_device*)capture_data->status.device.descriptor; uint16_t last_frame_counter = 0; @@ -307,7 +307,6 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { 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(); @@ -315,7 +314,7 @@ void is_nitro_acquisition_emulator_main_loop(CaptureData* capture_data) { frame_wait(single_frame_time, clock_last_reset, curr_capture_type, curr_capture_speed, curr_frame_counter, last_frame_counter); if (single_frame_time > 0) { - ret = is_nitro_read_frame_and_output(capture_data, inner_curr_in, curr_capture_type, clock_start); + ret = is_nitro_read_frame_and_output(capture_data, capture_buf, curr_capture_type, clock_start); if (ret < 0) { capture_error_print(true, capture_data, "Disconnected: Read error"); return; diff --git a/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp index baed143..061310e 100644 --- a/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp +++ b/source/CaptureDeviceSpecific/usb_ds_3ds_capture.cpp @@ -212,7 +212,10 @@ static uint64_t get_capture_size(const usb_device* usb_device_desc, bool enabled return sizeof(USB3DSCaptureReceived) - EXTRA_DATA_BUFFER_USB_SIZE; } -static usb_capture_status capture_read_oldds_3ds(libusb_device_handle *handle, const usb_device* usb_device_desc, CaptureReceived* data_buffer, int &bytesIn, bool enabled_3d) { +static usb_capture_status capture_read_oldds_3ds(CaptureData* capture_data, CaptureReceived* data_buffer, int &bytesIn) { + libusb_device_handle* handle = (libusb_device_handle*)capture_data->handle; + const usb_device* usb_device_desc = get_usb_device_desc(capture_data); + const bool enabled_3d = capture_data->status.enabled_3d; bytesIn = 0; int transferred = 0; int result = 0; @@ -386,15 +389,14 @@ uint64_t usb_get_video_in_size(CaptureData* capture_data) { void usb_capture_main_loop(CaptureData* capture_data) { if(!usb_is_initialized()) return; - const usb_device* usb_device_desc = get_usb_device_desc(capture_data); - int inner_curr_in = 0; + CaptureReceived* capture_buf = new CaptureReceived; auto clock_start = std::chrono::high_resolution_clock::now(); bool done = false; while((!done) && capture_data->status.connected && capture_data->status.running) { int read_amount = 0; - usb_capture_status result = capture_read_oldds_3ds((libusb_device_handle*)capture_data->handle, usb_device_desc, &capture_data->capture_buf[inner_curr_in], read_amount, capture_data->status.enabled_3d); + usb_capture_status result = capture_read_oldds_3ds(capture_data, capture_buf, read_amount); const auto curr_time = std::chrono::high_resolution_clock::now(); const std::chrono::duration diff = curr_time - clock_start; @@ -416,13 +418,9 @@ void usb_capture_main_loop(CaptureData* capture_data) { break; case USB_CAPTURE_SUCCESS: clock_start = curr_time; - capture_data->time_in_buf[inner_curr_in] = diff.count(); - capture_data->read[inner_curr_in] = read_amount; - - inner_curr_in = (inner_curr_in + 1) % NUM_CONCURRENT_DATA_BUFFERS; + capture_data->data_buffers.WriteToBuffer(capture_buf, read_amount, diff.count(), &capture_data->status.device); 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(); break; @@ -432,6 +430,7 @@ void usb_capture_main_loop(CaptureData* capture_data) { break; } } + delete capture_buf; } void usb_capture_cleanup(CaptureData* capture_data) { diff --git a/source/cc3dsfs.cpp b/source/cc3dsfs.cpp index 3c8fd97..03bf0e6 100755 --- a/source/cc3dsfs.cpp +++ b/source/cc3dsfs.cpp @@ -25,6 +25,9 @@ // Threshold to keep the audio latency limited in "amount of frames". #define NUM_CONCURRENT_AUDIO_BUFFERS ((MAX_MAX_AUDIO_LATENCY * 2) + 1) +#define LOW_POLL_DIVISOR 6 +#define NO_DATA_CONSECUTIVE_THRESHOLD 4 + struct OutTextData { std::string full_text; std::string small_text; @@ -231,31 +234,32 @@ static void executeSoundRestart(Audio &audio, AudioData* audio_data, bool do_res static void soundCall(AudioData *audio_data, CaptureData* capture_data) { std::int16_t (*out_buf)[MAX_SAMPLES_IN] = new std::int16_t[NUM_CONCURRENT_AUDIO_BUFFERS][MAX_SAMPLES_IN]; Audio audio(audio_data); - int curr_out, prev_out = NUM_CONCURRENT_DATA_BUFFERS - 1, audio_buf_counter = 0; + int audio_buf_counter = 0; const bool endianness = is_big_endian(); volatile int loaded_samples; while(capture_data->status.running) { if(capture_data->status.connected && capture_data->status.device.has_audio) { bool timed_out = !capture_data->status.audio_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)) { - loaded_samples = audio.samples.size(); - 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]; - bool conversion_success = convertAudioToOutput(curr_out, out_buf[audio_buf_counter], n_samples, endianness, capture_data); - if(!conversion_success) - audio_data->signal_conversion_error(); - audio.samples.emplace(out_buf[audio_buf_counter], n_samples, out_time); - if(++audio_buf_counter == NUM_CONCURRENT_AUDIO_BUFFERS) { - audio_buf_counter = 0; + if(!capture_data->status.cooldown_curr_in) { + CaptureDataSingleBuffer* data_buffer = capture_data->data_buffers.GetReaderBuffer(CAPTURE_READER_AUDIO); + if(data_buffer != NULL) { + loaded_samples = audio.samples.size(); + if((data_buffer->read > get_video_in_size(capture_data)) && (loaded_samples < MAX_MAX_AUDIO_LATENCY)) { + int n_samples = get_audio_n_samples(capture_data, data_buffer->read); + double out_time = data_buffer->time_in_buf; + bool conversion_success = convertAudioToOutput(out_buf[audio_buf_counter], n_samples, endianness, data_buffer, &capture_data->status); + if(!conversion_success) + audio_data->signal_conversion_error(); + audio.samples.emplace(out_buf[audio_buf_counter], n_samples, out_time); + if(++audio_buf_counter >= NUM_CONCURRENT_AUDIO_BUFFERS) + audio_buf_counter = 0; + audio.samples_wait.unlock(); } - audio.samples_wait.unlock(); + capture_data->data_buffers.ReleaseReaderBuffer(CAPTURE_READER_AUDIO); } } - prev_out = curr_out; loaded_samples = audio.samples.size(); if(audio.getStatus() != sf::SoundStream::Status::Playing) { @@ -322,7 +326,6 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, VideoOutputData *out_buf; double last_frame_time = 0.0; int num_elements_fps_array = 0; - int curr_out, prev_out = NUM_CONCURRENT_DATA_BUFFERS - 1; FrontendData frontend_data; ConsumerMutex draw_lock; reset_display_data(&frontend_data.display_data); @@ -375,26 +378,26 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, poll_timeout--; } else if(frontend_data.display_data.fast_poll) - poll_timeout = 6; + poll_timeout = LOW_POLL_DIVISOR; 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; - if(no_data_consecutive > 60) - no_data_consecutive = 60; - capture_data->status.video_wait.update_time_multiplier(get_time_multiplier(capture_data, no_data_consecutive > 3)); + if(no_data_consecutive > NO_DATA_CONSECUTIVE_THRESHOLD) + no_data_consecutive = NO_DATA_CONSECUTIVE_THRESHOLD; + capture_data->status.video_wait.update_time_multiplier(get_time_multiplier(capture_data, no_data_consecutive >= NO_DATA_CONSECUTIVE_THRESHOLD)); 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; + CaptureDataSingleBuffer* data_buffer = capture_data->data_buffers.GetReaderBuffer(CAPTURE_READER_VIDEO); - if(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)) { + if(data_buffer != NULL) { + last_frame_time = data_buffer->time_in_buf; + if(data_buffer->read >= get_video_in_size(capture_data)) { if(capture_data->status.cooldown_curr_in) blank_out = true; else { - bool conversion_success = convertVideoToOutput(curr_out, out_buf, capture_data); + bool conversion_success = convertVideoToOutput(out_buf, data_buffer, &capture_data->status); if(!conversion_success) UpdateOutText(out_text_data, "", "Video conversion failed...", TEXT_KIND_NORMAL); } @@ -407,12 +410,12 @@ static int mainVideoOutputCall(AudioData* audio_data, CaptureData* capture_data, else blank_out = true; } + capture_data->data_buffers.ReleaseReaderBuffer(CAPTURE_READER_VIDEO); } else if(timed_out) { blank_out = true; no_data_consecutive++; } - prev_out = curr_out; } else { if(last_connected) diff --git a/source/conversions.cpp b/source/conversions.cpp index 928bcce..389814d 100755 --- a/source/conversions.cpp +++ b/source/conversions.cpp @@ -7,56 +7,56 @@ #include -bool convertVideoToOutput(int index, VideoOutputData *p_out, CaptureData* capture_data) { - CaptureReceived *p_in = &capture_data->capture_buf[index]; +bool convertVideoToOutput(VideoOutputData *p_out, CaptureDataSingleBuffer* data_buffer, CaptureStatus* status) { + CaptureReceived *p_in = &data_buffer->capture_buf; bool converted = false; #ifdef USE_FTD3 - if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3) { - ftd3_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d); + if(status->device.cc_type == CAPTURE_CONN_FTD3) { + ftd3_convertVideoToOutput(p_in, p_out, status->enabled_3d); converted = true; } #endif #ifdef USE_FTD2 - if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2) { - ftd2_convertVideoToOutput(p_in, p_out, capture_data->status.enabled_3d); + if(status->device.cc_type == CAPTURE_CONN_FTD2) { + ftd2_convertVideoToOutput(p_in, p_out, status->enabled_3d); converted = true; } #endif #ifdef USE_DS_3DS_USB - if(capture_data->status.device.cc_type == CAPTURE_CONN_USB) { - usb_convertVideoToOutput(p_in, p_out, &capture_data->status.device, capture_data->status.enabled_3d); + if(status->device.cc_type == CAPTURE_CONN_USB) { + usb_convertVideoToOutput(p_in, p_out, &status->device, status->enabled_3d); converted = true; } #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->capture_type[index]); + if(status->device.cc_type == CAPTURE_CONN_IS_NITRO) { + usb_is_nitro_convertVideoToOutput(p_in, p_out, data_buffer->capture_type); converted = true; } #endif return converted; } -bool convertAudioToOutput(int index, std::int16_t *p_out, uint64_t n_samples, const bool is_big_endian, CaptureData* capture_data) { - if(!capture_data->status.device.has_audio) +bool convertAudioToOutput(std::int16_t *p_out, uint64_t n_samples, const bool is_big_endian, CaptureDataSingleBuffer* data_buffer, CaptureStatus* status) { + if(!status->device.has_audio) return true; - CaptureReceived *p_in = &capture_data->capture_buf[index]; + CaptureReceived *p_in = &data_buffer->capture_buf; uint8_t* base_ptr = NULL; #ifdef USE_FTD3 - if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD3) { - if(!capture_data->status.enabled_3d) + if(status->device.cc_type == CAPTURE_CONN_FTD3) { + if(!status->enabled_3d) base_ptr = (uint8_t*)p_in->ftd3_received.audio_data; else base_ptr = (uint8_t*)p_in->ftd3_received_3d.audio_data; } #endif #ifdef USE_FTD2 - if(capture_data->status.device.cc_type == CAPTURE_CONN_FTD2) { + if(status->device.cc_type == CAPTURE_CONN_FTD2) { } #endif #ifdef USE_DS_3DS_USB - if(capture_data->status.device.cc_type == CAPTURE_CONN_USB) { - if(!capture_data->status.enabled_3d) + if(status->device.cc_type == CAPTURE_CONN_USB) { + if(!status->enabled_3d) base_ptr = (uint8_t*)p_in->usb_received_3ds.audio_data; else base_ptr = (uint8_t*)p_in->usb_received_3ds_3d.audio_data; diff --git a/source/devicecapture.cpp b/source/devicecapture.cpp index bf8a30e..808a55e 100755 --- a/source/devicecapture.cpp +++ b/source/devicecapture.cpp @@ -140,8 +140,6 @@ bool connect(bool print_failed, CaptureData* capture_data, FrontendData* fronten } void captureCall(CaptureData* capture_data) { - int inner_curr_in = 0; - capture_data->status.curr_in = inner_curr_in; capture_data->status.cooldown_curr_in = FIX_PARTIAL_FIRST_FRAME_NUM; while(capture_data->status.running) {