File:  [Isaki's NoNo m68k/m88k emulator] / nono / host / sounddriver_wav.cpp
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Wed Apr 29 17:06:02 2026 UTC (2 months, 3 weeks ago) by root
Branches: MAIN, Isaki
CVS tags: v027, HEAD
nono 1.7.0

//
// nono
// Copyright (C) 2025 nono project
// Licensed under nono-license.txt
//

//
// サウンドの Wav ドライバ
//

#include "sounddriver_wav.h"
#include "mainapp.h"
#include <time.h>

// コンストラクタ
SoundDriverWav::SoundDriverWav(HostDevice *hostdev_)
	: inherited(hostdev_, "wav")
{
}

// デストラクタ
SoundDriverWav::~SoundDriverWav()
{
	CloseWav();
}

bool
SoundDriverWav::InitDriver(bool startup)
{
	if (inherited::InitDriver(startup) == false) {
		return false;
	}

	le16buf.resize(blkbytes / sizeof(int16));

	return true;
}

// ブロック書き出し
void
SoundDriverWav::Write(const int16 *blk)
{
	if (__predict_false(fp == NULL)) {
		if (OpenWav() == false) {
			return;
		}
	}

	// データはホストエンディアンなので LE16 にして末尾に追加。
	// LE 環境では無駄だが、LE は大抵速いのと #if になるほうが嫌なので。
	for (uint i = 0, end = le16buf.size(); i < end; i++) {
		le16buf[i] = htole16(blk[i]);
	}
	fwrite(le16buf.data(), sizeof(int16), le16buf.size(), fp);

	databytes += blkbytes;

	// データ長に関連するフィールドを更新。
	// +$04 に (ファイルサイズ - 8) で、
	// これはこのフィールド以降のデータ長なので
	// データ開始位置までの 0x24 バイト分とデータ長の和。
	fseek(fp, 0x04, SEEK_SET);
	fwrite4LE(fp, databytes + 0x24);

	// +$28 にデータ長。
	fseek(fp, 0x28, SEEK_SET);
	fwrite4LE(fp, databytes);

	// ポジションを末尾に戻しておく。
	fseek(fp, 0, SEEK_END);
}

bool
SoundDriverWav::OpenWav()
{
	struct timespec ts;
	char namebuf[32];
	struct tm tmbuf;

	// 現在時刻からファイル名を作る。
	clock_gettime(CLOCK_REALTIME, &ts);
	localtime_r(&ts.tv_sec, &tmbuf);
	strftime(namebuf, sizeof(namebuf), "%Y%m%d-%H%M%S.wav", &tmbuf);
	filename = std::string(namebuf);
	std::string pathname = gMainApp.GetVMDir() + "/" + filename;

	fp = fopen(pathname.c_str(), "w");
	if (fp == NULL) {
		return false;
	}

	// ヘッダを出力。
	fwrite(wav_header, 1, sizeof(wav_header), fp);

	// +$18 にサンプリング周波数。
	fseek(fp, 0x18L, SEEK_SET);
	fwrite4LE(fp, freq);

	// +$1c に 1秒あたりのバイト数。
	fwrite4LE(fp, freq * 4);

	// ポジションを末尾に戻しておく。
	fseek(fp, 0, SEEK_END);

	databytes = 0;

	return true;
}

// fp の指定の現在位置から val を LE32 で書き込む。
void
SoundDriverWav::fwrite4LE(FILE *fp_, uint32 val)
{
	uint32 data = htole32(val);
	fwrite(&data, sizeof(data), 1, fp_);
}

// WAV ファイルを閉じる。(何度呼び出しても副作用はない)
void
SoundDriverWav::CloseWav()
{
	if (fp) {
		fclose(fp);
		fp = NULL;
	}
}

void
SoundDriverWav::MonitorScreenMD(TextScreen& screen, int y)
{
	screen.Print(0, y, "Filename: %s", filename.c_str());
}

/*static*/ const uint8
SoundDriverWav::wav_header[0x2c] = {
	'R', 'I', 'F', 'F',			// +$00
	0, 0, 0, 0,					// +$04: ここに、(ファイルサイズ - 8)
	'W', 'A', 'V', 'E',			// +$08: フォーマット

	'f', 'm', 't', ' ',			// +$0c: fmt チャンク識別子
	16, 0, 0, 0,				// +$10: 以降のチャンクサイズ
	1, 0,						// +$14: フォーマット種別(1=リニアPCM)
	2, 0,						// +$16: チャンネル数
	0, 0, 0, 0,					// +$18: サンプリング周波数 [Hz]
	0, 0, 0, 0,					// +$1c: 1秒あたりのバイト数
	4, 0,						// +$20: こっちでいうフレームサイズ
	16, 0,						// +$22: ビット/サンプル

	'd', 'a', 't', 'a',			// +$24: data チャンク識別子
	0, 0, 0, 0,					// +$28: ここに、以降のチャンクサイズ
								// +$2c: ここからデータ
};

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.