ID3v2Metadata.cpp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. #include "ID3v2Metadata.h"
  2. #include "metadata/MetadataKeys.h"
  3. #include "nswasabi/ReferenceCounted.h"
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. api_metadata *ID3v2Metadata::metadata_api=0;
  7. static inline bool TestFlag(int flags, int flag_to_check)
  8. {
  9. if (flags & flag_to_check)
  10. return true;
  11. return false;
  12. }
  13. ID3v2Metadata::ID3v2Metadata()
  14. {
  15. id3v2_tag=0;
  16. #ifdef __APPLE__
  17. number_formatter = NULL;
  18. #endif
  19. }
  20. ID3v2Metadata::~ID3v2Metadata()
  21. {
  22. #ifdef __APPLE__
  23. if (NULL != number_formatter)
  24. CFRelease(number_formatter);
  25. #endif
  26. }
  27. int ID3v2Metadata::Initialize(api_metadata *metadata_api)
  28. {
  29. ID3v2Metadata::metadata_api = metadata_api;
  30. return NErr_Success;
  31. }
  32. int ID3v2Metadata::Initialize(nsid3v2_tag_t tag)
  33. {
  34. id3v2_tag = tag;
  35. return NErr_Success;
  36. }
  37. int ID3v2Metadata::GetGenre(int index, nx_string_t *value)
  38. {
  39. nx_string_t genre=0;
  40. int ret = NSID3v2_Tag_Text_Get(id3v2_tag, NSID3V2_FRAME_CONTENTTYPE, &genre, 0);
  41. if (ret != NErr_Success)
  42. return ret;
  43. if (index > 0)
  44. return NErr_EndOfEnumeration;
  45. if (genre)
  46. {
  47. *value = genre;
  48. #ifdef _WIN32
  49. // parse the (##) out of it
  50. wchar_t *tmp = genre->string;
  51. while (*tmp == ' ') tmp++;
  52. if (!wcsncmp(tmp, L"(RX)", 4))
  53. {
  54. *value = NXStringCreateFromUTF8("Remix");
  55. NXStringRelease(genre);
  56. if (*value)
  57. return NErr_Success;
  58. else
  59. return NErr_OutOfMemory;
  60. }
  61. else if (!wcsncmp(tmp, L"(CR)", 4))
  62. {
  63. *value = NXStringCreateFromUTF8("Cover");
  64. NXStringRelease(genre);
  65. if (*value)
  66. return NErr_Success;
  67. else
  68. return NErr_OutOfMemory;
  69. }
  70. if (*tmp == '(' || (*tmp >= '0' && *tmp <= '9')) // both (%d) and %d forms
  71. {
  72. int noparam = 0;
  73. if (*tmp == '(') tmp++;
  74. else noparam = 1;
  75. size_t genre_index = _wtoi(tmp);
  76. int cnt = 0;
  77. while (*tmp >= '0' && *tmp <= '9') cnt++, tmp++;
  78. while (*tmp == ' ') tmp++;
  79. if (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0)
  80. {
  81. if (genre_index < 256 && metadata_api)
  82. {
  83. int ret = metadata_api->GetGenre(genre_index, value);
  84. if (ret == NErr_Success)
  85. {
  86. NXStringRetain(*value);
  87. NXStringRelease(genre);
  88. return ret;
  89. }
  90. }
  91. }
  92. }
  93. #elif defined(__APPLE__)
  94. int ret = NErr_Success;
  95. CFMutableStringRef mutable_genre = CFStringCreateMutableCopy(NULL, 0, genre);
  96. CFStringTrimWhitespace(mutable_genre);
  97. CFIndex mutable_genre_length = CFStringGetLength(mutable_genre);
  98. if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(mutable_genre,
  99. CFSTR("(RX)"),
  100. CFRangeMake(0, mutable_genre_length),
  101. 0,
  102. NULL))
  103. {
  104. NXStringRelease(genre);
  105. *value = CFSTR("Remix");
  106. ret = NErr_Success;
  107. }
  108. else if (kCFCompareEqualTo == CFStringCompareWithOptionsAndLocale(mutable_genre,
  109. CFSTR("(CR)"),
  110. CFRangeMake(0, mutable_genre_length),
  111. 0,
  112. NULL))
  113. {
  114. NXStringRelease(genre);
  115. *value = CFSTR("Cover");
  116. ret = NErr_Success;
  117. }
  118. else
  119. {
  120. CFStringTrim(mutable_genre, CFSTR("("));
  121. CFStringTrim(mutable_genre, CFSTR(")"));
  122. mutable_genre_length = CFStringGetLength(mutable_genre);
  123. if (mutable_genre_length > 0
  124. && mutable_genre_length < 4)
  125. {
  126. if (NULL == number_formatter)
  127. {
  128. CFLocaleRef locale = CFLocaleCreate(NULL, CFSTR("en_US_POSIX"));
  129. number_formatter = CFNumberFormatterCreate(NULL, locale, kCFNumberFormatterDecimalStyle);
  130. CFRelease(locale);
  131. }
  132. SInt8 genre_index;
  133. CFRange number_range = CFRangeMake(0, mutable_genre_length);
  134. if (NULL != number_formatter
  135. && false != CFNumberFormatterGetValueFromString(number_formatter,
  136. mutable_genre,
  137. &number_range,
  138. kCFNumberSInt8Type,
  139. &genre_index)
  140. && number_range.length == mutable_genre_length
  141. && number_range.location == 0)
  142. {
  143. if (genre_index >= 0
  144. && genre_index < 256
  145. && metadata_api)
  146. {
  147. int ret = metadata_api->GetGenre(genre_index, value);
  148. if (ret == NErr_Success)
  149. {
  150. NXStringRetain(*value);
  151. NXStringRelease(genre);
  152. }
  153. ret = NErr_Success;
  154. }
  155. }
  156. }
  157. }
  158. CFRelease(mutable_genre);
  159. return ret;
  160. #elif defined(__linux__)
  161. char *tmp = genre->string;
  162. while (*tmp == ' ') tmp++;
  163. if (!strncmp(tmp, "(RX)", 4))
  164. {
  165. NXStringRelease(genre);
  166. return NXStringCreateWithUTF8(value, "Remix");
  167. }
  168. else if (!strncmp(tmp, "(CR)", 4))
  169. {
  170. NXStringRelease(genre);
  171. return NXStringCreateWithUTF8(value, "Cover");
  172. }
  173. if (*tmp == '(' || (*tmp >= '0' && *tmp <= '9')) // both (%d) and %d forms
  174. {
  175. int noparam = 0;
  176. if (*tmp == '(') tmp++;
  177. else noparam = 1;
  178. size_t genre_index = atoi(tmp);
  179. int cnt = 0;
  180. while (*tmp >= '0' && *tmp <= '9') cnt++, tmp++;
  181. while (*tmp == ' ') tmp++;
  182. if (((!*tmp && noparam) || (!noparam && *tmp == ')')) && cnt > 0)
  183. {
  184. if (genre_index < 256 && metadata_api)
  185. {
  186. int ret = metadata_api->GetGenre(genre_index, value);
  187. if (ret == NErr_Success)
  188. {
  189. NXStringRetain(*value);
  190. NXStringRelease(genre);
  191. return ret;
  192. }
  193. }
  194. }
  195. }
  196. #else
  197. #error port me!
  198. #endif
  199. }
  200. return NErr_Success;
  201. }
  202. static int ID3v2_GetText(nsid3v2_tag_t id3v2_tag, int frame_enum, unsigned int index, nx_string_t *value)
  203. {
  204. if (!id3v2_tag)
  205. return NErr_Empty;
  206. nsid3v2_frame_t frame;
  207. int ret = NSID3v2_Tag_GetFrame(id3v2_tag, frame_enum, &frame);
  208. if (ret != NErr_Success)
  209. return ret;
  210. if (index > 0)
  211. return NErr_EndOfEnumeration;
  212. return NSID3v2_Frame_Text_Get(frame, value, 0);
  213. }
  214. static int ID3v2_GetTXXX(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t *value)
  215. {
  216. if (!id3v2_tag)
  217. return NErr_Empty;
  218. nsid3v2_frame_t frame;
  219. int ret = NSID3v2_Tag_TXXX_Find(id3v2_tag, description, &frame, 0);
  220. if (ret != NErr_Success)
  221. return ret;
  222. if (index > 0)
  223. return NErr_EndOfEnumeration;
  224. return NSID3v2_Frame_UserText_Get(frame, 0, value, 0);
  225. }
  226. static int ID3v2_GetComments(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t *value)
  227. {
  228. if (!id3v2_tag)
  229. return NErr_Empty;
  230. nsid3v2_frame_t frame;
  231. int ret = NSID3v2_Tag_Comments_Find(id3v2_tag, description, &frame, 0);
  232. if (ret != NErr_Success)
  233. return ret;
  234. if (index > 0)
  235. return NErr_EndOfEnumeration;
  236. return NSID3v2_Frame_Comments_Get(frame, 0, 0, value, 0);
  237. }
  238. // only one of value1 or value2 should be non-NULL
  239. static int SplitSlash(nx_string_t track, nx_string_t *value1, nx_string_t *value2)
  240. {
  241. char track_utf8[64];
  242. size_t bytes_copied;
  243. int ret;
  244. ret = NXStringGetBytes(&bytes_copied, track, track_utf8, 64, nx_charset_utf8, nx_string_get_bytes_size_null_terminate);
  245. if (ret == NErr_Success)
  246. {
  247. size_t len = strcspn(track_utf8, "/");
  248. if (value2)
  249. {
  250. const char *second = &track_utf8[len];
  251. if (*second)
  252. second++;
  253. if (!*second)
  254. return NErr_Empty;
  255. return NXStringCreateWithUTF8(value2, second);
  256. }
  257. else
  258. {
  259. if (len == 0)
  260. return NErr_Empty;
  261. return NXStringCreateWithBytes(value1, track_utf8, len, nx_charset_utf8);
  262. }
  263. return NErr_Success;
  264. }
  265. return ret;
  266. }
  267. /* ifc_metadata implementation */
  268. int ID3v2Metadata::Metadata_GetField(int field, unsigned int index, nx_string_t *value)
  269. {
  270. if (!id3v2_tag)
  271. return NErr_Unknown;
  272. int ret;
  273. switch (field)
  274. {
  275. case MetadataKeys::ARTIST:
  276. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_LEADARTIST, index, value);
  277. case MetadataKeys::ALBUM_ARTIST:
  278. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BAND, index, value); /* Windows Media Player style */
  279. if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
  280. return ret;
  281. ret = ID3v2_GetTXXX(id3v2_tag, "ALBUM ARTIST", index, value); /* foobar 2000 style */
  282. if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
  283. return ret;
  284. ret = ID3v2_GetTXXX(id3v2_tag, "ALBUMARTIST", index, value); /* mp3tag style */
  285. if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
  286. return ret;
  287. return ID3v2_GetTXXX(id3v2_tag, "Band", index, value); /* audacity style */
  288. case MetadataKeys::ALBUM:
  289. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_ALBUM, index, value);
  290. case MetadataKeys::TITLE:
  291. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TITLE, index, value);
  292. case MetadataKeys::GENRE:
  293. return GetGenre(index, value);
  294. case MetadataKeys::TRACK:
  295. {
  296. ReferenceCountedNXString track;
  297. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
  298. if (ret == NErr_Success)
  299. return SplitSlash(track, value, 0);
  300. return ret;
  301. }
  302. break;
  303. case MetadataKeys::TRACKS:
  304. {
  305. ReferenceCountedNXString track;
  306. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
  307. if (ret == NErr_Success)
  308. return SplitSlash(track, 0, value);
  309. return ret;
  310. }
  311. break;
  312. case MetadataKeys::YEAR:
  313. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, value);
  314. if (ret == NErr_Success || ret == NErr_EndOfEnumeration)
  315. return ret;
  316. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_YEAR, index, value);
  317. case MetadataKeys::DISC:
  318. {
  319. ReferenceCountedNXString track;
  320. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
  321. if (ret == NErr_Success)
  322. return SplitSlash(track, value, 0);
  323. return ret;
  324. }
  325. break;
  326. case MetadataKeys::DISCS:
  327. {
  328. ReferenceCountedNXString track;
  329. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
  330. if (ret == NErr_Success)
  331. return SplitSlash(track, 0, value);
  332. return ret;
  333. }
  334. break;
  335. case MetadataKeys::COMPOSER:
  336. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_COMPOSER, index, value);
  337. case MetadataKeys::PUBLISHER:
  338. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PUBLISHER, index, value);
  339. case MetadataKeys::BPM:
  340. return ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BPM, index, value);
  341. case MetadataKeys::COMMENT:
  342. return ID3v2_GetComments(id3v2_tag, "", index, value);
  343. // TODO case MetadataKeys::PLAY_COUNT:
  344. // TODO case MetadataKeys::RATING:
  345. case MetadataKeys::TRACK_GAIN:
  346. return ID3v2_GetTXXX(id3v2_tag, "replaygain_track_gain", index, value);
  347. case MetadataKeys::TRACK_PEAK:
  348. return ID3v2_GetTXXX(id3v2_tag, "replaygain_track_peak", index, value);
  349. case MetadataKeys::ALBUM_GAIN:
  350. return ID3v2_GetTXXX(id3v2_tag, "replaygain_album_gain", index, value);
  351. case MetadataKeys::ALBUM_PEAK:
  352. return ID3v2_GetTXXX(id3v2_tag, "replaygain_album_peak", index, value);
  353. }
  354. return NErr_Unknown;
  355. }
  356. static int IncSafe(const char *&value, size_t &value_length, size_t increment_length)
  357. {
  358. /* eat leading spaces */
  359. while (*value == ' ' && value_length)
  360. {
  361. value++;
  362. value_length--;
  363. }
  364. if (increment_length > value_length)
  365. return NErr_NeedMoreData;
  366. value += increment_length;
  367. value_length -= increment_length;
  368. /* eat trailing spaces */
  369. while (*value == ' ' && value_length)
  370. {
  371. value++;
  372. value_length--;
  373. }
  374. return NErr_Success;
  375. }
  376. static int SplitSlashInteger(nx_string_t track, unsigned int *value1, unsigned int *value2)
  377. {
  378. char track_utf8[64];
  379. size_t bytes_copied;
  380. int ret;
  381. ret = NXStringGetBytes(&bytes_copied, track, track_utf8, 64, nx_charset_utf8, nx_string_get_bytes_size_null_terminate);
  382. if (ret == NErr_Success)
  383. {
  384. size_t len = strcspn(track_utf8, "/");
  385. if (track_utf8[len])
  386. *value2 = strtoul(&track_utf8[len+1], 0, 10);
  387. else
  388. *value2 = 0;
  389. track_utf8[len]=0;
  390. *value1 = strtoul(track_utf8, 0, 10);
  391. return NErr_Success;
  392. }
  393. return ret;
  394. }
  395. int ID3v2Metadata::Metadata_GetInteger(int field, unsigned int index, int64_t *value)
  396. {
  397. if (!id3v2_tag)
  398. return NErr_Unknown;
  399. switch(field)
  400. {
  401. case MetadataKeys::TRACK:
  402. {
  403. ReferenceCountedNXString track;
  404. int ret;
  405. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
  406. if (ret == NErr_Success)
  407. {
  408. unsigned int itrack, itracks;
  409. ret = SplitSlashInteger(track, &itrack, &itracks);
  410. if (ret == NErr_Success)
  411. {
  412. if (itrack == 0)
  413. return NErr_Empty;
  414. *value = itrack;
  415. return NErr_Success;
  416. }
  417. }
  418. return ret;
  419. }
  420. break;
  421. case MetadataKeys::TRACKS:
  422. {
  423. ReferenceCountedNXString track;
  424. int ret;
  425. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, &track);
  426. if (ret == NErr_Success)
  427. {
  428. unsigned int itrack, itracks;
  429. ret = SplitSlashInteger(track, &itrack, &itracks);
  430. if (ret == NErr_Success)
  431. {
  432. if (itracks == 0)
  433. return NErr_Empty;
  434. *value = itracks;
  435. return NErr_Success;
  436. }
  437. }
  438. return ret;
  439. }
  440. break;
  441. case MetadataKeys::DISC:
  442. {
  443. ReferenceCountedNXString track;
  444. int ret;
  445. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
  446. if (ret == NErr_Success)
  447. {
  448. unsigned int idisc, idiscs;
  449. ret = SplitSlashInteger(track, &idisc, &idiscs);
  450. if (ret == NErr_Success)
  451. {
  452. if (idisc == 0)
  453. return NErr_Empty;
  454. *value = idisc;
  455. return NErr_Success;
  456. }
  457. }
  458. return ret;
  459. }
  460. break;
  461. case MetadataKeys::DISCS:
  462. {
  463. ReferenceCountedNXString track;
  464. int ret;
  465. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, &track);
  466. if (ret == NErr_Success)
  467. {
  468. unsigned int idisc, idiscs;
  469. ret = SplitSlashInteger(track, &idisc, &idiscs);
  470. if (ret == NErr_Success)
  471. {
  472. if (idiscs == 0)
  473. return NErr_Empty;
  474. *value = idiscs;
  475. return NErr_Success;
  476. }
  477. }
  478. return ret;
  479. }
  480. break;
  481. case MetadataKeys::BPM:
  482. {
  483. ReferenceCountedNXString bpm;
  484. int ret;
  485. ret = ID3v2_GetText(id3v2_tag, NSID3V2_FRAME_BPM, index, &bpm);
  486. if (ret == NErr_Success)
  487. {
  488. /* TODO: benski> implement NXStringGetInt64Value */
  489. int value32;
  490. ret = NXStringGetIntegerValue(bpm, &value32);
  491. if (ret != NErr_Success)
  492. return ret;
  493. *value = value32;
  494. return NErr_Success;
  495. }
  496. return ret;
  497. }
  498. case MetadataKeys::PREGAP:
  499. {
  500. ReferenceCountedNXString str;
  501. char language[3];
  502. int ret = NSID3v2_Tag_Comments_Get(id3v2_tag, "iTunSMPB", language, &str, 0);
  503. if (ret == NErr_Success)
  504. {
  505. if (index > 0)
  506. return NErr_EndOfEnumeration;
  507. const char *itunsmpb;
  508. size_t itunsmpb_length;
  509. char temp[64] = {0};
  510. if (NXStringGetCString(str, temp, sizeof(temp)/sizeof(*temp), &itunsmpb, &itunsmpb_length) == NErr_Success)
  511. {
  512. /* skip first set of meaningless values */
  513. if (IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8)
  514. {
  515. /* read pre-gap */
  516. *value = strtoul(itunsmpb, 0, 16);
  517. return NErr_Success;
  518. }
  519. }
  520. return NErr_Error;
  521. }
  522. else
  523. return ret;
  524. }
  525. case MetadataKeys::POSTGAP:
  526. {
  527. ReferenceCountedNXString str;
  528. char language[3];
  529. int ret = NSID3v2_Tag_Comments_Get(id3v2_tag, "iTunSMPB", language, &str, 0);
  530. if (ret == NErr_Success)
  531. {
  532. if (index > 0)
  533. return NErr_EndOfEnumeration;
  534. const char *itunsmpb;
  535. size_t itunsmpb_length;
  536. char temp[64] = {0};
  537. if (NXStringGetCString(str, temp, sizeof(temp)/sizeof(*temp), &itunsmpb, &itunsmpb_length) == NErr_Success)
  538. {
  539. /* two separate calls so we can skip spaces properly */
  540. if (IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8
  541. && IncSafe(itunsmpb, itunsmpb_length, 8) == NErr_Success && itunsmpb_length >= 8)
  542. {
  543. *value = strtoul(itunsmpb, 0, 16);
  544. return NErr_Success;
  545. }
  546. }
  547. return NErr_Error;
  548. }
  549. else
  550. return ret;
  551. }
  552. }
  553. return NErr_Unknown;
  554. }
  555. int ID3v2Metadata::Metadata_GetReal(int field, unsigned int index, double *value)
  556. {
  557. if (!id3v2_tag)
  558. return NErr_Unknown;
  559. int ret;
  560. nx_string_t str;
  561. switch (field)
  562. {
  563. case MetadataKeys::TRACK_GAIN:
  564. ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_track_gain", index, &str);
  565. if (ret == NErr_Success)
  566. {
  567. ret = NXStringGetDoubleValue(str, value);
  568. NXStringRelease(str);
  569. }
  570. return ret;
  571. case MetadataKeys::TRACK_PEAK:
  572. ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_track_peak", index, &str);
  573. if (ret == NErr_Success)
  574. {
  575. ret = NXStringGetDoubleValue(str, value);
  576. NXStringRelease(str);
  577. }
  578. return ret;
  579. case MetadataKeys::ALBUM_GAIN:
  580. ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_album_gain", index, &str);
  581. if (ret == NErr_Success)
  582. {
  583. ret = NXStringGetDoubleValue(str, value);
  584. NXStringRelease(str);
  585. }
  586. return ret;
  587. case MetadataKeys::ALBUM_PEAK:
  588. ret = ID3v2_GetTXXX(id3v2_tag, "replaygain_album_peak", index, &str);
  589. if (ret == NErr_Success)
  590. {
  591. ret = NXStringGetDoubleValue(str, value);
  592. NXStringRelease(str);
  593. }
  594. return ret;
  595. }
  596. return NErr_Unknown;
  597. }
  598. static int ArtLookupType(uint8_t *id3v2_type, int metadata_key)
  599. {
  600. switch(metadata_key)
  601. {
  602. case MetadataKeys::ALBUM:
  603. *id3v2_type = 3;
  604. return NErr_Success;
  605. }
  606. return NErr_Unknown;
  607. }
  608. static int NXStringCreateWithMIME(nx_string_t *mime_type, nx_string_t in)
  609. {
  610. if (!mime_type)
  611. return NErr_Success;
  612. char temp[128];
  613. size_t copied;
  614. int ret = NXStringGetBytes(&copied, in, temp, 128, nx_charset_ascii, nx_string_get_bytes_size_null_terminate);
  615. if (ret != NErr_Success)
  616. return ret;
  617. if (strstr(temp, "/") != 0)
  618. {
  619. *mime_type = NXStringRetain(in);
  620. return NErr_Success;
  621. }
  622. else
  623. {
  624. char temp2[128];
  625. #ifdef _WIN32
  626. _snprintf(temp2, 127, "image/%s", temp);
  627. #else
  628. snprintf(temp2, 127, "image/%s", temp);
  629. #endif
  630. temp2[127]=0;
  631. return NXStringCreateWithUTF8(mime_type, temp2);
  632. }
  633. }
  634. int ID3v2Metadata::Metadata_GetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)
  635. {
  636. if (!id3v2_tag)
  637. return NErr_Unknown;
  638. uint8_t id3v2_picture_type;
  639. int ret = ArtLookupType(&id3v2_picture_type, field);
  640. if (ret != NErr_Success)
  641. return ret;
  642. if (!id3v2_tag)
  643. return NErr_Empty;
  644. bool found_one=false;
  645. nsid3v2_frame_t frame=0;
  646. ret = NSID3v2_Tag_GetFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, &frame);
  647. if (ret != NErr_Success)
  648. return ret;
  649. for (;;)
  650. {
  651. uint8_t this_type;
  652. if (NSID3v2_Frame_Picture_Get(frame, 0, &this_type, 0, 0, 0, 0) == NErr_Success && (this_type == id3v2_picture_type || (id3v2_picture_type == 3 && this_type == 0)))
  653. {
  654. found_one=true;
  655. if (index == 0)
  656. {
  657. if (artwork)
  658. {
  659. nx_data_t data=0;
  660. if (flags != DATA_FLAG_NONE)
  661. {
  662. const void *picture_data;
  663. size_t picture_length;
  664. ReferenceCountedNXString mime_local, description;
  665. ret = NSID3v2_Frame_Picture_Get(frame, TestFlag(flags, DATA_FLAG_MIME)?(&mime_local):0, &this_type, TestFlag(flags, DATA_FLAG_DESCRIPTION)?(&description):0, &picture_data, &picture_length, 0);
  666. if (ret != NErr_Success)
  667. return ret;
  668. if (TestFlag(flags, DATA_FLAG_DATA))
  669. {
  670. ret = NXDataCreate(&data, picture_data, picture_length);
  671. if (ret != NErr_Success)
  672. return ret;
  673. }
  674. else
  675. {
  676. ret = NXDataCreateEmpty(&data);
  677. if (ret != NErr_Success)
  678. return ret;
  679. }
  680. if (mime_local)
  681. {
  682. ReferenceCountedNXString mime_type;
  683. ret = NXStringCreateWithMIME(&mime_type, mime_local);
  684. if (ret != NErr_Success)
  685. {
  686. NXDataRelease(data);
  687. return ret;
  688. }
  689. NXDataSetMIME(data, mime_type);
  690. }
  691. if (description)
  692. {
  693. NXDataSetDescription(data, description);
  694. }
  695. }
  696. artwork->data = data;
  697. /* id3v2 doesn't store height and width, so zero these */
  698. artwork->width=0;
  699. artwork->height=0;
  700. }
  701. return NErr_Success;
  702. }
  703. else
  704. {
  705. index--; // keep looking
  706. }
  707. }
  708. if (NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &frame) != NErr_Success)
  709. {
  710. if (found_one)
  711. return NErr_EndOfEnumeration;
  712. else
  713. return NErr_Empty;
  714. }
  715. }
  716. }
  717. static int SetText(nsid3v2_tag_t id3v2_tag, int frame_id, unsigned int index, nx_string_t value)
  718. {
  719. if (index > 0)
  720. return NErr_Success;
  721. if (!value)
  722. {
  723. nsid3v2_frame_t frame;
  724. if (NSID3v2_Tag_GetFrame(id3v2_tag, frame_id, &frame) == NErr_Success)
  725. {
  726. for(;;)
  727. {
  728. nsid3v2_frame_t next;
  729. int ret = NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &next);
  730. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  731. if (ret != NErr_Success)
  732. break;
  733. frame=next;
  734. }
  735. }
  736. return NErr_Success;
  737. }
  738. else
  739. {
  740. return NSID3v2_Tag_Text_Set(id3v2_tag, frame_id, value, 0);
  741. }
  742. }
  743. static int SetTXXX(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t value, int text_flags)
  744. {
  745. if (index > 0)
  746. return NErr_EndOfEnumeration;
  747. if (!value)
  748. {
  749. nsid3v2_frame_t frame;
  750. for(;;)
  751. {
  752. if (NSID3v2_Tag_TXXX_Find(id3v2_tag, description, &frame, text_flags) == NErr_Success)
  753. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  754. else
  755. return NErr_Success;
  756. }
  757. }
  758. else
  759. {
  760. return NSID3v2_Tag_TXXX_Set(id3v2_tag, description, value, 0);
  761. }
  762. }
  763. static int SetComments(nsid3v2_tag_t id3v2_tag, const char *description, unsigned int index, nx_string_t value, int text_flags)
  764. {
  765. if (index > 0)
  766. return NErr_EndOfEnumeration;
  767. if (!value)
  768. {
  769. nsid3v2_frame_t frame;
  770. for(;;)
  771. {
  772. if (NSID3v2_Tag_Comments_Find(id3v2_tag, description, &frame, text_flags) == NErr_Success)
  773. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  774. else
  775. return NErr_Success;
  776. }
  777. }
  778. else
  779. {
  780. return NSID3v2_Tag_Comments_Set(id3v2_tag, description, "\0\0\0", value, 0);
  781. }
  782. }
  783. int ID3v2Metadata::MetadataEditor_SetField(int field, unsigned int index, nx_string_t value)
  784. {
  785. int ret;
  786. switch (field)
  787. {
  788. case MetadataKeys::ARTIST:
  789. return SetText(id3v2_tag, NSID3V2_FRAME_LEADARTIST, index, value);
  790. case MetadataKeys::ALBUM_ARTIST:
  791. ret = SetText(id3v2_tag, NSID3V2_FRAME_BAND, index, value);
  792. /* delete some of the alternates */
  793. SetTXXX(id3v2_tag, "ALBUM ARTIST", index, 0, 0); /* foobar 2000 style */
  794. SetTXXX(id3v2_tag, "ALBUMARTIST", index, 0, 0); /* mp3tag style */
  795. if (!value) /* this might be a valid field, so only delete it if we're specifically deleting album artist (because otherwise, if it's here it's going to get picked up by GetField */
  796. SetTXXX(id3v2_tag, "Band", index, 0, 0); /* audacity style */
  797. return ret;
  798. case MetadataKeys::ALBUM:
  799. return SetText(id3v2_tag, NSID3V2_FRAME_ALBUM, index, value);
  800. case MetadataKeys::TITLE:
  801. return SetText(id3v2_tag, NSID3V2_FRAME_TITLE, index, value);
  802. case MetadataKeys::GENRE:
  803. return SetText(id3v2_tag, NSID3V2_FRAME_CONTENTTYPE, index, value);
  804. case MetadataKeys::YEAR:
  805. /* try to set "newer" style TDRC, first */
  806. ret = SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, value);
  807. if (ret == NErr_Success)
  808. {
  809. /* if it succeeded, remove the older TYER tag */
  810. SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, 0);
  811. return ret;
  812. }
  813. /* fall back to using TYER */
  814. return SetText(id3v2_tag, NSID3V2_FRAME_RECORDINGTIME, index, 0);
  815. case MetadataKeys::TRACK:
  816. return SetText(id3v2_tag, NSID3V2_FRAME_TRACK, index, value);
  817. case MetadataKeys::DISC:
  818. return SetText(id3v2_tag, NSID3V2_FRAME_PARTOFSET, index, value);
  819. case MetadataKeys::COMPOSER:
  820. return SetText(id3v2_tag, NSID3V2_FRAME_COMPOSER, index, value);
  821. case MetadataKeys::PUBLISHER:
  822. return SetText(id3v2_tag, NSID3V2_FRAME_PUBLISHER, index, value);
  823. case MetadataKeys::BPM:
  824. return SetText(id3v2_tag, NSID3V2_FRAME_BPM, index, value);
  825. case MetadataKeys::COMMENT:
  826. return SetComments(id3v2_tag, "", index, value, 0);
  827. case MetadataKeys::TRACK_GAIN:
  828. return SetTXXX(id3v2_tag, "replaygain_track_gain", index, value, 0);
  829. case MetadataKeys::TRACK_PEAK:
  830. return SetTXXX(id3v2_tag, "replaygain_track_peak", index, value, 0);
  831. case MetadataKeys::ALBUM_GAIN:
  832. return SetTXXX(id3v2_tag, "replaygain_album_gain", index, value, 0);
  833. case MetadataKeys::ALBUM_PEAK:
  834. return SetTXXX(id3v2_tag, "replaygain_album_peak", index, value, 0);
  835. }
  836. return NErr_Unknown;
  837. }
  838. static int ID3v2_SetPicture(nsid3v2_frame_t frame, uint8_t id3v2_picture_type, artwork_t *artwork, data_flags_t flags)
  839. {
  840. int ret;
  841. const void *picture_data;
  842. size_t picture_length;
  843. ret = NXDataGet(artwork->data, &picture_data, &picture_length);
  844. if (ret != NErr_Success)
  845. return ret;
  846. ReferenceCountedNXString mime_type, description;
  847. if (TestFlag(flags, DATA_FLAG_MIME))
  848. NXDataGetMIME(artwork->data, &mime_type);
  849. if (TestFlag(flags, DATA_FLAG_DESCRIPTION))
  850. NXDataGetDescription(artwork->data, &description);
  851. return NSID3v2_Frame_Picture_Set(frame, mime_type, id3v2_picture_type, description, picture_data, picture_length, 0);
  852. }
  853. int ID3v2Metadata::MetadataEditor_SetArtwork(int field, unsigned int index, artwork_t *artwork, data_flags_t flags)
  854. {
  855. uint8_t id3v2_picture_type;
  856. int ret = ArtLookupType(&id3v2_picture_type, field);
  857. if (ret != NErr_Success)
  858. return ret;
  859. nsid3v2_frame_t frame=0;
  860. ret = NSID3v2_Tag_GetFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, &frame);
  861. if (ret != NErr_Success)
  862. {
  863. if (artwork && artwork->data)
  864. {
  865. /* create a new one and store */
  866. int ret = NSID3v2_Tag_CreateFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, 0, &frame);
  867. if (ret == NErr_Success)
  868. {
  869. ret = ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
  870. if (ret == NErr_Success)
  871. {
  872. ret = NSID3v2_Tag_AddFrame(id3v2_tag, frame);
  873. if (ret != NErr_Success)
  874. {
  875. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  876. }
  877. }
  878. }
  879. return ret;
  880. }
  881. else
  882. return NErr_Success;
  883. }
  884. for (;;)
  885. {
  886. /* iterate now, because we might delete the current frame */
  887. nsid3v2_frame_t next_frame=0;
  888. if (NSID3v2_Tag_GetNextFrame(id3v2_tag, frame, &next_frame) != NErr_Success)
  889. next_frame=0; /* just in case */
  890. uint8_t this_type;
  891. if (NSID3v2_Frame_Picture_Get(frame, 0, &this_type, 0, 0, 0, 0) == NErr_Success && (this_type == id3v2_picture_type || (id3v2_picture_type == 3 && this_type == 0)))
  892. {
  893. if (index == 0)
  894. {
  895. if (artwork && artwork->data)
  896. {
  897. return ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
  898. }
  899. else
  900. {
  901. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  902. }
  903. }
  904. else
  905. {
  906. index--; // keep looking
  907. }
  908. }
  909. if (!next_frame)
  910. {
  911. if (!artwork || !artwork->data)
  912. return NErr_Success;
  913. else
  914. {
  915. /* create a new one and store */
  916. int ret = NSID3v2_Tag_CreateFrame(id3v2_tag, NSID3V2_FRAME_PICTURE, 0, &frame);
  917. if (ret != NErr_Success)
  918. return ret;
  919. ret = ID3v2_SetPicture(frame, id3v2_picture_type, artwork, flags);
  920. if (ret != NErr_Success)
  921. return ret;
  922. ret = NSID3v2_Tag_AddFrame(id3v2_tag, frame);
  923. if (ret != NErr_Success)
  924. {
  925. NSID3v2_Tag_RemoveFrame(id3v2_tag, frame);
  926. }
  927. return ret;
  928. }
  929. }
  930. frame = next_frame;
  931. }
  932. return NErr_NotImplemented;
  933. }