Rewrite the auto-matcher for the karaoke timing copier

Operate on characters rather than bytes in the dialog so that it
actually works with Kanji.

Rewrite the auto-matcher to handle more cases and add unit tests for it.
This commit is contained in:
Thomas Goyne 2013-07-13 08:26:09 -07:00
parent 3e3bd37a26
commit 80c9f67ce8
18 changed files with 1165 additions and 529 deletions

View file

@ -198,7 +198,6 @@
<ClInclude Include="$(SrcDir)include\aegisub\toolbar.h" />
<ClInclude Include="$(SrcDir)include\aegisub\video_provider.h" />
<ClInclude Include="$(SrcDir)initial_line_state.h" />
<ClInclude Include="$(SrcDir)kana_table.h" />
<ClInclude Include="$(SrcDir)lpeg.h" />
<ClInclude Include="$(SrcDir)main.h" />
<ClInclude Include="$(SrcDir)mkv_wrap.h" />
@ -393,7 +392,6 @@
<ClCompile Include="$(SrcDir)hotkey.cpp" />
<ClCompile Include="$(SrcDir)hotkey_data_view_model.cpp" />
<ClCompile Include="$(SrcDir)initial_line_state.cpp" />
<ClCompile Include="$(SrcDir)kana_table.cpp" />
<ClCompile Include="$(SrcDir)lpeg.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<ForcedIncludeFiles></ForcedIncludeFiles>

View file

@ -339,9 +339,6 @@
<ClInclude Include="$(SrcDir)gl_text.h">
<Filter>Video\UI</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)kana_table.h">
<Filter>Features\Karaoke copier</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)subtitle_format.h">
<Filter>Subtitle formats</Filter>
</ClInclude>
@ -992,9 +989,6 @@
<ClCompile Include="$(SrcDir)gl_wrap.cpp">
<Filter>Video\UI</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)kana_table.cpp">
<Filter>Features\Karaoke copier</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)dialog_spellchecker.cpp">
<Filter>Features\Spell checker</Filter>
</ClCompile>

View file

@ -4,7 +4,6 @@
<ProjectGuid>{BB3FED86-DB7A-4DC7-964A-260FB86CDE61}</ProjectGuid>
<RootNamespace>libaegisub</RootNamespace>
</PropertyGroup>
<!-- Aegisub project configuration -->
<PropertyGroup Label="AegisubConfiguration">
<AegisubProjectType>lib</AegisubProjectType>
@ -13,7 +12,6 @@
<ImportGroup Label="PropertySheets">
<Import Project="$(MSBuildThisFileDirectory)..\aegisub.props" />
</ImportGroup>
<!-- Project specific configuration -->
<ItemDefinitionGroup>
<ClCompile>
@ -33,7 +31,6 @@
<ForcedIncludeFiles>lagi_pre.h</ForcedIncludeFiles>
</ClCompile>
</ItemDefinitionGroup>
<!-- Source files -->
<ItemGroup>
<ClInclude Include="$(SrcDir)common\charset_6937.h" />
@ -60,6 +57,8 @@
<ClInclude Include="$(SrcDir)include\libaegisub\hotkey.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\io.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\json.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\kana_table.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\karaoke_matcher.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\keyframe.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\line_iterator.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\line_wrap.h" />
@ -101,6 +100,8 @@
<ClCompile Include="$(SrcDir)common\hotkey.cpp" />
<ClCompile Include="$(SrcDir)common\io.cpp" />
<ClCompile Include="$(SrcDir)common\json.cpp" />
<ClCompile Include="$(SrcDir)common\kana_table.cpp" />
<ClCompile Include="$(SrcDir)common\karaoke_matcher.cpp" />
<ClCompile Include="$(SrcDir)common\keyframe.cpp" />
<ClCompile Include="$(SrcDir)common\log.cpp" />
<ClCompile Include="$(SrcDir)common\mru.cpp" />

View file

@ -155,6 +155,12 @@
<ClInclude Include="$(SrcDir)include\libaegisub\ass\uuencode.h">
<Filter>ASS</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\karaoke_matcher.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\kana_table.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">
@ -256,6 +262,12 @@
<ClCompile Include="$(SrcDir)ass\uuencode.cpp">
<Filter>ASS</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\kana_table.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
<ClCompile Include="$(SrcDir)common\karaoke_matcher.cpp">
<Filter>Source Files\Common</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="$(SrcDir)include\libaegisub\charsets.def">

View file

@ -28,6 +28,8 @@ SRC += \
common/hotkey.cpp \
common/io.cpp \
common/json.cpp \
common/kana_table.cpp \
common/karaoke_matcher.cpp \
common/keyframe.cpp \
common/log.cpp \
common/mru.cpp \

View file

@ -0,0 +1,622 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "../config.h"
#include "libaegisub/kana_table.h"
#include <boost/range/algorithm.hpp>
namespace {
agi::kana_pair kana_to_romaji[] = {
{"\xE3\x81\x81", "a"}, // ぁ
{"\xE3\x81\x82", "a"}, // あ
{"\xE3\x81\x83", "i"}, // ぃ
{"\xE3\x81\x84", "i"}, // い
{"\xE3\x81\x85", "u"}, // ぅ
{"\xE3\x81\x86", "u"}, // う
{"\xE3\x81\x87", "e"}, // ぇ
{"\xE3\x81\x88", "e"}, // え
{"\xE3\x81\x89", "o"}, // ぉ
{"\xE3\x81\x8A", "o"}, // お
{"\xE3\x81\x8B", "ka"}, // か
{"\xE3\x81\x8C", "ga"}, // が
{"\xE3\x81\x8D", "ki"}, // き
{"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
{"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
{"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
{"\xE3\x81\x8E", "gi"}, // ぎ
{"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
{"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
{"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
{"\xE3\x81\x8F", "ku"}, // く
{"\xE3\x81\x90", "gu"}, // ぐ
{"\xE3\x81\x91", "ke"}, // け
{"\xE3\x81\x92", "ge"}, // げ
{"\xE3\x81\x93", "ko"}, // こ
{"\xE3\x81\x94", "go"}, // ご
{"\xE3\x81\x95", "sa"}, // さ
{"\xE3\x81\x96", "za"}, // ざ
{"\xE3\x81\x97", "shi"}, // し
{"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
{"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
{"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
{"\xE3\x81\x98", "ji"}, // じ
{"\xE3\x81\x98\xE3\x82\x83", "ja"}, // じゃ
{"\xE3\x81\x98\xE3\x82\x85", "ju"}, // じゅ
{"\xE3\x81\x98\xE3\x82\x87", "jo"}, // じょ
{"\xE3\x81\x99", "su"}, // す
{"\xE3\x81\x9A", "zu"}, // ず
{"\xE3\x81\x9B", "se"}, // せ
{"\xE3\x81\x9C", "ze"}, // ぜ
{"\xE3\x81\x9D", "so"}, // そ
{"\xE3\x81\x9E", "zo"}, // ぞ
{"\xE3\x81\x9F", "ta"}, // た
{"\xE3\x81\xA0", "da"}, // だ
{"\xE3\x81\xA1", "chi"}, // ち
{"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
{"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
{"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
{"\xE3\x81\xA2", "ji"}, // ぢ
{"\xE3\x81\xA2\xE3\x82\x83", "ja"}, // ぢゃ
{"\xE3\x81\xA2\xE3\x82\x85", "ju"}, // ぢゅ
{"\xE3\x81\xA2\xE3\x82\x87", "jo"}, // ぢょ
{"\xE3\x81\xA3", "c"}, // っ
{"\xE3\x81\xA3", "k"}, // っ
{"\xE3\x81\xA3", "p"}, // っ
{"\xE3\x81\xA3", "s"}, // っ
{"\xE3\x81\xA3", "t"}, // っ
{"\xE3\x81\xA4", "tsu"}, // つ
{"\xE3\x81\xA5", "zu"}, // づ
{"\xE3\x81\xA6", "te"}, // て
{"\xE3\x81\xA7", "de"}, // で
{"\xE3\x81\xA8", "to"}, // と
{"\xE3\x81\xA9", "do"}, // ど
{"\xE3\x81\xAA", "na"}, // な
{"\xE3\x81\xAB", "ni"}, // に
{"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
{"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
{"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
{"\xE3\x81\xAC", "nu"}, // ぬ
{"\xE3\x81\xAD", "ne"}, // ね
{"\xE3\x81\xAE", "no"}, // の
{"\xE3\x81\xAF", "ha"}, // は
{"\xE3\x81\xAF", "wa"}, // は
{"\xE3\x81\xB0", "ba"}, // ば
{"\xE3\x81\xB1", "pa"}, // ぱ
{"\xE3\x81\xB2", "hi"}, // ひ
{"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
{"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
{"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
{"\xE3\x81\xB3", "bi"}, // び
{"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
{"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
{"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
{"\xE3\x81\xB4", "pi"}, // ぴ
{"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
{"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
{"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
{"\xE3\x81\xB5", "fu"}, // ふ
{"\xE3\x81\xB6", "bu"}, // ぶ
{"\xE3\x81\xB7", "pu"}, // ぷ
{"\xE3\x81\xB8", "he"}, // へ
{"\xE3\x81\xB8", "e"}, // へ
{"\xE3\x81\xB9", "be"}, // べ
{"\xE3\x81\xBA", "pe"}, // ぺ
{"\xE3\x81\xBB", "ho"}, // ほ
{"\xE3\x81\xBC", "bo"}, // ぼ
{"\xE3\x81\xBD", "po"}, // ぽ
{"\xE3\x81\xBE", "ma"}, // ま
{"\xE3\x81\xBF", "mi"}, // み
{"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
{"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
{"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
{"\xE3\x82\x80", "mu"}, // む
{"\xE3\x82\x81", "me"}, // め
{"\xE3\x82\x82", "mo"}, // も
{"\xE3\x82\x84", "ya"}, // や
{"\xE3\x82\x86", "yu"}, // ゆ
{"\xE3\x82\x88", "yo"}, // よ
{"\xE3\x82\x89", "ra"}, // ら
{"\xE3\x82\x8A", "ri"}, // り
{"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
{"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
{"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
{"\xE3\x82\x8B", "ru"}, // る
{"\xE3\x82\x8C", "re"}, // れ
{"\xE3\x82\x8D", "ro"}, // ろ
{"\xE3\x82\x8F", "wa"}, // わ
{"\xE3\x82\x90", "wi"}, // ゐ
{"\xE3\x82\x91", "we"}, // ゑ
{"\xE3\x82\x92", "wo"}, // を
{"\xE3\x82\x93", "m"}, // ん
{"\xE3\x82\x93", "n"}, // ん
{"\xE3\x82\xA1", "a"}, // ァ
{"\xE3\x82\xA2", "a"}, // ア
{"\xE3\x82\xA3", "i"}, // ィ
{"\xE3\x82\xA4", "i"}, // イ
{"\xE3\x82\xA4\xE3\x82\xA7", "ye"}, // イェ
{"\xE3\x82\xA5", "u"}, // ゥ
{"\xE3\x82\xA6", "u"}, // ウ
{"\xE3\x82\xA6\xE3\x82\xA3", "wi"}, // ウィ
{"\xE3\x82\xA6\xE3\x82\xA7", "we"}, // ウェ
{"\xE3\x82\xA6\xE3\x82\xA9", "wo"}, // ウォ
{"\xE3\x82\xA7", "e"}, // ェ
{"\xE3\x82\xA8", "e"}, // エ
{"\xE3\x82\xA9", "o"}, // ォ
{"\xE3\x82\xAA", "o"}, // オ
{"\xE3\x82\xAB", "ka"}, // カ
{"\xE3\x82\xAC", "ga"}, // ガ
{"\xE3\x82\xAD", "ki"}, // キ
{"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
{"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
{"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
{"\xE3\x82\xAE", "gi"}, // ギ
{"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
{"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
{"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
{"\xE3\x82\xAF", "ku"}, // ク
{"\xE3\x82\xB0", "gu"}, // グ
{"\xE3\x82\xB1", "ke"}, // ケ
{"\xE3\x82\xB2", "ge"}, // ゲ
{"\xE3\x82\xB3", "ko"}, // コ
{"\xE3\x82\xB4", "go"}, // ゴ
{"\xE3\x82\xB5", "sa"}, // サ
{"\xE3\x82\xB6", "za"}, // ザ
{"\xE3\x82\xB7", "shi"}, // シ
{"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
{"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
{"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
{"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
{"\xE3\x82\xB8", "ji"}, // ジ
{"\xE3\x82\xB8\xE3\x82\xA7", "je"}, // ジェ
{"\xE3\x82\xB8\xE3\x83\xA3", "ja"}, // ジャ
{"\xE3\x82\xB8\xE3\x83\xA5", "ju"}, // ジュ
{"\xE3\x82\xB8\xE3\x83\xA7", "jo"}, // ジョ
{"\xE3\x82\xB9", "su"}, // ス
{"\xE3\x82\xBA", "zu"}, // ズ
{"\xE3\x82\xBB", "se"}, // セ
{"\xE3\x82\xBC", "ze"}, // ゼ
{"\xE3\x82\xBD", "so"}, // ソ
{"\xE3\x82\xBE", "zo"}, // ゾ
{"\xE3\x82\xBF", "ta"}, // タ
{"\xE3\x83\x80", "da"}, // ダ
{"\xE3\x83\x81", "chi"}, // チ
{"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
{"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
{"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
{"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
{"\xE3\x83\x82", "ji"}, // ヂ
{"\xE3\x83\x82\xE3\x83\xA3", "ja"}, // ヂャ
{"\xE3\x83\x82\xE3\x83\xA5", "ju"}, // ヂュ
{"\xE3\x83\x82\xE3\x83\xA7", "jo"}, // ヂョ
{"\xE3\x83\x83", "c"}, // ッ
{"\xE3\x83\x83", "k"}, // ッ
{"\xE3\x83\x83", "p"}, // ッ
{"\xE3\x83\x83", "s"}, // ッ
{"\xE3\x83\x83", "t"}, // ッ
{"\xE3\x83\x84", "tsu"}, // ツ
{"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
{"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
{"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
{"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
{"\xE3\x83\x85", "zu"}, // ヅ
{"\xE3\x83\x86", "te"}, // テ
{"\xE3\x83\x86\xE3\x82\xA3", "ti"}, // ティ
{"\xE3\x83\x86\xE3\x82\xA5", "tu"}, // テゥ
{"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
{"\xE3\x83\x87", "de"}, // デ
{"\xE3\x83\x87\xE3\x82\xA3", "di"}, // ディ
{"\xE3\x83\x87\xE3\x82\xA5", "du"}, // デゥ
{"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
{"\xE3\x83\x88", "to"}, // ト
{"\xE3\x83\x89", "do"}, // ド
{"\xE3\x83\x8A", "na"}, // ナ
{"\xE3\x83\x8B", "ni"}, // ニ
{"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
{"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
{"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
{"\xE3\x83\x8C", "nu"}, // ヌ
{"\xE3\x83\x8D", "ne"}, // ネ
{"\xE3\x83\x8E", "no"}, //
{"\xE3\x83\x8F", "ha"}, // ハ
{"\xE3\x83\x90", "ba"}, // バ
{"\xE3\x83\x91", "pa"}, // パ
{"\xE3\x83\x92", "hi"}, // ヒ
{"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
{"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
{"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
{"\xE3\x83\x93", "bi"}, // ビ
{"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
{"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
{"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
{"\xE3\x83\x94", "pi"}, // ピ
{"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
{"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
{"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
{"\xE3\x83\x95", "fu"}, // フ
{"\xE3\x83\x95\xE3\x82\xA1", "fa"}, // ファ
{"\xE3\x83\x95\xE3\x82\xA3", "fi"}, // フィ
{"\xE3\x83\x95\xE3\x82\xA7", "fe"}, // フェ
{"\xE3\x83\x95\xE3\x82\xA9", "fo"}, // フォ
{"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
{"\xE3\x83\x96", "bu"}, // ブ
{"\xE3\x83\x97", "pu"}, // プ
{"\xE3\x83\x98", "he"}, // ヘ
{"\xE3\x83\x99", "be"}, // ベ
{"\xE3\x83\x9A", "pe"}, // ペ
{"\xE3\x83\x9B", "ho"}, // ホ
{"\xE3\x83\x9C", "bo"}, // ボ
{"\xE3\x83\x9D", "po"}, // ポ
{"\xE3\x83\x9E", "ma"}, // マ
{"\xE3\x83\x9F", "mi"}, // ミ
{"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
{"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
{"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
{"\xE3\x83\xA0", "mu"}, // ム
{"\xE3\x83\xA1", "me"}, // メ
{"\xE3\x83\xA2", "mo"}, // モ
{"\xE3\x83\xA4", "ya"}, // ヤ
{"\xE3\x83\xA6", "yu"}, // ユ
{"\xE3\x83\xA8", "yo"}, // ヨ
{"\xE3\x83\xA9", "ra"}, // ラ
{"\xE3\x83\xAA", "ri"}, // リ
{"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
{"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
{"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
{"\xE3\x83\xAB", "ru"}, // ル
{"\xE3\x83\xAC", "re"}, // レ
{"\xE3\x83\xAD", "ro"}, // ロ
{"\xE3\x83\xAF", "wa"}, // ワ
{"\xE3\x83\xB0", "wi"}, // ヰ
{"\xE3\x83\xB1", "we"}, // ヱ
{"\xE3\x83\xB2", "wo"}, // ヲ
{"\xE3\x83\xB3", "m"}, // ン
{"\xE3\x83\xB3", "n"}, // ン
{"\xE3\x83\xB4", "vu"}, // ヴ
{"\xE3\x83\xB4\xE3\x82\xA1", "va"}, // ヴァ
{"\xE3\x83\xB4\xE3\x82\xA3", "vi"}, // ヴィ
{"\xE3\x83\xB4\xE3\x82\xA7", "ve"}, // ヴェ
{"\xE3\x83\xB4\xE3\x82\xA9", "vo"}, // ヴォ
{"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
{"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
{"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
{"\xE3\x83\xBC", "a"}, // ー
{"\xE3\x83\xBC", "e"}, // ー
{"\xE3\x83\xBC", "i"}, // ー
{"\xE3\x83\xBC", "o"}, // ー
{"\xE3\x83\xBC", "u"}, // ー
};
agi::kana_pair romaji_to_kana[] = {
{"\xE3\x81\x81", "a"}, // ぁ
{"\xE3\x81\x82", "a"}, // あ
{"\xE3\x82\xA1", "a"}, // ァ
{"\xE3\x82\xA2", "a"}, // ア
{"\xE3\x83\xBC", "a"}, // ー
{"\xE3\x81\xB0", "ba"}, // ば
{"\xE3\x83\x90", "ba"}, // バ
{"\xE3\x81\xB9", "be"}, // べ
{"\xE3\x83\x99", "be"}, // ベ
{"\xE3\x81\xB3", "bi"}, // び
{"\xE3\x83\x93", "bi"}, // ビ
{"\xE3\x81\xBC", "bo"}, // ぼ
{"\xE3\x83\x9C", "bo"}, // ボ
{"\xE3\x81\xB6", "bu"}, // ぶ
{"\xE3\x83\x96", "bu"}, // ブ
{"\xE3\x81\xB3\xE3\x82\x83", "bya"}, // びゃ
{"\xE3\x83\x93\xE3\x83\xA3", "bya"}, // ビャ
{"\xE3\x81\xB3\xE3\x82\x87", "byo"}, // びょ
{"\xE3\x83\x93\xE3\x83\xA7", "byo"}, // ビョ
{"\xE3\x81\xB3\xE3\x82\x85", "byu"}, // びゅ
{"\xE3\x83\x93\xE3\x83\xA5", "byu"}, // ビュ
{"\xE3\x81\xA3", "c"}, // っ
{"\xE3\x83\x83", "c"}, // ッ
{"\xE3\x81\xA1\xE3\x82\x83", "cha"}, // ちゃ
{"\xE3\x83\x81\xE3\x83\xA3", "cha"}, // チャ
{"\xE3\x83\x81\xE3\x82\xA7", "che"}, // チェ
{"\xE3\x81\xA1", "chi"}, // ち
{"\xE3\x83\x81", "chi"}, // チ
{"\xE3\x81\xA1\xE3\x82\x87", "cho"}, // ちょ
{"\xE3\x83\x81\xE3\x83\xA7", "cho"}, // チョ
{"\xE3\x81\xA1\xE3\x82\x85", "chu"}, // ちゅ
{"\xE3\x83\x81\xE3\x83\xA5", "chu"}, // チュ
{"\xE3\x81\xA0", "da"}, // だ
{"\xE3\x83\x80", "da"}, // ダ
{"\xE3\x81\xA7", "de"}, // で
{"\xE3\x83\x87", "de"}, // デ
{"\xE3\x83\x87\xE3\x82\xA3", "di"}, // ディ
{"\xE3\x81\xA9", "do"}, // ど
{"\xE3\x83\x89", "do"}, // ド
{"\xE3\x83\x87\xE3\x82\xA5", "du"}, // デゥ
{"\xE3\x83\x87\xE3\x82\xA5", "dyu"}, // デゥ
{"\xE3\x81\x87", "e"}, // ぇ
{"\xE3\x81\x88", "e"}, // え
{"\xE3\x82\xA7", "e"}, // ェ
{"\xE3\x82\xA8", "e"}, // エ
{"\xE3\x83\xBC", "e"}, // ー
{"\xE3\x83\x95\xE3\x82\xA1", "fa"}, // ファ
{"\xE3\x83\x95\xE3\x82\xA7", "fe"}, // フェ
{"\xE3\x83\x95\xE3\x82\xA3", "fi"}, // フィ
{"\xE3\x83\x95\xE3\x82\xA9", "fo"}, // フォ
{"\xE3\x81\xB5", "fu"}, // ふ
{"\xE3\x83\x95", "fu"}, // フ
{"\xE3\x83\x95\xE3\x83\xA5", "fyu"}, // フュ
{"\xE3\x81\x8C", "ga"}, // が
{"\xE3\x82\xAC", "ga"}, // ガ
{"\xE3\x81\x92", "ge"}, // げ
{"\xE3\x82\xB2", "ge"}, // ゲ
{"\xE3\x81\x8E", "gi"}, // ぎ
{"\xE3\x82\xAE", "gi"}, // ギ
{"\xE3\x81\x94", "go"}, // ご
{"\xE3\x82\xB4", "go"}, // ゴ
{"\xE3\x81\x90", "gu"}, // ぐ
{"\xE3\x82\xB0", "gu"}, // グ
{"\xE3\x81\x8E\xE3\x82\x83", "gya"}, // ぎゃ
{"\xE3\x82\xAE\xE3\x83\xA3", "gya"}, // ギャ
{"\xE3\x81\x8E\xE3\x82\x87", "gyo"}, // ぎょ
{"\xE3\x82\xAE\xE3\x83\xA7", "gyo"}, // ギョ
{"\xE3\x81\x8E\xE3\x82\x85", "gyu"}, // ぎゅ
{"\xE3\x82\xAE\xE3\x83\xA5", "gyu"}, // ギュ
{"\xE3\x81\xAF", "ha"}, // は
{"\xE3\x83\x8F", "ha"}, // ハ
{"\xE3\x81\xB8", "he"}, // へ
{"\xE3\x83\x98", "he"}, // ヘ
{"\xE3\x81\xB2", "hi"}, // ひ
{"\xE3\x83\x92", "hi"}, // ヒ
{"\xE3\x81\xBB", "ho"}, // ほ
{"\xE3\x83\x9B", "ho"}, // ホ
{"\xE3\x81\xB2\xE3\x82\x83", "hya"}, // ひゃ
{"\xE3\x83\x92\xE3\x83\xA3", "hya"}, // ヒャ
{"\xE3\x81\xB2\xE3\x82\x87", "hyo"}, // ひょ
{"\xE3\x83\x92\xE3\x83\xA7", "hyo"}, // ヒョ
{"\xE3\x81\xB2\xE3\x82\x85", "hyu"}, // ひゅ
{"\xE3\x83\x92\xE3\x83\xA5", "hyu"}, // ヒュ
{"\xE3\x81\x83", "i"}, // ぃ
{"\xE3\x81\x84", "i"}, // い
{"\xE3\x82\xA3", "i"}, // ィ
{"\xE3\x82\xA4", "i"}, // イ
{"\xE3\x83\xBC", "i"}, // ー
{"\xE3\x81\x98\xE3\x82\x83", "ja"}, // じゃ
{"\xE3\x81\xA2\xE3\x82\x83", "ja"}, // ぢゃ
{"\xE3\x82\xB8\xE3\x83\xA3", "ja"}, // ジャ
{"\xE3\x83\x82\xE3\x83\xA3", "ja"}, // ヂャ
{"\xE3\x82\xB8\xE3\x82\xA7", "je"}, // ジェ
{"\xE3\x81\x98", "ji"}, // じ
{"\xE3\x81\xA2", "ji"}, // ぢ
{"\xE3\x82\xB8", "ji"}, // ジ
{"\xE3\x83\x82", "ji"}, // ヂ
{"\xE3\x81\x98\xE3\x82\x87", "jo"}, // じょ
{"\xE3\x81\xA2\xE3\x82\x87", "jo"}, // ぢょ
{"\xE3\x82\xB8\xE3\x83\xA7", "jo"}, // ジョ
{"\xE3\x83\x82\xE3\x83\xA7", "jo"}, // ヂョ
{"\xE3\x81\x98\xE3\x82\x85", "ju"}, // じゅ
{"\xE3\x81\xA2\xE3\x82\x85", "ju"}, // ぢゅ
{"\xE3\x82\xB8\xE3\x83\xA5", "ju"}, // ジュ
{"\xE3\x83\x82\xE3\x83\xA5", "ju"}, // ヂュ
{"\xE3\x81\xA3", "k"}, // っ
{"\xE3\x83\x83", "k"}, // ッ
{"\xE3\x81\x8B", "ka"}, // か
{"\xE3\x82\xAB", "ka"}, // カ
{"\xE3\x81\x91", "ke"}, // け
{"\xE3\x82\xB1", "ke"}, // ケ
{"\xE3\x81\x8D", "ki"}, // き
{"\xE3\x82\xAD", "ki"}, // キ
{"\xE3\x81\x93", "ko"}, // こ
{"\xE3\x82\xB3", "ko"}, // コ
{"\xE3\x81\x8F", "ku"}, // く
{"\xE3\x82\xAF", "ku"}, // ク
{"\xE3\x81\x8D\xE3\x82\x83", "kya"}, // きゃ
{"\xE3\x82\xAD\xE3\x83\xA3", "kya"}, // キャ
{"\xE3\x81\x8D\xE3\x82\x87", "kyo"}, // きょ
{"\xE3\x82\xAD\xE3\x83\xA7", "kyo"}, // キョ
{"\xE3\x81\x8D\xE3\x82\x85", "kyu"}, // きゅ
{"\xE3\x82\xAD\xE3\x83\xA5", "kyu"}, // キュ
{"\xE3\x82\x93", "m"}, // ん
{"\xE3\x83\xB3", "m"}, // ン
{"\xE3\x81\xBE", "ma"}, // ま
{"\xE3\x83\x9E", "ma"}, // マ
{"\xE3\x82\x81", "me"}, // め
{"\xE3\x83\xA1", "me"}, // メ
{"\xE3\x81\xBF", "mi"}, // み
{"\xE3\x83\x9F", "mi"}, // ミ
{"\xE3\x82\x82", "mo"}, // も
{"\xE3\x83\xA2", "mo"}, // モ
{"\xE3\x82\x80", "mu"}, // む
{"\xE3\x83\xA0", "mu"}, // ム
{"\xE3\x81\xBF\xE3\x82\x83", "mya"}, // みゃ
{"\xE3\x83\x9F\xE3\x83\xA3", "mya"}, // ミャ
{"\xE3\x81\xBF\xE3\x82\x87", "myo"}, // みょ
{"\xE3\x83\x9F\xE3\x83\xA7", "myo"}, // ミョ
{"\xE3\x81\xBF\xE3\x82\x85", "myu"}, // みゅ
{"\xE3\x83\x9F\xE3\x83\xA5", "myu"}, // ミュ
{"\xE3\x82\x93", "n"}, // ん
{"\xE3\x83\xB3", "n"}, // ン
{"\xE3\x81\xAA", "na"}, // な
{"\xE3\x83\x8A", "na"}, // ナ
{"\xE3\x81\xAD", "ne"}, // ね
{"\xE3\x83\x8D", "ne"}, // ネ
{"\xE3\x81\xAB", "ni"}, // に
{"\xE3\x83\x8B", "ni"}, // ニ
{"\xE3\x81\xAE", "no"}, // の
{"\xE3\x83\x8E", "no"}, //
{"\xE3\x81\xAC", "nu"}, // ぬ
{"\xE3\x83\x8C", "nu"}, // ヌ
{"\xE3\x81\xAB\xE3\x82\x83", "nya"}, // にゃ
{"\xE3\x83\x8B\xE3\x83\xA3", "nya"}, // ニャ
{"\xE3\x81\xAB\xE3\x82\x87", "nyo"}, // にょ
{"\xE3\x83\x8B\xE3\x83\xA7", "nyo"}, // ニョ
{"\xE3\x81\xAB\xE3\x82\x85", "nyu"}, // にゅ
{"\xE3\x83\x8B\xE3\x83\xA5", "nyu"}, // ニュ
{"\xE3\x81\x89", "o"}, // ぉ
{"\xE3\x81\x8A", "o"}, // お
{"\xE3\x82\xA9", "o"}, // ォ
{"\xE3\x82\xAA", "o"}, // オ
{"\xE3\x83\xBC", "o"}, // ー
{"\xE3\x81\xA3", "p"}, // っ
{"\xE3\x83\x83", "p"}, // ッ
{"\xE3\x81\xB1", "pa"}, // ぱ
{"\xE3\x83\x91", "pa"}, // パ
{"\xE3\x81\xBA", "pe"}, // ぺ
{"\xE3\x83\x9A", "pe"}, // ペ
{"\xE3\x81\xB4", "pi"}, // ぴ
{"\xE3\x83\x94", "pi"}, // ピ
{"\xE3\x81\xBD", "po"}, // ぽ
{"\xE3\x83\x9D", "po"}, // ポ
{"\xE3\x81\xB7", "pu"}, // ぷ
{"\xE3\x83\x97", "pu"}, // プ
{"\xE3\x81\xB4\xE3\x82\x83", "pya"}, // ぴゃ
{"\xE3\x83\x94\xE3\x83\xA3", "pya"}, // ピャ
{"\xE3\x81\xB4\xE3\x82\x87", "pyo"}, // ぴょ
{"\xE3\x83\x94\xE3\x83\xA7", "pyo"}, // ピョ
{"\xE3\x81\xB4\xE3\x82\x85", "pyu"}, // ぴゅ
{"\xE3\x83\x94\xE3\x83\xA5", "pyu"}, // ピュ
{"\xE3\x82\x89", "ra"}, // ら
{"\xE3\x83\xA9", "ra"}, // ラ
{"\xE3\x82\x8C", "re"}, // れ
{"\xE3\x83\xAC", "re"}, // レ
{"\xE3\x82\x8A", "ri"}, // り
{"\xE3\x83\xAA", "ri"}, // リ
{"\xE3\x82\x8D", "ro"}, // ろ
{"\xE3\x83\xAD", "ro"}, // ロ
{"\xE3\x82\x8B", "ru"}, // る
{"\xE3\x83\xAB", "ru"}, // ル
{"\xE3\x82\x8A\xE3\x82\x83", "rya"}, // りゃ
{"\xE3\x83\xAA\xE3\x83\xA3", "rya"}, // リャ
{"\xE3\x82\x8A\xE3\x82\x87", "ryo"}, // りょ
{"\xE3\x83\xAA\xE3\x83\xA7", "ryo"}, // リョ
{"\xE3\x82\x8A\xE3\x82\x85", "ryu"}, // りゅ
{"\xE3\x83\xAA\xE3\x83\xA5", "ryu"}, // リュ
{"\xE3\x81\xA3", "s"}, // っ
{"\xE3\x83\x83", "s"}, // ッ
{"\xE3\x81\x95", "sa"}, // さ
{"\xE3\x82\xB5", "sa"}, // サ
{"\xE3\x81\x9B", "se"}, // せ
{"\xE3\x82\xBB", "se"}, // セ
{"\xE3\x81\x97\xE3\x82\x83", "sha"}, // しゃ
{"\xE3\x82\xB7\xE3\x83\xA3", "sha"}, // シャ
{"\xE3\x82\xB7\xE3\x82\xA7", "she"}, // シェ
{"\xE3\x81\x97", "shi"}, // し
{"\xE3\x82\xB7", "shi"}, // シ
{"\xE3\x81\x97\xE3\x82\x87", "sho"}, // しょ
{"\xE3\x82\xB7\xE3\x83\xA7", "sho"}, // ショ
{"\xE3\x81\x97\xE3\x82\x85", "shu"}, // しゅ
{"\xE3\x82\xB7\xE3\x83\xA5", "shu"}, // シュ
{"\xE3\x81\x9D", "so"}, // そ
{"\xE3\x82\xBD", "so"}, // ソ
{"\xE3\x81\x99", "su"}, // す
{"\xE3\x82\xB9", "su"}, // ス
{"\xE3\x81\xA3", "t"}, // っ
{"\xE3\x83\x83", "t"}, // ッ
{"\xE3\x81\x9F", "ta"}, // た
{"\xE3\x82\xBF", "ta"}, // タ
{"\xE3\x81\xA6", "te"}, // て
{"\xE3\x83\x86", "te"}, // テ
{"\xE3\x83\x86\xE3\x82\xA3", "ti"}, // ティ
{"\xE3\x81\xA8", "to"}, // と
{"\xE3\x83\x88", "to"}, // ト
{"\xE3\x83\x84\xE3\x82\xA1", "tsa"}, // ツァ
{"\xE3\x83\x84\xE3\x82\xA7", "tse"}, // ツェ
{"\xE3\x83\x84\xE3\x82\xA3", "tsi"}, // ツィ
{"\xE3\x83\x84\xE3\x82\xA9", "tso"}, // ツォ
{"\xE3\x81\xA4", "tsu"}, // つ
{"\xE3\x83\x84", "tsu"}, // ツ
{"\xE3\x83\x86\xE3\x82\xA5", "tu"}, // テゥ
{"\xE3\x83\x86\xE3\x83\xA5", "tyu"}, // テュ
{"\xE3\x81\x85", "u"}, // ぅ
{"\xE3\x81\x86", "u"}, // う
{"\xE3\x82\xA5", "u"}, // ゥ
{"\xE3\x82\xA6", "u"}, // ウ
{"\xE3\x83\xBC", "u"}, // ー
{"\xE3\x83\xB4\xE3\x82\xA1", "va"}, // ヴァ
{"\xE3\x83\xB4\xE3\x82\xA7", "ve"}, // ヴェ
{"\xE3\x83\xB4\xE3\x82\xA3", "vi"}, // ヴィ
{"\xE3\x83\xB4\xE3\x82\xA9", "vo"}, // ヴォ
{"\xE3\x83\xB4", "vu"}, // ヴ
{"\xE3\x83\xB4\xE3\x83\xA3", "vya"}, // ヴャ
{"\xE3\x83\xB4\xE3\x83\xA7", "vyo"}, // ヴョ
{"\xE3\x83\xB4\xE3\x83\xA5", "vyu"}, // ヴュ
{"\xE3\x81\xAF", "wa"}, // は
{"\xE3\x82\x8F", "wa"}, // わ
{"\xE3\x83\xAF", "wa"}, // ワ
{"\xE3\x82\x91", "we"}, // ゑ
{"\xE3\x82\xA6\xE3\x82\xA7", "we"}, // ウェ
{"\xE3\x83\xB1", "we"}, // ヱ
{"\xE3\x82\x90", "wi"}, // ゐ
{"\xE3\x82\xA6\xE3\x82\xA3", "wi"}, // ウィ
{"\xE3\x83\xB0", "wi"}, // ヰ
{"\xE3\x82\x92", "wo"}, // を
{"\xE3\x82\xA6\xE3\x82\xA9", "wo"}, // ウォ
{"\xE3\x83\xB2", "wo"}, // ヲ
{"\xE3\x82\x84", "ya"}, // や
{"\xE3\x83\xA4", "ya"}, // ヤ
{"\xE3\x82\xA4\xE3\x82\xA7", "ye"}, // イェ
{"\xE3\x82\x88", "yo"}, // よ
{"\xE3\x83\xA8", "yo"}, // ヨ
{"\xE3\x82\x86", "yu"}, // ゆ
{"\xE3\x83\xA6", "yu"}, // ユ
{"\xE3\x81\x96", "za"}, // ざ
{"\xE3\x82\xB6", "za"}, // ザ
{"\xE3\x81\x9C", "ze"}, // ぜ
{"\xE3\x82\xBC", "ze"}, // ゼ
{"\xE3\x81\x9E", "zo"}, // ぞ
{"\xE3\x82\xBE", "zo"}, // ゾ
{"\xE3\x81\x9A", "zu"}, // ず
{"\xE3\x81\xA5", "zu"}, // づ
{"\xE3\x82\xBA", "zu"}, // ズ
{"\xE3\x83\x85", "zu"}, // ヅ
};
bool cmp_kana(agi::kana_pair const& kp, std::string const& kana) {
return strcmp(kp.kana, kana.c_str()) < 0;
}
struct cmp_romaji {
bool operator()(agi::kana_pair const& kp, std::string const& romaji) const {
return strcmp(kp.romaji, romaji.c_str()) < 0;
}
bool operator()(std::string const& romaji, agi::kana_pair const& kp) const {
return strcmp(kp.romaji, romaji.c_str()) > 0;
}
#ifdef _MSC_VER // debug iterator stuff needs this overload
bool operator()(agi::kana_pair const& a, agi::kana_pair const& b) const {
return strcmp(a.romaji, b.romaji) < 0;
}
#endif
};
}
namespace agi {
std::vector<const char *> kana_to_romaji(std::string const& kana) {
std::vector<const char *> ret;
for (auto pair = boost::lower_bound(::kana_to_romaji, kana, cmp_kana);
pair != std::end(::kana_to_romaji) && !strcmp(pair->kana, kana.c_str());
++pair)
ret.push_back(pair->romaji);
return ret;
}
boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const& romaji) {
for (size_t len = std::min<size_t>(3, romaji.size()); len > 0; --len) {
auto pair = boost::equal_range(::romaji_to_kana, romaji.substr(0, len).c_str(), cmp_romaji());
if (pair.first != pair.second)
return boost::make_iterator_range(pair.first, pair.second);
}
return boost::make_iterator_range(::romaji_to_kana, ::romaji_to_kana);
}
}

View file

@ -0,0 +1,209 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include "../config.h"
#include "libaegisub/karaoke_matcher.h"
#include "libaegisub/kana_table.h"
#include "libaegisub/util.h"
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/locale/boundary.hpp>
#include <boost/locale/collator.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <unicode/uchar.h>
#include <unicode/utf8.h>
namespace {
int32_t next_codepoint(const char *str, size_t *i) {
UChar32 c;
U8_NEXT_UNSAFE(str, *i, c);
return c;
}
bool is_whitespace(int32_t c) {
return !!u_isUWhiteSpace(c);
}
bool is_whitespace(std::string const& str) {
size_t i = 0;
while (auto c = next_codepoint(str.c_str(), &i)) {
if (!u_isUWhiteSpace(c))
return false;
}
return true;
}
// strcmp but ignoring case and accents
int compare(std::string const& a, std::string const& b) {
using namespace boost::locale;
return std::use_facet<collator<char>>(std::locale()).compare(collator_base::primary, a, b);
}
}
namespace agi {
karaoke_match_result auto_match_karaoke(std::vector<std::string> const& source_strings, std::string const& dest_string) {
karaoke_match_result result = { 0, 0 };
if (source_strings.empty()) return result;
using namespace boost::locale::boundary;
using boost::starts_with;
result.source_length = 1;
ssegment_index destination_characters(character, begin(dest_string), end(dest_string));
auto src = boost::to_lower_copy(source_strings[0]);
auto dst = destination_characters.begin();
auto dst_end = destination_characters.end();
// Eat all the whitespace at the beginning of the source and destination
// syllables and exit if either ran out.
auto eat_whitespace = [&]() -> bool {
size_t i = 0, first_non_whitespace = 0;
while (is_whitespace(next_codepoint(src.c_str(), &i)))
first_non_whitespace = i;
if (first_non_whitespace)
src = src.substr(first_non_whitespace);
while (dst != dst_end && is_whitespace(dst->str())) {
++dst;
++result.destination_length;
}
// If we ran out of dest then this needs to match the rest of the
// source syllables (this probably means the user did something wrong)
if (dst == dst_end) {
result.source_length = source_strings.size();
return true;
}
return src.empty();
};
if (eat_whitespace()) return result;
// We now have a non-whitespace character at the beginning of both source
// and destination. Check if the source starts with a romanized kana, and
// if it does then check if the destination also has the appropriate
// character. If it does, match them and repeat.
while (!src.empty()) {
// First check for a basic match of the first character of the source and dest
auto first_src_char = ssegment_index(character, begin(src), end(src)).begin()->str();
if (compare(first_src_char, dst->str()) == 0) {
++dst;
++result.destination_length;
src.erase(0, first_src_char.size());
if (eat_whitespace()) return result;
continue;
}
auto check = [&](kana_pair const& kp) -> bool {
if (!starts_with(&*dst->begin(), kp.kana)) return false;
src = src.substr(strlen(kp.romaji));
for (size_t i = 0; kp.kana[i]; ) {
i += dst->length();
++result.destination_length;
++dst;
}
return true;
};
bool matched = false;
for (auto const& match : romaji_to_kana(src)) {
if (check(match)) {
if (eat_whitespace()) return result;
matched = true;
break;
}
}
if (!matched) break;
}
// Source and dest are now non-empty and start with non-whitespace.
// If there's only one character left in the dest, it obviously needs to
// match all of the source syllables left.
if (std::distance(dst, dst_end) == 1) {
result.source_length = source_strings.size();
++result.destination_length;
return result;
}
// We couldn't match the current character, but if we can match the *next*
// syllable then we know that everything in between must belong to the
// current syllable. Do this by looking up to KANA_SEARCH_DISTANCE
// characters ahead in destination and seeing if we can match them against
// the beginning of a syllable after this syllable.
// If a match is found, make a guess at how much source and destination
// should be selected based on the distances it was found at.
// The longest kanji are 'uketamawa.ru' and 'kokorozashi', each with a
// reading consisting of five kana. This means each each character from
// the destination can match at most five syllables from the source.
static const int max_character_length = 5;
// Arbitrarily chosen limit on the number of dest characters to try
// skipping. Higher numbers probably increase false-positives.
static const int dst_lookahead_max = 3;
for (size_t lookahead = 0; lookahead < dst_lookahead_max; ++lookahead) {
if (++dst == dst_end) break;
// Transliterate this character if it's a known hiragana or katakana character
std::vector<const char *> translit;
auto next = std::next(dst);
if (next != dst_end)
boost::copy(kana_to_romaji(dst->str() + next->str()), back_inserter(translit));
boost::copy(kana_to_romaji(dst->str()), back_inserter(translit));
// Search for it and the transliterated version in the source
int src_lookahead_max = (lookahead + 1) * max_character_length;
int src_lookahead_pos = 0;
for (auto const& syl : source_strings) {
// Don't count blank syllables in the max search distance
if (is_whitespace(syl)) continue;
if (++src_lookahead_pos == 1) continue;
if (src_lookahead_pos > src_lookahead_max) break;
std::string lsyl = boost::to_lower_copy(syl);
if (!(starts_with(syl, dst->str()) || util::any_of(translit, [&](const char *str) { return starts_with(lsyl, str); })))
continue;
// The syllable immediately after the current one matched, so
// everything up to the match must go with the current syllable.
if (src_lookahead_pos == 2) {
result.destination_length += lookahead + 1;
return result;
}
// The match was multiple syllables ahead, so just divide the
// destination characters evenly between the source syllables
result.destination_length += 1;
result.source_length = static_cast<size_t>((src_lookahead_pos - 1.0) / (lookahead + 1.0) + .5);
return result;
}
}
// We wouldn't have gotten here if the dest was empty, so make sure at
// least one character is selected
result.destination_length = std::max<size_t>(result.destination_length, 1u);
return result;
}
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include <boost/range/iterator_range.hpp>
#include <vector>
namespace agi {
struct kana_pair {
const char *kana;
const char *romaji;
};
/// Transliterated romaji for the given kana, or nullptr if not applicable
std::vector<const char *> kana_to_romaji(std::string const& kana);
boost::iterator_range<const kana_pair *> romaji_to_kana(std::string const& romaji);
}

View file

@ -0,0 +1,30 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#include <string>
#include <vector>
namespace agi {
struct karaoke_match_result {
/// The number of strings in the source matched
size_t source_length;
/// The number of characters in the destination string matched
size_t destination_length;
};
/// Try to automatically select the portion of dst which corresponds to the first string in src
karaoke_match_result auto_match_karaoke(std::vector<std::string> const& src, std::string const& dst);
}

View file

@ -75,5 +75,11 @@ namespace agi {
/// elsewhere (because libstcc++ 4.7 is missing it).
void sleep_for(int ms);
// boost.range doesn't have wrappers for the C++11 stuff
template<typename Range, typename Predicate>
bool any_of(Range&& r, Predicate&& p) {
return std::any_of(std::begin(r), std::end(r), std::forward<Predicate>(p));
}
} // namespace util
} // namespace agi

View file

@ -194,7 +194,6 @@ SRC += \
hotkey.cpp \
hotkey_data_view_model.cpp \
initial_line_state.cpp \
kana_table.cpp \
lpeg.c \
main.cpp \
menu.cpp \

View file

@ -43,12 +43,17 @@
#include "compat.h"
#include "help_button.h"
#include "include/aegisub/context.h"
#include "kana_table.h"
#include "libresrc/libresrc.h"
#include "options.h"
#include "selection_controller.h"
#include "utils.h"
#include <libaegisub/karaoke_matcher.h>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/locale/boundary.hpp>
#include <boost/range/algorithm_ext.hpp>
#include <deque>
#include <wx/checkbox.h>
@ -76,12 +81,13 @@ class KaraokeLineMatchDisplay : public wxControl {
std::vector<MatchGroup> matched_groups;
std::deque<MatchSyllable> unmatched_source;
std::string unmatched_destination;
std::string destination_str;
boost::locale::boundary::ssegment_index destination;
boost::locale::boundary::ssegment_index::iterator match_begin, match_end;
int last_total_matchgroup_render_width;
size_t source_sel_length;
size_t destination_sel_length;
void OnPaint(wxPaintEvent &event);
@ -96,7 +102,7 @@ public:
/// Number of syllables not yet matched from source
size_t GetRemainingSource() const { return unmatched_source.size(); }
/// Number of characters not yet matched from destination
size_t GetRemainingDestination() const { return unmatched_destination.size(); }
size_t GetRemainingDestination() const { return distance(match_end, destination.end()); }
// Adjust source and destination match lengths
void IncreaseSourceMatch();
@ -147,7 +153,7 @@ wxSize KaraokeLineMatchDisplay::GetBestSize() const
return wxSize(min_width * 2, h_src + h_dst + 7);
}
int DrawBoxedText(wxDC &dc, const std::string &txt, int x, int y)
int DrawBoxedText(wxDC &dc, wxString const& txt, int x, int y)
{
int tw, th;
// Assume the pen, brush and font properties have already been set in the DC.
@ -164,10 +170,9 @@ int DrawBoxedText(wxDC &dc, const std::string &txt, int x, int y)
}
else
{
wxString wxtxt(to_wx(txt));
dc.GetTextExtent(wxtxt, &tw, &th);
dc.GetTextExtent(txt, &tw, &th);
dc.DrawRectangle(x, y-2, tw+4, th+4);
dc.DrawText(wxtxt, x+2, y);
dc.DrawText(txt, x+2, y);
return tw+3;
}
}
@ -256,11 +261,11 @@ void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &)
// Matched source syllables
int syl_x = next_x;
for (auto const& syl : grp.src)
syl_x += DrawBoxedText(dc, syl.text, syl_x, y_line1);
syl_x += DrawBoxedText(dc, to_wx(syl.text), syl_x, y_line1);
// Matched destination text
{
const int adv = DrawBoxedText(dc, grp.dst, next_x, y_line2);
const int adv = DrawBoxedText(dc, to_wx(grp.dst), next_x, y_line2);
// Adjust next_x here while we have the text_w
next_x = syl_x > next_x + adv ? syl_x : next_x + adv;
@ -292,24 +297,30 @@ void KaraokeLineMatchDisplay::OnPaint(wxPaintEvent &)
dc.SetBrush(wxBrush(inner_back));
}
syl_x += DrawBoxedText(dc, unmatched_source[j].text, syl_x, y_line1);
syl_x += DrawBoxedText(dc, to_wx(unmatched_source[j].text), syl_x, y_line1);
}
// Remaining destination
if (!unmatched_destination.empty())
if (match_begin != match_end)
{
dc.SetTextBackground(sel_back);
dc.SetTextForeground(sel_text);
dc.SetBrush(wxBrush(sel_back));
next_x += DrawBoxedText(dc, unmatched_destination.substr(0, destination_sel_length), next_x, y_line2);
wxString str;
for (auto it = match_begin; it != match_end; ++it)
str += to_wx(it->str());
next_x += DrawBoxedText(dc, str, next_x, y_line2);
}
if (destination_sel_length < unmatched_destination.size())
if (match_end != destination.end())
{
dc.SetTextBackground(inner_back);
dc.SetTextForeground(inner_text);
dc.SetBrush(wxBrush(inner_back));
DrawBoxedText(dc, unmatched_destination.substr(destination_sel_length), next_x, y_line2);
wxString str;
for (auto it = match_end; it != destination.end(); ++it)
str += to_wx(it->str());
DrawBoxedText(dc, str, next_x, y_line2);
}
}
@ -328,8 +339,12 @@ void KaraokeLineMatchDisplay::SetInputData(AssDialogue *src, AssDialogue *dst)
source_sel_length = 1;
}
unmatched_destination = dst ? dst->GetStrippedText() : "";
destination_sel_length = std::max<size_t>(1, unmatched_destination.size());
destination_str = dst ? dst->GetStrippedText() : "";
using namespace boost::locale::boundary;
destination = ssegment_index(character, begin(destination_str), end(destination_str));
match_begin = match_end = destination.begin();
if (!destination_str.empty())
++match_end;
Refresh(true);
}
@ -363,182 +378,34 @@ void KaraokeLineMatchDisplay::DecreaseSourceMatch()
void KaraokeLineMatchDisplay::IncreseDestinationMatch()
{
destination_sel_length = std::min(destination_sel_length + 1, GetRemainingDestination());
Refresh(true);
if (match_end != destination.end()) {
++match_end;
Refresh(true);
}
}
void KaraokeLineMatchDisplay::DecreaseDestinationMatch()
{
destination_sel_length = std::max<size_t>(destination_sel_length, 1) - 1;
Refresh(true);
if <