DSFファイル2

さて、前回定義した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 ヘッダを読んでみたいと思います。