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.

175 lines
4.6KB

  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. printf("rewind('%s')\n", msg);
  66. rtosc_arg_t arg = rtosc_argument(msg,1);
  67. rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
  68. rtosc_argument_string(msg)+2,
  69. &arg);
  70. cb(tmp);
  71. }
  72. void UndoHistoryImpl::replay(const char *msg)
  73. {
  74. printf("replay...'%s'\n", msg);
  75. rtosc_arg_t arg = rtosc_argument(msg,2);
  76. printf("replay address: '%s'\n", rtosc_argument(msg, 0).s);
  77. int len = rtosc_amessage(tmp, 256, rtosc_argument(msg,0).s,
  78. rtosc_argument_string(msg)+2,
  79. &arg);
  80. if(len)
  81. cb(tmp);
  82. }
  83. const char *getUndoAddress(const char *msg)
  84. {
  85. return rtosc_argument(msg,0).s;
  86. }
  87. bool UndoHistoryImpl::mergeEvent(time_t now, const char *msg, char *buf, size_t N)
  88. {
  89. if(history_pos == 0)
  90. return false;
  91. for(int i=history_pos-1; i>=0; --i) {
  92. if(difftime(now, history[i].first) > 2)
  93. break;
  94. if(!strcmp(getUndoAddress(msg),
  95. getUndoAddress(history[i].second)))
  96. {
  97. //We can splice events together, merging them into one event
  98. rtosc_arg_t args[3];
  99. args[0] = rtosc_argument(msg, 0);
  100. args[1] = rtosc_argument(history[i].second,1);
  101. args[2] = rtosc_argument(msg, 2);
  102. rtosc_amessage(buf, N, msg, rtosc_argument_string(msg), args);
  103. delete [] history[i].second;
  104. history[i].second = buf;
  105. history[i].first = now;
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. void UndoHistory::seekHistory(int distance)
  112. {
  113. //TODO print out the events that would need to take place to get to the
  114. //final destination
  115. //TODO limit the distance to be to applicable sizes
  116. //ie ones that do not exceed the known history/future
  117. long dest = impl->history_pos + distance;
  118. if(dest < 0)
  119. distance -= dest;
  120. if(dest > (long) impl->history.size())
  121. distance = impl->history.size() - impl->history_pos;
  122. if(!distance)
  123. return;
  124. printf("distance == '%d'\n", distance);
  125. printf("history_pos == '%ld'\n", impl->history_pos);
  126. //TODO account for traveling back in time
  127. if(distance<0)
  128. while(distance++)
  129. impl->rewind(impl->history[--impl->history_pos].second);
  130. else
  131. while(distance--)
  132. impl->replay(impl->history[impl->history_pos++].second);
  133. }
  134. unsigned UndoHistory::getPos(void) const
  135. {
  136. return impl->history_pos;
  137. }
  138. const char *UndoHistory::getHistory(int i) const
  139. {
  140. return impl->history[i].second;
  141. }
  142. size_t UndoHistory::size() const
  143. {
  144. return impl->history.size();
  145. }
  146. void UndoHistory::setCallback(std::function<void(const char*)> cb)
  147. {
  148. impl->cb = cb;
  149. }
  150. };