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.

188 lines
4.7KB

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