NrealLight
(3ヶ月前にメモ書きしていた記事です。)
概要
中国のスタートアップNreal(エンリアル)社が開発したMRグラス。
メガネ型、88g、4基のカメラで空間認識、視野角52度(Oculus Quest*1は100度)、解像度1920×1080(非公表数値; Oculus Questは1600x1440)。 SLAM(Simultaneous Localization and Mapping; 自己位置推定と環境地図作成)、平面認識、画像認識を搭載。 現実世界のオブジェクトを解析してCGを現実世界に反映させる高度なMR機能は対応していないらしい。
Snapdragon 855を搭載したAndroidスマートフォンとUSB Type-Cで有線接続して使用。以下は、Snapdragon 855搭載Androidスマートフォン:
アプリケーションは以下が動作可能になる:
主な出来事
時期 | 出来事 |
---|---|
2019年5月 | KDDIとの提携を発表 |
2019年12月9日 | KDDIとNreal社はMRグラス「NrealLight」を用いた開発プログラム「EVE2020」を開始 |
2019年9月 | 開発者版提供開始(1199ドル) |
2020年初旬 | 一般発売予定(499ドル) |
NRSDK 1.0 Beta
Unityのみ対応(いずれは Unreal Engine, Android Native にも対応)
空間コンピューティング(Spatial Computing)
- 6DoFのトラッキング(6DoF Tracking)
- 平面認識(Plane Detection)
- 1.0 Beta では水平のみ
- 画像認識(Image Tracking)
- 環境のマッピング
- 視界内の対象物を分析、認識、理解
レンダリングの最適化(Rendering Optimization)
- 読み込み時間を最小限に押さえて画像レンダリングを最適化
Multi-modal Interactions
- Nreal Light Controller (3DoF)
- Nreal Phone Controller (3DoF)
- 未サポート
Developer Tools
- Testing Tool
- 次バージョンでサポート
- Unity Editor の play mode で動作するエミュレーター
- Observer View
サードパーティSDK拡張 (3rd Party SDK Extension)
- センサー(例:RGBカメラ)からデータにアクセスする、など
参考文献
Chromeで範囲選択したテキストをDeepL翻訳で翻訳する
概要
Google翻訳を超える翻訳の精度と噂されるDeepL翻訳を使う際、わざわざDeepL翻訳のページを開いてテキストを入力するのは手間がかかります。 Google翻訳には選択範囲をGoogle翻訳で翻訳するChrome拡張機能がありますが、DeepL翻訳はまだ無いようです。 しかし、Chrome拡張機能「Selection Search」を使えば範囲選択した範囲を翻訳させることができます。 この記事では、「Selection Search」を使用してDeepL翻訳を使用する方法を説明します。
目次
確認環境
- Google Chrome 80.x
- Selection Search 0.8.55
解説
目指している状態は次の画像のとおりです。テキストを範囲選択し、メニューで en -> ja (英語から日本語に翻訳) 、ja -> en (日本語から英語に翻訳)を選択するとDeepLのページが開き翻訳が行われます。
まず、Chrome 拡張機能 Selection Search を追加します。
次に、Selection Search のオプションを変更します。オプションは次の画像のメニューから開けます。アドレスバーに chrome-extension://gipnlpdeieaidmmeaichnddnmjmcakoe/options/options.html
を貼り付けてもオプションが開けます。
オプション項目の Search engines を編集します。#en/ja/
や #ja/en/
で翻訳元言語と翻訳先言語を指定しています。
ここまででも構いませんが、お好みに合わせて以下の設定を変えるとよいでしょう。
Popup Menu を Auto にするとテキストを範囲選択するだけでメニューが開きます。範囲選択するだけでメニューが開くと邪魔な場合は、Mouse Click などを選択すると良いでしょう。
Other Options には Open search in new tab (新しいタブで検索結果を開く)設定があります。他にも設定があるので、色々と試しつつお好みの設定を探してみると良いと思います。
Android Studio のログにソースコードへのリンクを表示する
概要
Android Studio のログ出力にソースコードへのリンクを表示する方法です。
目次
確認環境
- AndroidStudio 3.5.2
- Kotlin 1.3.50
参考情報
- logging - How can we print line numbers to the log in java - Stack Overflow
- Javaで実行中のクラス名・メソッド名を取得する方法 - Qiita
- 実行中のメソッドのクラス名とメソッド名を取得する方法
解説
Android Studio のログ出力では、クラス名.メソッド名(ファイル名:行数)
の形式で表示したときにソースコードへのリンクが有効になります。
Logcat, Instrumentation Test の実行ログでリンクが有効になることを確認しています。
fun printLinkToCode(tag: String) { val clazz = object {}::class.java val enclosingClassName = requireNotNull(clazz.enclosingClass?.name) val enclosingMethodName = requireNotNull(clazz.enclosingMethod?.name) val stackTrace = Thread.currentThread().stackTrace val indexOfThisMethod = stackTrace.indexOfFirst { it.className == enclosingClassName && it.methodName == enclosingMethodName } val caller = stackTrace[indexOfThisMethod + 1] Log.i(tag, "${caller.className}.${caller.methodName}(${caller.fileName}:${caller.lineNumber})") }
Zero Caliber VR を Oculus Quest でプレイする
概要
SteamVR で配信されている Zero Caliber VR を Qculus Quest でプレイする方法です。Virtual Desktop を使用しています。
目次
確認環境
- Virtual Desktop 1.6.2 (SIdeloading)
- Zero Caliber VR (2019/10/14 時点のバージョン)
解説
SteamVR のアプリを Oculus Quest で実行する方法については、他の記事を参照してください。Zero Caliber VR を Oculus Quest で起動できた前提で話しを進めます。
2019/10/14 現在、Zero Caliber VR を Oculus Quest で実行すると Oculus Touch の操作を何も受け付けてくれません。この問題は SteamVR の Controller Settings から設定を行うことで解決できます。
Current Binding の Edit を選択すると Zero Caliber VR の Action を設定できます。少々面倒ですが、各ボタンに Action を割り当てます。私は下記の割り当てでチュートリアルまでクリアできました。
- Left Oculus Touch
- Trigger
- Click : TriggerLeft
- Pull : TriggerAxisLeft
- Joystick
- Click : TurnLeft (座ってプレイする場合)
- Position : MotionControllerThumbLeft
- Grip
- Click : GripLeft
- X Button
- Click : Open Console (お好みで)
- Y Button
- Click : MenuLeft (お好みで)
- Trigger
- Right Oculus Touch
- Trigger
- Click : TriggerRight
- Pull : TriggerAxisRight
- Joystick
- Click : TurnRight (座ってプレイする場合)
- Touch : Run (お好みで)
- Position : MotionControllerThumbRight
- Grip
- Click : GripRight
- A Button
- Click : Skill_ReleaseManazine (お好みで)
- B Button
- Click : Skill_FireModeChange (お好みで)
- Trigger
- 未割り当て
- Skill_RelaseSlider
- ControllerMovementLeft
- ControllerMovementRight
立ってプレイする場合には、自分が振り向けば良いので TurnLeft/Right は不要です。立ってプレイしないとしゃがめないので被弾してしまう場面があり、立ってプレイすることは必須なのですが...。
設定は保存できるので保存を推奨します。次回起動時、設定がクリアされてしまいます。設定を保存しておかないと、設定をやり直すことになります。
Room の使い方
概要
Android Kotlin Fundamentals 06.1 (Room) の備忘録です。ポイントとなるコードのみを抜粋しています。
目次
確認環境
- AndroidStudio 3.5
- compileSdkVersion 28
- minSdkVersion 19
- Gradle 5.4.1
- Kotlin 1.3.11
参考情報
- Android Kotlin Fundamentals Course
- 06.1 の内容から抜粋
解説
build.gradle
buildscript { ext { kotlin_version = '1.3.11' archLifecycleVersion = '1.1.1' room_version = '2.0.0' coroutine_version = '1.0.0' gradleVersion = '3.3.0' navigationVersion = '1.0.0-alpha08' dataBindingCompilerVersion = gradleVersion // Always need to be the same. } repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:$gradleVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" } } ...
app/build.gradle
... dependencies { ... implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" }
SleepNight.kt
... @Entity(tableName = "daily_sleep_quality_table") data class SleepNight( @PrimaryKey(autoGenerate = true) var nightId: Long = 0L, @ColumnInfo(name = "start_time_milli") val startTimeMilli: Long = System.currentTimeMillis(), @ColumnInfo(name = "end_time_milli") var endTimeMilli: Long = startTimeMilli, @ColumnInfo(name = "quality_rating") var sleepQuality: Int = -1 )
SleepDatabaseDao.kt
... @Dao interface SleepDatabaseDao { @Insert fun insert(night: SleepNight) @Update fun update(night: SleepNight) @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key") fun get(key: Long): SleepNight? @Query("DELETE FROM daily_sleep_quality_table") fun clear() @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1") fun getTonight(): SleepNight? @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC") fun getAllNights(): LiveData<List<SleepNight>> }
- アノテーションは Insert, Update, Delete, Query の 4 つ
- Delete アノテーションは 1 レコード削除の場合に使う
- LiveData を戻り値にするとデータベースのデータが変更された場合に変更を通知する
- (変更の検知はデータベース単位?テーブル単位?便利だけどパフォーマンスは問題にならない?)
SleepDatabase.kt
... @Database(entities = [SleepNight::class], version = 1, exportSchema = false) abstract class SleepDatabase : RoomDatabase() { abstract val sleepDatabaseDao: SleepDatabaseDao companion object { @Volatile private var INSTANCE: SleepDatabase? = null fun getInstance(context: Context): SleepDatabase { synchronized(this) { var instance = INSTANCE if (instance == null) { instance = Room.databaseBuilder(context.applicationContext, SleepDatabase::class.java, "sleep_history_database") .fallbackToDestructiveMigration() .build() INSTANCE = instance } return instance } } } }
- version はスキーマが変更された場合に上げる
- スキーマを変更する場合にはマイグレーションが必要 (Understanding migrations with Room - Android Developers - Medium)
- fallbackToDestructiveMigration メソッドを使うことで、マイグレーションなしでデータベースを破棄、再構築できる
- exportSchema はスキーマ定義をファイルに残す場合は true
- 但し、アノテーションプロセッサの引数 (
room.schemaLocation
) にファイルを保存するディレクトリを指定する必要がある - バージョン管理システムでスキーマのバージョン管理をする用途で使い、配布するアプリをビルドする際には false にする
- 但し、アノテーションプロセッサの引数 (
- Volatile はマルチスレッドの場合に最新の値を全スレッドで常に読み込める様にする (Javaの理論と実践: volatile を扱う)
- INSTANCE プロパティはマルチスレッドを考慮して読み書き時に synchronized でロックする
SleepDatabaseTest.kt
... @RunWith(AndroidJUnit4::class) class SleepDatabaseTest { private lateinit var sleepDao: SleepDatabaseDao private lateinit var db: SleepDatabase @Before fun createDb() { val context = InstrumentationRegistry.getInstrumentation().targetContext db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java) .allowMainThreadQueries() .build() sleepDao = db.sleepDatabaseDao } @After @Throws(IOException::class) fun closeDb() { db.close() } @Test @Throws(Exception::class) fun insertAndGetNight() { val night = SleepNight() sleepDao.insert(night) val tonight = sleepDao.getTonight() assertEquals(tonight?.sleepQuality, -1) } }
- inMemoryDatabaseBuilder を使うことでテスト毎にデータベースをクリアする
- allowMainThreadQueries を実行することでデータベースへの問い合わせを MainThread で実行できる様にする
- 通常は MainThread でデータベースに問い合わせるとエラーになる
Navigation component の使い方
概要
Android Kotlin Fundamentals 03.x (Fragment と Navigation) の備忘録です。ポイントとなるコードのみを抜粋しています。
目次
確認環境
- AndroidStudio 3.5
- compileSdkVersion 28
- minSdkVersion 19
- Gradle 5.4.1
- Kotlin 1.3.11
参考情報
- Android Kotlin Fundamentals Course
- 03.1, 03.2, 03.3 の内容から抜粋
解説
build.gradle
buildscript { ext { kotlin_version = '1.3.11' archLifecycleVersion = '1.1.1' gradleVersion = '3.5.0' supportlibVersion = '1.0.0-rc03' navigationVersion = '1.0.0-rc02' dataBindingCompilerVersion = gradleVersion // Always need to be the same. } repositories { google() jcenter() } dependencies { classpath "com.android.tools.build:gradle:$gradleVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" } }
- Safe Args Gradle plugin を classpath に追加
app/build.gradle
... apply plugin: 'androidx.navigation.safeargs' ... dependencies { ... implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion" implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion" ... }
- Safe Args Gradle plugin を有効化
- Navigation components 関連のライブラリを追加
activity_main.xml
... <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <androidx.drawerlayout.widget.DrawerLayout android:id="@+id/drawerLayout" ...> <!-- サンプルでは LinearLayout の子になっている --> <fragment android:id="@+id/myNavHostFragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/navigation" app:defaultNavHost="true" ... /> <com.google.android.material.navigation.NavigationView android:id="@+id/navView" android:layout_gravity="start" app:headerLayout="@layout/nav_header" app:menu="@menu/navdrawer_menu" ... /> </androidx.drawerlayout.widget.DrawerLayout> </layout>
- Drawer を使用しない場合は DrawerLayout は不要
- Navigation components を使って Fragment を切り替えるため NavHostFragment を使用
- defaultNavHost を true に設定した Fragment が Back ボタンをインターセプト
- NavigationView は Drawer に表示するナビゲーション
navigation.xml
... <navigation xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/navigation" app:startDestination="@id/titleFragment"> <fragment android:id="@+id/titleFragment" android:name="com.example.android.navigation.TitleFragment" android:label="fragment_title" tools:layout="@layout/fragment_title" > <action android:id="@+id/action_titleFragment_to_gameFragment" app:destination="@id/gameFragment" /> </fragment> <fragment android:id="@+id/gameFragment" android:name="com.example.android.navigation.GameFragment" android:label="fragment_game" tools:layout="@layout/fragment_game" > <action android:id="@+id/action_gameFragment_to_gameOverFragment" app:destination="@id/gameOverFragment" app:popUpTo="@+id/gameFragment" app:popUpToInclusive="true" /> <action android:id="@+id/action_gameFragment_to_gameWonFragment" app:destination="@id/gameWonFragment" app:popUpTo="@+id/gameFragment" app:popUpToInclusive="true" /> </fragment> <fragment android:id="@+id/gameOverFragment" android:name="com.example.android.navigation.GameOverFragment" android:label="fragment_game_over" tools:layout="@layout/fragment_game_over" > <action android:id="@+id/action_gameOverFragment_to_gameFragment" app:destination="@id/gameFragment" app:popUpTo="@+id/titleFragment" app:popUpToInclusive="false" /> </fragment> <fragment android:id="@+id/gameWonFragment" android:name="com.example.android.navigation.GameWonFragment" android:label="fragment_game_won" tools:layout="@layout/fragment_game_won" > <action android:id="@+id/action_gameWonFragment_to_gameFragment" app:destination="@id/gameFragment" app:popUpTo="@+id/titleFragment" app:popUpToInclusive="false" /> <argument android:name="numQuestions" app:argType="integer" /> <argument android:name="numCorrect" app:argType="integer" /> </fragment> <fragment android:id="@+id/aboutFragment" android:name="com.example.android.navigation.AboutFragment" android:label="fragment_about" tools:layout="@layout/fragment_about" /> <fragment android:id="@+id/rulesFragment" android:name="com.example.android.navigation.RulesFragment" android:label="fragment_rules" tools:layout="@layout/fragment_rules" /> </navigation>
- tools:layout は Design editor でレイアウトを表示するために指定
- app:popUpTo は Back 時の遷移先
- app:popUpToInclusive が false の場合は popUoTo の Fragment に遷移する
- app:popUpToInclusive が true の場合は popUoTo の Fragment もバックスタックから除かれ、もう一つ前の Fragment に遷移する
- titleFragment → gameFragment → gameOverFragment と順にスタックに積まれているので、popUpTo=gameFragment , inclusive=true だと titleFragment に遷移する
- Safe Args が有効の場合は XXXFragmentDirections クラス (NavDirections を実装) が生成される
- argument を指定すると XXXFragmentArgs クラスが生成され、遷移元の NavDirections のファクトリメソッドに引数が追加される
navdrawer_menu.xml
... <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/rulesFragment" android:title="@string/rules" /> <item android:id="@+id/aboutFragment" android:title="@string/about" /> </menu>
- item の id と navigation.xml で指定した fragment の id を合わせるとライブラリが良きに計らってくれる
MainActivity.kt
class MainActivity : AppCompatActivity() { private lateinit var drawerLayout: DrawerLayout override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) drawerLayout = binding.drawerLayout val navController = findNavController(R.id.myNavHostFragment) NavigationUI.setupWithNavController(binding.navView, navController) NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout) } override fun onSupportNavigateUp(): Boolean { return NavigationUI.navigateUp(findNavController(R.id.myNavHostFragment), drawerLayout) } }
- setupWithNavController はスライド操作で Drawer を表示するために必要
- setupActionBarWithNavController はアクションバーで Drawer の表示を行うために必要
- onSupportNavigateUp メソッドはアクションバーで戻る操作を行うために必要
TitleFragment.kt
class TitleFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val binding = DataBindingUtil.inflate<FragmentTitleBinding>(inflater, R.layout.fragment_title, container, false) binding.playButton.setOnClickListener { view -> view.findNavController().navigate(TitleFragmentDirections.actionTitleFragmentToGameFragment()) } setHasOptionsMenu(true) return binding.root } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { super.onCreateOptionsMenu(menu, inflater) inflater?.inflate(R.menu.options_menu, menu) } override fun onOptionsItemSelected(item: MenuItem?): Boolean { return NavigationUI.onNavDestinationSelected(item!!, view!!.findNavController()) || super.onOptionsItemSelected(item) } }
- navigate メソッドの引数は navigation.xml における action の id でもよい
- Safe Args を使用している場合は navigate メソッドの引数に NavDirections を使用できる
- onNavDestinationSelected は menu の item id から navigation の fragment id を特定して遷移する
GameFragment.kt
class GameFragment : Fragment() { ... override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { ... view.findNavController().navigate( GameFragmentDirections.actionGameFragmentToGameWonFragment( numQuestions, questionIndex )) ... } ... }
- Safe Args を使用しており、navigation の fragment に argument が指定されている場合には NavDirections のファクトリメソッドに引数が追加される
GameWonFragment.kt
class GameWonFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { ... val args = GameWonFragmentArgs.fromBundle(arguments!!) Toast.makeText(context, "NumCorrect: ${args.numCorrect}, NumQuestions: ${args.numQuestions}", Toast.LENGTH_LONG).show() setHasOptionsMenu(true) return binding.root } private fun getShareIntent() : Intent { val args = GameWonFragmentArgs.fromBundle(arguments!!) return Intent(Intent.ACTION_SEND) .setType("text/plain") .putExtra(Intent.EXTRA_TEXT, getString(R.string.share_success_text, args.numCorrect, args.numQuestions)) } private fun shareSuccess() { startActivity(getShareIntent()) } override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { super.onCreateOptionsMenu(menu, inflater) inflater?.inflate(R.menu.winner_menu, menu) if (getShareIntent().resolveActivity(activity!!.packageManager) == null) { menu?.findItem(R.id.share)?.isVisible = false } } override fun onOptionsItemSelected(item: MenuItem?): Boolean { when (item?.itemId) { R.id.share -> shareSuccess() } return super.onOptionsItemSelected(item) }
- XXXFragmentArgs.fromBundle を使えば型安全を得た状態で値を受け取れる
- onCreateOptionsMenu では ACTION_SEND に対応する Activity の存在を検証し、存在しない場合はメニューを非表示にする
無料のボイスチェンジャーを色々と試した
概要
無料のボイスチェンジャーをいくつか試した結果、VSTHost + RoVee に落ち着きました。OBS Studio と合わせて使うことを前提にしています。
目次
参考情報
- 「バ美肉」したい人必見!無償・有償の定番ボイスチェンジャー5選 (2019年3月31日) - エキサイトニュース
- 音屋がボイチェンをガチ検証してみた | ぷるれこ
- 恋声が合わなかった人にも試して欲しい、VSTプラグインでボイチェンする方法:とりすーぷのブロマガ - ブロマガ
解説
まずは参考情報のサイトを参照しつつ、試してみるボイスチェンジャーのソフトウェアをピックアップしました。
今回試したソフトウェアは以下の
- 恋声
- Gachikoe! Core (ガチコエ! Core) v0.0.3 - exe:cute project - BOOTH
- バ美声 β版ver0.260 - はんそで工房 - BOOTH
- ソフトウェア - RoVee 1.21 | g200kg Music & Software
試行
恋声
恋声は以前にも別記事で使用しています。2019年現在、最終更新は2018年になっています。
まず、恋声で良いなと思う点です。
- インストール不要で手軽に使える
- 男声→女声の初期設定がある
- 声の高さ(ピッチ)と声の性質(フォルマント)の設定の値の幅が広い
- 自分の声質(低い声)でも女声っぽくできた
しかし、不満に思う点もあります。
- 動作が不安定
これが私としては致命的な欠点であり、使用を躊躇する点です。
Gachikoe! Core
2019年現在、絶賛開発中のボイスチェンジャーです。
良いなと思う点は以下です。
- インストール不要で手軽に使える
- 設定項目が少なく簡潔
- 低遅延、高音質らしい
- 安定して動作する
長時間使用したわけではないので、安定して動作すると言っても恋声と比較しての話です。恋声は短時間の使用でも、動作が不安定になることが頻繁にありました。
恋声に思っていた致命的な欠点は改善されますが、再び致命的な欠点があります。
- 設定の幅が狭い
- 自分の声質(低い声)だと女声にはならない
今後に期待のボイスチェンジャーですが、現状では私のニーズを満たすものではありません。低遅延、高音質だとしても、作り出される声がそもそもニーズに満たないと仕方がなく、次へ。
バ美声
2019年現在、こちらも絶賛開発中のボイスチェンジャーです。
良いなと思う点は Gachikoe! Core と同じです。そして、欠点も同じです。Gachikoe! Core とそっくりなコンセプトだと思いました。
こちらも今後に期待のボイスチェンジャーですが、現状では私のニーズを満たすものではなく、次を試すことにしました。
RoVee
2019年現在、最終更新が2013年となっており既に更新が止まっている様子です。他の3つのソフトウェアは単体で動作するソフトウェアでしたが、RoVeeはVSTプラグインとなっており単体では動作しません。OBS StudioにはVSTプラグインによって音声変換を拡張する機能があります。RoVeeをOSB Studioに組み込んでの使用を考えました。
試してみたところ、OSB StudioでマイクのフィルタにRoVeeを表示できましたが、設定を行うウィンドウが表示されません。設定を変更できなければ使い物になりません。調べているとOSB StudioでRoVeeを使っている方もいる様ですが、設定の変更方法までは分かりませんでした。
OSB Studioで直接使うことは断念し、VSTプラグインを組み合わせて音声変換を行うVSTHostを使うことにしました。しかし、VSTHostの公式ダウンロードサイトが表示できません。非公式と思われるサイトは見つかりました。安全性に不安はありますが、下記のサイトからダウンロードできます。
- http://ja.softoware.org/apps/get-vsthost-64-bit-for-windows.html
- https://softfamous.com/vsthost/download/
VSTHostでRoVeeを読み込ませることで音声を変換できました。
この組み合わせの良いなと思う点は以下です。
- 多少の手間はかかるが、インストールは不要
- 設定は多少複雑だが、VSTプラグインは多様でありカスタマイズの自由度が高い
- 自分の声質(低い声)でも女声っぽくなるし、設定を頑張れば質も良くなりそう
- 恋声が合わなかった人にも試して欲しい、VSTプラグインでボイチェンする方法:とりすーぷのブロマガ - ブロマガを見ると、プラグイン次第で質を良くできる
- 動作は安定している
不満に思う点と言えば下記くらい。
VSTHostは今後にメンテナンスされることがなく、使えなくなるかもしれないという不安はあります。その頃には、現在開発中のボイスチェンジャーが機能拡張されていることを祈ります。
まとめ
VSTHostを使えば複数のVSTプラグインを組み合わせて様々な変換を行えます。男声を女声にするだけでなく、複数の声を混ぜたり、エコーをかけたりと組み合わせ次第では無限です。あまり注意していませんでしたが、変換による遅延もそれほど感じませんでした。無料でありながら、安定して動作しつつ、カスタマイズの自由度があり自分の声にも対応できそうだと思います。