声優統計コーパスを使ってみる

MikuHatsune2017-06-27

声優統計コーパスというものがある。
日本声優統計学会
プロの女性声優 3 名が 3 パターンの感情表現で読み上げた音声 2 時間分 を「声優統計コーパス」として無料公開します - 糞糞糞ネット弁慶
 
音素バランス文という、音声言語研究では非常になんかいい例文があって、それをプロの声優に読み上げてもらうことで、テキストマイニング、音声研究に役立てようというデータベース。
基本的に利用、解析、ダウンロードは無料で、「同人誌や論文などで利用される場合」となぜか同人誌のほうが論文に先んじて書かれる始末。
声優は女性声優3人が、100の音素バランス文を普通に、喜んで、怒って、の3つの感情パターンで読み上げているため、音声の感情の研究にも使える。
BGM のない、アフレコ音声なので、音声合成などにも使えそうである。
ここで、音素バランス文の構築自体は、別の話なので上のリンクからたぶん関連記事に辿れるとおもう。
 
例えば21番の音素バランス文は、
 
痛みは点滴より鎮痛薬を静脈投与することで鎮痛を行う
イタミワテンテキヨリチンツーヤクヲジョーミャクトーヨスルコトデチンツーヲオコナウ
i,t/a,m/i,w/a,t/e,N,t/e,k/i,y/o,r/i,ch/i,N,ts/u:,y/a,k/u,o,j/o:,my/a,k/u,t/o:,y/o,s/u,r/u,k/o,t/o,d/e,ch/i,N,ts/u:,o,o,k/o,n/a,u
 
という文を、3人の女性声優が3感情パターンの9通りで読んでくれるので、これだけで一生遊んで暮らせるくらいのレベルである。
 
ひとまず、これらをダウンロードしてcorpus というディレクトリにいれたとして、3人の声優の声と、感情と、それらの9パターンの組み合わせがなんとなくもやっと分離できるかやってみる。やっていることはここの焼き直し。
 
21番目の音素バランス文の、藤東知夏の怒りをスペクトログラムにした。実はコレ、土谷麻貴の怒りと一緒のように聴こえるのだが??

library(seewave)
library(tuneR)
library(phonTools)
library(stringr)

# ID, 声優名、感情を取り出しておく
l <- do.call(cbind, mapply(strsplit, list.files(), "_"))
f <- list.files(recursive=TRUE, full.names=TRUE)
l <- list(cv=unique(l[1,]), em=unique(l[2,]), id=unique(str_extract(f, "\\d+")))

wav <- mapply(readWave, f)
idx <- which(grepl("021", f))
w <- wav[[ idx[1] ]]
spectro(w, flim=c(0, 5)) # スペクトログラム

 
収録時間をプロットしてみた。土谷麻貴の収録時間はほかの二人に比べて短い気がする。といっても、後ろの空白時間がけっこうあって、これは空白時間を削除するプログラムの仕様なのかもしれない。

sec <- mapply(function(z) length(z@left) / z@samp.rate, wav)
s0 <- mapply(function(z) sec[grep(paste(l[[1]][z], l[[2]][z], sep="_"), f)], seq(l), SIMPLIFY=FALSE)
par(mar=c(5, 5, 2, 2), las=1, cex.lab=1.5, cex.axis=1.5)
matplot(do.call(cbind, s0), pch=16, xlab="Voice ID", ylab="Recoding second")
legend("topleft", legend=l[[1]], pch=16, col=seq(l[[1]]))

 
さてここで、100文3声優3感情の900音声について一括してなんかやってみる。音声データから各音声の特徴を表しているであろうフォルマントをごっそり上から数個取ってきて、適当に3次元に次元を落としていい感じに分かれるのかどうかやってみる。
ここでフォルマントとはという話は一切しない。
 
適当にbandwidth をいじるとすべての音声について少なくとも6つフォルマントがとれたので、6次元データをtsne により3次元にして3D プロットした。なんかいい感じにクラスターはできそうである。

 
声優ごとにわける。藤東知夏土谷麻貴上村彩子である。たいしていい感じにわかれていないようである。

 
感情ごとにわける。がangry、がhappy、がnormalである。たいしていい感じにわかれていないようである。

 
声優と感情ごとにわける。藤東知夏angry、マゼンダ藤東知夏happy、ピンク藤東知夏normal、
土谷麻貴angry、オレンジ土谷麻貴happy、マルーン土谷麻貴normal、
暗緑上村彩子angry、上村彩子happy、黄緑上村彩子normal である。たいしていい感じにわかれていないようである。

 
今回は旬が命でたいした解析はできなかったが、音声、感情、テキスト、となんでも使えそうなので使っていこう。

library(foreach)
library(doSNOW)
library(tsne)

# 少し時間がかかるので並列処理
cl <- makeCluster(8, type="SOCK")
registerDoSNOW(cl)

frm <- foreach(i = seq(wav), .combine=c, .packages="phonTools") %dopar% {
  findformants(wav[[i]]@left, fs=wav[[i]]@samp.rate, verify=FALSE, maxbw=1000)
}
stopCluster(cl)

res <- frm[seq(frm) %% 2 ==1]

# tsne で適当に3次元に落とす
h <- t(sapply(res, head, min(sapply(res, length))))
sne <- tsne(h, k=3)

cols <- c("red", "yellow", "darkseagreen")
# 声優ごと
idx <- apply(mapply(function(z) grepl(z, f), l[[1]]), 1, which)
plot3d(sne, col=cols[idx], type="s", radius=2)

# 感情ごと
idx <- apply(mapply(function(z) grepl(z, f), l[[2]]), 1, which)
plot3d(sne, col=cols[idx], type="s", radius=2)

# 声優と感情の9パターン
cols0 <- c("red", "magenta", "hotpink", "yellow", "orange", "maroon", "darkseagreen", "green", "lightgreen")
idx9 <- c(outer(l[[1]], l[[2]], paste, sep="_"))

idx <- apply(mapply(function(z) grepl(z, f), idx9), 1, which)
plot3d(sne, col=cols0[idx], type="s", radius=2)