| @@ -1,6 +1,6 @@ | |||||
| #include <map> | #include <map> | ||||
| #include <utility> | #include <utility> | ||||
| #include <queue> | |||||
| #include <deque> | |||||
| #include <mutex> | #include <mutex> | ||||
| #include <midi.hpp> | #include <midi.hpp> | ||||
| @@ -288,30 +288,13 @@ std::vector<int> Input::getChannels() { | |||||
| static const size_t InputQueue_maxSize = 8192; | static const size_t InputQueue_maxSize = 8192; | ||||
| struct InputQueue_Compare { | |||||
| bool operator()(const Message& a, const Message& b) { | |||||
| return a.getFrame() > b.getFrame(); | |||||
| } | |||||
| }; | |||||
| struct InputQueue_Queue : std::priority_queue<Message, std::vector<Message>, InputQueue_Compare> { | |||||
| void reserve(size_t capacity) { | |||||
| c.reserve(capacity); | |||||
| } | |||||
| void clear() { | |||||
| // Messing with the protected container is dangerous, but completely clearing it should be fine. | |||||
| c.clear(); | |||||
| } | |||||
| }; | |||||
| struct InputQueue::Internal { | struct InputQueue::Internal { | ||||
| InputQueue_Queue queue; | |||||
| std::deque<Message> queue; | |||||
| std::mutex mutex; | std::mutex mutex; | ||||
| }; | }; | ||||
| InputQueue::InputQueue() { | InputQueue::InputQueue() { | ||||
| internal = new Internal; | internal = new Internal; | ||||
| internal->queue.reserve(InputQueue_maxSize); | |||||
| } | } | ||||
| InputQueue::~InputQueue() { | InputQueue::~InputQueue() { | ||||
| @@ -323,19 +306,28 @@ void InputQueue::onMessage(const Message& message) { | |||||
| // Reject MIDI message if queue is full | // Reject MIDI message if queue is full | ||||
| if (internal->queue.size() >= InputQueue_maxSize) | if (internal->queue.size() >= InputQueue_maxSize) | ||||
| return; | return; | ||||
| // Message timestamp must be monotonically increasing, otherwise clear the queue. | |||||
| if (!internal->queue.empty() && message.getFrame() < internal->queue.back().getFrame()) { | |||||
| internal->queue.clear(); | |||||
| } | |||||
| // Push to queue | // Push to queue | ||||
| internal->queue.push(message); | |||||
| internal->queue.push_back(message); | |||||
| } | } | ||||
| bool InputQueue::tryPop(Message* messageOut, int64_t maxFrame) { | bool InputQueue::tryPop(Message* messageOut, int64_t maxFrame) { | ||||
| // Check if queue is empty before locking, to avoid frequent unnecessary locking | |||||
| if (internal->queue.empty()) | if (internal->queue.empty()) | ||||
| return false; | return false; | ||||
| std::lock_guard<std::mutex> lock(internal->mutex); | std::lock_guard<std::mutex> lock(internal->mutex); | ||||
| const Message& msg = internal->queue.top(); | |||||
| if (msg.getFrame() <= maxFrame) { | |||||
| *messageOut = msg; | |||||
| internal->queue.pop(); | |||||
| if (internal->queue.empty()) | |||||
| return false; | |||||
| const Message& message = internal->queue.front(); | |||||
| if (message.getFrame() <= maxFrame) { | |||||
| *messageOut = message; | |||||
| internal->queue.pop_front(); | |||||
| return true; | return true; | ||||
| } | } | ||||