A simple and dumb lv2 plugin to test host state support
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.

382 lines
11KB

  1. /*
  2. Copyright (C) 2020 falkTX
  3. This work is free. You can redistribute it and/or modify it under the
  4. terms of the Do What The Fuck You Want To Public License, Version 2,
  5. as published by Sam Hocevar. See the COPYING file for more details.
  6. */
  7. #define _GNU_SOURCE
  8. #include <lv2.h>
  9. #include <lv2/atom/atom.h>
  10. #include <lv2/log/logger.h>
  11. #include <lv2/state/state.h>
  12. #include <stdlib.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include <time.h>
  16. #include <unistd.h>
  17. #define PLUGIN_URI "https://git.kx.studio/falkTX/lv2-state-test"
  18. #ifdef DO_NOT_HAVE_LV2_STATE_FREE_PATH
  19. // forwards compatibility with old lv2 headers
  20. #define LV2_STATE__freePath LV2_STATE_PREFIX "freePath"
  21. typedef void* LV2_State_Free_Path_Handle;
  22. typedef struct {
  23. LV2_State_Free_Path_Handle handle;
  24. void (*free_path)(LV2_State_Free_Path_Handle handle, char* path);
  25. } LV2_State_Free_Path;
  26. #endif
  27. typedef struct {
  28. bool activated, deactivated, saved, restored;
  29. LV2_Log_Logger logger;
  30. const LV2_Atom_Sequence* seqIn;
  31. LV2_Atom_Sequence* seqOut;
  32. const LV2_State_Free_Path* freePath;
  33. const LV2_State_Make_Path* makePath;
  34. const LV2_State_Map_Path* mapPath;
  35. char* randomNumStr;
  36. LV2_URID numberURID, atomStringURID;
  37. } StateTest;
  38. static void lv2_free_path(const LV2_State_Free_Path* freePath, char* path)
  39. {
  40. if (freePath != NULL)
  41. freePath->free_path(freePath->handle, path);
  42. else
  43. free(path);
  44. }
  45. static void create_paths(const LV2_State_Make_Path* const makePath,
  46. const LV2_State_Free_Path* const freePath,
  47. LV2_Log_Logger* const logger,
  48. const char* const randomNumStr,
  49. const bool instantiate)
  50. {
  51. char* path;
  52. int status;
  53. FILE* fd;
  54. // test getting initial dir
  55. path = makePath->path(makePath->handle, ".");
  56. if (path != NULL)
  57. {
  58. status = access(path, F_OK);
  59. lv2_log_note(logger, "state-test %s, host has makePath and initial path is: '%s' (access = %i)\n",
  60. instantiate ? "init" : "save", path, status);
  61. lv2_free_path(freePath, path);
  62. }
  63. else
  64. {
  65. lv2_log_warning(logger, "state-test %s, host has makePath but failed to get initial path\n",
  66. instantiate ? "init" : "save");
  67. }
  68. // test creating single file
  69. path = makePath->path(makePath->handle, "single-file.txt");
  70. if (path != NULL)
  71. {
  72. status = access(path, F_OK);
  73. lv2_log_note(logger, "state-test %s, request for single-file.txt path resulted in '%s' (access = %i)\n",
  74. instantiate ? "init" : "save",
  75. path, status);
  76. fd = fopen(path, "w");
  77. if (fd != NULL)
  78. {
  79. fputs(randomNumStr, fd);
  80. fputs("\n", fd);
  81. fclose(fd);
  82. lv2_log_note(logger, "state-test %s, wrote '%s' to single-file.txt successfully\n",
  83. instantiate ? "init" : "save", randomNumStr);
  84. }
  85. else
  86. {
  87. lv2_log_error(logger, "state-test %s, failed to open single-file.txt for writing\n",
  88. instantiate ? "init" : "save");
  89. }
  90. lv2_free_path(freePath, path);
  91. }
  92. else
  93. {
  94. lv2_log_error(logger, "state-test %s, request for single-file.txt path failed\n",
  95. instantiate ? "init" : "save");
  96. }
  97. // test creating subdirs
  98. path = makePath->path(makePath->handle, "subdir1/subdir2/subdir3/subdir-file.txt");
  99. if (path != NULL)
  100. {
  101. status = access(path, F_OK);
  102. lv2_log_note(logger, "state-test %s, request for subdir-file.txt path resulted in '%s' (access = %i)\n",
  103. instantiate ? "init" : "save",
  104. path, status);
  105. fd = fopen(path, "w");
  106. if (fd != NULL)
  107. {
  108. fputs(randomNumStr, fd);
  109. fputs("\n", fd);
  110. fclose(fd);
  111. lv2_log_note(logger, "state-test %s, wrote '%s' to subdir-file.txt successfully\n",
  112. instantiate ? "init" : "save", randomNumStr);
  113. }
  114. else
  115. {
  116. lv2_log_error(logger, "state-test %s, failed to open subdir-file.txt for writing\n",
  117. instantiate ? "init" : "save");
  118. }
  119. lv2_free_path(freePath, path);
  120. }
  121. else
  122. {
  123. lv2_log_error(logger, "state-test %s, request for subdir-file.txt path failed\n",
  124. instantiate ? "init" : "save");
  125. }
  126. }
  127. static LV2_Handle instantiate(const LV2_Descriptor* const descriptor,
  128. const double sample_rate,
  129. const char* const bundle_path,
  130. const LV2_Feature* const* const features)
  131. {
  132. const LV2_State_Free_Path* freePath = NULL;
  133. const LV2_State_Make_Path* makePath = NULL;
  134. const LV2_State_Map_Path* mapPath = NULL;
  135. LV2_Log_Log* log = NULL;
  136. LV2_URID_Map* uridMap = NULL;
  137. for (int i=0; features[i] != NULL; ++i)
  138. {
  139. /**/ if (strcmp(features[i]->URI, LV2_STATE__freePath) == 0)
  140. freePath = features[i]->data;
  141. else if (strcmp(features[i]->URI, LV2_STATE__makePath) == 0)
  142. makePath = features[i]->data;
  143. else if (strcmp(features[i]->URI, LV2_STATE__mapPath) == 0)
  144. mapPath = features[i]->data;
  145. else if (strcmp(features[i]->URI, LV2_LOG__log) == 0)
  146. log = features[i]->data;
  147. else if (strcmp(features[i]->URI, LV2_URID__map) == 0)
  148. uridMap = features[i]->data;
  149. }
  150. if (uridMap == NULL)
  151. {
  152. fprintf(stderr, "uridMap feature missing\n");
  153. return NULL;
  154. }
  155. StateTest* instance = calloc(1, sizeof(StateTest));
  156. instance->freePath = freePath;
  157. instance->makePath = makePath;
  158. instance->mapPath = mapPath;
  159. lv2_log_logger_init(&instance->logger, uridMap, log);
  160. srand(time(NULL));
  161. srand((int)(uintptr_t)instance);
  162. asprintf(&instance->randomNumStr, "%d", rand());
  163. instance->atomStringURID = uridMap->map(uridMap->handle, LV2_ATOM__String);
  164. instance->numberURID = uridMap->map(uridMap->handle, PLUGIN_URI "#number");
  165. if (makePath != NULL)
  166. create_paths(makePath, freePath, &instance->logger, instance->randomNumStr, true);
  167. else
  168. lv2_log_note(&instance->logger, "state-test init, host does not have makePath\n");
  169. return instance;
  170. // unused
  171. (void)descriptor;
  172. (void)sample_rate;
  173. (void)bundle_path;
  174. }
  175. #define instancePtr ((StateTest*)instance)
  176. static void connect_port(LV2_Handle instance, uint32_t port, void* data_location)
  177. {
  178. switch (port)
  179. {
  180. case 0:
  181. instancePtr->seqIn = data_location;
  182. break;
  183. case 1:
  184. instancePtr->seqOut = data_location;
  185. break;
  186. }
  187. }
  188. static void activate(LV2_Handle instance)
  189. {
  190. instancePtr->activated = true;
  191. }
  192. static void run(LV2_Handle instance, uint32_t sample_count)
  193. {
  194. if (instancePtr->deactivated)
  195. {
  196. instancePtr->deactivated = false;
  197. lv2_log_note(&instancePtr->logger, "plugin was deactivated\n");
  198. }
  199. if (instancePtr->activated)
  200. {
  201. instancePtr->activated = false;
  202. lv2_log_note(&instancePtr->logger, "plugin was activated\n");
  203. }
  204. if (instancePtr->restored)
  205. {
  206. instancePtr->restored = false;
  207. lv2_log_note(&instancePtr->logger, "plugin state was restored\n");
  208. }
  209. if (instancePtr->saved)
  210. {
  211. instancePtr->saved = false;
  212. lv2_log_note(&instancePtr->logger, "plugin state was saved\n");
  213. }
  214. return;
  215. // unused
  216. (void)sample_count;
  217. }
  218. static void deactivate(LV2_Handle instance)
  219. {
  220. instancePtr->deactivated = true;
  221. }
  222. static void cleanup(LV2_Handle instance)
  223. {
  224. free(instance);
  225. }
  226. static LV2_State_Status save(LV2_Handle instance,
  227. LV2_State_Store_Function store,
  228. LV2_State_Handle handle,
  229. uint32_t flags,
  230. const LV2_Feature* const* features)
  231. {
  232. const LV2_State_Free_Path* freePath = NULL;
  233. const LV2_State_Make_Path* makePath = NULL;
  234. for (int i=0; features[i] != NULL; ++i)
  235. {
  236. /**/ if (freePath == NULL && strcmp(features[i]->URI, LV2_STATE__freePath) == 0)
  237. freePath = features[i]->data;
  238. else if (makePath == NULL && strcmp(features[i]->URI, LV2_STATE__makePath) == 0)
  239. makePath = features[i]->data;
  240. }
  241. create_paths(makePath, freePath, &instancePtr->logger, instancePtr->randomNumStr, false);
  242. store(handle,
  243. instancePtr->numberURID,
  244. instancePtr->randomNumStr,
  245. strlen(instancePtr->randomNumStr)+1,
  246. instancePtr->atomStringURID,
  247. LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE);
  248. instancePtr->saved = true;
  249. return LV2_STATE_SUCCESS;
  250. // TODO
  251. (void)flags;
  252. }
  253. static LV2_State_Status restore(LV2_Handle instance,
  254. LV2_State_Retrieve_Function retrieve,
  255. LV2_State_Handle handle,
  256. uint32_t flags,
  257. const LV2_Feature* const* features)
  258. {
  259. const LV2_State_Free_Path* freePath = NULL;
  260. const LV2_State_Make_Path* makePath = NULL;
  261. for (int i=0; features[i] != NULL; ++i)
  262. {
  263. /**/ if (freePath == NULL && strcmp(features[i]->URI, LV2_STATE__freePath) == 0)
  264. freePath = features[i]->data;
  265. else if (makePath == NULL && strcmp(features[i]->URI, LV2_STATE__makePath) == 0)
  266. makePath = features[i]->data;
  267. }
  268. if (makePath == NULL)
  269. return LV2_STATE_ERR_NO_FEATURE;
  270. char* const path = makePath->path(makePath->handle, ".");
  271. if (path != NULL)
  272. {
  273. lv2_log_note(&instancePtr->logger, "state-test restore ok, path is: '%s'\n", path);
  274. if (freePath != NULL)
  275. freePath->free_path(freePath->handle, path);
  276. else
  277. free(path);
  278. }
  279. else
  280. {
  281. lv2_log_note(&instancePtr->logger, "state-test restore ok, but failed to get initial path\n");
  282. }
  283. // TODO restore previously saved randomNumStr
  284. instancePtr->restored = true;
  285. return LV2_STATE_SUCCESS;
  286. // TODO
  287. (void)retrieve;
  288. (void)handle;
  289. (void)flags;
  290. }
  291. static const void* extension_data(const char* const uri)
  292. {
  293. static const LV2_State_Interface iface = {
  294. .save = save,
  295. .restore = restore
  296. };
  297. if (strcmp(uri, LV2_STATE__interface) == 0)
  298. return &iface;
  299. return NULL;
  300. }
  301. LV2_SYMBOL_EXPORT
  302. const LV2_Descriptor* lv2_descriptor(const uint32_t index)
  303. {
  304. static const LV2_Descriptor desc = {
  305. .URI = PLUGIN_URI,
  306. .instantiate = instantiate,
  307. .connect_port = connect_port,
  308. .activate = activate,
  309. .run = run,
  310. .deactivate = deactivate,
  311. .cleanup = cleanup,
  312. .extension_data = extension_data
  313. };
  314. return index == 0 ? &desc : NULL;
  315. }