コアダンプの数だけ強くなれるよ

見習いエンジニアの備忘log

位相反転でボーカルを消す(WAVEファイル)

WAVEファイルのいじり方が段々分かってきたので今回は歌入り楽曲データのボーカルを抜くプログラムを作成してみる。環境はLinux(CentOS7)、言語はC言語。

はじめに

音楽プレーヤー等にイヤホンを半挿しにするとボーカルが消える(音が小さくなる)現象をご存じの方は多いと思う。楽曲用のソフトウェアをいじる方は位相反転というワードのほうがしっくりくるかもしれない。

この現象は左右の音声波のどちらか一方が位相反転(プラスとマイナスが入れ替わる)して合成されることで発生している。大抵ボーカルの音声は真ん中(左右とも同程度の成分量)なので波の打消しによって波が小さく(音が小さく)なるという理屈。説明は下記を参考とさせていただく。

soundside.blog3.fc2.com


okwave.jp



対象の楽曲


著作権とか色々あるので下記の楽曲を使わせていただくことにした。

ボーカル素材|著作権フリーの無料音楽素材ダウンロードサイト「ミュージックノート」

f:id:segmentation-fault:20170808230324p:plain



ソースコード


ボーカル消去は下記の処理で実現する。合算値のオーバーフローを考慮してないので所々で変な音になるかもしれない。

    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に変換しています)



ボーカル消去前


ボーカル消去後