位相反転でボーカルを消す(WAVEファイル)
WAVEファイルのいじり方が段々分かってきたので今回は歌入り楽曲データのボーカルを抜くプログラムを作成してみる。環境はLinux(CentOS7)、言語はC言語。
目次
はじめに
音楽プレーヤー等にイヤホンを半挿しにするとボーカルが消える(音が小さくなる)現象をご存じの方は多いと思う。楽曲用のソフトウェアをいじる方は位相反転というワードのほうがしっくりくるかもしれない。
この現象は左右の音声波のどちらか一方が位相反転(プラスとマイナスが入れ替わる)して合成されることで発生している。大抵ボーカルの音声は真ん中(左右とも同程度の成分量)なので波の打消しによって波が小さく(音が小さく)なるという理屈。説明は下記を参考とさせていただく。
対象の楽曲
著作権とか色々あるので下記の楽曲を使わせていただくことにした。
ボーカル素材|著作権フリーの無料音楽素材ダウンロードサイト「ミュージックノート」

ソースコード
ボーカル消去は下記の処理で実現する。合算値のオーバーフローを考慮してないので所々で変な音になるかもしれない。
for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
wave_stereo_t* val =
(wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];
/* 片方を位相反転して合算する */
val->right = val->right + val->left * (-1);
/* 左右とも同値にする */
val->left = val->right;
}
ソースコード全体(vo_cancel.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
/* RIFFチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('RIFF'固定) */
uint32_t chunk_size; /* チャンクサイズ */
char format_type[4]; /* フォーマットタイプ('WAVE'固定) */
} RIFF_chunk_t;
/* fmt チャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('fmt '固定) */
uint32_t chunk_size; /* チャンクサイズ */
uint16_t format_type; /* フォーマットタイプ */
uint16_t channel; /* チャンネル数 */
uint32_t sample_per_sec; /* サンプリング周波数 */
uint32_t byte_per_sec; /* 1秒あたりバイト数 */
uint16_t block_size; /* ブロックサイズ */
uint16_t bit_per_sample; /* 量子化精度 */
} fmt_chunk_t;
/* dataチャンク */
typedef struct {
char chunk_id[4]; /* チャンク識別子('data')固定 */
uint32_t chunk_size; /* チャンクサイズ(データ長) */
uint8_t dat[0]; /* データ(可変長) */
} data_chunk_t;
typedef struct {
RIFF_chunk_t riff;
fmt_chunk_t fmt;
data_chunk_t data;
} wave_format_t;
typedef struct {
int16_t left;
int16_t right;
} wave_stereo_t;
static int revertAndSynthsisWave(char* input_file, char* output_file);
int main(int argc, char*argv[])
{
if (argc != 3) {
fprintf(stderr, "usage:%s <input file> <output file>\n", argv[0]);
exit(EXIT_FAILURE);
}
int rc = revertAndSynthsisWave(argv[1], argv[2]);
if (0 != rc) {
fprintf(stderr, "revertAndSynthsisWave() failed(%d)\n", rc);
exit(EXIT_FAILURE);
}
return 0;
}
static int revertAndSynthsisWave(char* input_file, char* output_file)
{
int rc = -1;
FILE* ifp = NULL;
FILE* ofp = NULL;
char buff[sizeof(wave_format_t)] = {0};
wave_format_t* wave = NULL;
int t = 0;
if (access(input_file, R_OK) != 0) {
perror("access");
goto error_end;
}
ifp = fopen(input_file, "rb");
if (NULL == ifp ) {
perror("fopen");
goto error_end;
}
if (fread(buff, 1, sizeof(buff), ifp) <= 0) {
perror("fread");
goto error_close_end;
}
wave = (wave_format_t* )malloc(((wave_format_t* )buff)->riff.chunk_size);
if (NULL == wave) {
goto error_close_end;
}
*wave = *((wave_format_t* )buff);
if (fread(wave->data.dat, 1, wave->data.chunk_size, ifp) <= 0) {
perror("fread");
goto error_free_end;
}
for(t = 0; t < wave->data.chunk_size/sizeof(wave_stereo_t); t++) {
wave_stereo_t* val =
(wave_stereo_t* )&wave->data.dat[t * sizeof(*val)];
/* 片方を位相反転して合算する */
val->right = val->right + val->left * (-1);
/* 左右とも同値にする */
val->left = val->right;
}
ofp = fopen(output_file, "wb");
if (NULL == ofp) {
perror("fopen");
goto error_free_end;
}
fwrite((void*)wave, 1, sizeof(*wave)+wave->data.chunk_size, ofp);
rc = 0;
fclose(ofp);
error_free_end:
free(wave);
error_close_end:
fclose(ifp);
error_end:
return rc;
}
実行結果
加工前のnoise.wavからボーカルを消去したnoise_vocancel.wavを作成する。
[user@localhost vo_cancel]$ gcc -o vo_cancel vo_cancel.c
[user@localhost vo_cancel]$ ./vo_cancel noise.wav noise_vocancel.wav
[user@localhost vo_cancel]$ ls
noise_vocancel.wav noise.wav vo_cancel vo_cancel.c
再生してみると下記のようになる。(wavだと都合が悪いのでmp3に変換しています)
ボーカル消去前
ボーカル消去後
コメント