位相反転でボーカルを消す(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に変換しています)
ボーカル消去前
ボーカル消去後