Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.4KB

  1. #include <deque>
  2. #include <cstring>
  3. #include <cstdio>
  4. #include <cassert>
  5. #include <ctime>
  6. #include <rtosc/rtosc.h>
  7. #include <rtosc/undo-history.h>
  8. using std::pair;
  9. using std::make_pair;
  10. namespace rtosc {
  11. class UndoHistoryImpl
  12. {
  13. public:
  14. UndoHistoryImpl(void)
  15. :max_history_size(20)
  16. {}
  17. std::deque<pair<time_t, const char *>> history;
  18. long history_pos;
  19. unsigned max_history_size;//XXX Expose this via a public API
  20. std::function<void(const char*)> cb;
  21. void rewind(const char *msg);
  22. void replay(const char *msg);
  23. bool mergeEvent(time_t t, const char *msg, char *buf, size_t N);
  24. };
  25. UndoHistory::UndoHistory(void)
  26. {
  27. impl = new UndoHistoryImpl;
  28. impl->history_pos = 0;
  29. }
  30. void UndoHistory::recordEvent(const char *msg)
  31. {
  32. //TODO Properly account for when you have traveled back in time.
  33. //while this could result in another branch of history, the simple method
  34. //would be to kill off any future redos when new history is recorded
  35. if(impl->history.size() != (unsigned) impl->history_pos) {
  36. impl->history.resize(impl->history_pos);
  37. }
  38. size_t len = rtosc_message_length(msg, -1);
  39. char *data = new char[len];
  40. time_t now = time(NULL);
  41. //printf("now = '%ld'\n", now);
  42. if(!impl->mergeEvent(now, msg, data, len)) {
  43. memcpy(data, msg, len);
  44. impl->history.push_back(make_pair(now, data));
  45. impl->history_pos++;
  46. if(impl->history.size() > impl->max_history_size)
  47. {
  48. delete[] impl->history[0].second;
  49. impl->history.pop_front();
  50. impl->history_pos--;
  51. }
  52. }
  53. }
  54. void UndoHistory::showHistory(void) const
  55. {
  56. int i = 0;
  57. for(auto s : impl->history)
  58. printf("#%d type: %s dest: %s arguments: %s\n", i++,
  59. s.second, rtosc_argument(s.second, 0).s, rtosc_argument_string(s.second));
  60. }
  61. static char tmp[256];
  62. void UndoHistoryImpl::rewind(const char *msg)
  63. {
  64. memset(tmp, 0, sizeof(tmp));
  65. rtosc_arg_t arg = rtosc_argument(msg,1);
  66. rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
  67. rtosc_argument_string(msg)+2,
  68. &arg);
  69. cb(tmp);
  70. }
  71. void UndoHistoryImpl::replay(const char *msg)
  72. {
  73. rtosc_arg_t arg = rtosc_argument(msg,2);
  74. int len = rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
  75. rtosc_argument_string(msg)+2,
  76. &arg);
  77. if(len)
  78. cb(tmp);
  79. }
  80. const char *getUndoAddress(const char *msg)
  81. {
  82. return rtosc_argument(msg,0).s;
  83. }
  84. bool UndoHistoryImpl::mergeEvent(time_t now, const char *msg, char *buf, size_t N)
  85. {
  86. if(history_pos == 0)
  87. return false;
  88. for(int i=history_pos-1; i>=0; --i) {
  89. if(difftime(now, history[i].first) > 2)
  90. break;
  91. if(!strcmp(getUndoAddress(msg),
  92. getUndoAddress(history[i].second)))
  93. {
  94. //We can splice events together, merging them into one event
  95. rtosc_arg_t args[3];
  96. args[0] = rtosc_argument(msg, 0);
  97. args[1] = rtosc_argument(history[i].second,1);
  98. args[2] = rtosc_argument(msg, 2);
  99. rtosc_amessage(buf, N, msg, rtosc_argument_string(msg), args);
  100. delete [] history[i].second;
  101. history[i].second = buf;
  102. history[i].first = now;
  103. return true;
  104. }
  105. }
  106. return false;
  107. }
  108. void UndoHistory::seekHistory(int distance)
  109. {
  110. //TODO print out the events that would need to take place to get to the
  111. //final destination
  112. //TODO limit the distance to be to applicable sizes
  113. //ie ones that do not exceed the known history/future
  114. long dest = impl->history_pos + distance;
  115. if(dest < 0)
  116. distance -= dest;
  117. if(dest > (long) impl->history.size())
  118. distance = impl->history.size() - impl->history_pos;
  119. if(!distance)
  120. return;
  121. //TODO account for traveling back in time
  122. if(distance<0)
  123. while(distance++)
  124. impl->rewind(impl->history[--impl->history_pos].second);
  125. else
  126. while(distance--)
  127. impl->replay(impl->history[impl->history_pos++].second);
  128. }
  129. unsigned UndoHistory::getPos(void) const
  130. {
  131. return impl->history_pos;
  132. }
  133. const char *UndoHistory::getHistory(int i) const
  134. {
  135. return impl->history[i].second;
  136. }
  137. size_t UndoHistory::size() const
  138. {
  139. return impl->history.size();
  140. }
  141. void UndoHistory::setCallback(std::function<void(const char*)> cb)
  142. {
  143. impl->cb = cb;
  144. }
  145. };