mp3 파일을 뜯어보는 C++ 코드를 작성했는데, 파일의 대부분이 0으로 출력됩니다.

HDNua의 이미지

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를 읽는 보다 정확한 방법이 있는지 궁금합니다.

HDNua의 이미지

binary로 읽으려면 get 대신 read를 써야하는군요.. 간단한 문제였네요.
소중한 답변 감사합니다.

저는 이렇게 생각했습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.