--- nono/vm/sound.cpp 2026/04/29 17:05:55 1.1.1.1 +++ nono/vm/sound.cpp 2026/04/29 17:06:00 1.1.1.2 @@ -8,7 +8,7 @@ // サウンドレンダラ // -// vtime / BLK_NSEC をブロック時刻と呼ぶことにする。 +// vtime / BLK_TSEC をブロック時刻と呼ぶことにする。 // // 各サウンドトラックは 2 (=NSRCBLKS) ブロック分のバッファを持つ。 // ブロック境界の 000'000_nsec ちょうどにソースデバイスとサウンドレンダラの @@ -20,6 +20,7 @@ // 4 (=NOUTBLKS) ブロック。少なくとも 3 あればいいのできりをよくする。 #include "sound.h" +#include "bitmap.h" #include "event.h" #include "hostsound.h" #include "mainapp.h" @@ -123,7 +124,8 @@ SoundRenderer::SoundRenderer() ClearDBFS(master_dbfs); monitor = gMonitorManager->Regist(ID_MONITOR_SOUND, this); - monitor->func = ToMonitorCallback(&SoundRenderer::MonitorUpdate); + monitor->SetCallback(&SoundRenderer::MonitorScreen, + &SoundRenderer::MonitorBitmap); monitor->SetSize(40, 2); } @@ -164,11 +166,11 @@ SoundRenderer::Init() syncer = GetSyncer(); // トラック合成用バッファ。(1ブロック分) - auto mixer_frames_per_blk = (uint64)mixerfreq * BLK_NSEC / 1_sec; + auto mixer_frames_per_blk = (uint64)mixerfreq * BLK_TSEC / 1_sec; mixer_samples_per_blk = FRAMES_TO_SAMPLES(mixer_frames_per_blk); mixbuf.reset(new(std::nothrow) int16[mixer_samples_per_blk]); if (mixbuf == NULL) { - warnx("Failed to allocate %zu bytes at %s", + warnx("Could not allocate %zu bytes at %s", SAMPLES_TO_BYTES(mixer_samples_per_blk), __method__); return false; } @@ -176,6 +178,7 @@ SoundRenderer::Init() // 出力バッファなど実行時にも変わるものを設定。 request_outfreq = mixerfreq; // XXX 周波数変換はまだ if (Reconfig(true) == false) { + // エラーメッセージは表示済み。 return false; } @@ -213,14 +216,14 @@ SoundRenderer::Reconfig(bool startup) need_resize = true; } - auto out_frames_per_blk = (uint64)outfreq * BLK_NSEC / 1_sec; + auto out_frames_per_blk = (uint64)outfreq * BLK_TSEC / 1_sec; out_samples_per_blk = FRAMES_TO_SAMPLES(out_frames_per_blk); if (need_resize) { auto samples = out_samples_per_blk * NOUTBLKS; outbuf.reset(new(std::nothrow) int16[samples]); if (outbuf == NULL) { - warnx("Failed to allocate %zu bytes at %s", + warnx("Could not allocate %zu bytes at %s", SAMPLES_TO_BYTES(samples), __method__); return false; } @@ -228,6 +231,7 @@ SoundRenderer::Reconfig(bool startup) // ホストドライバも必ず再切替になる。 if (hostsnd->NotifyReselectDriver() == false) { + warn("%s: NotifyReselectDriver failed?", __method__); return false; } @@ -268,7 +272,7 @@ SoundRenderer::StartPlay(SoundTrack *tra if (play_event->IsRunning() == false) { // ミキサーイベントが停止してたらイベントを起動する。 // 最初の1回は、ブロック境界になるようにする。 - play_event->time = BLK_NSEC - (scheduler->GetVirtTime() % BLK_NSEC); + play_event->time = BLK_TSEC - (scheduler->GetVirtTime() % BLK_TSEC); scheduler->StartEvent(play_event); syncer->NotifySoundRunning(true); @@ -307,7 +311,7 @@ SoundRenderer::Callback(Event *event) // ブロック境界を繰り上がったところで呼ばれているので // 処理したいのは一つ前のブロック。 - uint64 vseq = (scheduler->GetVirtTime() / BLK_NSEC) - 1; + uint64 vseq = (scheduler->GetVirtTime() / BLK_TSEC) - 1; // 裏(レンダラ)スレッドに通知するだけ。 // キューが一杯ならロストするだけ (だけ?)。 @@ -318,7 +322,7 @@ SoundRenderer::Callback(Event *event) } // 次のイベント。 - event->time = BLK_NSEC; + event->time = BLK_TSEC; scheduler->StartEvent(event); } @@ -515,7 +519,7 @@ SoundRenderer::ClearDBFS(std::array dbfs) { - const int METER_LEN = 20; // メータ部分の文字数 - const int METER_DIV = 3; // 1文字あたりの dbFS - screen.Puts(x, y + 0, "L:"); screen.Puts(x, y + 1, "R:"); x += 2; @@ -592,3 +593,28 @@ SoundRenderer::MonitorUpdateLevelMeter(T } } } + +void +SoundRenderer::MonitorBitmap(Monitor *mon, BitmapRGBX& bitmap) +{ + // テキストで描いたレベルメータに区切りを入れる。 + + uint fw = mon->GetFontWidth(); + uint fh = mon->GetFontHeight(); + + uint x = 20; + uint yend = (1 + tracks.size()) * 3; + for (uint y = 0; y < yend; y += 3) { + // L,R を区切る横線 + uint px = mon->BX(x); + uint py = mon->BY(y + 1); + bitmap.DrawLineH(BGPANEL, px, py, px + fw * METER_LEN); + + // メモリを区切る縦線 (高さ 2CH 分) + for (uint i = 0; i < METER_LEN; i++) { + px = mon->BX(x + i) + fw - 1; + py = mon->BY(y); + bitmap.DrawLineV(BGPANEL, px, py, py + fh * 2); + } + } +}