さて、前回定義したDSDヘッダの構造体のうち、
DSD チャンク, fmt チャンク, data チャンク を読んでみる。
追加で、ヘッダファイル(dsf.h)に以下を定義する。
//ヘッダファイルの先頭に以下を #pragma pack(1) //--------------------------
そして、ヘッダファイルの最後に以下を追加.
//最初のDSDヘッダー全体サイズ(metaはファイルの最後なので除くサイズ) #define DSD_HEADER_SIZE 100 //実際にはここにDATAオフセットを定義しているので、ファイルとしての先頭からのDSDヘッダーサイズは以下 #define DSD_HEADER_REAL_SIZE 92 struct dsf_file{ struct dsd_chunk_header dch; struct fmt_chunk_header fch; struct data_chunk data; };
そして、main.c を作成する。
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <errno.h> #include <stdarg.h> #include <sys/stat.h> #include "dsf.h" int read_dsf(struct dsf_file* df, FILE* fp); void inputLine(const char* msg, char* input, size_t size); void msgWaitOut(const char *fmt, ...); int main(int argc, char** argv) { struct stat st; char fname[256]; fname[0] = '\0'; if (argc != 2) { inputLine("DSFファイル名を入力してください", fname, sizeof(fname)); } else { size_t len = strlen(argv[1]); if (len >= sizeof(fname)) { msgWaitOut("DSFファイル名が長すぎます。\n%d バイト以内で指定してください。"); return 1; } strcpy(fname,argv[1]); } if (fname[0] == '\0') { msgWaitOut("DSFファイル名が指定されていません"); return 1; } //ファイルの存在確認 int ret = stat(fname, &st); if (ret != 0) { msgWaitOut("ERROR:%s は存在していません", fname); return -1; } //ファイルを開く FILE* fp = fopen(fname, "rb"); if (fp == NULL) { perror(fname); return -1; //exit( EXIT_FAILURE ); } struct dsf_file df; if (read_dsf(&df,fp)) { fclose(fp); return -1; } puts("done."); fclose(fp); return 0; } int read_dsf(struct dsf_file* df, FILE* fp) { //以下はすべてリトルエンディアンでのコピーなので、ビックエンディアンの場合は //書き換えが必要になる。コンパイル時の #ifdef 等で本来は分岐させるべき //fread(buffer, DSD_HEADER_REAL_SIZE, 1, fp); fread(df, DSD_HEADER_REAL_SIZE, 1, fp); //ファイルサイズの取得 fpos_t f_size = 0; fseek(fp, 0, SEEK_END); fgetpos(fp,&f_size); //DSDヘッダか? if (df->dch.name[0] != 'D' || df->dch.name[1] != 'S' || df->dch.name[2] != 'D' || df->dch.name[3] != ' ') { puts("UNKNOWN DSF.(name of dsd header"); return 1; } //チェック:DSD チャンクサイズ if (df->dch.size.ll != 28) { printf("UNKNOWN DSF.(size of dsd header is %llu\n)", df->dch.size.ll); return 2; } printf("dsd chunk size:%llu\n", df->dch.size.ll); //ファイルサイズ if (f_size != df->dch.total_size.ll) { printf("diffrent file size(real:%llu). dsd file total size(%llu)\n", f_size, df->dch.total_size.ll); return 3; } printf("file size:%llu\n", df->dch.total_size.ll); //metaデータへのポインタ(0はmetaデータがなし) printf("dsd meta offset :%llu\n", df->dch.meta_offset.ll); //fmtチャンク //fmtチャンクは正しいか? if (df->fch.name[0] != 'f' || df->fch.name[1] != 'm' || df->fch.name[2] != 't' || df->fch.name[3] != ' ') { puts("UNKNOWN DSF.(name of fmt chunk name"); return 4; } //fmt chunk size. printf("fmt chunk size:%llu\n", df->fch.size.ll); //Format version. 4byte で 1 がセットされている 1以外はUnknownと判断する if (df->fch.format_version.l != 1){ printf("unknown fmt format version = %lu\n", df->fch.format_version.l); return 5; } //Format ID. // 0がセットされている 0以外はUnknownと判断する. 0の意味は DSD raw if (df->fch.format_id.l != 0){ printf("unknown fmt format id = %lu\n", df->fch.format_id.l); return 5; } // Channel Type. printf("Channel Type: "); switch(df->fch.channel_type.l){ case 1: puts("mono"); break; case 2: puts("stereo"); break; case 3: puts("3 channels"); break; case 4: puts("quad"); break; case 5: puts("4 channels"); break; case 6: puts("5 channels"); break; case 7: puts("5.1 channels"); break; default: printf("unknown (%lu)\n", df->fch.channel_type.l); return 6; } //Channel num //1: mono, 2: stereo,… 6: 6 channels if (df->fch.channel_num.l < 1 || df->fch.channel_num.l > 6){ printf("Invalid number of channel (%lu)\n", df->fch.channel_num.l); return 7; } printf("Number of Channel: %lu\n", df->fch.channel_num.l); //Sampling frequency. (サンプリング周波数)単位はHzで値は、2822400, 5644800, … printf("Sampling frequency: %lu\n", df->fch.sampling_frequency.l); //Bits per sample. 単位はbitで値は、1 or 8 になる。 if (df->fch.bits_per_sample.l != 1 && df->fch.bits_per_sample.l != 8) { printf("Invalid Bits per sample. (%lu)\n", df->fch.bits_per_sample.l); return 8; } printf("Bits per sample. (%lu)\n", df->fch.bits_per_sample.l); /* Sample count サンプル数は1 チャンネルごとのサンプル数となる。 例) n 秒間のデータの場合、サンプル数は サンプリング周波数* n となる */ printf("Sample count. (%llu)\n", df->fch.sample_count.ll); /* Block size per channel. 単位はbyteで4096 チャンネルごとのブロックサイズは固定である。 データチャンクの末尾のブロックで、使われなかった領域はゼロ(0x00)で満たす。 */ if (df->fch.block_size.l != 4096) { printf("Invalid Block size per channel. (%lu)\n", df->fch.block_size.l); return 9; } printf("Block size per channel. (%lu)\n", df->fch.block_size.l); //Reserved. Fill Zero(値はすべて0でセット) printf("Reserved: %lu\n", df->fch.reserved.l); //DATA CHUNK //dataチャンクは正しいか? if (df->data.name[0] != 'd' || df->data.name[1] != 'a' || df->data.name[2] != 't' || df->data.name[3] != 'a') { puts("UNKNOWN DSF.(name of data chunk name"); return 4; } //data chunkのサイズ(8byte) name で4+8(サイズ)+n(サンプルデータ) printf("size of data chunk.(%llu)\n", df->data.size.ll); return 0; } void inputLine(const char* msg, char* input, size_t size) { if (msg != NULL) { puts(msg); } if (fgets(input, size, stdin) == NULL) {//エラー input[0] = '\0'; } else { size_t len = strlen(input); len--; if (input[len] == '\n') { input[len] = '\0'; } } } void msgWaitOut(const char *fmt, ...) { va_list list; va_start(list, fmt); vprintf(fmt, list); va_end(list); getc(stdin); }
これをコンパイルして、コマンドラインで実行してDSDファイルを読んでみる。
http://www.oppodigital.jp/support/dsd-by-davidelias/
からダウンロードした
○ DSF – DSD64 – ステレオ (241.7MB)
をプログラムで読んだ結果が、以下になりました。
——————————————————–
dsd chunk size:28
file size:253534447
dsd meta offset :253534300
fmt chunk size:52
Channel Type: stereo
Number of Channel: 2
Sampling frequency: 2822400
Bits per sample. (1)
Sample count. (1014136832)
Block size per channel. (4096)
Reserved: 0
size of data chunk.(253534220)
——————————————————–
こんな感じで、次回は ID3v2 ヘッダを読んでみたいと思います。