フェルマータ

個人用のメモ。ソフトウェアの導入とかが多くなる予定。

作りたいもののポエム3

概要

shurabap.hatenablog.jp

一昨日からの続きやるよ。

作りたいもの

  1. UTAU 音源より表現力の高い音源形式
  2. ↑のファイル読み書き部分

その他要件

  1. 既存リソースを活かすために UTAU 音源からインポートをできるようにする
  2. 波形情報と演奏情報とキャラクタ情報は厳密に分けて設計すること
  3. 欲を言えば複数音源を自在に切り替え、あるいは混ぜられるようにすること(キャラクタの枠組みを越えるべき)

データの設計

音楽的コンテキストは必要ないという結論に達した。本質的に波形接続型歌声合成器はサンプラーなのだからマッピングさえあれば全て型がつくのである(きっと

segment (切りだされた波形)

segment は波形ファイルへの参照と波形の切り出し位置、伸長の位置を指定する。

{
  "id": "segment0001"
  "path": "akasatana.wav",
  "begin": 500,
  "length": 500,
  "temporal_position" : 100,
  "front_fixed_range" : 100,
  "rear_fixed_range" : 100
}
  • id: segment ID
  • begin: 開始位置
  • length: 波形長
  • temporal_position: ビートの位置(先行発音相当)
  • front_fixed_range: 前半の固定長(固定長相当)
  • rear_fixed_range: 後半の固定長

mapping(発音・音程・Velocityと、波形との対応)

ある発音がある音程である Velocity で指定された際にどの波形をどの程度利用するのかというマッピングを表す。

{
  "pronounce" : {
    "before" : "a"
    "this" : "k a"
    "after" : "s a"
  },
  "segments": [
    {
      "segment_id": "segment0001",
      "note": {
        "before": 60,
        "this": 60,
        "after": 60
      },
      "velocity": 64
    },
    {
      "segment_id": "segment0001",
      "note": {
        "before": 60,
        "this": 60,
        "after": 60
      },
      "velocity": 64
    }
  ]
}
  • pronounce: 発音
    • before: 先行音の発音
    • this: 発音
    • after: 後続音の発音
  • segments: マッピングされるセグメント群
    • segment_id: セグメントの識別子
    • note: 音高
      • before: 先行音の音高
      • this: 音高
      • after: 後続音の音高
    • velocity: ベロシティ

根本的には note, velocity の二次元平面上のマッピングなのでもうちょっとマシな書き方がありそう。 結構空間が広いのでデータ量が爆発するのが難点。

pronounce

${consonant} ${vowel} の形式で表す。子音が存在しない場合は前半を省略する。

TBD

作りたいもののポエム2

概要

shurabap.hatenablog.jp

昨日の続きやるよ。

作りたいもの

  1. UTAU 音源より表現力の高い音源形式
  2. ↑のファイル読み書き部分

とりあえず一旦ここまで書けたらいいこととする。(大きく作って死ぬことが多い気がする)

その他要件

  1. 既存リソースを活かすために UTAU 音源からインポートをできるようにする
  2. 波形情報と演奏情報とキャラクタ情報は厳密に分けて設計すること
  3. 欲を言えば複数音源を自在に切り替え、あるいは混ぜられるようにすること(キャラクタの枠組みを越えるべき)

ざっくりとした設計

segment (切りだされた波形)

segment は機械的な意味しか有しない。構成としては波形ファイルへの参照と波形の切り出し位置、伸長の位置を指定する以外の機能を持たない。

{
  "segments": [
    {
      "id": "segment0001"
      "path": "akasatana.wav",
      "begin": 500,
      "length": 500,
      "temporal_position" : 100,
      "front_fixed_range" : 100,
      "rear_fixed_range" : 100
    },
    {
      "id": "segment0002"
      "path": "akasatana.wav",
      "begin": 500,
      "length": 500,
      "temporal_position" : 100,
      "front_fixed_range" : 100,
      "rear_fixed_range" : 100
    }
  ]
}
  • id: segment ID
  • begin: 開始位置
  • length: 波形長
  • temporal_position: ビートの位置(先行発音相当)
  • front_fixed_range: 前半の固定長(固定長相当)
  • rear_fixed_range: 後半の固定長

切り出しと伸長はこれだけあればいい。これ以上の情報は別の情報に切り分けるべきだ。 UTAU の oto.ini は音楽的コンテキストを含まないのでこの設定は UTAU で言えば oto.ini に等しい。

切り出し部分についてはこれで良い。このレイヤーには発音や音程など発音のコンテキストを混ぜてはいけない。

phoneme (発音と音符の一断片)

phoneme は音楽的な一断片を表す。切りだされた波形とそれに対するメタ情報からなる。

{
  "phonemes": [
    {
      "id": "phoneme0001"
      "segment_id": "segment0001",
        "context" : {
          "pronounce": {
          "previous" : "-",
          "this" : "a",
          "next" : "k a"
        },
          "note" : {
          "previous" : 60,
          "this" : 60,
          "next" : 60
        },
          "tempo" : 120,
          "strength" : 0
        }
      }
    },
    {
      "id": "phoneme0002"
      "segment_id": "segment0002",
        "context" : {
          "pronounce": {
          "previous" : "a",
          "this" : "k a",
          "next" : "s a"
        },
          "note" : {
          "previous" : 60,
          "this" : 60,
          "next" : 60
        },
          "tempo" : 120,
          "strength" : 0
        }
      }
    }
  ]
}

ここまでは妥当な感じがする。

作りたいもののポエム

いきさつ

最近なにもプログラムを書いていなかった気がしてつらくなってきたので、なにか書くお題でも用意すればいいんじゃないかと思い立った。

作りたいもの

  1. UTAU 音源より表現力の高い音源形式
  2. ↑のファイル読み書き部分

とりあえず一旦ここまで書けたらいいこととする。(大きく作って死ぬことが多い気がする)

その他要件

  1. 既存リソースを活かすために UTAU 音源からインポートをできるようにする
  2. 波形情報と演奏情報とキャラクタ情報は厳密に分けて設計すること
  3. 欲を言えば複数音源を自在に切り替え、あるいは混ぜられるようにすること(キャラクタの枠組みを越えるべき)

ざっくりとした設計

とりあえずこんな感じでどうだろうみたいなのを書いてみる。 おそらく仕様が長大になるのでどっかで分割するべき。

{
  "waves": [
    {
      "path": "akasatana.wav",
      "segment": [
        {
          "begin": 500,
          "length": 500,
          "temporal_position" : 100,
          "front_fixed_range" : 100,
          "rear_fixed_range" : 100,
          "context" : {
            "pronounce": {
              "previous" : "-",
              "this" : "a",
              "next" : "k a"
            },
            "note" : {
              "previous" : 60,
              "this" : 60,
              "next" : 60
            },
            "tempo" : 120,
            "strength" : 0
          }
        },
        {
          "begin": 500,
          "length": 500,
          "temporal_position" : 100,
          "front_fixed_range" : 100,
          "rear_fixed_range" : 100, 
          "context" : {
            "pronounce": {
              "previous" : "-",
              "this" : "k a",
              "next" : "s a"
            },
            "note" : {
              "previous" : 60,
              "this" : 60,
              "next" : 60
            },
            "tempo" : 120,
            "strength" : 0
          }
        }
      ]
    }
  ]
}
  • begin: 開始位置
  • length: 波形長
  • temporal_position: ビートの位置(先行発音相当)
  • front_fixed_range: 前半の固定長(固定長相当)
  • rear_fixed_range: 後半の固定長
  • context
    • pronounce
      • previous: 先発の前の発音
      • this: この波形の発音
      • next: 後続の発音
    • note
      • previous: 先発の前の音程
      • this: この波形の音程
      • next: 後続の音程
    • tempo: 収録テンポ
    • strength: 音の強さ(うーん)

context は分離した方がいいかもしれない。

TBD..

Qt の QUndoStack の挙動

ポエム

 Qt には Undo framework があり、 QUndoStackQUndoCommand を積んでいけば、いい感じに undo, redo できるようになる。 このときメモリが解放されるのかなど若干動作に癖というか、細かい部分の挙動の記述が少ないので記事にしておく。

github.com

挙動の確認は動かすのが面倒なので実際のコードを見た。ヘルプ書いてあっても英語だしあんま変わんないんだよね。

メモリ解放

 QUndoStack は以下のときにメモリ解放をしている。

  • push 時に、 redo 側に残されたコマンド
  • デストラクタ実行時に登録されているコマンド

つまり自前で QUndoCommand のメモリ管理はせずに QUndoStack に突っ込んでおけば良い。

push 時の挙動

 確信が無かったので調べたけど、 QUndoStackQUndoCommandpush した際に一発 redo を呼んでいる。 Command と同様の操作をする必要はない。まあそれもそうか。

Qt のシグナルを一旦無効にする。

ポエム

 Qt の SIGNAL/SLOT は強力なんだが、 Undo, Redo を実装しようとしてシグナル止めて無いとユーザ編集と Undo, Redo によるモデルの変更が分からなくて Undo スタックがぶっ壊れるなと思ったので一旦シグナルを止める方法を調べた。

やり方

bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);

まんまや。

参考資料

stackoverflow.com

スタオバさいつよ。

Qt の ui ファイルを cmake (CLion)から使う

ポエム

 何度やっても QT_UIC_EXECUTABLE is missing といいよる cmake に愛想を尽かさず頑張った。 Qt の *.ui ファイルを使う方法がわかったので記事を書く。Qt 記事少なすぎでしょみんなもっと記事書いて!英語記事ですらほとんどないじゃん。

症状

 以下の様な CMakeLists を 使うと、 QT_UIC_EXECUTABLE 無いんだけどwwwと cmake が激おこしてた。

# sources
qt_wrap_ui(vsampler_corpus_editor view/ui_CorpusMetaInfoView.h view/ui_CorpusMetaInfoView.cpp vioew/CorpusMetaInfoView.ui)
add_library(vsampler_corpus_editor ${files_you_need})

対処法

 リファレンス見ると一見これで正しそうだしなんとかなりそうだがお前の qt_wrap_ui の使い方は間違っている。 結局ちゃんと設定ができていないのが問題だったらしい。

# sources
qt5_wrap_ui(QT_CORPUS_EDITOR_UI_HEADERS hoge.ui foo.ui)
add_library(vsampler_corpus_editor ${QT_CORPUS_EDITOR_UI_HEADERS} ${files_you_need})

こうしろってさ。んで cpp, c++ ファイルからこうせよってさ。

#include "ui_Hoge.h"
#include "Hoge.h"

hoge::Hoge(QWidget *parent)
        : QMainWindow(parent), ui(new Ui::Hoge) {
    ui->setupUi(this);
}

hoge::Hoge::~Hoge() {
    delete ui;
}

cpp ファイルの方は Qt Creator に準じているだけ。これで *.ui のヘッダファイルが生成されていい感じになる。 一週間かかった死にたい。

Qt を cmake から使う(CLion から使う)。

概要

 Qt の開発というととりあえず QtCreator を使えばいいだろ、と思うのだが、最近 JetBrains から CLion という神ツールが発売された。

www.jetbrains.com

業務が IntelliJ だもので操作感が同じというのもあるんだが、 C++リファクタリングができるというさいつよツールなのでこれを使いたい。 しかしながら CLion は cmake 形式のプロジェクトしか読み込めない。

対処

 Qt は cmake のビルドにも対応している。良い。(QtCreator から使うと恐ろしく扱いづらいが)

CMake Manual | Qt 5.4

基本はこれにしたがって設定すれば良い。あとは作った cmake プロジェクトを CLion から読みこめるので Qt を利用したコードを CLion から利用できる。 SIGANAL/SLOT などの Qt の C++ 拡張部分も問題なく使えた。

システム変数の追加

 CMAKE_PREFIX_PATH に Qt のディレクトリを追加する。僕の環境だと C:\Qt\5.3\mingw482_32 だった。 これで cmake が Qt の位置を知ることができる。

cmake の記述

# Created by Hal@shurabaP
project(sample)

# configuration for Qt
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)

# cmake options
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

# dependency
find_package(Qt5Core)
get_target_property(QtCore_location Qt5::Core LOCATION)
find_package(Qt5Gui)
get_target_property(QtCore_location Qt5::Gui LOCATION)
find_package(Qt5Multimedia)
get_target_property(QtCore_location Qt5::Multimedia LOCATION)
find_package(Qt5Widgets)
get_target_property(QtCore_location Qt5::Widgets LOCATION)

# sources
add_executable(sample main.cpp)

target_link_libraries(sample LINK_PUBLIC Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::Widgets)

cmake よく知らないから間違ってるかも。とりあえずビルドして実行できたので僕はこの設定を使っている。