-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathfile_callbacks.cpp
140 lines (119 loc) · 5.4 KB
/
file_callbacks.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#include "file_callbacks.h"
namespace Callbacks {
GodotFileRunner* GodotFileRunner::get_singleton() {
static GodotFileRunner singleton;
return &singleton;
}
void GodotFileRunner::queueReadRequest(FMOD_ASYNCREADINFO* request, ReadPriority priority) {
// High priority requests have to be processed first.
if (priority == ReadPriority::HIGH) {
// lock so we can't add and remove elements from the queue at the same time.
std::lock_guard<std::mutex> lk(read_mut);
requests.push_front(request);
} else {
// lock so we can't add and remove elements from the queue at the same time.
std::lock_guard<std::mutex> lk(read_mut);
requests.push_back(request);
}
read_cv.notify_one();
}
void GodotFileRunner::cancelReadRequest(FMOD_ASYNCREADINFO* request) {
// lock so we can't add and remove elements from the queue at the same time.
{
std::lock_guard<std::mutex> lk(read_mut);
requests.erase(request);
}
// We lock and check if the current request is the one being canceled.
// In this case, we wait until it's done.
{
std::unique_lock<std::mutex> lk(cancel_mut);
if (request == current_request) { cancel_cv.wait(lk); }
}
}
void GodotFileRunner::run() {
while (!stop) {
// waiting for the container to have one request
{
std::unique_lock<std::mutex> lk(read_mut);
read_cv.wait(lk, [this] { return !requests.is_empty() || stop; });
}
while (!requests.is_empty()) {
// lock so we can't add and remove elements from the queue at the same time.
// also store the current request so it cannot be cancel during process.
{
std::lock_guard<std::mutex> lk(read_mut);
current_request = requests.front()->get();
requests.pop_front();
}
// We get the Godot File object from the handle
GodotFileHandle* handle {reinterpret_cast<GodotFileHandle*>(current_request->handle)};
godot::Ref<godot::FileAccess> file {handle->file};
// update the position of the cursor
file->seek(current_request->offset);
// We read and store the requested data in an array.
godot::PackedByteArray buffer {file->get_buffer(current_request->sizebytes)};
int size {static_cast<int>(buffer.size())};
const uint8_t* data {buffer.ptr()};
// We copy the data to FMOD buffer
memcpy(current_request->buffer, data, size * sizeof(uint8_t));
current_request->bytesread = size;
// Don't forget the return an error if end of the file is reached
FMOD_RESULT result;
if (file->eof_reached()) {
result = FMOD_RESULT::FMOD_ERR_FILE_EOF;
} else {
result = FMOD_RESULT::FMOD_OK;
}
current_request->done(current_request, result);
// Request no longer processed
{
std::lock_guard<std::mutex> lk(cancel_mut);
current_request = nullptr;
}
cancel_cv.notify_one();
}
}
}
void GodotFileRunner::start() {
stop = false;
fileThread = std::thread(&GodotFileRunner::run, this);
}
void GodotFileRunner::finish() {
stop = true;
// we need to notify the loop one last time, otherwise it will stay stuck in the wait method.
read_cv.notify_one();
fileThread.join();
}
FMOD_RESULT F_CALLBACK godotFileOpen(const char* name, unsigned int* filesize, void** handle, void* userdata) {
godot::Ref<godot::FileAccess> access = godot::FileAccess::open(name, godot::FileAccess::ModeFlags::READ);
if (access->get_error() == godot::Error::OK) {
*filesize = access->get_length();
GodotFileHandle* fileHandle {new GodotFileHandle {access}};
*handle = reinterpret_cast<void*>(fileHandle);
return FMOD_RESULT::FMOD_OK;
}
return FMOD_RESULT::FMOD_ERR_FILE_BAD;
}
FMOD_RESULT F_CALLBACK godotFileClose(void* handle, void* userdata) {
godot::Ref<godot::FileAccess> file {reinterpret_cast<GodotFileHandle*>(handle)->file};
delete reinterpret_cast<GodotFileHandle*>(handle);
return FMOD_RESULT::FMOD_OK;
}
FMOD_RESULT F_CALLBACK godotSyncRead(FMOD_ASYNCREADINFO* info, void* userdata) {
GodotFileRunner* runner {GodotFileRunner::get_singleton()};
int priority {info->priority};
GodotFileRunner::ReadPriority priorityRank;
if (priority == 100) {
priorityRank = GodotFileRunner::ReadPriority::HIGH;
} else {
priorityRank = GodotFileRunner::ReadPriority::NORMAL;
}
runner->queueReadRequest(info, priorityRank);
return FMOD_RESULT::FMOD_OK;
}
FMOD_RESULT F_CALLBACK godotSyncCancel(FMOD_ASYNCREADINFO* info, void* userdata) {
GodotFileRunner* runner {GodotFileRunner::get_singleton()};
runner->cancelReadRequest(info);
return FMOD_RESULT::FMOD_OK;
}
}// namespace Callbacks