忘備録-備忘録

技術的な備忘録

WaveファイルをC言語のデータに変換

2017-04-16 18:06:12 | raspberry ...
Waveファイルのデータ部分を取り出しC言語の配列に変換するプログラムです。Waveファイルの入出力部分にはWaveファイルを入出力してみるのプログラムを使わさせてもらいました。
データの変換にはsoxを使うと便利です。
raspberry piでのsoxのインストール
$ sudo apt-get install sox
$ sudo apt-get install libsox-fmt-all


変換プログラム
/*
  read Wav file
  see http://hooktail.org/computer/index.php?Wave%A5%D5%A5%A1%A5%A4%A5%EB%A4%F2%C6%FE%BD%D0%CE%CF%A4%B7%A4%C6%A4%DF%A4%EB
waveファイルはデータをリトルエンディアンで保存している
このプログラムはCPUがリトルエンディアンでないと動作しない
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define HEADERSIZE 44

typedef struct {
  int16_t l;
  int16_t r;
} Soundsample16;

typedef struct {
  uint8_t l;
  uint8_t r;
} Soundsample8;

typedef struct {
  uint16_t channelnum;      //モノラルなら1、ステレオなら2
  uint32_t samplingrate;     //Hz単位
  uint16_t bit_per_sample;    //1サンプルあたりのbit数
  uint32_t datanum;        //モノラルならサンプル数を、ステレオなら左右1サンプルずつの組の数

  uint8_t *monaural8;     //8ビットモノラルのデータならこれを使う
  int16_t *monaural16;     //16ビットモノラルならばこれを使う
  Soundsample8 *stereo8;        //8ビットステレオならばこれを使う
  Soundsample16 *stereo16;      //16ビットステレオならばこれを使う
} Sound;

//取得に成功すればポインタを失敗すればNULLを返す
Sound *Read_Wave(char *filename);

//書き込みに成功すれば0を失敗すれば1を返す
int Write_Wave(char *filename, Sound *snd);

//Soundを作成し、引数の情報に合わせて領域の確保をする。使われる形式以外の領域のポインタはNULL
//成功すればポインタを、失敗すればNULLを返す
Sound *Create_Sound(uint16_t channelnum, uint32_t samplingrate, uint16_t bit_per_sample, uint32_t datasize);

//Soundを開放する
void Free_Sound(Sound *snd);

Sound *Read_Wave(char *filename)
{
  uint32_t i;
  uint8_t header_buf[20];             //フォーマットチャンクのサイズまでのヘッダ情報を取り込む
  FILE *fp;
  Sound *snd;
  uint32_t datasize;                   //波形データのバイト数
  uint16_t fmtid;                     //fmtのIDを格納する
  uint16_t channelnum;                //チャンネル数
  uint32_t samplingrate;               //サンプリング周波数
  uint16_t bit_per_sample;            //量子化ビット数
  uint8_t *buf;                       //フォーマットチャンクIDから拡張部分までのデータを取り込む
  uint32_t fmtsize;

  if ((fp = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "Error: %s could not read.\n", filename);
    return NULL;
  }

  fread(header_buf, sizeof(uint8_t), 20, fp);   //フォーマットチャンクサイズまでのヘッダ部分を取り込む

  //ファイルがRIFF形式であるか
  if (strncmp(header_buf, "RIFF", 4)) {
    fprintf(stderr, "Error: %s is not RIFF.\n", filename);
    fclose(fp);
    return NULL;
  }

  //ファイルがWAVEファイルであるか
  if (strncmp(header_buf + 8, "WAVE", 4)) {
    fprintf(stderr, "Error: %s is not WAVE.\n", filename);
    fclose(fp);
    return NULL;
  }

  //fmt のチェック
  if (strncmp(header_buf + 12, "fmt ", 4)) {
    fprintf(stderr, "Error: %s fmt not found.\n", filename);
    fclose(fp);
    return NULL;
  }

  memcpy(&fmtsize, header_buf + 16, sizeof(fmtsize));

  if ((buf = (uint8_t *)malloc(sizeof(uint8_t) * fmtsize)) == NULL) {
    fprintf(stderr, "Allocation error size fmtsize %x\n",fmtsize);
    fclose(fp);
    return NULL;
  }

  fread(buf, sizeof(uint8_t), fmtsize, fp);     //フォーマットIDから拡張部分までのヘッダ部分を取り込む

  memcpy(&fmtid, buf, sizeof(fmtid));     //LinearPCMファイルならば1が入る

  if (fmtid != 1) {
    fprintf(stderr, "Error: %s is not LinearPCM.\n", filename);
    fclose(fp);
    return NULL;
  }

  memcpy(&channelnum, buf + 2, sizeof(channelnum)); //チャンネル数を取得
  memcpy(&samplingrate, buf + 4, sizeof(samplingrate)); //サンプリング周波数を取得
  memcpy(&bit_per_sample, buf + 14, sizeof(bit_per_sample)); //量子化ビット数を取得

  fread(buf, sizeof(uint8_t), 8, fp);     //factもしくはdataのIDとサイズを取得8バイト

  if (!strncmp(buf, "fact", 4)) {
    fread(buf, sizeof(uint8_t), 4, fp);
    fread(buf, sizeof(uint8_t), 8, fp);
  }

  if (strncmp(buf, "data", 4)) {
    fprintf(stderr, "Error: %s data part not found.\n", filename);
    fclose(fp);
    return NULL;
  }

  memcpy(&datasize, buf + 4, sizeof(datasize)); //波形データのサイズの取得

  if ((snd = Create_Sound(channelnum, samplingrate, bit_per_sample, datasize)) == NULL) {
    fclose(fp);
    return NULL;
  }

  if (channelnum == 1 && bit_per_sample == 8) {
    fread(snd->monaural8, sizeof(uint8_t), snd->datanum, fp);   //データ部分を全て取り込む
  } else if (channelnum == 1 && bit_per_sample == 16) {
    fread(snd->monaural16, sizeof(int16_t), snd->datanum, fp);
  } else if (channelnum == 2 && bit_per_sample == 8) {
    for (i = 0; i < snd->datanum; i++) {
      fread(&(snd->stereo8[i].l), sizeof(uint8_t), 1, fp);
      fread(&(snd->stereo8[i].r), sizeof(uint8_t), 1, fp);
    }
  } else if (channelnum == 2 && bit_per_sample == 16) {
    for (i = 0; i < snd->datanum; i++) {
      fread(&(snd->stereo16[i].l), sizeof(int16_t), 1, fp);
      fread(&(snd->stereo16[i].r), sizeof(int16_t), 1, fp);
    }
  } else {
    fprintf(stderr, "Header is destroyed.");
    fclose(fp);
    Free_Sound(snd);
  }

  return snd;
}

int Write_Wave(char *filename, Sound *snd)
{
  int i;
  FILE *fp;
  uint8_t header_buf[HEADERSIZE]; //ヘッダを格納する
  uint32_t fswrh;  //リフヘッダ以外のファイルサイズ
  uint32_t fmtchunksize; //fmtチャンクのサイズ
  uint32_t dataspeed;    //データ速度
  uint16_t blocksize;   //1ブロックあたりのバイト数
  uint32_t datasize;     //周波数データのバイト数
  uint16_t fmtid;       //フォーマットID

  if ((fp = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "Error: %s could not open.\n", filename);
    return 1;
  }

  fmtchunksize = 16;
  blocksize = snd->channelnum * (snd->bit_per_sample / 8);
  dataspeed = snd->samplingrate * blocksize;
  datasize = snd->datanum * blocksize;
  fswrh = datasize + HEADERSIZE - 8;
  fmtid = 1;

  header_buf[0] = 'R';
  header_buf[1] = 'I';
  header_buf[2] = 'F';
  header_buf[3] = 'F';
  memcpy(header_buf + 4, &fswrh, sizeof(fswrh));
  header_buf[8] = 'W';
  header_buf[9] = 'A';
  header_buf[10] = 'V';
  header_buf[11] = 'E';
  header_buf[12] = 'f';
  header_buf[13] = 'm';
  header_buf[14] = 't';
  header_buf[15] = ' ';
  memcpy(header_buf + 16, &fmtchunksize, sizeof(fmtchunksize));
  memcpy(header_buf + 20, &fmtid, sizeof(fmtid));
  memcpy(header_buf + 22, &(snd->channelnum), sizeof(snd->channelnum));
  memcpy(header_buf + 24, &(snd->samplingrate), sizeof(snd->samplingrate));
  memcpy(header_buf + 28, &dataspeed, sizeof(dataspeed));
  memcpy(header_buf + 32, &blocksize, sizeof(blocksize));
  memcpy(header_buf + 34, &(snd->bit_per_sample), sizeof(snd->bit_per_sample));
  header_buf[36] = 'd';
  header_buf[37] = 'a';
  header_buf[38] = 't';
  header_buf[39] = 'a';
  memcpy(header_buf + 40, &datasize, sizeof(datasize));

  fwrite(header_buf, sizeof(uint8_t), HEADERSIZE, fp);

  if (snd->channelnum == 1 && snd->bit_per_sample == 8) {
    fwrite(snd->monaural8, sizeof(uint8_t), snd->datanum, fp);    //データ部分を全て書き込む
  } else if (snd->channelnum == 1 && snd->bit_per_sample == 16) {
    fwrite(snd->monaural16, sizeof(int16_t), snd->datanum, fp);
  } else if (snd->channelnum == 2 && snd->bit_per_sample == 8) {
    for (i = 0; i < snd->datanum; i++) {
      fwrite(&(snd->stereo8[i].l), sizeof(uint8_t), 1, fp);
      fwrite(&(snd->stereo8[i].r), sizeof(uint8_t), 1, fp);
    }
  } else {
    for (i = 0; i < snd->datanum; i++) {
      fwrite(&(snd->stereo16[i].l), sizeof(int16_t), 1, fp);
      fwrite(&(snd->stereo16[i].r), sizeof(int16_t), 1, fp);
    }
  }

  fclose(fp);

  return 0;
}

Sound *Create_Sound(uint16_t channelnum, uint32_t samplingrate, uint16_t bit_per_sample, uint32_t datasize)
{
  Sound *snd;

  if ((snd = (Sound *)malloc(sizeof(Sound))) == NULL) {
    fprintf(stderr, "Allocation error size Sound %lx\n",sizeof(Sound));
    return NULL;
  }

  snd->channelnum = channelnum;
  snd->samplingrate = samplingrate;
  snd->bit_per_sample = bit_per_sample;
  snd->datanum = datasize / (channelnum * (bit_per_sample / 8));
  snd->monaural8 = NULL;
  snd->monaural16 = NULL;
  snd->stereo8 = NULL;
  snd->stereo16 = NULL;

  if (channelnum == 1 && bit_per_sample == 8) {
    if ((snd->monaural8 = (uint8_t *)malloc(datasize)) == NULL) {
      fprintf(stderr, "Allocation error size datasize %x\n",datasize);
      free(snd);
      return NULL;
    }
  } else if (channelnum == 1 && bit_per_sample == 16) {
    if ((snd->monaural16 = (int16_t *)malloc(sizeof(int16_t) * snd->datanum)) == NULL) {
      fprintf(stderr, "Allocation error size sizeof(int16_t) * snd->datanum %lx\n",(long)(sizeof(int16_t) * snd->datanum));
      free(snd);
      return NULL;
    }
  } else if (channelnum == 2 && bit_per_sample == 8) {
    if ((snd->stereo8 = (Soundsample8 *)malloc(sizeof(Soundsample8) * snd->datanum)) == NULL) {
      fprintf(stderr, "Allocation error size sizeof(Soundsample8) * snd->datanum %lx\n",(long)(sizeof(Soundsample8) * snd->datanum));
      free(snd);
      return NULL;
    }
  } else if (channelnum == 2 && bit_per_sample == 16) {
    if ((snd->stereo16 = (Soundsample16 *)malloc(sizeof(Soundsample16) * snd->datanum)) == NULL) {
      fprintf(stderr, "Allocation error size sizeof(Soundsample16) * snd->datanum %lx\n",(long)(sizeof(Soundsample16) * snd->datanum));
      free(snd);
      return NULL;
    }
  } else {
    fprintf(stderr, "Channelnum or Bit/Sample unknown\n");
    free(snd);
    return NULL;
  }

  return snd;
}

void Free_Sound(Sound *snd)
{
  if (snd->channelnum == 1 && snd->bit_per_sample == 8) {
    free(snd->monaural8);
  } else if (snd->channelnum == 1 && snd->bit_per_sample == 16) {
    free(snd->monaural16);
  } else if (snd->channelnum == 2 && snd->bit_per_sample == 8) {
    free(snd->stereo8);
  } else {
    free(snd->stereo16);
  }

  free(snd);
}

int main(int argc, char *argv[])
{
  FILE *fp;
  int i, x, max, min;

  if (argc != 3) {
    fprintf(stderr, "Usage: program <inputfile> <outputfile>\n");
    exit(1);
  }

  Sound *snd;

  if ((snd = Read_Wave(argv[1])) == NULL) {
    exit(1);
  }

  printf("Sampling rate %d\n", snd->samplingrate);
  printf("bit_per_sample %d\n", snd->bit_per_sample);
  printf("channel number %d\n", snd->channelnum);
  printf("sample number %d\n", snd->datanum);

  fp = fopen(argv[2], "w");
  if (fp == NULL) {
    perror("fopen");
    exit(1);
  }
  max = min = 0x7fff;
  fprintf(fp, "const uint16_t voice[] = {\n");
  if (snd->channelnum == 1) {
    for (i = 0; i < snd->datanum; i++) {
      x = snd->monaural16[i] + 0x7fff;
      fprintf(fp, "%d,\n", x);
      if (max < x) max = x;
      if (min > x) min = x;
    }
  } else {
    for (i = 0; i < snd->datanum; i++) {
      x = (snd->stereo16[i].l + snd->stereo16[i].r) / 2 + 0x7fff;
      fprintf(fp, "%d,\n", x);
      if (max < x) max = x;
      if (min > x) min = x;
    }
  }
  fprintf(fp, "};\n");
  Free_Sound(snd);
  printf("data max %d\ndata min %d\n", max, min);
  return 0;
}



RX210のPWMを使い音声再生

2017-04-16 13:49:18 | RX210
RX210のPORT3のBIT4にスピーカを接続しPWMを使用して、音声データを出力するプログラムです。割り込みを使用して1周期ごとにパルスの幅を変更します。

#include "iodefine.h"
#include <machine.h>
#include "voice.h"      //音声データの配列
#include "vect.h"

#define PCLK 25000      // 周辺機器のクロック設定 25MHz

/*
   クロック周波数を50MHzに変更
*/
void change_oscillation_PLL(void)
{
  unsigned int i;

  SYSTEM.PRCR.WORD = 0xA507;
  SYSTEM.VRCR = 0x00;
  SYSTEM.MOFCR.BYTE = (0x30);
  SYSTEM.MOSCWTCR.BYTE = (0x0D);
  SYSTEM.MOSCCR.BYTE = 0x00;
  while (0x00 != SYSTEM.MOSCCR.BYTE);
  for (i = 0; i < 100; i++) nop();
  SYSTEM.PLLCR.WORD = (0x0901);
  SYSTEM.PLLWTCR.BYTE = (0x09);
  SYSTEM.PLLCR2.BYTE = 0x00;
  for (i = 0; i < 100; i++) nop();
  SYSTEM.OPCCR.BYTE = (0x00);
  while (0 != SYSTEM.OPCCR.BIT.OPCMTSF);
  SYSTEM.SCKCR.LONG = 0x21821211;
  while (0x21821211 != SYSTEM.SCKCR.LONG);
  SYSTEM.BCKCR.BYTE = 0x01;
  while (0x01 != SYSTEM.BCKCR.BYTE);
  SYSTEM.SCKCR3.WORD = (0x0400);  /* PLL */
  while ((0x0400) != SYSTEM.SCKCR3.WORD);
  SYSTEM.PRCR.WORD = 0xA500;
}

/*
  タイマの設定 MTU0
*/
void initMTU0pwm(void)
{
  SYSTEM.PRCR.WORD = 0x0A502;
  MSTP(MTU0) = 0;                //MTU0 モジュールスタンバイ解除
  SYSTEM.PRCR.WORD = 0x0A500;
  /* 端子の設定 */
  PORT3.PODR.BIT.B4 = 0;    // P34 MTIOC0A 出力初期値
  PORT3.PDR.BIT.B4 = 1;   // P34 出力設定
  PORT3.PMR.BIT.B4 = 0;   // P34ポートとして使用
  MPC.PWPR.BIT.B0WI = 0;    // PFSWE書き込み可
  MPC.PWPR.BIT.PFSWE = 1;   // PFSレジスタへの書き込み可
  MPC.P34PFS.BIT.PSEL = 1;  // P34をMTIOC0Aとして使用
  MPC.PWPR.BIT.PFSWE = 0;   // PFSレジスタへの書き込み禁止
  PORT3.PMR.BIT.B4 = 1;   // P34周辺機器として使用
  /* MTUタイマの設定 */
  MTU.TSTR.BIT.CST0 = 0x00; //MTUカウント停止
  MTU0.TCR.BIT.TPSC = 0x00; // CLK PCLK/1 でカウント
  MTU0.TCR.BIT.CCLR = 0x02; // TCNT0はTGRBのコンペアマッチでクリア
  MTU0.TMDR.BIT.MD = 0x02;  // タイマーPWMモード1
  // 出力波形の設定
  MTU0.TIORH.BIT.IOA = 0x05;  // MTIOC0A 初期出力はHigh出力コンペアマッチでLow出力
  MTU0.TIORH.BIT.IOB = 0x06;  // MTIOC0B 初期出力はHigh出力コンペアマッチでHigh出力
  MTU0.TIORL.BIT.IOC = 0x05;  // MTIOC0C 初期出力はHigh出力コンペアマッチでLow出力
  MTU0.TIORL.BIT.IOD = 0x06;  // MTIOC0D 初期出力はHigh出力コンペアマッチでHigh出力
  MTU0.TGRA = 0;        // MTIOC0Aデューティー比
  MTU0.TGRB = 1563;     // MTU0 周波数   15994.9Hz サンプリング周波数設定
  //    MTU0.TGRB = 1136;     // MTU0 周波数   22007.0Hz
  MTU0.TGRC = 0;        // MTIOC0Cデューティー比
  MTU0.TGRD = 1563;     // MTU0 周波数 MTU3.TGRBと同じ値に
  //    MTU0.TGRD = 1136;     // MTU0 周波数   22007.0Hz
  MTU0.TCNT = 0;        // カウンタクリア
  IR(MTU0, TGIB0) = 0;    // 割り込みフラグクリア
  IEN(MTU0, TGIB0) = 1;   // Enable TGIB0
  IPR(MTU0, TGIB0) = 8;   // Set interrupt priority level
  MTU0.TIER.BIT.TGIEB = 1;  // TGIEB割り込み許可
  MTU.TSTR.BIT.CST0 = 0x01; // タイマ動作
}

//音声データ内のどの位置まで再生したか
unsigned long pos;

void main(void)
{
  change_oscillation_PLL();
  initMTU0pwm();
  pos = 0;
  setpsw_i();           // 割込み許可   割込み禁止はclrpsw_i()
  while (1) {
    /* ハードウェアで波形出力するのでプログラムでは何もしなくてもよい */
  }
}

// MTU0 TGIB0 割り込みルーチン
#include "iodefine.h"
void Excep_MTU0_TGIB0(void)
{
  MTU0.TGRA = voice[pos] >> 5;
  pos++;
  if (pos > 50570) pos = 0;
}



RX210でAD変換

2017-04-16 10:11:53 | RX210
ルネサスエレクトロニクス社のマイコンRX210を使用した12bitAD変換のサンプルコードです。AN01端子の電圧を変換します。入力端子により結果の保存されるレジスタが変化するので注意が必要です。
  1. #include <stdio.h>
  2. #include <machine.h>
  3. #include "iodefine.h"
  4. /*
  5.  * ADコンバータと温度センサーの初期化
  6.  */
  7. void ad_init(void)
  8. {
  9.     SYSTEM.PRCR.WORD = 0xA502;                    /* protect off */
  10.     MSTP(S12AD) = 0;                            /* ADコンバータ */
  11.     SYSTEM.PRCR.WORD = 0xA500;                    /* protect on */
  12.     PORT4.PDR.BIT.B1 = 0;                        /* ポートを入力に */
  13.     MPC.PWPR.BIT.B0WI = 0;                        /* enable to write PFSWE bit */
  14.     MPC.PWPR.BIT.PFSWE = 1;                        /* enable to write PFS register */
  15.     //MPC.P40PFS.BIT.ASEL = 1;                    /* AN000 */
  16.     MPC.P41PFS.BIT.ASEL = 1;                    /* AN001 */
  17.     MPC.PWPR.BIT.PFSWE = 0;                        /* disable to write PFS register */
  18.     MPC.PWPR.BIT.B0WI = 1;                        /* disable to write PFSWE bit */
  19.     S12AD.ADANSA.BIT.ANSA1 = 1;                 /* A/D チャネル選択レジスタA */
  20.     S12AD.ADCSR.BIT.ADCS = 0;                    /* シングルスキャンモード */
  21.     //S12AD.ADSSTR1 = 10;                        /* アナログ入力サンプリング時間 */
  22.     S12AD.ADCSR.BIT.EXTRG = 0;
  23. }
  24. /*
  25.  * 電圧の取得
  26.  */
  27. unsigned int get_ad(void)
  28. {
  29.     volatile int i;
  30.     unsigned int temp;
  31.     S12AD.ADCSR.BIT.ADST = 1;                /* AD変換開始 */
  32.     while(S12AD.ADCSR.BIT.ADST == 1);        /* 終了まで待つ */
  33.     temp = S12AD.ADDR1;                        /* 結果の読み出し */
  34.     /* 変換結果の入るレジスタはピンで決まる ADDR0~ADDR15 */
  35.     return temp;
  36. }
  37. int main(void)
  38. {
  39.     int adv,i;
  40.     ad_init(); //AD変換初期化
  41.     printf("A/D converter test.\n");
  42.     while (1) {
  43.         adv = get_ad();
  44.         printf("AD value %d\n",adv);
  45.         for(i=0;i<50000;i++) nop();
  46.     }
  47. }