foobar2000の楽曲再生データを編集するためのモジュール

foobar2000の楽曲データをいじくるためのモジュール

作ったので公開。

コード

foobar2000のPlaybackStatisticsでエクスポートできるXMLをいじくるためのメソッド集的なモジュールを作成した。

一挙公開する。(2019/9/16更新)

リスト1 標準モジュールFB2KUtil
Option Explicit

'Constants'
'2012年1月1日 0時00分00秒の日付時刻値の上11桁'
Private Const STANDARD_DATE_VALUE As Currency = 12969817200#
Private Const STANDARD_DATE As Date = #1/1/2012#
'一日あたりの秒数'
Private Const DAY_BY_SECONDS As Currency = 86400
Private Const HOUR_BY_SECONDS As Currency = 3600
Private Const MINUTE_BY_SECONDS As Currency = 60

'///XMLファイルの中身を作る'
Public Function createXMLContents( _
            ByVal contents As String) As String
  Const XML_DECLARATION As String = _
        "" & _
        vbCrLf
  Dim ret As String
  If Not (Right(contents, 2) = vbCrLf) Then _
    contents = contents & vbCrLf
  ret = XML_DECLARATION
  ret = ret & contents
  ret = ret & ""
  createXMLContents = ret
End Function
            
'///データを連結してXML要素を作る'
Public Function getXMLElement( _
            ByVal targetID As String, _
            ByVal count As Long, _
            ByVal fpDate As Date, _
            ByVal fpTime As Date, _
            ByVal lpDate As Date, _
            ByVal lpTime As Date, _
            ByVal aDate As Date, _
            ByVal aTime As Date, _
            ByVal rating As Long) As String
  Dim idStr As String
  idStr = vbTab & ""
  getXMLElement = idStr & countStr & fpStr & lpStr & aStr & ratingStr
End Function

'///FB2Kの楽曲データからID値を取得する'
Public Function getID( _
            ByVal targetElement As String) As String
  Const ADD_COUNT As Long = 11  '最初の「<」の11字後ろがIDの先頭'
  Const ID_LENGTH As Long = 16  'ID値は16文字'
  Dim ret As String
  Dim startPos As Long
  startPos = InStr(1, targetElement, "<") + ADD_COUNT
  ret = Mid(targetElement, startPos, ID_LENGTH)
  getID = ret
End Function

'///FB2Kの楽曲データからFirstPlayedのDate型データを取得する'
Public Function getFirstPlayDateTime( _
            ByVal targetElement As String) As Date
  Dim ret As Date
  ret = 0
  Dim targetSerial As Currency
  targetSerial = getFirstPlayedSerial(targetElement)
  'FirstPlayedの値がないときは、0が返る'
  If targetSerial = 0 Then GoTo Finalizer
  ret = getDateTime(targetSerial)
Finalizer:
  getFirstPlayDateTime = ret
End Function

'///FB2Kの楽曲データからLastPlayedのDate型データを取得する'
Public Function getLastPlayDateTime( _
            ByVal targetElement As String) As Date
  Dim ret As Date
  ret = 0
  Dim targetSerial As Currency
  targetSerial = getLastPlayedSerial(targetElement)
  'LastPlayedの値がないときは、0が返る'
  If targetSerial = 0 Then GoTo Finalizer
  ret = getDateTime(targetSerial)
Finalizer:
  getLastPlayDateTime = ret
End Function

Public Function getAddedDateTime( _
            ByVal targetElement As String) As Date
  Dim ret As Date
  ret = 0
  Dim targetSerial As Currency
  targetSerial = getAddedSerial(targetElement)
  'Addedの値がないときは、0が返る'
  If targetSerial = 0 Then GoTo Finalizer
  ret = getDateTime(targetSerial)
Finalizer:
  getAddedDateTime = ret
End Function

'///日付時刻値を求める'
Private Function getDateTimeValue( _
             ByVal targetDate As Date, _
             ByVal targetTime As Date) As String
  Const HOUR_TO_SECONDS As Currency = 3600
  Const MINUTE_TO_SECONDS As Currency = 60
  Dim ret As Currency
  '基準日との日数差を計算する'
  Dim dateDiff As Currency
  dateDiff = DateSerial(Year(targetDate), Month(targetDate), Day(targetDate)) - _
             DateSerial(Year(STANDARD_DATE), Month(STANDARD_DATE), Day(STANDARD_DATE))
  '日数差を秒に換算する'
  dateDiff = dateDiff * DAY_BY_SECONDS
  '基準日の値に加算する'
  ret = STANDARD_DATE_VALUE + dateDiff
  '時間を秒に換算して値に加算する'
  Dim timeValue As Currency
  timeValue = Hour(targetTime) * HOUR_TO_SECONDS + _
              Minute(targetTime) * MINUTE_TO_SECONDS + _
              Second(targetTime)
  ret = ret + timeValue
  '文字列に変換して下7桁を0で埋める'
  getDateTimeValue = CStr(ret) & "0000000"
End Function

'///FB2Kの楽曲データからFirstPlayedのシリアル値を取得する'
Private Function getFirstPlayedSerial( _
            ByVal targetElement As String) As Currency
  Dim ret As Currency
  ret = getDateTimeSerial(targetElement, "FirstPlayed=""")
  getFirstPlayedSerial = ret
End Function

'///FB2Kの楽曲データからLastPlayedのシリアル値を取得する'
Private Function getLastPlayedSerial( _
            ByVal targetElement As String) As Currency
  Dim ret As Currency
  ret = getDateTimeSerial(targetElement, "LastPlayed=""")
  getLastPlayedSerial = ret
End Function

'///FB2Kの楽曲データからAddedのシリアル値を取得する'
Private Function getAddedSerial( _
            ByVal targetElement As String) As Currency
  Dim ret As Currency
  ret = getDateTimeSerial(targetElement, "Added=""")
  getAddedSerial = ret
End Function

'///日付時刻を表すFB2K独自のシリアル値(の上11ケタ)を返す'
'   【例】FirstPlayedのシリアル値を取得したいときは、引数keyWordに'
'         "FirstPlayed="""を指定する。'
Private Function getDateTimeSerial( _
             ByVal targetElement As String, _
             ByVal keyWord As String) As Currency
  'シリアル値18文字中上11ケタを返す'
  Const NUMBER_OF_DIGITS As Long = 11
  Dim ret As Currency
  Dim pos As Long
  pos = InStr(1, targetElement, keyWord)
  If pos = 0 Then ret = 0: GoTo Finalizer
  'シリアル値開始位置を割り出す。LastPlayedなら、'
  '「LastPlayed="」の開始位置に「LastPlayed="」の文字数を足せばよい。'
  pos = pos + Len(keyWord)
  ret = CCur(Mid(targetElement, pos, NUMBER_OF_DIGITS))
Finalizer:
  getDateTimeSerial = ret
End Function

'///FB2Kの日付時刻コードを、Date型に変換する'
Private Function getDateTime( _
            ByVal datetimeSerial As Currency) As Date
  Dim ret As Date
  '日付時刻コードの上11ケタを取得'
  datetimeSerial = Left(datetimeSerial, 11)
  '基準日(2012/1/1)との差分(FB2K独自のシリアル値)を取得'
  Dim serialDiff As Currency
  serialDiff = datetimeSerial - STANDARD_DATE_VALUE
  '基準日に加算する日数を取得'
  Dim addDay As Currency
  addDay = serialDiff \ DAY_BY_SECONDS
  '加算日数を加算'
  ret = STANDARD_DATE + addDay
  '加算する時間を取得'
  Dim addHour As Long
  Dim tmp As Long
  tmp = serialDiff Mod DAY_BY_SECONDS
  addHour = tmp \ HOUR_BY_SECONDS
  '加算する分を取得'
  Dim addMinute As Long
  tmp = tmp Mod HOUR_BY_SECONDS
  addMinute = tmp \ MINUTE_BY_SECONDS
  '加算する秒を取得'
  Dim addSecond As Long
  addSecond = tmp Mod MINUTE_BY_SECONDS
  '時刻を追加'
  ret = ret + TimeSerial(addHour, addMinute, addSecond)
  getDateTime = ret
End Function

'///FB2Kの楽曲データからCountの値を取得する'
Public Function getCount( _
            ByVal targetElement As String) As Long
  Dim ret As Long
  ret = 0
  ret = CLng(getQuortedValue(targetElement, "Count="""))
  getCount = ret
End Function

'///FB2Kの楽曲データからRatingの値を取得する'
Public Function getRating( _
            ByVal targetElement As String) As Long
  Dim ret As Long
  'Rating文字列を取得'
  ret = CLng(getQuortedValue(targetElement, "Rating="""))
  '数値(0~5)に変換'
  ret = getRatingValue(CStr(ret))
  getRating = ret
End Function

'///ダブルクォーテーションで囲まれた値を文字列として取り出す'
'   【例】Count="12" から「12」を取り出したいときは、引数KeyWordに'
'         "Count="""を渡す。'
Private Function getQuortedValue( _
             ByVal targetElement As String, _
             ByVal keyWord As String) As String
  Dim ret As String
  ret = ""
  'keyWordがtargetElement内になければ「0」を返す'
  Dim n As Long
  n = InStr(1, targetElement, keyWord)
  If n = 0 Then ret = "0": GoTo Finalizer
  'keyWordの次の文字から1文字づつ、次の「"」にぶつかるまで連結'
  n = n + Len(keyWord)
  Do
    ret = ret & Mid(targetElement, n, 1)
    n = n + 1
  Loop Until Mid(targetElement, n, 1) = """"
Finalizer:
  getQuortedValue = ret
End Function

'///Ratingの値を固有の文字に置き換える'
Private Function getRatingCode( _
             ByVal rating As Long) As String
  Dim ret As String
  Select Case rating
    Case 0: ret = "0"
    Case 1: ret = "63"
    Case 2: ret = "106"
    Case 3: ret = "149"
    Case 4: ret = "191"
    Case 5: ret = "234"
    Case Else: ret = "0"
  End Select
  getRatingCode = ret
End Function
'///Ratingの固有文字を数値に置き換える'
Private Function getRatingValue( _
             ByVal ratingCode As String) As Long
  Dim ret As Long
  ret = 0
  Select Case ratingCode
    Case "0":   ret = 0
    Case "63":  ret = 1
    Case "106": ret = 2
    Case "149": ret = 3
    Case "191": ret = 4
    Case "234": ret = 5
  End Select
  getRatingValue = ret
End Function

とりあえず、めんどくさいので説明の類は省略。

上記メソッドをうまく使えば、foobar2000の再生データをほぼ自由自在に書き換えることができる。

使用例

現在のところ、私はExcelのワークシートで

f:id:akashi_keirin:20190812144552j:plain

このような表を作り、A列の「XML Element」欄にPlayback StatisticsでエクスポートしたXMLの要素を貼り付けておき、マクロでB列以降に一旦データを出力。編集して再度XMLファイル化し、foobar2000にインポート、という手順で楽曲の再生データを編集している。

動作の様子は

f:id:akashi_keirin:20190812144617g:plain

こんな感じ。

ちなみに、foobar2000上では

f:id:akashi_keirin:20190812144558j:plain

こんな感じです。

おわりに

あまり需要はなさそうですね。寂しいなあ。