概要

最近は、CDを簡単にリッピングできしかもタイトルなどもインターネットから自動的に取得され非常に便利です。
リッピングした楽曲は個人で聞く範囲で、カーナビやスマートフォンに入れておりますが、パソコンと違いタイトルに長い文字を表示することは困難です。 インターネットから取得されたアーティスト名等の中には全角英数字があり、見た目も文字幅的にもいまいちです。したがって、タイトルの大文字を小文字に変換するプログラムを作成しました。
本プログラムはコマンドラインで動作します。
指定されたフォルダーを検索しタイトル、アルバムタイトル、アーティスト名に全角英数が見つかった場合半角英数に変換します。
使い方

wmchg3 [フォルダ名]

ソースコード

wmchg3.cpp

// wmaファイルのタイトル、アルバムタイトル、アーティストを全角英数から半角英数に変換します。
// サブディレクトを自動的に検索し複数のファイルを同時に変換します。
// Visual C++ 2008 Unicode 32/64bit
// 2014/04/06

#include <windows.h>
#include <stdio.h>
#include <locale.h>
#include <tchar.h>
#include <wmsdk.h>
#include "list.hpp"

#pragma comment(lib,"wmvcore.lib")

struct FILE_LINK{
	TCHAR* full_name;			//	ファイル名
	TCHAR* sname;
	FILE_LINK* next;	//	次のファイル名へのポインタ
	FILE_LINK(){
		sname=full_name=0;
		next=0;
	}
};

struct FILE_LIST : public LIST{
	void append(FILE_LINK* p){
		LIST::append((void*)p);
	}
	FILE_LINK* next(void){
		return (FILE_LINK*)LIST::next();
	}
	FILE_LINK* first(void){
		return (FILE_LINK*)LIST::first();
	}
};

//	再帰呼び出しによりサブディレクトリ内の指定ファイルを検索する
//	パス名の最後が:で終わる場合は、指定ドライブのカレントディレクトリ内を検索
//	パス名の最後が\の場合は、ディレクトリ内を検索

int get_dir_list(FILE_LIST& list,TCHAR* pass,TCHAR* card){
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	TCHAR find_pass[MAX_PATH];
	TCHAR find_top_pass[MAX_PATH];
	TCHAR sub_pass[MAX_PATH];
	TCHAR make_pass[MAX_PATH];
	int num=0;	//	見つかったファイル数
	int l=_tcslen(pass);
	int c=pass[l-1];	//	パスの最後の文字
	_tcscpy_s(find_top_pass,sizeof(find_top_pass)/sizeof(TCHAR),pass);
	if(c!=_T('\\') && c!=_T(':')){
		find_top_pass[l]=_T('\\');
		find_top_pass[l+1]=_T('\0');
	}

	_stprintf_s(find_pass,sizeof(find_pass)/sizeof(TCHAR),_TEXT("%s*.*"),find_top_pass);	//	サブディレクトリの検索
	hFind = FindFirstFile(find_pass, &FindFileData);
	if(hFind != INVALID_HANDLE_VALUE){
		do{
			if( (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
				if(_tcscmp(FindFileData.cFileName,_TEXT("..")) && _tcscmp(FindFileData.cFileName,_TEXT(".")) ){	//	親ディレクトリは無視する
					_stprintf_s(sub_pass,sizeof(sub_pass)/sizeof(TCHAR),_TEXT("%s%s"),find_top_pass,FindFileData.cFileName);				
					num+=get_dir_list(list,sub_pass,card);	//	サブディレクトリの中を検索
				}
		}while( FindNextFile(hFind,&FindFileData) );
	}
	FindClose(hFind);
	int pass_len=(int)_tcslen(find_top_pass)+1;
	_stprintf_s(find_pass,sizeof(find_pass)/sizeof(TCHAR),_TEXT("%s%s"),find_top_pass,card);	//	ファイルの検索
	hFind = FindFirstFile(find_pass, &FindFileData);
	if(hFind != INVALID_HANDLE_VALUE){
		do {
			if( !(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
				++num;
				_stprintf_s(make_pass,sizeof(make_pass)/sizeof(TCHAR),_TEXT("%s%s"),find_top_pass,FindFileData.cFileName);
				size_t len=_tcslen(make_pass);
				TCHAR* n=new TCHAR[len+1];
				_tcscpy_s(n,len+1,make_pass);
				FILE_LINK* p=new FILE_LINK;
				p->full_name=n;
				p->sname=n+pass_len;
				p->next=0;
				list.append(p);
			}
		}while( FindNextFile(hFind,&FindFileData) );
		return num;
	}else
		return num;
}		

//	全角文字を半角に変換する 変換された文字があった場合はtrueを返す

bool zen2han(TCHAR* s){
	bool f=false;
	TCHAR* p=s;

	while(*p){
		if(0xff01<=*p  && *p<=0xff5d){	//	全角数字英字記号の場合
			*p-=0xff00-0x20;
			f=true;
		}else if(*p==_T(' ')){
			*p=_T(' ');
			f=true;
		}
		++p;
	}
	return f;
}

bool wma_title_chg(IWMMetadataEditor* pEditor,TCHAR* fname){

	bool flag=false;
	HRESULT hr;
	IWMHeaderInfo3* pHeaderInfo=NULL;

	hr=pEditor->Open(fname);
	pEditor->QueryInterface(IID_IWMHeaderInfo3,(void**)&pHeaderInfo);

	WORD wStreamNum = 0;
	WMT_ATTR_DATATYPE Type;
	WORD cbLength;

	TCHAR* getname;
	TCHAR pwszTitle[1024];

	Type=WMT_TYPE_STRING;
	getname=_TEXT("Title");
	//	タイトルの文字数を取得
	hr=pHeaderInfo->GetAttributeByName(&wStreamNum,getname,&Type, NULL, &cbLength );
	if( ! FAILED(hr)){
		if( cbLength){
		//	タイトルを取得
			hr = pHeaderInfo->GetAttributeByName( &wStreamNum, getname, &Type,(BYTE *) pwszTitle, &cbLength );
			if( !FAILED( hr ) ){
				if(zen2han(pwszTitle)){
			//	タイトルを変更
					hr=pHeaderInfo->SetAttribute(wStreamNum,getname,Type, (BYTE*)pwszTitle, cbLength );
					flag=true;
				}
			}
		}
	}

	getname=_TEXT("WM/AlbumTitle");
	//	タイトルの文字数を取得
	hr=pHeaderInfo->GetAttributeByName(&wStreamNum,getname,&Type, NULL, &cbLength );
	if(!FAILED(hr)){
		if( cbLength){
	//	タイトルを取得
			hr = pHeaderInfo->GetAttributeByName( &wStreamNum, getname, &Type,(BYTE *) pwszTitle, &cbLength );
			if( !FAILED( hr ) ){
				if(zen2han(pwszTitle)){
				//	タイトルを変更
					hr=pHeaderInfo->SetAttribute(wStreamNum,getname,Type, (BYTE*)pwszTitle, cbLength );
					flag=true;
				}
			}
		}
	}

	getname=_TEXT("WM/AlbumArtist");
	//	タイトルの文字数を取得
	hr=pHeaderInfo->GetAttributeByName(&wStreamNum,getname,&Type, NULL, &cbLength );
	if(!FAILED(hr)){
		if( cbLength){
			//	タイトルを取得
			hr = pHeaderInfo->GetAttributeByName( &wStreamNum, getname, &Type,(BYTE *) pwszTitle, &cbLength );
			if( !FAILED( hr ) ){
				if(zen2han(pwszTitle)){
				//	タイトルを変更
					hr=pHeaderInfo->SetAttribute(wStreamNum,getname,Type, (BYTE*)pwszTitle, cbLength );
					flag=true;
				}
			}
		}
	}
	pHeaderInfo->Release();
	if(flag==true){
	//	変更を保存
		pEditor->Flush();
		_tprintf(_TEXT("%s\t"),pwszTitle);
	}
	pEditor->Close();
	return flag;
	
}

int _tmain(int argc, TCHAR** argv){
	_tsetlocale(LC_ALL,_TEXT(""));

	HRESULT hr;
	IWMMetadataEditor* pEditor=NULL;
	hr=WMCreateEditor(&pEditor);	// オブジェクトを作成

	FILE_LIST file_list;
	int num;
	TCHAR* pass;
	TCHAR files[32];
	TCHAR* file_p[16];
	int i=0;

	switch(argc){
	case 2:
		pass=argv[1];
		_tcscpy_s(files,sizeof(files)/sizeof(TCHAR),_TEXT("*.wma"));
		break;
	case 3:
		pass=argv[1];
		_tcscpy_s(files,sizeof(files)/sizeof(TCHAR),argv[2]);
		break;
	default:
		_tprintf(_TEXT("wmaファイルのタイトル、アルバムタイトル、アーティストを全角英数から半角英数に変換します。\n"));
		_tprintf(_TEXT("wmchg3 [ディレクトリ名] [ワイルカード]\n"));
		_tprintf(_TEXT("[ワイルカード]省略時は*.*が適用される\n"));
		_tprintf(_TEXT("ワイルカードは*.jpg;*.bmpの様に;で区切ることにより複数指定可能\n"));
		return 1;
	}

	TCHAR* pp=files;
	TCHAR* top=files;
	while(*pp){
		if(*pp==_T(';')){
			*pp=_T('\0');
			file_p[i++]=top;
			top=pp+1;
		}
		++pp;
	}
	if(*top){
		file_p[i++]=top;
	}
	file_p[i]=0;
	i=0;
	while(file_p[i]){
		num=get_dir_list(file_list,pass,file_p[i]);
		++i;
	}
	int n=0;
	FILE_LINK* p=file_list.first();
	while(p){
		if(wma_title_chg(pEditor,p->full_name)==true){
			_tprintf(_TEXT("%s\n"),p->full_name);
			++n;
		}
		p=file_list.next();
	}
	_tprintf(_TEXT("%i個のファイルを変換しました。\n"),n);
	return 0;
}

list.hpp

#ifndef LIST_HPP
#define LIST_HPP 1

class LINK{
	LINK* next;
	LINK* back;
	void* data;
protected:
	LINK(){
		next=0;
		back=0;
		data=0;
	}
	void set(void* d){
		data=d;
	}
	friend class LIST;
	friend int cmp(const void* d,const void* s){
		if((char*)d>(char*)s)
			return 1;
		if((char*)d<(char*)s)
			return -1;
		return 0;
		
	}
};

typedef void* LPVOID;

class LIST{
	LINK* top;	//	リストの先頭
	LINK* end;	//	リストの最後尾
	LINK* pos;	//	リストのカレントポジション
	int max;	//	リストの要素数
protected:
	LIST(){
		pos=top=end=0;
		max=0;
	}
	void append(void* d){	//	リストの最後尾にデータを追加
		LINK* p=new LINK;
		++max;
		p->data=d;
		if(top==0){
			pos=top=end=p;
		}else{
			end->next=p;
			p->back=end;
			end=p;
		}
	}
	void* next(void){	//	カレントポジジョンからデータを得た後、カレントポジションを1個進める。
		LINK* t=pos;
		if(pos==0){
			return 0;
		}else{
			pos=pos->next;
			return t->data;
		}
	}
	void* first(void){	//	カレントポジションを先頭に移動させ先頭データを得た後、カレントポジションを1個進める。
		pos=top;
		return next();
	}
	void all_del(void){	//	リスト全部を削除する
		while(top){
			LINK* t=top->next;
			delete top;
			top=t;
		}
		top=end=pos=0;
		max=0;
	}
	void repeat_del(int(*cmp )(const void* d,const void* s)){	//	ソートされたリストから重複する要素を削除
		LINK* lp=top;
		if(lp){
			LINK* bp=0;
			while(lp){
				if(bp){
					if( (*cmp)((void**)&lp->data,(void**)&bp->data) ){	//	前の要素と異なる場合
						bp=lp;
					}else{	//	前の要素と同じ場合
						LINK* t=lp->next;
						del(lp);
						--max;
						lp=t;
						continue;
					}
				}else
					bp=lp;
				lp=lp->next;
			}
		}
	}	
	void qsort(int(*cmp )(const void* d,const void* s)){
		if(max){
			int n=0;
			LPVOID* vec=new LPVOID[max];	//	リストの要素を配列にコピーする
			if(vec==0)
				return;
			void* p;
			p=first();
			while(p){
				vec[n++]=p;
				p=next();
			}
			::qsort((void*)vec,max,sizeof(LPVOID),cmp);
			LINK* lp=top;						//	配列をリストにコピー
			n=0;
			while(lp){
				lp->set(vec[n++]);
				lp=lp->next;
			}
			delete vec;
		}
	}
	void del(LINK*	p){	// リストからpを削除する
		if(p){
			if(p==top){
				LINK* t=top;
				top=top->next;
				top->back=0;
				if(pos==p){
					pos=p->next;
				}
				delete p;
				return;
			}
			if(p==end){
				LINK* t=end;
				end=end->back;
				end->next=0;
				delete p;
				return;
			}
			LINK* t=p;
			p->back->next=p->next;
			p->next->back=p->back;
			if(pos==p){
				pos=p->next;
			}
			delete t;
		}

	}
public:
	int get_max(void){
		return max;
	}
};

#endif

ソース・実行ファイル

Visual C++ 2008用 ソース・実行ファイルのダウンロード