|
|
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());
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.