|
|
nono 1.7.0
//
// nono
// Copyright (C) 2025 nono project
// Licensed under nono-license.txt
//
//
// サウンドレンダラ
//
#pragma once
#include "thread.h"
#include "fixedqueue.h"
#include <array>
#include <condition_variable>
#include <mutex>
class BitmapRGBX;
class Syncer;
class HostSoundDevice;
// 定数。
class Sound
{
public:
// (1フレームの)チャンネル数 (デバイスと SoundRenderer の間はステレオ固定)。
static constexpr int NCHAN = 2;
// 1フレームのバイト数。
static constexpr size_t FRAME_BYTES = NCHAN * sizeof(int16);
// 1ブロックの時間。
// ADPCM の制約からこれ以上小さくは出来ない(し大きくする必要もない)。
static constexpr uint64 BLK_TSEC = 40_msec;
// サウンドソースのブロック数。
// ダブルバッファ方式なので 2。
static constexpr uint NSRCBLKS = 2;
// 合成バッファのブロック数。
// 1ブロック境界ごとに逐次処理するので 1。
static constexpr uint NMIXBLKS = 1;
// 出力バッファのブロック数。
// 最低 3 ブロック必要だが、2 のべき乗だとお得。
static constexpr uint NOUTBLKS = 4;
};
enum SoundStat {
Stopped, // デバイスは再生をしていない
Drained,
Running, // デバイスは再生中
Draining, // デバイスはこのブロック処理後に再生を停止
};
// サウンドの 1 トラック (ソースデバイス) 分。
class SoundTrack : private Sound
{
public:
SoundTrack(Device *dev_, size_t frames_per_blk_);
int16 *GetBlockBySeq(uint64 vseq);
int16 *GetBlockByIdx(uint32 idx);
int16 *AllocSeq(uint64 vseq);
void ClearSeq(uint64 vseq);
// ソースデバイスを返す。
Device *GetDevice() const noexcept { return dev; }
// ソースデバイス名を返す。
const std::string& GetName() const { return dev->GetName(); }
// 再生状態
SoundStat status {};
// レベル(L,R)。
std::array<double, NCHAN> dbfs {};
private:
Device *dev {};
// NBLKS 分のバッファ。
std::unique_ptr<int16[]> buf {};
// buf の各ブロックが有効ならそのシーケンス。-1 なら未使用。
std::array<uint64, NSRCBLKS> blkseq {};
// 1ブロックのサンプル数。
size_t samples_per_blk {};
};
// サウンドレンダラ
class SoundRenderer : public ThreadDevice, private Sound
{
using inherited = ThreadDevice;
private:
// ソースデバイスの最大数。2 のべき乗であること。
static constexpr uint32 MAX_SOUND_SOURCES = 4;
static constexpr uint32 REQ_TERMINATE = 0x80000000;
static constexpr uint32 REQ_RECONFIG = 0x00000001;
static constexpr int METER_LEN = 20; // メータ部分の文字数
static constexpr int METER_DIV = 3; // 1文字あたりの dbFS
public:
SoundRenderer();
~SoundRenderer() override;
bool Create() override;
void SetLogLevel(int loglevel_) override;
bool Init() override;
void ResetHard(bool poweron) override;
SoundTrack *RegistTrack(Device *);
void StartPlay(SoundTrack *);
void StopPlay(SoundTrack *);
uint GetMixerFreq() const noexcept { return mixerfreq; }
uint GetOutFrequency() const noexcept { return outfreq; }
uint GetOutBytesPerBlk() const noexcept;
static void ClearDBFS(std::array<double, NCHAN>& dbFS);
protected:
void ThreadRun() override;
void Terminate() override;
bool Reconfig(bool startup);
void Callback(Event *);
void Mix(uint64 vseq);
void AddPCM(int16 *dst, const int16 *src);
void CalcDBFS(std::array<double, NCHAN>& dbFS, const int16 *src);
DECLARE_MONITOR_SCREEN(MonitorScreen);
DECLARE_MONITOR_BITMAP(MonitorBitmap);
void MonitorScreenLevelMeter(TextScreen&, int x, int y,
const std::array<double, NCHAN> dbfs);
// VM スレッドからの通知用。
uint32 request {};
std::mutex mtx {};
std::condition_variable cv {};
// VM スレッドからの処理依頼キュー。
// キューにする必要ある?
FixedQueue<uint64, NSRCBLKS> request_q {};
// サウンドトラック(ソース)。
std::vector<SoundTrack> tracks {};
// サウンドミキサーのサンプリングレート [Hz]。
// サウンドトラック(ソース)もこの周波数。機種によって異なる。
uint mixerfreq {};
// ミキサーバッファ 1ブロックのサンプル数。
size_t mixer_samples_per_blk {};
// 合成用バッファ。
std::unique_ptr<int16[]> mixbuf {};
// マスターレベル。
std::array<double, NCHAN> master_dbfs {};
// 出力サンプリングレート [Hz]
uint outfreq {};
// 出力バッファ 1ブロックのサンプル数。
size_t out_samples_per_blk {};
// 出力バッファ。
std::unique_ptr<int16[]> outbuf {};
// 現在の出力ブロック位置。
uint outpos {};
// 出力キュー。バッファの先頭だけで扱う。
FixedQueue<const uint16 *, NOUTBLKS> output_q {};
// 出力サンプリングレート変更指示。
uint request_outfreq {};
std::unique_ptr<HostSoundDevice> hostsnd /*{}*/;
Event *play_event {};
Monitor *monitor {};
Syncer *syncer {};
};
inline SoundRenderer *GetSoundRenderer() {
return Object::GetObject<SoundRenderer>(OBJ_SOUND_RENDERER);
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.