リムナンテスは愉快な気分

徒然なるままに、言語、数学、音楽、プログラミング、時々人生についての記事を書きます

楔形文字で学ばないアッカド語文法(6)G過去形と語順

そろそろアッカド語やらねば本当に忘れそう(何なら忘れた)


さて、この記事は

についての記事です。

1. 復習:G語幹について

セム語では子音からなる「語根」間に母音を挟みこむことでできる「語幹」が特徴的でした。アッカド語でも例に漏れず、母音の挟み込み方によって様々な機能を持った語幹が生まれるのでした。

メインはG語幹、D語幹、S語幹、N語幹の4系統。アッカド語のG語幹(不定形)は、1,2,3を語幹子音として1a2ā3(-um)のように表されるのでした。

第6回ではG語幹系列の過去形について。

2. G過去形

1,2,3をそれぞれ語幹子音とすると、G過去形の基本形は 12V3 です。このVのことを幹母音(theme vowelとかstem vowel)といいます。

ただ、幹母音がa, i, uのどれになるかは語根によって決まるので、いちいち覚えないといけない。めんどくさい!

  • šakānum 「置く(こと)」 → iškun 「置いた」(幹母音はu)
  • šarāqum 「盗む(こと)」 → išriq 「盗んだ」(幹母音はi)
  • ṣabātum 「掴む(こと)」 → iṣbat 「掴んだ」(幹母音はa)

最初のi-は三人称単数接頭辞です。G過去形では人称、性、数によって接頭辞、接尾辞がくっついたりくっつかなかったりします。他の人称、性、数においても、G過去形語幹に以下のような接頭辞、接尾辞がつきます。

単数 複数
男性 女性 男性 女性
3人称 (3cs) i- (3mp) i- -ū (3fp) i- -ā
2人称 (2ms) ta- (2fs) ta- -ī (2cp) ta- -ā
1人称 (1cs) a- (1cp) ni-

3人称単数、2人称複数、1人称単数と複数に関しては、男性名詞と女性名詞で同形になります。ですので、覚える活用の種類は全部で8種類です。


以下活用例です。

置く
(šakānum)
盗む
(šarāqum)
掴む
(ṣabātum)
3cs iškun išriq iṣbat
2ms taškun tašriq taṣbat
2fs taškunī tašriqī taṣbatī
1cs aškun ašriq aṣbat
3mp iškunū išriqū iṣbatū
3fp iškunā išriqā iṣbatā
2cp taškunā tašriqā taṣbatā
1cp niškun nišriq niṣbat

3. 語順

アッカド語の語順は日本語と同じく"SOV"です。もう少し詳細に言うと、散文の動詞節における語順は"SOAV"です。

Subject(主語)— Object(直接目的語)— Adjuncts(付属語)— Verb(動詞)

付属語とは、副詞、前置詞句、間接目的語などのことを指します。
日本語と同様に、他動詞文であっても全ての要素を入れなければならないということはなく主語や目的語を省くことができます。

wardum išriq
「男奴隷は盗んだ」

wardum 男奴隷
išriq 盗んだ < šarāqum


強調したい語を文頭に持ってくることができたりするので語順は自由です。ただし動詞は必ず文末にきます。特に書き言葉の場合、アッカド語には読点がないため動詞が実質的な文末マーカーとして機能します。したがって次の文章は2文です。

antum kaspam iṣbat ina bītim iškun
「女奴隷は銀を掴み、家に置いた

antum 女奴隷
kaspum
iṣbat 掴んだ < ṣabātum
ina 〜の中へ
bītum
iškun 置いた < šakānum


ここでは iṣbat(掴んだ)と iškun(置いた)の2つが動詞ですので、"antum kaspam iṣbat"までで一文、"ina bītim iškun"でもうい一文、と解釈されます。
…という説明が英語話者向けになされていますが、この文章の場合は上記訳文のように接続っぽく訳すのが良さそう。

4. 動詞の人称、性、数の例外

基本的に主語の人称、性、数に合わせて普通に変化させればよいのですが、いくつか例外があります。


双数主語は単独で男性名詞であっても女性複数扱い(ごく稀に男性複数扱い)。

šarrām iškunā
「2人の王が座した」

šarratān iškunā
「2人の女王が座した」

šarrum
šarratum 女王


主語が複合主語なら複数扱い。さらに、一項目でも男性名詞なら男性複数扱い。

mārum u mārtum imqutū
「息子と娘が落ちた」

antum wardum u mārātum imqutū
「女奴隷と男奴隷と娘たちが落ちた」

aššatum u mārātum imqutā
「妻と娘が落ちた」

mārum 息子
mārtum
imqut 落ちた < maqātum
aššatum

ちなみにA, B, Cを並列で述べる時は”A B u C”となるようです。


集合名詞は単複両方取りうる。

ṣābum ālam iṣbat / iṣbatū
「軍隊は街を掴んだ(掌握した)

ṣābum 軍隊
ālum

5. まとめ

  • G過去形で挿入される母音は動詞ごとに決まっている
  • 語順はSOVで日本語とだいたい一緒


←前:楔形文字で学ばないアッカド語文法(5)語根 - リムナンテスは愉快な気分
→次:楔形文字で学ばないアッカド語文法(7)母音脱落・G動形容詞 - リムナンテスは愉快な気分

参考文献
  • J. Huehnergard, A Grammar of Akkadian (3rd ed. 2011), Harvard Semitic Museum Studies 45, ISBN 978-1-57506-922-7.
  • D. Snell, Enkonduko en la Akadan (Tria, reviziita eldono), esperantigita de Michael Wolf, Biblical Institute Press, Rome, 1988, ISBN: 88-7653-566-7.

効率的な語学の勉強法 〜とりあえず作文〜

f:id:frecafloros:20200621150113j:plain


結論。
作文しろ。作文しながら単語と文法覚えろ。


あらゆる言語を学ぶ際に、どのように学ぶべきか?英語でも中国語でも、どんな言語をやる場合でも通用するような学習方法はなんだろうか。20以上の言語を学んだ経験から、それは、作文であるという結論に至った。

あくまでも自分の場合は、ということではあるが。ひたすら多読するのが得意な人、とにかくネイティブと話したら覚えられる人、聴いたらコピーできる人、暗記が得意だから単語覚えてたら文法やらなくてもなんか喋れる人。人によって向いている方法、向いていない方法があると思います。

私の場合は単語は覚えられなかった上に、よく学校で教えられているような「英文法」が全く要領掴めず、努力不足の烙印を押されてやる気を失っていました。が、ある時から短文の小テストを課されるようになったので止む無く英語の短文を暗記してたら知らない間にわかるようになった。

というわけで、効率的な語学の勉強法について、方法論をまとめました。
基本的に学ぶ言語がどんな言語であっても通用するはずです。
ただし、例文と訳が載ってる教材を用意してください。


前提・モチベーションについて

そもそもなぜ言語を学ぼうと思ったのか?

飽きるので、退屈なので、なんかモチベを維持してくれ。

結局理由なんてなんでもいいよ。
というモチベーションを上げてから本題。


基本方針

ユニットごとに回します。
だいたい20〜30ページを1ユニットとします。
ニューエクスプレスプラスとかなら4課1ユニットとして扱っていいんじゃないんですかね。
1課のボリュームが多いとかあったら適当にキリのいいところで区切ってください。

1周目

とりあえず読む、問題を解く(見ながら)
目標は理解すること。覚えられるなら覚えるに越したことはない。本番は2周目なので。

2周目

暗記パート
例文、作文問題(和文◯訳問題)を完璧にする。
日本語訳を見て、英語なら英語、中国語なら中国語に訳す=文章を作る。
見ながら解いた or 間違えた問題をチェックして、もう一度作文する。何も見ずに間違えずに書けた文章はそれでおしまい。

これをノーミスになるまで繰り返す。

というのを例文および和文◯訳問題でやる。
余力があれば◯文和訳問題の和文をどこかにメモっておいてそれについても和文◯訳トレーニングしてもいい。
めんどくさかったらとりあえずやらんでいい。

これで一通り覚えた状態になったら次のユニットを回す。


復習のタイミング

一回やったくらいで覚えるわけがないんだけどそれでいい。
で、進めていくと、なんかどこかでつっかえる。8割くらいわからない、みたいな状態になったら、多分過去にやったことを忘れてきているので、復習する。
再び上の2周目のあれを繰り返す。

とりあえずまえのユニットをやって、それでも無理ならさらに遡って…とやっていって先に進めたらOK。


学習のコツ

単語の習得方法

なんとかして引っかかりを作る。
ある意味作文するために「使う」という行為自体も記憶の手助けになるはず。
あとは語源を調べろ。固有語か?外来語か?合成語か?何語の何と比較できる?
語族・語派が同じ言語と比較しろ。ex,チェコ語ならロシア語と比較するとか。感覚的にわかるけど、改めて整理することで腑に落ちるのを感じて欲しい。
別に忘れてもいい。

文法の習得方法

なぜその和文でその語形を使ったのか?などを説明できるようになる、ということを意識する。説明できる状態になる、というのが一つの指標。文法は語学の近道。

大抵の言語の大抵のまともな文章は「文法」と呼ばれる秩序をもって単語が並べられているわけであって、意味もなく並んでいるわけではない。そこんところがわからない文法不要論者(なぜか英語至上主義者に多い)の文法不要論に耳を貸さずに文法は勉強しましょう。

別に忘れてもいい。

語学における心持ち

忘れること前提で反復しろ。
お前は生まれた瞬間から卒なく二足歩行できたわけではない。なんかハイハイから始めてたっちしてコケながら歩いたんだよ。うん。語学も一緒。
というか反復するだけでいいから簡単。頭使わない。あほでもできる。記憶力なくてもできる。記憶力に自信ないかもしれんが箸使えるだろ。それは何年もかけて箸使ってきたからじゃないのか?そんだけの基本能力があればいける。
記憶力ある人もいるけどとりあえずその事実は忘れよう。彼らは特殊なので。

◯◯語だけ出来てもしょうがないというのもある。まあ一理ある。ただ大抵の人はやはりそれに見合った能力が後からついてくると思う。まあ散々英語できてどうしようもなくだめな人もいますが。

おわりに

最後愚痴ばかりになってしまいましたが。
とりあえず、この方法を徹底すればどんな言語でもいけるはず。
そして、あわよくば今学習している言語などサクッと覚えて、さらに第2外国語、第3外国語、と歩みを進めて欲しい。
まあ日本語、英語(、最近は中国語も?)くらいを分かっていれば十二分に生きてはいけるんですけど、言語って3000とか6000とかあると言われているので、もう少しマイナーな言語にも目を向けて楽しんで欲しいかなと、そう思います。

余談:どの言語をやるか?(多言語学習者向け)

これ多言語学習者の永遠の悩みだと思うんですよね。
まあ、語族とか語派を揃えるのが効率いい

自分はもう語族ぐちゃぐちゃすぎて最高に非効率ですが。
そこそこ真面目にやったのでいうと、

まあでも楽しいのでいいかなという。
結局は興味もてるかどうかなので。

日本語の子音を捉えなおす

ちょっとしたメモです。

wikipedia英語版では以下のような表で日本語の子音が紹介されています。

Bilabial Alveolar Alveolo-
palatal
Palatal Velar Uvular Glottal
Nasal m n (ɲ) (ŋ) (ɴ)
Stop pb td
Affricate (t͡s)(d͡z) (t͡ɕ)(d͡ʑ)
Fricative (ɸ) sz (ɕ)(ʑ) (ç) h
Liquid r
Semivowel j w
Special moras /N/,/Q/


しかし現代日本語では直音と拗音、つまり例えば「マ」と「ミャ」は明確に区別されるわけで、多くの人が参照するであろうwikipediaなどの記事でそういった情報が欠落しているのは如何なものかと思うわけでして。wikipediaに限らず市販の文法書でも子音体系としての説明がされていないような気がしますが。

「マ」と「ミャ」のような対立は日本語以外でも存在し、例えばロシア語やマーシャル語で「マ」と「ミャ」が区別されます。これらの言語で語られるように、日本語の子音表を作ると次のような表になるはず。



Bilabial Alveolar Palatal Velar Uvular Glottal
plain pal. plain pal. plain pal. lab.
Nasal m n ɲ (ŋ) (ɴ)
Stop voiceless p t k (kʷ)
voiced b d g (gʷ)
Affricate voiceless t͡s t͡ɕ
voiced d͡z d͡ʑ
Fricative voiceless ɸ s ɕ ç h
voiced z ʑ
Liquid r
Semivowel j w
Special moras /N/, /Q/


タ行の周辺が綺麗な対応関係にないのでごちゃごちゃしてはいますが、書かれるべきはこういう表なのではないでしょうか。

あとハ行は厳密に音声基準で表に振り分けています。ハ行系列は

  • は、-、-、へ、ほ
  • ひゃ、ひ、ひゅ、ひぇ、ひょ
  • ふぁ、ふぃ、ふ、ふぇ、ふぉ

の3種類なので音韻論的な整合性を考えるとplain、pal.、lab.でもいいのですが、めんどくさいので上のような形でまとめました。


こうやって考えると、日本語の子音って30個超ありますよね。

f:id:frecafloros:20210809171637p:plain

チェコ語の1格所有代名詞と4格人称代名詞

ロシア語を学習していたときにも思ったのですが、所有代名詞と人称代名詞の語形が結構似ているので混乱するんですよね。どっちが所有代名詞でどっちが人称代名詞かわからなくなるので、一旦表にしてみました。とりあえず単数だけ。

比較でロシア語(ラテン文字転写)も載せておきます。

1格所有代名詞

チェコ語 ロシア語
m n,f m n f
1sg můj moje moj mojó mojá
2sg tvůj tvoje tvoj tvojó tvojá
3sg 彼の jeho jevó
彼女の jeji jejó
1pl náš naše naš náše náša
2pl váš vaše vaš váše váša
3pl jejich ix


4格人称代名詞

チェコ語 ロシア語
1sg mne; mě menjá
2sg tebe; tě tebjá
3sg m活 jeho (něho), jej (něj); ho jevó (nevó)
m不活 jej (něj); ho
n je (ně), jej (něj); ho
f ji (ni) jejó (nejó)
1pl nás nas
2pl vás vas
3pl je (ně) ix (nix)


括弧は前置詞に続く場合の語形


1pl、2plはšが1格所有代名詞、sが4格人称代名詞。

1pl、2plはšが1格所有代名詞、sが4格人称代名詞。

1pl、2plはšが1格所有代名詞sが4格人称代名詞


流石に覚えただろこれで。

【gi-gtk】Haskellでdesktop GUIを実装するための最初の一歩

qiita.com

HaskellGUIライブラリ

wxHaskellとかいろいろありますがgtkでなんとかする。
gtkもいろいろな種類があり、どれを使えばいいのかわからんし今もわかってませんが、とりあえずこの通りにやれば動く。

前提

前準備

$ stack new <プロジェクト名>

で新規プロジェクトを立ち上げると勝手にいろんなファイルを作ってくれます。

package.yamlに以下のようにgi-gtkを追加します。

dependencies:
- base >= 4.7 && < 5
- haskell-gi-base
- gi-gtk

gi-gtkはstack buid時にコンパイルされますが、依存関係の問題があるらしいので以下のコマンドを実行する。

$ brew install gobject-introspection gtk+ gtk+3

mac 以外でやる場合は公式を参照。

Hello World

gtkの"Hello World"がこちら。

{-# LANGUAGE OverloadedStrings, OverloadedLabels #-}
module Main where

import qualified GI.Gtk as Gtk
import Data.GI.Base

main :: IO ()
main = do
    Gtk.init Nothing
    win <- new Gtk.Window [ #title := "やあ" ]
    on win #destroy Gtk.mainQuit
    button <- new Gtk.Button [ #label := "押してくれ" ]
    on button #clicked (set button [ #sensitive := False, #label := "クリックしてくれてありがとう!" ] )
    #add win button
    #showAll win
    Gtk.main

これをapp/Main.hsに書いて(src/Lib.hsは消してもOK)

$ stack build

して

$ stack exec <プロジェクト名>-exe

するとウィンドウが表示されるはず。

f:id:frecafloros:20210703220115p:plain

f:id:frecafloros:20210703220128p:plain

自由加群【環上の加群 5】

環の時にもPIDだのUFDのEDだのクラスがたくさんありました。
加群にもその特性によって様々なクラスがあります。

f:id:frecafloros:20210615221350p:plain

今回は最も基本的なクラスである自由加群について。
自由加群とは、基底を持つ加群のことである。

基底

ベクトル空間(線型空間)には必ず基底が存在しました(選択公理→Tukeyの補題から言える)。つまり、空間の全てのベクトルを表すことのできる一次独立(線型独立)なベクトルの集合があった。

しかし加群の場合、詳細は後述しますが必ずしも基底を持つとは限りません。基底を持たない加群と区別するために、基底を持つ加群=ベクトル空間に限りなく近い加群は特別に自由加群と名付けられています。何がどう自由なのかよくわかりませんが、多分圏論をやればわかると思う。

基底を定義します。


def 5.1R-加群M の部分集合 S=\{x_1,\cdots,x_n\}\subset MM の基底であるとは、
 (1) SM を生成する
 (2) S=\{x_1,\cdots,x_n\} が一次独立である
を満たすことである。

つまり、SM の一次独立な生成系なら、SM の基底。(1)は S の元と R の元を使って M の全ての元を表すことができるということ、(2)は M の元を S の元と R の元で一意に表せることを意味します。

もう少しちゃんと説明をすると、

(1) M の任意の元 x\in M が、S の元 x_j に係数である R の元 a_j をかけたものの有限和(x=a_1x_1+a_2x_2+\cdots +a_jx_j)で表すことができる
(2) a_1x_1+\cdots +a_jx_j=0\in M であれば a_1=\cdots=a_j=0\in R

であるとき、SMの基底です。

自由加群


def 5.2R-加群M が基底を持つとき(つまりMが一次独立な生成系 S を持つとき)、M自由 R-加群という。

基底を持つ加群が自由加群
つまるところ、R^I=\oplus_{i\in I}R と同型な加群が自由加群
ベクトル空間は必ず基底を持ちましたが、加群は基底を持つとは限りません。なので、基底を持つか持たないかを区別します。

自由加群の例

\mathbb{Z}[X] は自由 \mathbb{Z}-加群

\mathbb{Z}[X]X^n, n\in\mathbb{N}不定元(変数)とした整数係数の多項式環のことです。
例えば  1, X, X^2, 2X+1\in\mathbb{Z}[X]

定義の R=\mathbb{Z}M=\mathbb{Z}[X] とすると、
\mathbb{Z}[X] の部分集合 S=\{1,X,X^2,\cdots,X^n,\cdots\}\mathbb{Z}[X] を生成します。
また、S\mathbb{Z} 上一次独立です。
したがって、def 5.1 より SM=\mathbb{Z}[X]\mathbb{Z} 基底なので def 5.2 より基底 S を持つ \mathbb{Z}[X] は自由 \mathbb{Z}-加群となります。


ちなみに、 R の元を各基底に掛けていると考えると、無限次元整数ベクトル空間と同一視できます。


なお便宜上、零加群\{0\}空集合を基底とした自由加群と見なされます。

自由加群でない例

基底を持たない加群とはなんぞや、というお話。


\mathbb{Q}\mathbb{Z} 上の加群だが、自由 \mathbb{Z}-加群ではない

任意の2つの有理数 a,b\in\mathbb{Q}を既約分数表示して

a=\frac{p}{q}, b=\frac{r}{s}, (p,q,r,s\in\mathbb{Z})

とすると、(qr)a-(sp)b=0 であり qr\neq 0, sp\neq 0 なので一次従属。したがって基底があるとすれば1個しかないはずだが、全ての有理数を1つの有理数の整数倍で表すことはできない(=\mathbb{Q} 全体を生成できる有理数は存在しない)ので\mathbb{Q}は基底を持たない。


\mathbb{Z}/n\mathbb{Z} は自由 \mathbb{Z}-加群ではない

もう少し簡単な例。例えば \mathbb{Z}/6\mathbb{Z} を考えます。
6 で割った余りが  n になる整数全体の集合を \bar{n} とすると、集合として

\mathbb{Z}/6\mathbb{Z}=\{\bar{0},\bar{1},\bar{2},\bar{3},\bar{4},\bar{5}\}
と表記できます。ここで \bar{1}0,1,2,3,4,5 を掛けると \mathbb{Z}/6\mathbb{Z} の元の全てを表すことができるので、\mathbb{Z}/6\mathbb{Z}\bar{1} で生成されると言えそうです。つまり、\langle\bar{1}\rangle=\mathbb{Z}/6\mathbb{Z} です。

f:id:frecafloros:20210616232300p:plain

というわけで \bar{1} は基底の候補です。しかし一方で、

6\cdot\bar{1}=\overline{6\cdot 1}=\bar{6}=\bar{0}
となるので、生成系 \{\bar{1}\} は一次独立ではありません。

したがって生成系 \{\bar{1}\} が基底ではないので、\mathbb{Z}/6\mathbb{Z} は自由 \mathbb{Z}-加群ではありません。

階数

可換環で自由で基底があって基底として有限個で取れるとき、ベクトル空間の次元の概念が  R-加群で一般化される。
どういうことかというと、基底があるので自由加群の階数(rank)が定義できます。


def 5.3可換環 R の有限生成自由 R-加群 M の基底の個数を M の階数(rank)といい、{\rm rank}_R M と表す。

R が「可換」であることがミソ。R が非可換環だとすると、R\simeq R^2 がとれてしまったりするのでrankが定義できなくなる。

ガウス整数環 \mathbb{Z}[i] について。
\mathbb{Z}[i]\{1,i\} を基底に持つ自由 \mathbb{Z}-加群であり、基底が2つなので \mathrm{rank}_\mathbb{Z}\mathbb{Z}[i]=2 といえます。

また、\{1,\sqrt{2},i,i\sqrt{2}\}\mathbb{Q} 上一次独立なので、

M=\mathbb{Z}\oplus\mathbb{Z}\sqrt{2}\oplus\mathbb{Z}i\oplus\mathbb{Z}i\sqrt{2}

という直和を考えると、M\{1,\sqrt{2},i,i\sqrt{2}\} を基底に持つ自由 \mathbb{Z}-加群と言えます。
したがって \mathrm{rank}_\mathbb{Z}M=4

また一方で、\{1,\sqrt{2}\} を基底に持つ自由 \mathbb{Z}[i]-加群ともみなせるので、\mathrm{rank}_\mathbb{Z}\mathbb{Z}[i]=2


↑初:加群の定義【環上の加群 1】 - リムナンテスは愉快な気分
←前:直積と直和【環上の加群 4】 - リムナンテスは愉快な気分
→次:表現行列

【p5.js】絶対和音感アプリで和音を可視化する

f:id:frecafloros:20210613231655p:plain

こういうのを作ってます。一応今回で完結。


前回記事を見る場合はこちらから:
limnanthaceae.hatenablog.com



シリーズの最初から見る場合はこちらから:
limnanthaceae.hatenablog.com


せっかくp5.jsを使っているので、もう少し動的なあれこれを実装します。

その前にレイアウト変更

スマホで閲覧しても見やすいような配置に変更します。
constellation→chord→config→buttonの順番でしたが、configを前に持ってきてconfig→constellation→chord→buttonの順に。各要素が縦に並ぶようにして、constellation→chord→buttonが一画面に収まるようにします(configからスクロールしないと操作できないというのは些か面倒ではあるので折りたたみ式とかにするかも)。PCではconfigが左半分、それ以外が右半分という表示になります。

wrapを使うと、画面の横幅が小さくなった時に自動的に縦配列になります。

.container{
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

ついでなので、「全選択」とblack adder chord(イキスギコード)*1も追加。


See the Pen
chordtrainer-10
by frecafloros (@frecafloros)
on CodePen.

ボタン改良

「解答」ボタンと「次の問題」ボタンを統合します。こいつらは元々交互に押されるものであって2つもいらないので。

本当はjQueryを使うのが簡単に実装できる方法だとは思いますが、今回はcssjavascriptで制御します。

css側からテキストを制御するために、:before:after*2といった「擬似要素」と呼ばれるものを活用します。普通にcsscontentで指定するだけでは文字列の書き換えをすることが(多分)できないのですが、擬似要素で要素もどきを作ることで文字列の書き換えができるようになります。

:before:afterは要素の前後に内容を追加することができるもので、class名:beforeで要素の直前、class名:afterで要素の直後に内容を追加します。

「次の問題」のclassをqa-next、「解答」のclassをqa-answerとして、次のように指定します。

<div class="obj-qa">
	<button class="qa-next" id="btn-qa"> </button>
</div>
#btn-qa{
	width: 80px;
}

/* 次の問題ボタン */
.qa-next{
	font-size: 0px;
}

.qa-next:after{
	font-size: 14px;
	content: '次の問題';
}

/* 解答ボタン */
.qa-answer{
	font-size: 0px;
}

.qa-answer:after{
	font-size: 14px;
	content: '解答';
}

続いて、「次の問題」→「解答」→「次の問題」→…の(テキストと押したときの効果の)切り替えは要素のclassをjsで書き換えることにより実装します。ボタンを押すたびに関数の実行、qa-nextclassとqa-answerclassの入れ替えを実施します。

// qaボタン制御
let buttonQA = ()=>{
	const buttonQA = document.getElementById("btn-qa");
	let buttonQAClass = document.getElementsByClassName("qa-next");
	if(buttonQAClass.length == 0){
		// 現在のclassがqa-answer
		buttonAnswer(); // 解答の表示
		buttonQA.classList.remove("qa-answer");
		buttonQA.classList.add("qa-next");
	}else{
		// 現在のclassがqa-next
		buttonNext(); // 出題
		buttonQA.classList.remove("qa-next");
		buttonQA.classList.add("qa-answer");
	}
}

getElementsByClassNameは指定したclassの要素を抽出してくる関数で、要素をリスト形式で管理します。今回はフラグの代わりとして使っていて、リストの長さが0(=qa-nextのクラスとなっている要素が無い=今のクラスがqa-answer)かそうでないか(=今のクラスがqa-next)で処理を分岐させています。


ボタンのデザインも少し変更したものがこちら。


See the Pen
chordtrainer-11
by frecafloros (@frecafloros)
on CodePen.

サウンドビジュアライズ

本題。解答表示後、和音の可視化をします。

音の振幅値をとるためにp5.soundを導入します。これはp5.jsの拡張ライブラリです。(本当はTone.jsでやりたかったけど振幅値をリアルタイムで取れなかったのでp5側でやります。)
振幅値とるの無理だったので決めうちで実装。後述。

解答表示する前に可視化されると盛大なネタバレになってしまうので、表示したい時に表示できるようにしたい。

loop()noLoop()を使います。

まずsetup()の時点ではリアルタイムで描画しないのでnoLoop()で止めておきます。

draw()が実質メインループになりますので、ここで描画処理させます。今回はメインループの外に関数drawConstellation();soundVisualizer()を作りました。一定時間が経過するとnoLoop()を実行してループが停止します。あんまりメインループにループ停止書かないほうがいい気はする。

// chord constellationの描画
function setup(){
	createCanvas(canvasWidth, canvasHeight).parent('chord-constellation');

	textAlign(CENTER,CENTER);
	textSize(16);
	textFont('Optima');

	translate(canvasWidth/2, canvasHeight/2);
	const r = 80;
	for(let i=0;i<12;i++){
		text(pitch2keyname[i], r*sin(i*TAU/12), -1*r*cos(i*TAU/12));
	}

	// 描画停止
	noLoop();
}

// サウンドビジュアル描画(メインループ?)
function draw(){
	// user gesture
	onclickSound.onclick = buttonSound;
	onclickQA.onclick = buttonQA;

	if(t<60){
		// 描画
		drawConstellation();
		soundVisualizer();

		// buttonQAを非活性化
		buttonQAId.setAttribute("disabled", true);
	}else{
		// 描画停止
		noLoop();
		
		// buttonQAを活性化
		buttonQAId.removeAttribute("disabled");
	}
}


constellation の描画中に次の問題に行かれると次の問題の constellation を描画されてしまうので、constellation 描画中は「次の問題」ボタンを非活性にしています。非活性化はsetAttribute("disabled", true)、活性化はremoveAttribute("disabled")。"disabled"という属性をつけたり剥いだりして実装しています。


soundVisualizer()では、和音構成音に対応した円弧を描画します。

function soundVisualizer(){
	t = t + 1;

	// 円弧の size 設定
	if(t<4){
		// Attack
		size = rVisual/4*t;
	}else if(t<10){
		// Decay
		size = rVisual - rVisual/4*(t-4)/6;
	}else{
		// Release
		size = rVisual*3/4 - rVisual*3/4*(t-10)/50;
	}

	//円弧を描く
	colorMode(HSB);
	for(let i=0;i<chord.length;i++){
		tmpTone = (rootPc + chordTypes[chordNum]['chordKeys'][i])%12;
		fill(30*tmpTone,60,85,0.4);
		stroke(30*tmpTone,60,85);
		tmpRad = tmpTone*TAU/12 - HALF_PI;
		arc(canvasWidth/2,canvasHeight/2,size,size,tmpRad-0.2,tmpRad+0.2,PIE);
	}
}


上半分で、円弧のサイズを数式的に決め打ちしてます。最初に勢いよく大きくなって、少ししぼんだらゆっくり消えゆく感じ。

下半分で実際に円弧を描画しています。
音に沿って色相を動かしたかったので、HSBで色を指定しています。colorMode(HSB)をすると、fillstrokeの色指定がHSBになります。


下はデモです。



See the Pen
chordtrainer-12
by frecafloros (@frecafloros)
on CodePen.


一応やりたいことはこれでだいたい済んだので、あとはアプリで和音感を鍛える修行します。

おわり。

*1:解釈の仕方は色々ありますが、本アプリでの扱いは #IVaug/I [I blk]。

*2:css3ではコロン2つの::before、::after表記。特にcss3である必要がないと思うのでコロン1つでよいかと。