music_player_worker.c 13 KB

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