|
|
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: ここからデータ
};
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.