|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2025 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: //
8: // サウンドの Wav ドライバ
9: //
10:
11: #include "sounddriver_wav.h"
12: #include "mainapp.h"
13: #include <time.h>
14:
15: // コンストラクタ
16: SoundDriverWav::SoundDriverWav(HostDevice *hostdev_)
17: : inherited(hostdev_, "wav")
18: {
19: }
20:
21: // デストラクタ
22: SoundDriverWav::~SoundDriverWav()
23: {
24: CloseWav();
25: }
26:
27: bool
28: SoundDriverWav::InitDriver(bool startup)
29: {
30: if (inherited::InitDriver(startup) == false) {
31: return false;
32: }
33:
34: le16buf.resize(blkbytes / sizeof(int16));
35:
36: return true;
37: }
38:
39: // ブロック書き出し
40: void
41: SoundDriverWav::Write(const int16 *blk)
42: {
43: if (__predict_false(fp == NULL)) {
44: if (OpenWav() == false) {
45: return;
46: }
47: }
48:
49: // データはホストエンディアンなので LE16 にして末尾に追加。
50: // LE 環境では無駄だが、LE は大抵速いのと #if になるほうが嫌なので。
51: for (uint i = 0, end = le16buf.size(); i < end; i++) {
52: le16buf[i] = htole16(blk[i]);
53: }
54: fwrite(le16buf.data(), sizeof(int16), le16buf.size(), fp);
55:
56: databytes += blkbytes;
57:
58: // データ長に関連するフィールドを更新。
59: // +$04 に (ファイルサイズ - 8) で、
60: // これはこのフィールド以降のデータ長なので
61: // データ開始位置までの 0x24 バイト分とデータ長の和。
62: fseek(fp, 0x04, SEEK_SET);
63: fwrite4LE(fp, databytes + 0x24);
64:
65: // +$28 にデータ長。
66: fseek(fp, 0x28, SEEK_SET);
67: fwrite4LE(fp, databytes);
68:
69: // ポジションを末尾に戻しておく。
70: fseek(fp, 0, SEEK_END);
71: }
72:
73: bool
74: SoundDriverWav::OpenWav()
75: {
76: struct timespec ts;
77: char namebuf[32];
78: struct tm tmbuf;
79:
80: // 現在時刻からファイル名を作る。
81: clock_gettime(CLOCK_REALTIME, &ts);
82: localtime_r(&ts.tv_sec, &tmbuf);
83: strftime(namebuf, sizeof(namebuf), "%Y%m%d-%H%M%S.wav", &tmbuf);
84: filename = std::string(namebuf);
85: std::string pathname = gMainApp.GetVMDir() + "/" + filename;
86:
87: fp = fopen(pathname.c_str(), "w");
88: if (fp == NULL) {
89: return false;
90: }
91:
92: // ヘッダを出力。
93: fwrite(wav_header, 1, sizeof(wav_header), fp);
94:
95: // +$18 にサンプリング周波数。
96: fseek(fp, 0x18L, SEEK_SET);
97: fwrite4LE(fp, freq);
98:
99: // +$1c に 1秒あたりのバイト数。
100: fwrite4LE(fp, freq * 4);
101:
102: // ポジションを末尾に戻しておく。
103: fseek(fp, 0, SEEK_END);
104:
105: databytes = 0;
106:
107: return true;
108: }
109:
110: // fp の指定の現在位置から val を LE32 で書き込む。
111: void
112: SoundDriverWav::fwrite4LE(FILE *fp_, uint32 val)
113: {
114: uint32 data = htole32(val);
115: fwrite(&data, sizeof(data), 1, fp_);
116: }
117:
118: // WAV ファイルを閉じる。(何度呼び出しても副作用はない)
119: void
120: SoundDriverWav::CloseWav()
121: {
122: if (fp) {
123: fclose(fp);
124: fp = NULL;
125: }
126: }
127:
128: void
1.1.1.2 ! root 129: SoundDriverWav::MonitorScreenMD(TextScreen& screen, int y)
1.1 root 130: {
131: screen.Print(0, y, "Filename: %s", filename.c_str());
132: }
133:
134: /*static*/ const uint8
135: SoundDriverWav::wav_header[0x2c] = {
136: 'R', 'I', 'F', 'F', // +$00
137: 0, 0, 0, 0, // +$04: ここに、(ファイルサイズ - 8)
138: 'W', 'A', 'V', 'E', // +$08: フォーマット
139:
140: 'f', 'm', 't', ' ', // +$0c: fmt チャンク識別子
141: 16, 0, 0, 0, // +$10: 以降のチャンクサイズ
142: 1, 0, // +$14: フォーマット種別(1=リニアPCM)
143: 2, 0, // +$16: チャンネル数
144: 0, 0, 0, 0, // +$18: サンプリング周波数 [Hz]
145: 0, 0, 0, 0, // +$1c: 1秒あたりのバイト数
146: 4, 0, // +$20: こっちでいうフレームサイズ
147: 16, 0, // +$22: ビット/サンプル
148:
149: 'd', 'a', 't', 'a', // +$24: data チャンク識別子
150: 0, 0, 0, 0, // +$28: ここに、以降のチャンクサイズ
151: // +$2c: ここからデータ
152: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.