music_player_worker.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. #include "music_player_worker.h"
  2. #include <furi_hal.h>
  3. #include <furi.h>
  4. #include <storage/storage.h>
  5. #include <lib/flipper_format/flipper_format.h>
  6. #include <m-array.h>
  7. #define TAG "MusicPlayerWorker"
  8. #define MUSIC_PLAYER_FILETYPE "Flipper Music Format"
  9. #define MUSIC_PLAYER_VERSION 0
  10. #define SEMITONE_PAUSE 0xFF
  11. #define NOTE_C4 261.63f
  12. #define NOTE_C4_SEMITONE (4.0f * 12.0f)
  13. #define TWO_POW_TWELTH_ROOT 1.059463094359f
  14. typedef struct {
  15. uint8_t semitone;
  16. uint8_t duration;
  17. uint8_t dots;
  18. } NoteBlock;
  19. ARRAY_DEF(NoteBlockArray, NoteBlock, M_POD_OPLIST);
  20. struct MusicPlayerWorker {
  21. FuriThread* thread;
  22. bool should_work;
  23. MusicPlayerWorkerCallback callback;
  24. void* callback_context;
  25. float volume;
  26. uint32_t bpm;
  27. uint32_t duration;
  28. uint32_t octave;
  29. NoteBlockArray_t notes;
  30. };
  31. static int32_t music_player_worker_thread_callback(void* context) {
  32. furi_assert(context);
  33. MusicPlayerWorker* instance = context;
  34. NoteBlockArray_it_t it;
  35. NoteBlockArray_it(it, instance->notes);
  36. while(instance->should_work) {
  37. if(NoteBlockArray_end_p(it)) {
  38. NoteBlockArray_it(it, instance->notes);
  39. osDelay(10);
  40. } else {
  41. NoteBlock* note_block = NoteBlockArray_ref(it);
  42. float note_from_a4 = (float)note_block->semitone - NOTE_C4_SEMITONE;
  43. float frequency = NOTE_C4 * powf(TWO_POW_TWELTH_ROOT, note_from_a4);
  44. float duration =
  45. 60.0 * osKernelGetTickFreq() * 4 / instance->bpm / note_block->duration;
  46. while(note_block->dots > 0) {
  47. duration += duration / 2;
  48. note_block->dots--;
  49. }
  50. uint32_t next_tick = furi_hal_get_tick() + duration;
  51. float volume = instance->volume;
  52. if(instance->callback) {
  53. instance->callback(
  54. note_block->semitone,
  55. note_block->dots,
  56. note_block->duration,
  57. 0.0,
  58. instance->callback_context);
  59. }
  60. furi_hal_speaker_stop();
  61. furi_hal_speaker_start(frequency, volume);
  62. while(instance->should_work && furi_hal_get_tick() < next_tick) {
  63. volume *= 0.9945679;
  64. furi_hal_speaker_set_volume(volume);
  65. furi_hal_delay_ms(2);
  66. }
  67. NoteBlockArray_next(it);
  68. }
  69. }
  70. furi_hal_speaker_stop();
  71. return 0;
  72. }
  73. MusicPlayerWorker* music_player_worker_alloc() {
  74. MusicPlayerWorker* instance = malloc(sizeof(MusicPlayerWorker));
  75. NoteBlockArray_init(instance->notes);
  76. instance->thread = furi_thread_alloc();
  77. furi_thread_set_name(instance->thread, "MusicPlayerWorker");
  78. furi_thread_set_stack_size(instance->thread, 1024);
  79. furi_thread_set_context(instance->thread, instance);
  80. furi_thread_set_callback(instance->thread, music_player_worker_thread_callback);
  81. return instance;
  82. }
  83. void music_player_worker_free(MusicPlayerWorker* instance) {
  84. furi_assert(instance);
  85. furi_thread_free(instance->thread);
  86. NoteBlockArray_clear(instance->notes);
  87. free(instance);
  88. }
  89. static bool is_digit(const char c) {
  90. return isdigit(c) != 0;
  91. }
  92. static bool is_letter(const char c) {
  93. return islower(c) != 0 || isupper(c) != 0;
  94. }
  95. static bool is_space(const char c) {
  96. return c == ' ' || c == '\t';
  97. }
  98. static size_t extract_number(const char* string, uint32_t* number) {
  99. size_t ret = 0;
  100. while(is_digit(*string)) {
  101. *number *= 10;
  102. *number += (*string - '0');
  103. string++;
  104. ret++;
  105. }
  106. return ret;
  107. }
  108. static size_t extract_dots(const char* string, uint32_t* number) {
  109. size_t ret = 0;
  110. while(*string == '.') {
  111. *number += 1;
  112. string++;
  113. ret++;
  114. }
  115. return ret;
  116. }
  117. static size_t extract_char(const char* string, char* symbol) {
  118. if(is_letter(*string)) {
  119. *symbol = *string;
  120. return 1;
  121. } else {
  122. return 0;
  123. }
  124. }
  125. static size_t extract_sharp(const char* string, char* symbol) {
  126. if(*string == '#' || *string == '_') {
  127. *symbol = '#';
  128. return 1;
  129. } else {
  130. return 0;
  131. }
  132. }
  133. static size_t skip_till(const char* string, const char symbol) {
  134. size_t ret = 0;
  135. while(*string != '\0' && *string != symbol) {
  136. string++;
  137. ret++;
  138. }
  139. if(*string != symbol) {
  140. ret = 0;
  141. }
  142. return ret;
  143. }
  144. static bool music_player_worker_add_note(
  145. MusicPlayerWorker* instance,
  146. uint8_t semitone,
  147. uint8_t duration,
  148. uint8_t dots) {
  149. NoteBlock note_block;
  150. note_block.semitone = semitone;
  151. note_block.duration = duration;
  152. note_block.dots = dots;
  153. NoteBlockArray_push_back(instance->notes, note_block);
  154. return true;
  155. }
  156. static int8_t note_to_semitone(const char note) {
  157. switch(note) {
  158. case 'C':
  159. return 0;
  160. // C#
  161. case 'D':
  162. return 2;
  163. // D#
  164. case 'E':
  165. return 4;
  166. case 'F':
  167. return 5;
  168. // F#
  169. case 'G':
  170. return 7;
  171. // G#
  172. case 'A':
  173. return 9;
  174. // A#
  175. case 'B':
  176. return 11;
  177. default:
  178. return 0;
  179. }
  180. }
  181. static bool music_player_worker_parse_notes(MusicPlayerWorker* instance, const char* string) {
  182. const char* cursor = string;
  183. bool result = true;
  184. while(*cursor != '\0') {
  185. if(!is_space(*cursor)) {
  186. uint32_t duration = 0;
  187. char note_char = '\0';
  188. char sharp_char = '\0';
  189. uint32_t octave = 0;
  190. uint32_t dots = 0;
  191. // Parsing
  192. cursor += extract_number(cursor, &duration);
  193. cursor += extract_char(cursor, &note_char);
  194. cursor += extract_sharp(cursor, &sharp_char);
  195. cursor += extract_number(cursor, &octave);
  196. cursor += extract_dots(cursor, &dots);
  197. // Post processing
  198. note_char = toupper(note_char);
  199. if(!duration) {
  200. duration = instance->duration;
  201. }
  202. if(!octave) {
  203. octave = instance->octave;
  204. }
  205. // Validation
  206. bool is_valid = true;
  207. is_valid &= (duration >= 1 && duration <= 128);
  208. is_valid &= ((note_char >= 'A' && note_char <= 'G') || note_char == 'P');
  209. is_valid &= (sharp_char == '#' || sharp_char == '\0');
  210. is_valid &= (octave >= 0 && octave <= 16);
  211. is_valid &= (dots >= 0 && dots <= 16);
  212. if(!is_valid) {
  213. FURI_LOG_E(
  214. TAG,
  215. "Invalid note: %u%c%c%u.%u",
  216. duration,
  217. note_char == '\0' ? '_' : note_char,
  218. sharp_char == '\0' ? '_' : sharp_char,
  219. octave,
  220. dots);
  221. result = false;
  222. break;
  223. }
  224. // Note to semitones
  225. uint8_t semitone = 0;
  226. if(note_char == 'P') {
  227. semitone = SEMITONE_PAUSE;
  228. } else {
  229. semitone += octave * 12;
  230. semitone += note_to_semitone(note_char);
  231. semitone += sharp_char == '#' ? 1 : 0;
  232. }
  233. if(music_player_worker_add_note(instance, semitone, duration, dots)) {
  234. FURI_LOG_D(
  235. TAG,
  236. "Added note: %c%c%u.%u = %u %u",
  237. note_char == '\0' ? '_' : note_char,
  238. sharp_char == '\0' ? '_' : sharp_char,
  239. octave,
  240. dots,
  241. semitone,
  242. duration);
  243. } else {
  244. FURI_LOG_E(
  245. TAG,
  246. "Invalid note: %c%c%u.%u = %u %u",
  247. note_char == '\0' ? '_' : note_char,
  248. sharp_char == '\0' ? '_' : sharp_char,
  249. octave,
  250. dots,
  251. semitone,
  252. duration);
  253. }
  254. cursor += skip_till(cursor, ',');
  255. }
  256. if(*cursor != '\0') cursor++;
  257. }
  258. return result;
  259. }
  260. bool music_player_worker_load(MusicPlayerWorker* instance, const char* file_path) {
  261. furi_assert(instance);
  262. furi_assert(file_path);
  263. bool ret = false;
  264. if(strcasestr(file_path, ".fmf")) {
  265. ret = music_player_worker_load_fmf_from_file(instance, file_path);
  266. } else {
  267. ret = music_player_worker_load_rtttl_from_file(instance, file_path);
  268. }
  269. return ret;
  270. }
  271. bool music_player_worker_load_fmf_from_file(MusicPlayerWorker* instance, const char* file_path) {
  272. furi_assert(instance);
  273. furi_assert(file_path);
  274. bool result = false;
  275. string_t temp_str;
  276. string_init(temp_str);
  277. Storage* storage = furi_record_open("storage");
  278. FlipperFormat* file = flipper_format_file_alloc(storage);
  279. do {
  280. if(!flipper_format_file_open_existing(file, file_path)) break;
  281. uint32_t version = 0;
  282. if(!flipper_format_read_header(file, temp_str, &version)) break;
  283. if(string_cmp_str(temp_str, MUSIC_PLAYER_FILETYPE) || (version != MUSIC_PLAYER_VERSION)) {
  284. FURI_LOG_E(TAG, "Incorrect file format or version");
  285. break;
  286. }
  287. if(!flipper_format_read_uint32(file, "BPM", &instance->bpm, 1)) {
  288. FURI_LOG_E(TAG, "BPM is missing");
  289. break;
  290. }
  291. if(!flipper_format_read_uint32(file, "Duration", &instance->duration, 1)) {
  292. FURI_LOG_E(TAG, "Duration is missing");
  293. break;
  294. }
  295. if(!flipper_format_read_uint32(file, "Octave", &instance->octave, 1)) {
  296. FURI_LOG_E(TAG, "Octave is missing");
  297. break;
  298. }
  299. if(!flipper_format_read_string(file, "Notes", temp_str)) {
  300. FURI_LOG_E(TAG, "Notes is missing");
  301. break;
  302. }
  303. if(!music_player_worker_parse_notes(instance, string_get_cstr(temp_str))) {
  304. break;
  305. }
  306. result = true;
  307. } while(false);
  308. furi_record_close("storage");
  309. flipper_format_free(file);
  310. string_clear(temp_str);
  311. return result;
  312. }
  313. bool music_player_worker_load_rtttl_from_file(MusicPlayerWorker* instance, const char* file_path) {
  314. furi_assert(instance);
  315. furi_assert(file_path);
  316. bool result = false;
  317. string_t content;
  318. string_init(content);
  319. Storage* storage = furi_record_open("storage");
  320. File* file = storage_file_alloc(storage);
  321. do {
  322. if(!storage_file_open(file, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) {
  323. FURI_LOG_E(TAG, "Unable to open file");
  324. break;
  325. };
  326. uint16_t ret = 0;
  327. do {
  328. uint8_t buffer[65] = {0};
  329. ret = storage_file_read(file, buffer, sizeof(buffer) - 1);
  330. for(size_t i = 0; i < ret; i++) {
  331. string_push_back(content, buffer[i]);
  332. }
  333. } while(ret > 0);
  334. string_strim(content);
  335. if(!string_size(content)) {
  336. FURI_LOG_E(TAG, "Empty file");
  337. break;
  338. }
  339. if(!music_player_worker_load_rtttl_from_string(instance, string_get_cstr(content))) {
  340. FURI_LOG_E(TAG, "Invalid file content");
  341. break;
  342. }
  343. result = true;
  344. } while(0);
  345. storage_file_free(file);
  346. furi_record_close("storage");
  347. string_clear(content);
  348. return result;
  349. }
  350. bool music_player_worker_load_rtttl_from_string(MusicPlayerWorker* instance, const char* string) {
  351. furi_assert(instance);
  352. const char* cursor = string;
  353. // Skip name
  354. cursor += skip_till(cursor, ':');
  355. if(*cursor != ':') {
  356. return false;
  357. }
  358. // Duration
  359. cursor += skip_till(cursor, '=');
  360. if(*cursor != '=') {
  361. return false;
  362. }
  363. cursor++;
  364. cursor += extract_number(cursor, &instance->duration);
  365. // Octave
  366. cursor += skip_till(cursor, '=');
  367. if(*cursor != '=') {
  368. return false;
  369. }
  370. cursor++;
  371. cursor += extract_number(cursor, &instance->octave);
  372. // BPM
  373. cursor += skip_till(cursor, '=');
  374. if(*cursor != '=') {
  375. return false;
  376. }
  377. cursor++;
  378. cursor += extract_number(cursor, &instance->bpm);
  379. // Notes
  380. cursor += skip_till(cursor, ':');
  381. if(*cursor != ':') {
  382. return false;
  383. }
  384. cursor++;
  385. if(!music_player_worker_parse_notes(instance, cursor)) {
  386. return false;
  387. }
  388. return true;
  389. }
  390. void music_player_worker_set_callback(
  391. MusicPlayerWorker* instance,
  392. MusicPlayerWorkerCallback callback,
  393. void* context) {
  394. furi_assert(instance);
  395. instance->callback = callback;
  396. instance->callback_context = context;
  397. }
  398. void music_player_worker_set_volume(MusicPlayerWorker* instance, float volume) {
  399. furi_assert(instance);
  400. instance->volume = volume;
  401. }
  402. void music_player_worker_start(MusicPlayerWorker* instance) {
  403. furi_assert(instance);
  404. furi_assert(instance->should_work == false);
  405. instance->should_work = true;
  406. furi_thread_start(instance->thread);
  407. }
  408. void music_player_worker_stop(MusicPlayerWorker* instance) {
  409. furi_assert(instance);
  410. furi_assert(instance->should_work == true);
  411. instance->should_work = false;
  412. furi_thread_join(instance->thread);
  413. }