|
|
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 ! 129: SoundDriverWav::MonitorUpdateMD(TextScreen& screen, int y) ! 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.