waveファイルに含まれている音の音程を変化させるには?


takasi  2006-11-18 06:39:51  No: 63627

単音のみのメロディが入っているwaveファイルに伴奏をつけたいのですが具体的にどのようにすればよいのかわかりません。
自分の考えた理想としての流れは

wav読み込み→フーリエ→スペクトルの振幅を取る→その中から一番低い周波数を基本周波数とする→その周波数+45、+90のメロディを作成し、3和音にして流す。

という流れです。C,C++の経験はないため稚拙な説明になってしまいましたがご教授いただければありがたいです。

質問がありましたら何でも答えますのでよろしくお願いしたします。

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <iostream>

#define INFO(msg) std::cout << __LINE__ << ":" << msg << std::endl
#define  PI  3.14159265359
#pragma warning(disable : 4996)

typedef struct{
  unsigned int  mainChunk;            //文字列"RIFF"
  unsigned int  mainLength;            //総データ長(byte)
  unsigned int  chunk_type;            //チャンクタイプ
  unsigned int  subchunk;            //サブチャンク内容
  unsigned int  subchunkLength;          //サブチャンク長(byte)
  unsigned short  format;              //通常はPCM
  unsigned short  mode;              //チャネル数
  unsigned int  frequency;            //サンプリング周波数(Hz)
  unsigned int  bitPerSec;            //ビットレート
  unsigned short  bytePerSample;          //byte/サンプル
  unsigned short  bitPerSample;          //bit/サンプル(量子化ビット数)
  unsigned int  dataChunk;            //文字列"data"
  unsigned int  dataLength;            //データ長
}WAVE_HEADER;

typedef struct{
  short mono;
}WAVE_LR;

int main(void){
  FILE *file1;
  FILE *file2;
  file1=fopen("WAVE.wav","rb");  //rb=read binary
  file2=fopen("EVAW.wav","wb");  //wb=write binary
FILE  *fd;

fd = fopen("data.txt", "w");
 
  WAVE_LR data;
  WAVE_HEADER header;
  int i;
  int j=0;
  int l=0;
    int k=0;

  fread(&header, sizeof(WAVE_HEADER), 1, file1);  //&xxx=xxxを読み込む  sizeof=サイズを調べる(必要サイズを確保する)
  fwrite(&header, sizeof(WAVE_HEADER), 1, file2);
  //読み込みデータ格納先のポインタ,読み込みデータのバイト長さ,読み込みデータの数,FILEポインタ

  static const int FRAME_SIZE = 1024;
  double frame[FRAME_SIZE]; // 1024サンプルずつ読み込む
  double fft_frame_real[FRAME_SIZE];
  double fft_frame_imag[FRAME_SIZE];
  double fft_db[FRAME_SIZE];

   short *sample1=new short [header.dataLength/2];  //new short [header.dataLength/2]=2byteずつ読み込むためデータ長の半分で十分 
  short *sample2=new short [header.dataLength/2];  

  for(i=0; i<(signed)header.dataLength/2; i++)  //signed=符号付き(+/−)
  {
    fread(&data, sizeof(short),1, file1);

    sample1[i]=data.mono;

  }
double max[5];
double maxHz[5];

//1024サンプルごとにそれぞれの周波数の振幅がでてくる

while( fread( frame, sizeof(short), 1024, file1 )){
  // フーリエ変換
  for( int i = 0 ; i < FRAME_SIZE; i++ )
 {
      fft_frame_real[i] = 0;
    fft_frame_imag[i] = 0;
  for( int j = 1; j < FRAME_SIZE; j++)
   {
     fft_frame_real[i] += frame[j] * cos( (2*PI * i * j) / FRAME_SIZE );
     fft_frame_imag[i] -= frame[j] * sin( (2*PI * i * j) / FRAME_SIZE );
   }
 }

//y=Asin(2πt/T)・・・・正弦波

//振幅(振幅が大きいところ上位5個を拾って見比べてどれが基本周波数か判断する)
  double db = 0;

  for( int i = 0; i < FRAME_SIZE; i++ )
  {
    fft_db[i] = 0;
    for( int j = 0; j < FRAME_SIZE; j++ )
      fft_db[i] += fft_frame_real[j]*fft_frame_real[j] + fft_frame_imag[j]*fft_frame_imag[j];
    fft_db[i] = sqrt( fft_db[i] );
  }
  for(int k = 0; k < 5; k++){

fprintf( fd,"%d\n" ,fft_db );
i += 1;
}

/*新たな配列を用意してそれに基本周波数+45したのを逆フーリエ変換
例えば一番大きいのがfft_db[64]だったら、周波数は,16kHzサンプリングの場合
16000 / 1024 * 64 = 2000で2千Hzが基本周波数の倍音となる。つまり基本周波数は2000の約数の中に含まれている*/

//基本周波数を見つける

maxHz[k] = 16000 / 1024 * i;

for(int i=0; i<FRAME_SIZE; i++)
{
  if( max[i] < fft_db[i] )
{
  max[i] = fft_db[i];
  maxHz[k] = 16000 / 1024 * i;
}
}

//基本周波数+α

/*
double l;
l = maxHz[0];

{
sin(l*2*PI) + sin{(l+45)*2*PI} + sin{(l+90)*2*PI};
}
*/

 // 逆フーリエ変換
  for( int i = 0 ; i < FRAME_SIZE; i++ )
  {
       fft_frame_real[i] = 0;
       fft_frame_imag[i] = 0;
    for( int j = 1; j < FRAME_SIZE; j++)
   {
       fft_frame_real[i] += fft_db[j] * cos( (2*PI * i * j) / FRAME_SIZE );
       fft_frame_imag[i] += fft_db[j] * sin( (2*PI * i * j) / FRAME_SIZE );
    }
  }
}

//とった基本周波数の値をメモ帳に記録

  for(i=0; i<(signed)header.dataLength/2; i++){
    fwrite(&sample1[i], sizeof(short), 1, file2);
  }

  fclose(file1);
  fclose(file2);
  delete []sample1;
  delete []sample2;
  

}


デットロック  2006-11-18 11:17:33  No: 63628

こりゃまたすごい質問だね。質問者のレベルが全然わからんw

C,C++の経験がないということだけど、他の言語の経験はあるの?あるならその言語でWAVEファイルの扱いとかフーリエ変換とかしたことある?
あと音楽の経験はあるわけ?単音のみのメロディが入っているmidiファイルに伴奏をつけて出力することは出来るの?

最後のmidiファイルの奴が出来るだけでも、有料アプリとして売って商売が出来そうだけど…

上記の返答が全部NOなら君には無理でしょう。Cの経験は無いけどアセンブラバリバリの天才プログラマーとかなら話は別だけどw


デットロック  2006-11-18 11:21:43  No: 63629

×全部NOなら
○1つでもNOなら


takasi  2006-11-18 20:12:09  No: 63630

そうですか・・・あるのは音楽の作曲理論だけです。Cは基本的な部分を勉強中です。せめてwaveファイル内の一番低い音程がわかるように作れればいいのですが・・・

fprintf( fd,"%d\n" ,fft_db );のfft_dbの部分をmaxHzにして動かしたら基本周波数がメモ帳にでてくるとおもってたですけど振れ幅の部分がおかしいのか基本周波数の部分がおかしいのか変な値がでてきてしまいました。


takasi  2006-11-19 04:07:42  No: 63631

その後なんとか基本周波数を取り出せましたが、周波数を変える所がなかなかうまくいきません。


デットロック  2006-11-19 08:32:17  No: 63632

>その後なんとか基本周波数を取り出せましたが
本当に?たぶん勘違いだと思うけどw
適当に取り出した値がたまたま周波数っぽい値だっただけじゃないの?

これが最後の返答だけど、wavファイルの構造はこういう風になってる。ちゃんと仕様書を読んで理解して、その通りに取り出そう。全てはそれから。
http://www.kk.iij4u.or.jp/~kondo/wave/


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加