diff --git a/source/device.cpp b/source/device.cpp index 196f5b4..469fcb1 100644 --- a/source/device.cpp +++ b/source/device.cpp @@ -16,6 +16,9 @@ #include "wav.hpp" #include +#include +#include +#include #include #include #include @@ -43,6 +46,14 @@ static const unsigned int sampleRateInts[6] = { 48'000, 96'000 }; +static const unsigned int sampleRateBSizes[6] = { + 8000, + 16000, + 20000, + 32000, + 48000, + 96000, +}; static bool measureCodeTime = false; static bool drawSamples = false; @@ -56,11 +67,14 @@ static bool popupRequestDraw = false; static bool popupRequestLog = false; static std::mutex mutexDrawSamples; -static std::vector drawSamplesBuf; -static std::vector drawSamplesBuf2; +//static std::vector drawSamplesBuf; +//static std::vector drawSamplesBuf2; static std::ofstream logSamplesFile; static wav::clip wavOutput; +static std::deque drawSamplesQueue; +static unsigned int drawSamplesBufferSize = 4096; + static void measureCodeTask(stmdsp::device *device) { if (device == nullptr) @@ -77,28 +91,94 @@ static void drawSamplesTask(stmdsp::device *device) const bool doLogger = logResults && logSamplesFile.good(); - const auto bsize = m_device->get_buffer_size(); - const float srate = sampleRateInts[m_device->get_sample_rate()]; - const unsigned int delay = bsize / srate * 1000.f * 0.5f; + const double bufferSize = m_device->get_buffer_size(); + const double sampleRate = sampleRateInts[m_device->get_sample_rate()]; + double samplesToPush = sampleRate * 0.0007 * 2; - while (m_device->is_running()) { - { - std::scoped_lock lock (mutexDrawSamples); - drawSamplesBuf = m_device->continuous_read(); - if (drawSamplesInput && popupRequestDraw) - drawSamplesBuf2 = m_device->continuous_read_input(); + std::vector lastReadBuffer (bufferSize, 2048); + double lastReadIndexD = 0; + double desiredTime = bufferSize / sampleRate * 1.01; + + auto bufferTime = std::chrono::high_resolution_clock::now(); + while (m_device && m_device->is_running()) { + if (samplesToPush < bufferSize) { + if (lastReadIndexD >= bufferSize - 1) + lastReadIndexD -= bufferSize - 1; + + unsigned int lastReadIndex = std::floor(lastReadIndexD); + unsigned int end = lastReadIndexD + samplesToPush; + + { + std::scoped_lock lock (mutexDrawSamples); + for (; lastReadIndexD < end; lastReadIndexD += 1) { + if (lastReadIndex >= lastReadBuffer.size()) { + auto old = samplesToPush; + + auto now = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = now - bufferTime; + + lastReadBuffer = m_device->continuous_read(); + if (drawSamplesInput && popupRequestDraw) + drawSamplesBuf2 = m_device->continuous_read_input(); + if (lastReadBuffer.empty()) { + do { + std::this_thread::sleep_for(std::chrono::microseconds(2)); + lastReadBuffer = m_device->continuous_read(); + } while (lastReadBuffer.empty() && m_device->is_running()); + } + + bufferTime = now; + + if (double c = diff.count(); c > desiredTime + 0.001) { + // Too slow + samplesToPush = (samplesToPush * c / desiredTime) * 0.98; + if (samplesToPush > bufferSize) + samplesToPush = bufferSize; + } else if (c < desiredTime - 0.001) { + // Too fast + samplesToPush = (samplesToPush * c / desiredTime) * 0.98; + } + + lastReadIndex = 0; + } + + drawSamplesQueue.push_back(lastReadBuffer[lastReadIndex++]); + } + } + + std::this_thread::sleep_for(std::chrono::microseconds(700)); } + // Full blast + else { + lastReadBuffer = m_device->continuous_read(); + while (lastReadBuffer.empty() && m_device->is_running()) { + std::this_thread::sleep_for(std::chrono::microseconds(1)); + lastReadBuffer = m_device->continuous_read(); + } + + { + std::scoped_lock lock (mutexDrawSamples); + for (const auto& s : lastReadBuffer) + drawSamplesQueue.push_back(s); + } + + auto now = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = now - bufferTime; + bufferTime = now; + + double c = diff.count(); + if (c < desiredTime) { + samplesToPush = (samplesToPush * c / desiredTime) * 0.98; + } - if (doLogger) { - for (const auto& s : drawSamplesBuf) - logSamplesFile << s << '\n'; + std::this_thread::sleep_for(std::chrono::microseconds(10)); } - std::this_thread::sleep_for(std::chrono::milliseconds(delay)); + //if (doLogger) { + // for (const auto& s : drawSamplesBuf) + // logSamplesFile << s << '\n'; + //} } - - std::fill(drawSamplesBuf.begin(), drawSamplesBuf.end(), 2048); - std::fill(drawSamplesBuf2.begin(), drawSamplesBuf2.end(), 2048); } static void feedSigGenTask(stmdsp::device *device) @@ -264,34 +344,62 @@ void deviceRenderDraw() { if (popupRequestDraw) { ImGui::Begin("draw", &popupRequestDraw); - ImGui::Checkbox("Draw input", &drawSamplesInput); - { - std::scoped_lock lock (mutexDrawSamples); - auto drawList = ImGui::GetWindowDrawList(); - const ImVec2 p0 = ImGui::GetWindowPos(); - const auto size = ImGui::GetWindowSize(); - //ImVec2 p1 (p0.x + size.x, p0.y + size.y); - //ImU32 col_a = ImGui::GetColorU32(IM_COL32(0, 0, 0, 255)); - //ImU32 col_b = ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)); - //drawList->AddRectFilledMultiColor(p0, p1, col_a, col_b, col_b, col_a); - - const unsigned int didx = 1.f / (size.x / static_cast(drawSamplesBuf.size())); - ImVec2 pp = p0; - for (auto i = 0u; i < drawSamplesBuf.size(); i += didx) { - ImVec2 next (pp.x + 1, p0.y + (float)drawSamplesBuf[i] / 4095.f * size.y); - drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(128, 0, 0, 255))); - pp = next; - } + ImGui::Checkbox("Draw input", &drawSamplesInput); - if (drawSamplesInput) { - pp = p0; - for (auto i = 0u; i < drawSamplesBuf2.size(); i += didx) { - ImVec2 next (pp.x + 1, p0.y + (float)drawSamplesBuf2[i] / 4095.f * size.y); - drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(0, 0, 128, 255))); - pp = next; - } + static std::vector buffer; + static decltype(buffer.begin()) bufferCursor; + static long unsigned int drawChunkSize = 0; + static std::chrono::time_point timee; + + if (buffer.size() != drawSamplesBufferSize) { + buffer.resize(drawSamplesBufferSize); + bufferCursor = buffer.begin(); + } + + { + std::scoped_lock lock (mutexDrawSamples); + auto qsize = drawSamplesQueue.size(); + auto count = qsize;//std::min(qsize, drawChunkSize); + //if (count > 0) + // printf("draw: %9llu/%9lu\n", qsize, count); + for (auto i = count; i; --i) { + *bufferCursor = drawSamplesQueue.front(); + drawSamplesQueue.pop_front(); + if (++bufferCursor == buffer.end()) { + bufferCursor = buffer.begin(); + + //auto now = std::chrono::high_resolution_clock::now(); + //std::chrono::duration diff = now - timee; + //timee = now; + //printf("\rtime: %.9lf", diff); + //fflush(stdout); } } + } + + auto drawList = ImGui::GetWindowDrawList(); + ImVec2 p0 = ImGui::GetWindowPos(); + auto size = ImGui::GetWindowSize(); + p0.y += 65; + size.y -= 70; + drawList->AddRectFilled(p0, {p0.x + size.x, p0.y + size.y}, IM_COL32(0, 0, 0, 255)); + + const unsigned int didx = 1.f / (size.x / static_cast(buffer.size())); + ImVec2 pp = p0; + for (auto i = 0u; i < buffer.size(); i += didx) { + ImVec2 next (pp.x + 1, p0.y + size.y - (float)buffer[i] / 4095.f * size.y); + drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(255, 0, 0, 255))); + pp = next; + } + + if (drawSamplesInput) { + pp = p0; + for (auto i = 0u; i < drawSamplesBuf2.size(); i += didx) { + ImVec2 next (pp.x + 1, p0.y + size.y - (float)buffer[i] / 4095.f * size.y); + drawList->AddLine(pp, next, ImGui::GetColorU32(IM_COL32(0, 0, 255, 255))); + pp = next; + } + } ImGui::End(); } } @@ -304,21 +412,21 @@ void deviceRenderMenu() static const char *connectLabel = "Connect"; if (ImGui::MenuItem(connectLabel)) { + connectLabel = isConnected ? "Connect" : "Disconnect"; deviceConnect(); - connectLabel = isConnected ? "Disconnect" : "Connect"; } ImGui::Separator(); static const char *startLabel = "Start"; if (ImGui::MenuItem(startLabel, nullptr, false, isConnected)) { + startLabel = isRunning ? "Start" : "Stop"; deviceStart(); - startLabel = isRunning ? "Stop" : "Start"; } /** TODO test siggen formula TODO improve siggen audio streaming -TODO draw: smoothly chain captures +TODO draw: smoothly chain captures for 96kHz */ if (ImGui::MenuItem("Upload algorithm", nullptr, false, isConnected)) deviceAlgorithmUpload(); @@ -389,8 +497,14 @@ void deviceRenderToolbar() for (int i = 0; i < 6; ++i) { if (ImGui::Selectable(sampleRateList[i])) { sampleRatePreview = sampleRateList[i]; - if (m_device != nullptr && !m_device->is_running()) - m_device->set_sample_rate(i); + if (m_device != nullptr && !m_device->is_running()) { + do { + m_device->set_sample_rate(i); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } while (m_device->get_sample_rate() != i); + + drawSamplesBufferSize = sampleRateBSizes[i]; + } } } ImGui::EndCombo(); @@ -404,7 +518,9 @@ void deviceConnect() if (auto devices = scanner.scan(); devices.size() > 0) { m_device = new stmdsp::device(devices.front()); if (m_device->connected()) { - sampleRatePreview = sampleRateList[m_device->get_sample_rate()]; + auto sri = m_device->get_sample_rate(); + sampleRatePreview = sampleRateList[sri]; + drawSamplesBufferSize = sampleRateBSizes[sri]; log("Connected!"); } else { delete m_device; @@ -429,7 +545,11 @@ void deviceStart() } if (m_device->is_running()) { - m_device->continuous_stop(); + { + std::scoped_lock lock (mutexDrawSamples); + std::this_thread::sleep_for(std::chrono::microseconds(150)); + m_device->continuous_stop(); + } if (logResults) { logSamplesFile.close(); logResults = false; diff --git a/source/main.cpp b/source/main.cpp index ce1ea20..36e8c6e 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -19,7 +19,6 @@ #include -// Externs extern ImFont *fontSans; extern ImFont *fontMono; diff --git a/source/stmdsp/stmdsp.cpp b/source/stmdsp/stmdsp.cpp index b3fc8c3..bb417ca 100644 --- a/source/stmdsp/stmdsp.cpp +++ b/source/stmdsp/stmdsp.cpp @@ -27,7 +27,7 @@ namespace stmdsp } device::device(const std::string& file) : - m_serial(file, 1000000/*230400*/, serial::Timeout::simpleTimeout(50)) + m_serial(file, 8'000'000/*230400*/, serial::Timeout::simpleTimeout(50)) { if (m_serial.isOpen()) { m_serial.flush();