スキップしてメイン コンテンツに移動

JavaのID3タグ解析ライブラリ

Javaでmp3ファイルのID3タグを解析するライブラリを色々と調べてみました。参考になれば幸いです。
以下に挙げていくコードは、特に意味のあることをしている訳ではありませんが、 どうやってタグの読み込み、書き換えを行えるかというサンプルとして参考にして頂ければと思います。

MyID3: a Java ID3 Tag Library

import java.io.File;
import java.io.IOException;

import org.cmc.music.common.MusicMetadata;
import org.cmc.music.myid3.MusicMetadataSet;
import org.cmc.music.myid3.MyID3;

/**
 * {@link http://www.fightingquaker.com/myid3/}
 */
public class MyID3Tester {

 public static void main(String[] args) throws IOException {
  File mp3File = new File("C:\\temp\\music.mp3");
  MyID3 id3 = new MyID3();
  MusicMetadataSet src_set = id3.read(mp3File); // read metadata

  if (src_set == null){
   System.out.println("could not read data");
   return;
  }

  // You can extract simplified information
  MusicMetadata metadata = src_set.getSimplified();
  
  System.out.println(metadata.getArtist());
  System.out.println(metadata.getAlbum());
  
  // this doesn't work for me somehow :(
  System.out.println(metadata.getSongTitle());
  System.out.println(metadata.getComment());
 }
}

JID3 - A Java ID3 Class Library Implementation

import java.io.File;

import org.blinkenlights.jid3.ID3Exception;
import org.blinkenlights.jid3.ID3Tag;
import org.blinkenlights.jid3.MP3File;
import org.blinkenlights.jid3.MediaFile;
import org.blinkenlights.jid3.v1.ID3V1Tag;
import org.blinkenlights.jid3.v2.ID3V2_3_0Tag;

/**
 * {@link http://jid3.blinkenlights.org/}

 */
public class JID3Tester
{
    public static void main(String[] args) throws Exception {
        // the file we are going to modify
        File oSourceFile = new File("C:\\temp\\music.mp3");

        // create an MP3File object representing our chosen file
        MediaFile mediaFile = new MP3File(oSourceFile);

        // you can check artist name from v1 tag if the tag is available
        ID3V1Tag tag1 = mediaFile.getID3V1Tag();
        if (tag1 != null) {
            System.out.println(tag1.getArtist());
        }

        // print tags
        System.out.println("****Original tag****");
        printTags(mediaFile.getTags());

        // remove all tags from **file** e.g. persisted
        mediaFile.removeTags();

        // create a v2.3.0 tag object, and set some frames
        // if you want put v1 tag you can achieve similarly
        // ID3V1_1Tag newTag = new ID3V1_1Tag();
        ID3V2_3_0Tag newTag = new ID3V2_3_0Tag();
        newTag.setArtist("new artist");
        mediaFile.setID3Tag(newTag);

        // persist netags to file
        mediaFile.sync();

        // again print tags
        System.out.println("\n****New tag****");
        printTags(mediaFile.getTags());
    }

    // some utility methods etc for your understanding.... hope these are helpful :)
    private static final String album = "Album: ";
    private static final String artist = "Artist: ";
    private static final String title = "Title: ";
    private static final String year = "Year: ";
    private static final String genre = "Genre: ";
    private static final String trackNumber = "Track Number: ";
    private static final String totlaTracks = "Total Tracks: ";

    private static void printTags(ID3Tag[] tags) throws ID3Exception {
        for (int i = 0; i < tags.length; i++) {
            ID3Tag tag = tags[i];
            if (tag instanceof ID3V1Tag) {
                System.out.println("---ID3V1---");
                JID3TagUtils.print((ID3V1Tag) tag);
            } else if (tag instanceof ID3V2_3_0Tag) {
                System.out.println("---ID3V2_3_0---");
                JID3TagUtils.print((ID3V2_3_0Tag) tag);
            }
        }
    }

    public static void clearID3V1Tag(MediaFile mediaFile) throws ID3Exception {
        ID3V1Tag tag = new ID3V1_1Tag();
        mediaFile.setID3Tag(tag);
        mediaFile.sync();
    }

    public static void clearTag(MediaFile mediaFile) throws ID3Exception {
        ID3Tag[] tags = mediaFile.getTags();
        for (int i = 0; i < tags.length; i++) {
            ID3Tag tag = tags[i];
            if (tag instanceof ID3V1Tag) {
                ID3V1Tag tag1 = (ID3V1Tag) tag;
                tag1.setAlbum("");
                tag1.setArtist("");
                tag1.setComment("");
                tag1.setGenre(null);
                tag1.setYear("");
            } else if (tag instanceof ID3V2_3_0Tag) {
                ID3V2_3_0Tag tag2 = (ID3V2_3_0Tag) tag;
                tag2.setAlbum("");
                tag2.setArtist("");
                tag2.setComment("");
                tag2.setGenre("");
                tag2.setYear(0);
            }
        }
    }

    public static void print(ID3V1Tag tag) {
        System.out.println(album + tag.getAlbum());
        System.out.println(artist + tag.getArtist());
        System.out.println(title + tag.getTitle());
        System.out.println(year + tag.getYear());
        System.out.println(genre + tag.getGenre());
    }

    public static void print(ID3V2Tag tag) throws ID3Exception {
        System.out.println(album + tag.getAlbum());
        System.out.println(artist + tag.getArtist());
        System.out.println(title + tag.getTitle());
        System.out.println(genre + tag.getGenre());
        try {

            System.out.println(totlaTracks + tag.getTotalTracks());
        } catch (ID3Exception e) {
            // simply ignore because the library throw exception when the tag is
            // simply missing
        }

        try {
            System.out.println(trackNumber + tag.getTrackNumber());
        } catch (ID3Exception e) {
        }
        try {
            System.out.println(year + tag.getYear());
        } catch (ID3Exception e) {
        }
    }
}

Java ID3 Tag Library

import java.io.File;
import java.io.IOException;

import org.farng.mp3.MP3File;
import org.farng.mp3.TagConstant;
import org.farng.mp3.TagException;
import org.farng.mp3.id3.AbstractID3v2;
import org.farng.mp3.id3.AbstractID3v2Frame;
import org.farng.mp3.id3.FrameBodyTIT2;
import org.farng.mp3.id3.ID3v2_2;

/**
 * {@link http://javamusictag.sourceforge.net/}
 */
public class JavaID3TagTester {
 public static void main(String[] args) throws IOException, TagException {
  File sourceFile =  new File("C:\\temp\\music.mp3");
  MP3File mp3file = new MP3File(sourceFile);
  
  if(mp3file.hasID3v2Tag()){
   AbstractID3v2 tag = mp3file.getID3v2Tag();
   System.out.println("Has ID3v2 tag");
   // get song  title
   System.out.println("title:" + tag.getSongTitle());

   AbstractID3v2 tag2 = mp3file.getID3v2Tag();
   System.out.println("comment: " + tag2.getSongComment());

   if(tag instanceof ID3v2_2){
    // override new title
    AbstractID3v2Frame frame = tag2.getFrame("TIT2");
    if(frame != null){
     ((FrameBodyTIT2) frame.getBody()).setText("New Title");
    }
    
    // persist to file
    mp3file.save(TagConstant.MP3_FILE_SAVE_OVERWRITE);
   }
  }
  if(mp3file.hasID3v1Tag()){
   System.out.println("Has ID3v1 tag");
  }
     
 }
}

id3lib

残念ながらこのライブラリは試したことはありません。

Java library MP3

残念ながらこのライブラリは試したことはありません。

コメント

このブログの人気の投稿

Eclipseでコードカバレッジのハイライトを削除する方法

Eclipseには便利なコードカバレッジ表示機能が搭載されていますが、コード内に緑、赤、黄の色付けがされて煩く感じるときもあると思います。 1度カバレッジの色付けが出てしまった後に消す方法の紹介です(方法は簡単)。 下記のキャプチャの青いマーカーで示した「Remove All Sessions」のボタンを押せばすべて消えます。

「特定の文字から始まらない文字列」にマッチする正規表現

「特定の文字から始まらない文字列」 にマッチする正規表現の例です。  以下の例では、Aから始まらない文字列にマッチする正規表現を示しています。 ^(?!A).*$ 私も正規表現の組み方で四苦八苦することがあります。以下の書籍は実践的に様々な正規表現のパターンを例示してくれているので、重宝しています。

ダイソーで買った200円のドライバーセットでHDDを分解

HDDの処分 最近は個人情報の問題もあって、HDDを処分する前にちゃんとデータの消去を気にすることも多くなってきました。消去方法としては大きく分けて下記の3つがあります。 データ消去ソフトでフォーマット HDD内部のプラッタを物理破壊 データ消去を行ってくれる専門の業者や家電量販店(Sofmapやビックカメラで実施していると思います。費用発生。)に持ち込み。 データ消去ソフトでのフォーマットは簡単ですが、欠点として「フォーマットに時間がかかる」「セクタ破損などで中途半端に壊れたディスクのフォーマットができない」などがあります。 またHDD内部のプラッタの物理破壊については、HDDを分解するために、通常のプラスやマイナスドライバーではなく、星形ネジに対応したトルクスドライバーが必要とのこともあって、少し面倒です。 筆者は今回、今後もHDDの廃棄をするだろうなあと思い、思い切って自分で分解して廃棄することにチャレンジしてみました。(家電量販店に持って行くよりも安くできないかというどケチ丸出しですw) HDDの星形ネジ こんなやつです。ちなみに写真はSeagateのST2000DL003というHDDで撮影しました。 トルクスドライバー というわけで、分解のために Amazonでトルクスドライバー を探しました。 調べると T8のもだと使えそう とのことで、いろいろと物色。 セットのものとか T8一本で立派なやつとか 色々あったのですが、HDD壊すだけで800円かぁ(←どケチ)、と思って購入を躊躇。 ネット上で調べると100円ショップのダイソーでも、トルクスドライバーを販売しているとの情報をキャッチ!近所のダイソーに行って、探したところ星形のヘッド交換に対応した精密ドライバーセットがありました。 プラスが10種類、マイナスが8種類、六角が6種類、星形が6種類(今回ほしかったもの)のセットで、何とお値段税抜き200円!、税抜き200円!と安かったので、ダメもとで購入しました。 結論から言うと 買って大正解 でした。 ダイソーの精密ドライバーセット こんな商品です! 星形対応のヘッドを装着するとこんな感じ。ドライバーのグリップもゴムで滑らない様になっていて使いやす...

SQLで特定の文字を組み合わせたランダムな文字列を生成

簡易的な方法として「指定した文字列からランダムに1文字選ぶ」を必要な文字の長さ分concat関数でつなげれば実現できます。 1文字ずつ文字を選ぶので、あまり性能もよくない上、セキュリティ的な観点からのランダム性も担保されていないので、あくまで開発中に必要になった時に使う程度が無難だと思います。 下記に英数字大文字小文字を含んだランダムな3文字の文字列を生成するクエリを示します。 # RAND関数で指定した文字列からランダムに1文字選択。 # 下記の例の62の部分はa~z、A~Z、1~9の文字数の合計値を入れた結果 SELECT CONCAT( SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1), SUBSTRING('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789', FLOOR(RAND() * 62 + 1), 1) ) AS random_string;

PHPの配列(array)のメモリ使用量の考察

はじめに 最近PHP上に大量のデータをメモリ上に展開していたのですが、配列(array)の形式(連想配列 or 単純な配列)や配列の要素のデータ構造(数字、配列、文字列など)で大きくメモリ使用量に差が出てくることに気づき、簡単なプログラムを組んで調べてみました。 あくまで筆者の環境での結果なので、細かい数値は参考程度に見てください。 測定環境と方法 OS: Windows 10 PHP 7.4.5 (php-7.4.5-nts-Win32-vc15-x64) 配列に要素を追加するプログラムを書いて、PHPのmemory_get_usage(true)関数を使って実メモリ使用量を計測しました。 計測結果 No. 方式 1MB当たり作成できる 要素数 プログラム 補足 1 キーも値も整数の配列 (整数IDを想定) 28571 // 2,000,000 / 70MB $row = []; for($i = 0; $i < 2000000; $i++) { $row[] = $i; } No.2~6でテストしたプログラム中の要素数は200,000。これだけ一桁多い! 2 キーが文字列、値が整数の連想配列 8333 // 200,000 / 24MB $row = []; for($i = 0; $i < 200000; $i++) { $row[$i.'_key_string'] = $i; } キーの文字列が長い方がメモリ使用量多くなる。 3 キーが整数、値が連想配列の配列 DBから取得してきたデータを想定 2325 // 200,000 / 86MB $row = []; for($i = 0; $i < 200000; $i++) { row[] = ['id' => $i]; } 4 キーが整数、値が連想配列の配列(配列に複数の値を保持) DBから取得してきたデータを想定 2127 // 200,000 /...

ADODB.streamオブジェクトを使って文字列とByte配列を相互変換(Excel VBA)

ADODB.streamオブジェクトを使って文字列をByte配列に変換するコードのサンプルです。 ExcelVBAでADODB.streamを使う際には、 1. ExcelのMicrosoft Visual Basic エディタのメニューバーから「ツール->参照設定」とたどる。 2. 表示されたダイアログからMicrosoft ActiveX Data Objectsにチェックを入れる。 という手順が必要です。 文字列からByte配列へ Private Function ADOS_EncodeStringToByte(ByVal cset As String, ByRef strUni As String) As Byte() On Error GoTo e Dim objStm As ADODB.stream: Set objStm = New ADODB.stream objStm.Mode = adModeReadWrite objStm.Open objStm.Type = adTypeText objStm.Charset = cset objStm.WriteText strUni objStm.Position = 0 objStm.Type = adTypeBinary Select Case UCase(cset) Case "UNICODE", "UTF-16" objStm.Position = 2 Case "UTF-8" objStm.Position = 3 End Select ADOS_EncodeStringToByte = objStm.Read() objStm.Close Set objStm = Nothing Exit Function e: Debug.Print "Error occurred while encoding characters" & Err.Description If objStm Is No...

Visual Studio 2010 SP1のアンインストール

Visual Studio 2013に乗り換えるためにVisual Studio 2010をアンインストールしようとしたところで問題発生。。。 先にVisual Studio 2010本体をアンインストールした後、Visual Studio 2010 SP1をアンインストールできなくて困っていました。 Google先生で調べたところ、以下の情報が見つかり、書かれていた通り実施したところ無事Visual Studio 2010 SP1のアンインストールに成功しました。 How to uninstall/remove Visual Studio SP1 アンインストール手順は以下の通りです。 http://www.microsoft.com/en-gb/download/details.aspx?id=23691 からMicrosoft Visual Studio 2010 Service Pack 1 (Installer)をダウンロード VS10sp1-KB983509.exeというファイル名でダウンロードされる(はず)。 コマンドプロンプトから以下のコマンドを実行 (以下の例は、c:\tempにVS10sp1-KB983509.exeがある場合) c:\temp\VS10sp1-KB983509.exe /uninstall /force ダイアログが立ち上がるので、アンインストールを選択して次へ進めばOK!

MySQL: SELECTの結果をUNIONして ORDER BYする際の最適化方法

SELECTの結果をUNIONして ORDER BY する際には下記の点に注意する必要があります。 無駄なメモリ消費 ソートにINDEXが利かない (≒CPU負荷増大) 対応策 可能であればPush-down Condition (各サブクエリ内でORDER BY, LIMIT, OFFSETを適用してからUNION, ORDER BYを実行する)を利用することで、 パフォーマンスを改善できる場合があります。 下記に例を示します。 もともとのクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) UNION ALL SELECT tableB.column1, tableB.column2 FROM tableB WHERE (条件) ) AS tmp ORDER BY tmp.column1, tmp.column2 LIMIT 100, 20 Push-down Conditionを用いて書き直したクエリ SELECT tmp.* FROM ( SELECT tableA.column1, tableA.column2 FROM tableA WHERE (条件) ORDER BY tableA.column1, tableA.column2 LIMIT 30 # ただしこのPush-down Conditionの手法も下記の場合は、効果が半減しますので注意が必要です。 OFFSETの値が大きい場合は、結局全結果セットUNIONと変わらない サブクエリ内のソートで、INDEXが効かない場合

PHPでファイルを指定した行数ごとに分割

ファイルを指定した行数ごとに分割するためには、Linuxのsplitコマンドを使えば簡単に実現できます。 PHPではexec関数にsplitコマンドを渡して実行すればよいですが、下記の弱点があります。 Linuxのコマンドに依存 (PHPの場合はほとんどLinux環境で動作させることが普通なのでそこまで問題にならないかも知れません)。 exec関数は慎重に引数を渡さないと、OSコマンドインジェクション脆弱性を引き起こす可能性がある。 そこで、今回はPHPでファイルを指定した行数ごとに分割するプログラムを書いてみました。 <?php class FileSplitter { private $lines; private $fileCount; public function split($filePath, $linesPerFile, $outputDir) { $this->fileCount = 0; $this->lines = null; $file = new \SplFileObject($filePath); $lineCount = 0; try{ while (!$file->eof()) { if($lineCount % $linesPerFile === 0) { $this->writeToFile($this->generateOutputFilePath($outputDir, $file)); } $this->lines[] = $file->fgets(); $lineCount++; } $this->writeToFile($this->generateOutpu...

MySQLでGROUP_CONCATしたフィールドに対して疑似的にLIMITを実現する方法

MySQLでGROUP_CONCATしたフィールドに対して疑似的にLIMITを実現するには、GROUP_CONCATで生成された文字列に対して、SUBSTRING_INDEXを使って文字列を切り出す方法が簡単です。 # 下記はid, codeをカラムに持つテーブルで、codeカラムでGROUP BYして、codeごとにidをlimitで取得する例です。 SELECT code ,SUBSTRING_INDEX(GROUP_CONCAT(id ORDER BY id DESC), ',', :limit) # :limitの部分に取得したい件数を指定。 FROM table GROUP BY code 長所は、下記のように条件を指定して、LIMIT句で取得件数を指定したクエリを何度も発行する必要がないところです。 特に、一回あたりクエリの発行コストが高い場合は、GROUP_CONCATを使って一度に取得したほうが最終的な実行時間をかなり節約することができます。 SELECT id FROM table WHERE code = 'A' ORDER BY id DESC LIMIT :limit; ただし、短所も多いので、使用する際は、これらの短所について十分に考慮したうえで使ってください。 GROUP_CONCATで生成された文字列に対して、SUBSTRING_INDEXを使って文字列を切り出すという文字列処理なので、無駄が多い。 特にGROUP_CONCATで生成された元の文字列が長い場合。 GROUP_CONCATの区切り文字が、GROUP_CONCATされる元の文字列に含まれていると正しくLIMITされない。 例:GROUP_CONCATされる元の文字列にカンマが含まれているのに、カンマを区切り文字で指定している場合。 GROUP_CONCATの最大文字数制限を超えた場合は、機能しない。 MySQLのGROUP_CONCATの最大文字数制限は、「SHOW VARIABLES LIKE '%group_concat%';」で調べられます。デフォルト値は1024のようです。 ...