File:  [Isaki's NoNo m68k/m88k emulator] / nono / host / hostsound.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
//

//
// サウンドのホストデバイス
//

// 再生時のフロー
//
// Sound Thread             :     Host thread
//
// HostSoundDevice::Play()  :   HostDevice::ThreadRun
//     |
//     +-------------->| queue |----+         … 送信キューに追加し
//     +-------------->| pipe  |----+         … パイプでホストスレッドに通知
//     |                            |
//   <-+                    :       v
//                                 --- kevent
//                          :       |
//                              HostSoundDevice::Write()
//                          :       |
//                              SoundDriver*::Write()
//                          :       |
//                                  +------> Playback in the real world

#include "hostsound.h"
#include "config.h"
#include "sound.h"
#if defined(HAVE_HOSTSOUND_ALSA)
#include "sounddriver_alsa.h"
#endif
#if defined(HAVE_HOSTSOUND_NETBSD)
#include "sounddriver_netbsd.h"
#endif
#if defined(HAVE_HOSTSOUND_SNDIO)
#include "sounddriver_sndio.h"
#endif
#include "sounddriver_none.h"
#include "sounddriver_wav.h"
#include "uimessage.h"

// コンストラクタ
HostSoundDevice::HostSoundDevice(Device *parent_)
	: inherited(parent_, OBJ_HOSTSOUND, ""/*portname*/)
{
	monitor = gMonitorManager->Regist(ID_MONITOR_HOSTSOUND, this);
	monitor->SetCallback(&HostSoundDevice::MonitorScreen);
	monitor->SetSize(48, 6);
}

// デストラクタ
HostSoundDevice::~HostSoundDevice()
{
}

// ログレベル設定
void
HostSoundDevice::SetLogLevel(int loglevel_)
{
	inherited::SetLogLevel(loglevel_);

	RWLockGuard_Read guard(driverlock);
	if ((bool)driver) {
		driver->SetLogLevel(loglevel_);
	}
}

// 動的コンストラクションその2
bool
HostSoundDevice::Create2()
{
	if (inherited::Create2() == false) {
		return false;
	}

	// SelectDriver() は SoundRenderer::Reconfig() から呼ばれるので、
	// ここでは不要。

	return true;
}

// ドライバ(再)選択。
// エラーが起きた場合、
// 起動時なら、warn() 等で表示して false を返す(終了する)。
// 実行中なら、warn() 等で表示してもいいが、必ず None にフォールバックして
// true を返すこと。
bool
HostSoundDevice::SelectDriver(bool startup)
{
	auto sound = dynamic_cast<SoundRenderer *>(parent);
	freq = sound->GetOutFrequency();
	bytes_per_blk = sound->GetOutBytesPerBlk();

	RWLockGuard_Write guard(driverlock);

	driver.reset();
	errmsg.clear();

	// ドライバはここで選択。
	const ConfigItem& item = gConfig->Find("hostsound-driver");
	std::string type = item.AsString();

	// auto は環境による。
	if (type == "auto") {
#if defined(HAVE_HOSTSOUND_NETBSD)
		type = "netbsd";
#elif defined(HAVE_HOSTSOUND_SNDIO)
		type = "sndio";
#elif defined(HAVE_HOSTSOUND_ALSA)
		type = "alsa";
#else
		type = "none";
#endif
	}

	enum {
		ERR_CONFIG,		// 設定でのエラー
		ERR_INVALID,	// 知らないドライバ
		ERR_NOTSUPP,	// コンパイルされてない
	} reason = ERR_CONFIG;

	if (type == "none") {
		CreateNone();

	} else if (type == "wav") {
		CreateWav();

	} else if (type == "netbsd") {
#if defined(HAVE_HOSTSOUND_NETBSD)
		CreateNetBSD();
#else
		reason = ERR_NOTSUPP;
#endif

	} else if (type == "sndio") {
#if defined(HAVE_HOSTSOUND_SNDIO)
		CreateSndIO();
#else
		reason = ERR_NOTSUPP;
#endif

	} else if (type == "alsa") {
#if defined(HAVE_HOSTSOUND_ALSA)
		CreateALSA();
#else
		reason = ERR_NOTSUPP;
#endif

	} else {
		// 知らないドライバ種別
		reason = ERR_INVALID;
	}

	if ((bool)driver == false) {
		// 指定のドライバが使えなかった場合、
		// o 起動時ならエラー終了する。
		// o 実行中なら none にフォールバックする。
		if (startup) {
			switch (reason) {
			 case ERR_INVALID:
				item.Err("Invalid driver name");
				break;
			 case ERR_CONFIG:
				item.Err("Could not configure the driver");
				warnx("(See details with option -C -Lhostsound=1)");
				break;
			 case ERR_NOTSUPP:
				item.Err("HostSound driver %s not compiled", type.c_str());
				break;
			 default:
				assert(false);
			}
			return false;
		} else {
			gMainApp.GetUIMessage()->Post(UIMessage::HOSTSOUND_FAILED);

			CreateNone();
		}
	}
	assert((bool)driver);

	// ドライバ名は Capitalize だがログはほぼ小文字なので雰囲気を揃える…
	putmsg(1, "selected host driver: %s",
		string_tolower(driver->GetDriverName()).c_str());

	return true;
}

// None ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostSoundDevice::CreateNone()
{
	try {
		driver.reset(new SoundDriverNone(this));
	} catch (...) { }
	if ((bool)driver) {
		if (driver->InitDriver()) {
			// 成功
			return;
		}
		errmsg = driver->errmsg;
		driver.reset();
	}
}

// Wav ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostSoundDevice::CreateWav()
{
	try {
		driver.reset(new SoundDriverWav(this));
	} catch (...) { }
	if ((bool)driver) {
		if (driver->InitDriver()) {
			// 成功
			return;
		}
		errmsg = driver->errmsg;
		driver.reset();
	}
}

#if defined(HAVE_HOSTSOUND_NETBSD)
// NetBSD ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostSoundDevice::CreateNetBSD()
{
	try {
		driver.reset(new SoundDriverNetBSD(this));
	} catch (...) { }
	if ((bool)driver) {
		if (driver->InitDriver()) {
			// 成功
			return;
		}
		errmsg = driver->errmsg;
		driver.reset();
	}
}
#endif

#if defined(HAVE_HOSTSOUND_SNDIO)
// sndio ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostSoundDevice::CreateSndIO()
{
	try {
		driver.reset(new SoundDriverSndIO(this));
	} catch (...) { }
	if ((bool)driver) {
		if (driver->InitDriver()) {
			// 成功
			return;
		}
		errmsg = driver->errmsg;
		driver.reset();
	}
}
#endif

#if defined(HAVE_HOSTSOUND_ALSA)
// ALSA ドライバを生成する。
// 戻り値はなく、成否は (bool)driver で判断する。
void
HostSoundDevice::CreateALSA()
{
	try {
		driver.reset(new SoundDriverALSA(this));
	} catch (...) { }
	if ((bool)driver) {
		if (driver->InitDriver()) {
			// 成功
			return;
		}
		errmsg = driver->errmsg;
		driver.reset();
	}
}
#endif

// サウンドスレッドからの再生(の指示) (サウンドスレッドで呼ばれる)。
bool
HostSoundDevice::Play(const int16 *blk)
{
	playq.Enqueue(blk);

	// パイプで通知。
	return WritePipe(PIPE_TX);
}

// 外部への書き出し (再生)
void
HostSoundDevice::Write()
{
	const int16 * const *blkp;

	RWLockGuard_Read guard(driverlock);
	assert((bool)driver);

	// 再生リングバッファから1ブロック取り出す。
	while ((blkp = playq.BeginRead()) != NULL) {
		stat.write_blk++;
		driver->Write(*blkp);
		playq.EndRead();
	}
}

// 外部からの読み込み (録音)
// 未対応
int
HostSoundDevice::Read()
{
	return 0;
}

const std::string&
HostSoundDevice::GetDriverName()
{
	RWLockGuard_Read guard(driverlock);
	assert((bool)driver);
	return driver->GetDriverName();
}

// モニタ
void
HostSoundDevice::MonitorScreen(Monitor *, TextScreen& screen)
{
	screen.Clear();

	{
		RWLockGuard_Read gurad(driverlock);
		screen.Print(0, 0, "HostSound Driver: %s",
			driver->GetDriverName().c_str());
		// 次の1行はドライバ依存情報
		driver->MonitorScreenMD(screen, 1);
	}

	screen.Print(0, 2, "Output Frequency: %u Hz", freq);

	int y = 4;
	screen.Print(0, y++, "%-17s%13s %17s", "<Play>", "Blocks", "Bytes");
	screen.Print(0, y++, "%-17s%13s %17s", "Write to host",
		format_number(stat.write_blk).c_str(),
		format_number(stat.write_blk * bytes_per_blk).c_str());
}

unix.superglobalmegacorp.com

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