mp3 파일을 뜯어보는 C++ 코드를 작성했는데, 파일의 대부분이 0으로 출력됩니다.
mp3 파일 태그를 수정하는 프로그램을 만들고 싶은데 적당한 C# 라이브러리를 못 찾아서, C# mp3 태그 관리 라이브러리를 직접 만드려는 목적으로 먼저 C++을 이용하여 mp3의 바이너리를 보고 있었습니다.
그런데 Windows 7에 기본으로 제공되는 음악 세 개를 열어보니 거의 대부분의 바이트가 0이었습니다. 다음은 이를 구현하는 코드입니다.
(원래는 사진을 첨부하고 싶었지만 잘 안 되는군요)
#include <iostream> #include <fstream> #include <exception> #include <sstream> #include <cstring> #include <iomanip> ////////////////////////////////////////////////////////////////// // // Title: Edit MP3 with metadata's indexes // Date: 2015. 04. 16. 18:19 // Author: HDNua (DoYeong Han, from Republic of Korea) // E-mail: <a href="mailto:rbfwmqwntm@naver.com" rel="nofollow">rbfwmqwntm@naver.com</a>, <a href="http://blog.naver.com/rbfwmqwntm //" rel="nofollow">http://blog.naver.com/rbfwmqwntm //</a> ////////////////////////////////////////////////////////////////// // definitions typedef char byte; typedef std::string Exception; // constants & definition const int MAX_CACHE_SIZ = 20 * 1024 * 1024; // fields byte cache[MAX_CACHE_SIZ] = ""; int cache_size = 0; // methods std::string get_metadata_str(byte *bin, int start, int len=1); // print out to os from bin[start] to bin[end] void set_metadata_str(const char *str, byte *bin, int start, int range); // replace byte array's data with str int read_bin(byte *dst_bin, const char *filename); // read file's binary data, save to byte array and returns read byte size bool is_id3v2(const byte *bin); // check whether the binary data is ID3v2 format // bench int main(void) { try { const std::string emptystr = ""; std::string dir = "song/"; std::string name = "Kalimba"; std::string ext = ".mp3"; std::string filename = dir + name + ext; //dir + idx + ext; read_bin(cache, filename.c_str()); if (is_id3v2(cache) == false) { throw Exception("invalid mp3 file data"); } std::stringstream out_hexa; std::stringstream out_byte; std::stringstream out; std::ostream &os = out; // get default mp3 file's data os<<filename<<": "<<std::endl; os<<"- mp3 size: "<<(cache_size / 1024.0)<<"KB ("<<cache_size<<" bytes)"<<std::endl; // show binary data map const int width = 40; const int width_i = 8; int unit = 150000; int start = unit * 0; int end = unit * 1; for (int i=start; i<end; ++i) { if (i % width == 0) { os<<out_hexa.str()<<"| "<<out_byte.str()<<"|"<<std::endl; out_hexa.str(emptystr); out_byte.str(emptystr); os.width(width_i); os<<std::hex<<i<<"("; os.width(width_i); os<<std::dec<<i<<"): "; } int value = (int)(cache[i] & 0xff); char ch = cache[i] ? cache[i] : '.'; out_hexa.width(2); out_hexa.fill('0'); out_hexa<<std::hex<<value<<' '; out_byte<<ch; } std::cout<<out.str(); /* // get mp3 file's metadata os<<"- TRACK: ["<<get_metadata_str(cache, 62)<<']'<<std::endl; os<<"- YEAR: ["<<get_metadata_str(cache, 74, 4)<<']'<<std::endl; os<<"- ALBUM: ["<<get_metadata_str(cache, 89, 3)<<']'<<std::endl; os<<"- GENRE: ["<<get_metadata_str(cache, 103, 6)<<']'<<std::endl; os<<"- ALBUMARTIST: ["<<get_metadata_str(cache, 120, 18)<<']'<<std::endl; os<<"- TITLE: ["<<get_metadata_str(cache, 149, 8)<<']'<<std::endl; os<<"- ARTIST: ["<<get_metadata_str(cache, 168, 6)<<']'<<std::endl; // change mp3 files metadata set_metadata_str("6", cache, 62, 1); set_metadata_str("1999", cache, 74, 4); set_metadata_str("하!", cache, 89, 3); set_metadata_str("새장르", cache, 103, 6); set_metadata_str("앨범가수", cache, 120, 18); set_metadata_str("타이틀", cache, 149, 8); set_metadata_str("가수", cache, 168, 6); // create new file and write modified metadata to it os<<"Creating new file and writing modified metadata: "; std::string newFileIdx = "6"; std::string newFileName = dir + newFileIdx + ext; std::ofstream fout(newFileName.c_str()); fout.write(cache, cache_size); fout.close(); os<<"Complete"<<std::endl; */ } catch (Exception &ex) { std::cerr<<ex<<std::endl; } return 0; } // implementations void set_metadata_str(const char *str, byte *bin, int start, int range) { strncpy(&bin[start], str, range); } std::string get_metadata_str(byte *bin, int start, int len) { // print out to os from bin[start] to bin[end] std::stringstream ss; for (int i=start, end=start+len; i<end; ++i) { char ch = bin[i]; ss<<ch; } return ss.str(); } int read_bin(byte *dst_bin, const char *filename) { // read file's binary data, save to byte array and returns read byte size std::ifstream fin(filename, std::ios::binary); if (fin.fail()) { throw Exception("cannot find file: " + std::string(filename)); } fin.get(dst_bin, MAX_CACHE_SIZ); fin.seekg(0, fin.end); cache_size = fin.tellg(); fin.seekg(0, fin.beg); return cache_size; } bool is_id3v2(const byte *bin) { // check whether the binary data is ID3v2 format // check binary's first 3 bytes are equals to string 'ID3' return (bin[0] == 'I' && bin[1] == 'D' && bin[2] == '3'); }
프로젝트 생성 후 실행 파일이 있는 위치에 Kalimbar.mp3를 넣으시면 됩니다. ID3v2 기준으로 구현했습니다.
제 코드의 구현이 잘못되었는지, mp3 파일 자체가 암호화되어있어서 그냥 ifstream으로 열면 0으로 채워지는지 잘 모르겠습니다. (시간에 따라 소리의 높낮이나 강약 등이 다를텐데 모두 0이라는 게 상식적으로 이해되지 않기도 하구요.)
ID3v2가 frame 별로 구성이 되어있어서 태그 정보가 수정될 때마다 프레임이 추가된다고 알고 있는데, 저는 이로 인한 오버헤드를 방지하기 위해 음악 파일의 소리 정보를 복사한 후, ID3v2로 frame을 새로 만들고 그 파일의 적절한 위치에 소리 정보를 붙여넣을 생각이라 소리 정보가 어디서부터 시작해서 어떻게 끝나는지를 꼭 봐고 싶습니다.
mp3를 제대로 배운 적이 없고 어떻게 검색해야 답이 나올 지 알 수 없어 커뮤니티에 질문합니다.
혹시 이에 관해 알고 계시는 분이 있나요?
-----
다시 읽어보니 말이 장황한 것 같아 질문만 다시 한 번 정리하겠습니다.
제 코드가 맞는지(실제로 mp3 파일의 대부분이 0으로 차있는지), 아니면 mp3를 읽는 보다 정확한 방법이 있는지 궁금합니다.
http://www.cplusplus.com/refe
http://www.cplusplus.com/reference/istream/istream/get/
http://www.cplusplus.com/reference/istream/istream/read/
참고하세요.
답변 감사합니다.
binary로 읽으려면 get 대신 read를 써야하는군요.. 간단한 문제였네요.
소중한 답변 감사합니다.
저는 이렇게 생각했습니다.
댓글 달기