// returns buffer len; caller frees int ExportMP2::AddTags(AudacityProject * WXUNUSED(project), char **buffer, bool *endOfFile, const Tags *tags) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); for (const auto &pair : tags->GetRange()) { const auto &n = pair.first; const auto &v = pair.second; const char *name = "TXXX"; if (n.CmpNoCase(TAG_TITLE) == 0) { name = ID3_FRAME_TITLE; } else if (n.CmpNoCase(TAG_ARTIST) == 0) { name = ID3_FRAME_ARTIST; } else if (n.CmpNoCase(TAG_ALBUM) == 0) { name = ID3_FRAME_ALBUM; } else if (n.CmpNoCase(TAG_YEAR) == 0) { // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR), // so we add old one as well. AddFrame(tp, n, v, "TYER"); name = ID3_FRAME_YEAR; } else if (n.CmpNoCase(TAG_GENRE) == 0) { name = ID3_FRAME_GENRE; } else if (n.CmpNoCase(TAG_COMMENTS) == 0) { name = ID3_FRAME_COMMENT; } else if (n.CmpNoCase(TAG_TRACK) == 0) { name = ID3_FRAME_TRACK; } AddFrame(tp, n, v, name); } tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }
// returns buffer len; caller frees int Tags::ExportID3(char **buffer, bool *endOfFile) { #ifdef USE_LIBID3TAG struct id3_tag *tp = id3_tag_new(); if (mTitle != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TITLE, mTitle.mb_str())); if (mArtist != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ARTIST, mArtist.mb_str())); if (mAlbum != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_ALBUM, mAlbum.mb_str())); if (mYear != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_YEAR, mYear.mb_str())); if (mComments != wxT("")) id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_COMMENT, mComments.mb_str())); if (mTrackNum >= 0) { wxString trackNumStr; trackNumStr.Printf(wxT("%d"), mTrackNum); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_TRACK, trackNumStr.mb_str())); } if (mGenre >= 0) { if (mID3V2) { wxString genreStr = GetGenreNum(mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } else { wxString genreStr; genreStr.Printf(wxT("%d"), mGenre); id3_tag_attachframe(tp, MakeID3Frame(ID3_FRAME_GENRE, genreStr.mb_str())); } } if (mID3V2) { tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression // If this version of libid3tag supports it, use v2.3 ID3 // tags instead of the newer, but less well supported, v2.4 // that libid3tag uses by default. #ifdef ID3_TAG_OPTION_ID3V2_3 tp->options |= ID3_TAG_OPTION_ID3V2_3; #endif *endOfFile = false; } else { tp->options |= ID3_TAG_OPTION_ID3V1; *endOfFile = true; } id3_length_t len; len = id3_tag_render(tp, 0); *buffer = (char *)malloc(len); len = id3_tag_render(tp, (id3_byte_t *)*buffer); id3_tag_delete(tp); return len; #else //ifdef USE_LIBID3TAG return 0; #endif }
int BarFlyID3WriteFile(char const* file_path, struct id3_tag const* tag, BarSettings_t const* settings) { /* * './' + artist + '/' + album + '/' + BAR_FLY_TMP_MP3_FILE_NAME + '\0' */ int const TMP_FILE_PATH_LENGTH = 2 + BAR_FLY_NAME_LENGTH + 1 + BAR_FLY_NAME_LENGTH + 1 + strlen(BAR_FLY_TMP_MP3_FILE_NAME) + 1; int exit_status = 0; int status_int; id3_length_t size1; id3_length_t size2; id3_byte_t* tag_buffer = NULL; FILE* audio_file = NULL; FILE* tmp_file = NULL; uint8_t audio_buffer[BAR_FLY_COPY_BLOCK_SIZE]; char tmp_file_path[TMP_FILE_PATH_LENGTH]; size_t read_count; size_t write_count; tmp_file_path[0] = '\0'; /* * For starters libid3tag kinda sucks. It will only write a tag to a file * if the new tag is the same size as the old tag. Which in this case, * since there is no tag, will never work. So writing of the tag to the * file has to be done manually. */ /* * Render the tag to a buffer that can then be written to the audio file. */ size1 = id3_tag_render(tag, NULL); tag_buffer = malloc(size1); if (tag_buffer == NULL) { BarUiMsg(settings, MSG_ERR, "Failed to allocate memory (bytes = %d).\n", size1); goto error; } size2 = id3_tag_render(tag, tag_buffer); if (size1 != size2) { BarUiMsg(settings, MSG_ERR, "Invalid tag size (expected = %d, " "recevied = %d).\n", size1, size2); goto error; } /* * Prepending data to a file is not trivial in C. Here the approach taken * is to create a temporary file, write the tag to the beginning of the * file, copy the audio file block by block to the tmp file, then overwrite * the audio file with the tmp file. This was done in order to minimize the * chance of ending up with a broken audio file in case the program stopped * durring this process. */ /* * Open the audio file. */ audio_file = fopen(file_path, "rb"); if (audio_file == NULL) { BarUiMsg(settings, MSG_ERR, "Could not read the audio file (%s) " "(%d:%s).\n", file_path, errno, strerror(errno)); goto error; } /* * Open the tmp file. */ if (strchr(file_path, '/') == NULL) { strcpy(tmp_file_path, BAR_FLY_TMP_MP3_FILE_NAME); } else { strncpy(tmp_file_path, file_path, TMP_FILE_PATH_LENGTH); tmp_file_path[TMP_FILE_PATH_LENGTH - 1] = '\0'; dirname(tmp_file_path); strcat(tmp_file_path, "/"); strcat(tmp_file_path, BAR_FLY_TMP_MP3_FILE_NAME); } tmp_file = fopen(tmp_file_path, "w+b"); if (tmp_file == NULL) { BarUiMsg(settings, MSG_ERR, "Could not open the temporary file (%s) " "(%d:%s).\n", tmp_file_path, errno, strerror(errno)); goto error; } /* * Write the tag to the tmp file. */ write_count = fwrite(tag_buffer, 1, size2, tmp_file); if (write_count != size2) { BarUiMsg(settings, MSG_ERR, "Could not write the tag to the file (%s) " "(%d:%s).\n", tmp_file_path, errno, strerror(errno)); goto error; } /* * Read the audio file block by block until the end is reached. Each block * is written to the tmp file. */ while (feof(audio_file) == 0) { read_count = fread(audio_buffer, 1, BAR_FLY_COPY_BLOCK_SIZE, audio_file); if ((read_count != BAR_FLY_COPY_BLOCK_SIZE) && (feof(audio_file) == 0)) { BarUiMsg(settings, MSG_ERR, "Failed to read the audio file (%s) " "(%d:%s).\n", file_path, errno, strerror(errno)); goto error; } write_count = fwrite(audio_buffer, 1, read_count, tmp_file); if (write_count != read_count) { BarUiMsg(settings, MSG_ERR, "Failed to write to the tmp file " "(%s).\n", tmp_file_path); goto error; } } /* * The entire contents of the audio file was copied to the tmp file. Close * the two files. */ fclose(tmp_file); tmp_file = NULL; fclose(audio_file); audio_file = NULL; /* * Overwrite the audio file with the tmp file. */ status_int = rename(tmp_file_path, file_path); if (status_int != 0) { BarUiMsg(settings, MSG_ERR, "Could not overwrite the audio file " "(%d:%s).\n", errno, strerror(errno)); goto error; } goto end; error: /* * Delete the tmp file if it exists. */ unlink(tmp_file_path); exit_status = -1; end: if (tag_buffer != NULL) { free(tag_buffer); } if (audio_file != NULL) { fclose(audio_file); } if (tmp_file != NULL) { fclose(tmp_file); } return exit_status; }
int BarFlyID3WriteFile(char const* file_path, struct id3_tag const* tag, BarSettings_t const* settings) { int exit_status = 0; int status_int; id3_length_t size1; id3_length_t size2; id3_byte_t* tag_buffer = NULL; FILE* audio_file = NULL; int tmp_file = -1; uint8_t audio_buffer[BAR_FLY_COPY_BLOCK_SIZE]; char tmp_file_path[FILENAME_MAX]; strncpy(tmp_file_path, settings->audioFileDir, strlen(settings->audioFileDir)+1); strncat(tmp_file_path, "/pianobarfly-XXXXXX", 19+1); size_t read_count; size_t write_count; /* * For starters libid3tag kinda sucks. It will only write a tag to a file * if the new tag is the same size as the old tag. Which in this case, * since there is no tag, will never work. So writing of the tag to the * file has to be done manually. */ /* * Render the tag to a buffer that can then be written to the audio file. */ size1 = id3_tag_render(tag, NULL); tag_buffer = malloc(size1); if (tag_buffer == NULL) { BarUiMsg(settings, MSG_ERR, "Failed to allocate memory (bytes = %li).\n", size1); goto error; } size2 = id3_tag_render(tag, tag_buffer); if (size1 != size2) { BarUiMsg(settings, MSG_ERR, "Invalid tag size (expected = %li, " "recevied = %li).\n", size1, size2); goto error; } /* * Prepending data to a file is not trivial in C. Here the approach taken * is to create a temporary file, write the tag to the beginning of the * file, copy the audio file block by block to the tmp file, then overwrite * the audio file with the tmp file. This was done in order to minimize the * chance of ending up with a broken audio file in case the program stopped * durring this process. */ /* * Open the audio file. */ audio_file = fopen(file_path, "rb"); if (audio_file == NULL) { BarUiMsg(settings, MSG_ERR, "Could not read the audio file (%s) " "(%d:%s).\n", file_path, errno, strerror(errno)); goto error; } /* * Open the tmp file. * * Assigning the return value of tmpnam() to a junk pointer to get the * compiler to be quiet. */ tmp_file = mkstemp(tmp_file_path); if (tmp_file == -1) { BarUiMsg(settings, MSG_ERR, "Could not open the temporary file (%s) " "(%d:%s).\n", tmp_file_path, errno, strerror(errno)); goto error; } /* * Write the tag to the tmp file. */ write_count = write(tmp_file, tag_buffer, size2); if (write_count != size2) { BarUiMsg(settings, MSG_ERR, "Could not write the tag to the file (%s) " "(%d:%s).\n", tmp_file_path, errno, strerror(errno)); goto error; } /* * Read the audio file block by block until the end is reached. Each block * is written to the tmp file. */ while (feof(audio_file) == 0) { read_count = fread(audio_buffer, 1, BAR_FLY_COPY_BLOCK_SIZE, audio_file); if ((read_count != BAR_FLY_COPY_BLOCK_SIZE) && (feof(audio_file) == 0)) { BarUiMsg(settings, MSG_ERR, "Failed to read the audio file (%s) " "(%d:%s).\n", file_path, errno, strerror(errno)); goto error; } write_count = write(tmp_file, audio_buffer, read_count); if (write_count != read_count) { BarUiMsg(settings, MSG_ERR, "Failed to write to the tmp file " "(%s).\n", tmp_file_path); goto error; } } /* * The entire contents of the audio file was copied to the tmp file. Close * the two files. */ close(tmp_file); tmp_file = -1; fclose(audio_file); audio_file = NULL; /* * Overwrite the audio file with the tmp file. */ status_int = rename(tmp_file_path, file_path); if (status_int != 0) { BarUiMsg(settings, MSG_ERR, "Could not overwrite the audio file " "(%d:%s).\n", errno, strerror(errno)); goto error; } goto end; error: /* * Delete the tmp file if it exists. */ unlink(tmp_file_path); exit_status = -1; end: if (tag_buffer != NULL) { free(tag_buffer); } if (audio_file != NULL) { fclose(audio_file); } if (tmp_file != -1) { close(tmp_file); } return exit_status; }