commit
cae913d3d5
|
@ -10,13 +10,189 @@
|
||||||
|
|
||||||
namespace Service::CSND {
|
namespace Service::CSND {
|
||||||
|
|
||||||
|
enum class CommandId : u16 {
|
||||||
|
Start = 0x000,
|
||||||
|
Pause = 0x001,
|
||||||
|
SetEncoding = 0x002,
|
||||||
|
SetSecondBlock = 0x003,
|
||||||
|
SetLoopMode = 0x004,
|
||||||
|
// unknown = 0x005,
|
||||||
|
SetLinearInterpolation = 0x006,
|
||||||
|
SetPsgDuty = 0x007,
|
||||||
|
SetSampleRate = 0x008,
|
||||||
|
SetVolume = 0x009,
|
||||||
|
SetFirstBlock = 0x00A,
|
||||||
|
SetFirstBlockAdpcmState = 0x00B,
|
||||||
|
SetSecondBlockAdpcmState = 0x00C,
|
||||||
|
SetSecondBlockAdpcmReload = 0x00D,
|
||||||
|
ConfigureChannel = 0x00E,
|
||||||
|
ConfigurePsg = 0x00F,
|
||||||
|
ConfigurePsgNoise = 0x010,
|
||||||
|
// 0x10x commands are audio capture related
|
||||||
|
// unknown = 0x200
|
||||||
|
UpdateState = 0x300,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Type0Command {
|
||||||
|
u16_le next_command_offset;
|
||||||
|
enum_le<CommandId> command_id;
|
||||||
|
u8 finished;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u32_le value;
|
||||||
|
INSERT_PADDING_BYTES(0x10);
|
||||||
|
} start;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u32_le value;
|
||||||
|
INSERT_PADDING_BYTES(0x10);
|
||||||
|
} pause;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
Encoding value;
|
||||||
|
INSERT_PADDING_BYTES(0x13);
|
||||||
|
} set_encoding;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
LoopMode value;
|
||||||
|
INSERT_PADDING_BYTES(0x13);
|
||||||
|
} set_loop_mode;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u32_le value;
|
||||||
|
INSERT_PADDING_BYTES(0x10);
|
||||||
|
} set_linear_interpolation;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u8 value;
|
||||||
|
INSERT_PADDING_BYTES(0x13);
|
||||||
|
} set_psg_duty;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u32_le value;
|
||||||
|
INSERT_PADDING_BYTES(0x10);
|
||||||
|
} set_sample_rate;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u16_le left_channel_volume;
|
||||||
|
u16_le right_channel_volume;
|
||||||
|
u16_le left_capture_volume;
|
||||||
|
u16_le right_capture_volume;
|
||||||
|
INSERT_PADDING_BYTES(0xC);
|
||||||
|
} set_volume;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u32_le address;
|
||||||
|
u32_le size;
|
||||||
|
INSERT_PADDING_BYTES(0xC);
|
||||||
|
} set_block; // for either first block or second block
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
s16_le predictor;
|
||||||
|
u8 step_index;
|
||||||
|
INSERT_PADDING_BYTES(0x11);
|
||||||
|
} set_adpcm_state; // for either first block or second block
|
||||||
|
|
||||||
|
struct {
|
||||||
|
u32_le channel;
|
||||||
|
u8 value;
|
||||||
|
INSERT_PADDING_BYTES(0x13);
|
||||||
|
} set_second_block_adpcm_reload;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
BitField<0, 6, u32> channel;
|
||||||
|
BitField<6, 1, u32> linear_interpolation;
|
||||||
|
|
||||||
|
BitField<10, 2, u32> loop_mode;
|
||||||
|
BitField<12, 2, u32> encoding;
|
||||||
|
BitField<14, 1, u32> enable_playback;
|
||||||
|
|
||||||
|
BitField<16, 16, u32> sample_rate;
|
||||||
|
};
|
||||||
|
|
||||||
|
u16_le left_channel_volume;
|
||||||
|
u16_le right_channel_volume;
|
||||||
|
u16_le left_capture_volume;
|
||||||
|
u16_le right_capture_volume;
|
||||||
|
u32_le block1_address;
|
||||||
|
u32_le block2_address;
|
||||||
|
u32_le size;
|
||||||
|
} configure_channel;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
BitField<0, 6, u32> channel;
|
||||||
|
BitField<14, 1, u32> enable_playback;
|
||||||
|
BitField<16, 16, u32> sample_rate;
|
||||||
|
};
|
||||||
|
u16_le left_channel_volume;
|
||||||
|
u16_le right_channel_volume;
|
||||||
|
u16_le left_capture_volume;
|
||||||
|
u16_le right_capture_volume;
|
||||||
|
u32_le duty;
|
||||||
|
INSERT_PADDING_BYTES(0x8);
|
||||||
|
} configure_psg;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
union {
|
||||||
|
BitField<0, 6, u32> channel;
|
||||||
|
BitField<14, 1, u32> enable_playback;
|
||||||
|
};
|
||||||
|
u16_le left_channel_volume;
|
||||||
|
u16_le right_channel_volume;
|
||||||
|
u16_le left_capture_volume;
|
||||||
|
u16_le right_capture_volume;
|
||||||
|
INSERT_PADDING_BYTES(0xC);
|
||||||
|
} configure_psg_noise;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong");
|
||||||
|
|
||||||
|
struct MasterState {
|
||||||
|
u32_le unknown_channel_flag;
|
||||||
|
u32_le unknown;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(MasterState) == 0x8, "MasterState structure size is wrong");
|
||||||
|
|
||||||
|
struct ChannelState {
|
||||||
|
u8 active;
|
||||||
|
INSERT_PADDING_BYTES(0x3);
|
||||||
|
s16_le adpcm_predictor;
|
||||||
|
u8 adpcm_step_index;
|
||||||
|
INSERT_PADDING_BYTES(0x1);
|
||||||
|
|
||||||
|
// 3dbrew says this is the current physical address. However the assembly of CSND module
|
||||||
|
// from 11.3 system shows this is simply assigned as 0, which is also documented on ctrulib.
|
||||||
|
u32_le zero;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(ChannelState) == 0xC, "ChannelState structure size is wrong");
|
||||||
|
|
||||||
|
struct CaptureState {
|
||||||
|
u8 active;
|
||||||
|
INSERT_PADDING_BYTES(0x3);
|
||||||
|
u32_le zero;
|
||||||
|
};
|
||||||
|
static_assert(sizeof(CaptureState) == 0x8, "CaptureState structure size is wrong");
|
||||||
|
|
||||||
void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) {
|
void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x01, 5, 0);
|
IPC::RequestParser rp(ctx, 0x01, 5, 0);
|
||||||
const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::PAGE_SIZE);
|
const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::PAGE_SIZE);
|
||||||
const u32 offset0 = rp.Pop<u32>();
|
master_state_offset = rp.Pop<u32>();
|
||||||
const u32 offset1 = rp.Pop<u32>();
|
channel_state_offset = rp.Pop<u32>();
|
||||||
const u32 offset2 = rp.Pop<u32>();
|
capture_state_offset = rp.Pop<u32>();
|
||||||
const u32 offset3 = rp.Pop<u32>();
|
type1_command_offset = rp.Pop<u32>();
|
||||||
|
|
||||||
using Kernel::MemoryPermission;
|
using Kernel::MemoryPermission;
|
||||||
mutex = system.Kernel().CreateMutex(false, "CSND:mutex");
|
mutex = system.Kernel().CreateMutex(false, "CSND:mutex");
|
||||||
|
@ -32,8 +208,10 @@ void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_WARNING(Service_CSND,
|
LOG_WARNING(Service_CSND,
|
||||||
"(STUBBED) called, size=0x{:08X} "
|
"(STUBBED) called, size=0x{:08X} "
|
||||||
"offset0=0x{:08X} offset1=0x{:08X} offset2=0x{:08X} offset3=0x{:08X}",
|
"master_state_offset=0x{:08X} channel_state_offset=0x{:08X} "
|
||||||
size, offset0, offset1, offset2, offset3);
|
"capture_state_offset=0x{:08X} type1_command_offset=0x{:08X}",
|
||||||
|
size, master_state_offset, channel_state_offset, capture_state_offset,
|
||||||
|
type1_command_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSND_SND::Shutdown(Kernel::HLERequestContext& ctx) {
|
void CSND_SND::Shutdown(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -53,31 +231,178 @@ void CSND_SND::Shutdown(Kernel::HLERequestContext& ctx) {
|
||||||
void CSND_SND::ExecuteCommands(Kernel::HLERequestContext& ctx) {
|
void CSND_SND::ExecuteCommands(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x03, 1, 0);
|
IPC::RequestParser rp(ctx, 0x03, 1, 0);
|
||||||
const u32 addr = rp.Pop<u32>();
|
const u32 addr = rp.Pop<u32>();
|
||||||
|
LOG_WARNING(Service_CSND, "(STUBBED) called, addr=0x{:08X}", addr);
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
if (!shared_memory) {
|
if (!shared_memory) {
|
||||||
rb.Push<u32>(1);
|
rb.Push(ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::CSND,
|
||||||
|
ErrorSummary::InvalidState, ErrorLevel::Status));
|
||||||
LOG_ERROR(Service_CSND, "called, shared memory not allocated");
|
LOG_ERROR(Service_CSND, "called, shared memory not allocated");
|
||||||
} else {
|
return;
|
||||||
u8* ptr = shared_memory->GetPointer(addr);
|
|
||||||
Type0Command command;
|
|
||||||
|
|
||||||
std::memcpy(&command, ptr, sizeof(Type0Command));
|
|
||||||
command.finished |= 1;
|
|
||||||
std::memcpy(ptr, &command, sizeof(Type0Command));
|
|
||||||
|
|
||||||
rb.Push(RESULT_SUCCESS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_WARNING(Service_CSND, "(STUBBED) called, addr=0x{:08X}", addr);
|
u32 offset = addr;
|
||||||
|
while (offset != 0xFFFF) {
|
||||||
|
Type0Command command;
|
||||||
|
u8* ptr = shared_memory->GetPointer(offset);
|
||||||
|
std::memcpy(&command, ptr, sizeof(Type0Command));
|
||||||
|
offset = command.next_command_offset;
|
||||||
|
|
||||||
|
switch (command.command_id) {
|
||||||
|
case CommandId::Start:
|
||||||
|
// TODO: start/stop the sound
|
||||||
|
break;
|
||||||
|
case CommandId::Pause:
|
||||||
|
// TODO: pause/resume the sound
|
||||||
|
break;
|
||||||
|
case CommandId::SetEncoding:
|
||||||
|
channels[command.set_encoding.channel].encoding = command.set_encoding.value;
|
||||||
|
break;
|
||||||
|
case CommandId::SetSecondBlock:
|
||||||
|
channels[command.set_block.channel].block2_address = command.set_block.address;
|
||||||
|
channels[command.set_block.channel].block2_size = command.set_block.size;
|
||||||
|
break;
|
||||||
|
case CommandId::SetLoopMode:
|
||||||
|
channels[command.set_loop_mode.channel].loop_mode = command.set_loop_mode.value;
|
||||||
|
break;
|
||||||
|
case CommandId::SetLinearInterpolation:
|
||||||
|
channels[command.set_linear_interpolation.channel].linear_interpolation =
|
||||||
|
command.set_linear_interpolation.value != 0;
|
||||||
|
break;
|
||||||
|
case CommandId::SetPsgDuty:
|
||||||
|
channels[command.set_psg_duty.channel].psg_duty = command.set_psg_duty.value;
|
||||||
|
break;
|
||||||
|
case CommandId::SetSampleRate:
|
||||||
|
channels[command.set_sample_rate.channel].sample_rate = command.set_sample_rate.value;
|
||||||
|
break;
|
||||||
|
case CommandId::SetVolume:
|
||||||
|
channels[command.set_volume.channel].left_channel_volume =
|
||||||
|
command.set_volume.left_channel_volume;
|
||||||
|
channels[command.set_volume.channel].right_channel_volume =
|
||||||
|
command.set_volume.right_channel_volume;
|
||||||
|
channels[command.set_volume.channel].left_capture_volume =
|
||||||
|
command.set_volume.left_capture_volume;
|
||||||
|
channels[command.set_volume.channel].right_capture_volume =
|
||||||
|
command.set_volume.right_capture_volume;
|
||||||
|
break;
|
||||||
|
case CommandId::SetFirstBlock:
|
||||||
|
channels[command.set_block.channel].block1_address = command.set_block.address;
|
||||||
|
channels[command.set_block.channel].block1_size = command.set_block.size;
|
||||||
|
break;
|
||||||
|
case CommandId::SetFirstBlockAdpcmState:
|
||||||
|
channels[command.set_adpcm_state.channel].block1_adpcm_state = {
|
||||||
|
command.set_adpcm_state.predictor, command.set_adpcm_state.step_index};
|
||||||
|
channels[command.set_adpcm_state.channel].block2_adpcm_state = {};
|
||||||
|
channels[command.set_adpcm_state.channel].block2_adpcm_reload = false;
|
||||||
|
break;
|
||||||
|
case CommandId::SetSecondBlockAdpcmState:
|
||||||
|
channels[command.set_adpcm_state.channel].block2_adpcm_state = {
|
||||||
|
command.set_adpcm_state.predictor, command.set_adpcm_state.step_index};
|
||||||
|
channels[command.set_adpcm_state.channel].block2_adpcm_reload = true;
|
||||||
|
break;
|
||||||
|
case CommandId::SetSecondBlockAdpcmReload:
|
||||||
|
channels[command.set_second_block_adpcm_reload.channel].block2_adpcm_reload =
|
||||||
|
command.set_second_block_adpcm_reload.value != 0;
|
||||||
|
break;
|
||||||
|
case CommandId::ConfigureChannel: {
|
||||||
|
auto& configure = command.configure_channel;
|
||||||
|
auto& channel = channels[configure.channel];
|
||||||
|
channel.linear_interpolation = configure.linear_interpolation != 0;
|
||||||
|
channel.loop_mode = static_cast<LoopMode>(configure.loop_mode.Value());
|
||||||
|
channel.encoding = static_cast<Encoding>(configure.encoding.Value());
|
||||||
|
channel.sample_rate = configure.sample_rate;
|
||||||
|
channel.left_channel_volume = configure.left_channel_volume;
|
||||||
|
channel.right_channel_volume = configure.right_channel_volume;
|
||||||
|
channel.left_capture_volume = configure.left_capture_volume;
|
||||||
|
channel.right_capture_volume = configure.right_capture_volume;
|
||||||
|
channel.block1_address = configure.block1_address;
|
||||||
|
channel.block2_address = configure.block2_address;
|
||||||
|
channel.block1_size = channel.block2_size = configure.size;
|
||||||
|
if (configure.enable_playback) {
|
||||||
|
// TODO: startthe sound
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CommandId::ConfigurePsg: {
|
||||||
|
auto& configure = command.configure_psg;
|
||||||
|
auto& channel = channels[configure.channel];
|
||||||
|
channel.encoding = Encoding::Psg;
|
||||||
|
channel.psg_duty = configure.duty;
|
||||||
|
channel.sample_rate = configure.sample_rate;
|
||||||
|
channel.left_channel_volume = configure.left_channel_volume;
|
||||||
|
channel.right_channel_volume = configure.right_channel_volume;
|
||||||
|
channel.left_capture_volume = configure.left_capture_volume;
|
||||||
|
channel.right_capture_volume = configure.right_capture_volume;
|
||||||
|
if (configure.enable_playback) {
|
||||||
|
// TODO: startthe sound
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CommandId::ConfigurePsgNoise: {
|
||||||
|
auto& configure = command.configure_psg_noise;
|
||||||
|
auto& channel = channels[configure.channel];
|
||||||
|
channel.encoding = Encoding::Psg;
|
||||||
|
channel.left_channel_volume = configure.left_channel_volume;
|
||||||
|
channel.right_channel_volume = configure.right_channel_volume;
|
||||||
|
channel.left_capture_volume = configure.left_capture_volume;
|
||||||
|
channel.right_capture_volume = configure.right_capture_volume;
|
||||||
|
if (configure.enable_playback) {
|
||||||
|
// TODO: startthe sound
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CommandId::UpdateState: {
|
||||||
|
MasterState master{0, 0};
|
||||||
|
std::memcpy(shared_memory->GetPointer(master_state_offset), &master, sizeof(master));
|
||||||
|
|
||||||
|
u32 output_index = 0;
|
||||||
|
for (u32 i = 0; i < ChannelCount; ++i) {
|
||||||
|
if ((acquired_channel_mask & (1 << i)) == 0)
|
||||||
|
continue;
|
||||||
|
ChannelState state;
|
||||||
|
state.active = false;
|
||||||
|
state.adpcm_predictor = channels[i].block1_adpcm_state.predictor;
|
||||||
|
state.adpcm_predictor = channels[i].block1_adpcm_state.step_index;
|
||||||
|
state.zero = 0;
|
||||||
|
std::memcpy(
|
||||||
|
shared_memory->GetPointer(channel_state_offset + sizeof(state) * output_index),
|
||||||
|
&state, sizeof(state));
|
||||||
|
++output_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 i = 0; i < MaxCaptureUnits; ++i) {
|
||||||
|
if (!capture_units[i])
|
||||||
|
continue;
|
||||||
|
CaptureState state;
|
||||||
|
state.active = false;
|
||||||
|
state.zero = 0;
|
||||||
|
std::memcpy(shared_memory->GetPointer(capture_state_offset + sizeof(state) * i),
|
||||||
|
&state, sizeof(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
LOG_ERROR(Service_CSND, "Unimplemented command ID 0x{:X}",
|
||||||
|
static_cast<u16>(command.command_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*shared_memory->GetPointer(addr + offsetof(Type0Command, finished)) = 1;
|
||||||
|
|
||||||
|
rb.Push(RESULT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSND_SND::AcquireSoundChannels(Kernel::HLERequestContext& ctx) {
|
void CSND_SND::AcquireSoundChannels(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x05, 0, 0);
|
IPC::RequestParser rp(ctx, 0x05, 0, 0);
|
||||||
|
|
||||||
|
// This is "almost" hardcoded, as in CSND initializes this with some code during sysmodule
|
||||||
|
// startup, but it always compute to the same value.
|
||||||
|
acquired_channel_mask = 0xFFFFFF00;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
rb.Push<u32>(0xFFFFFF00);
|
rb.Push(acquired_channel_mask);
|
||||||
|
|
||||||
LOG_WARNING(Service_CSND, "(STUBBED) called");
|
LOG_WARNING(Service_CSND, "(STUBBED) called");
|
||||||
}
|
}
|
||||||
|
@ -85,6 +410,8 @@ void CSND_SND::AcquireSoundChannels(Kernel::HLERequestContext& ctx) {
|
||||||
void CSND_SND::ReleaseSoundChannels(Kernel::HLERequestContext& ctx) {
|
void CSND_SND::ReleaseSoundChannels(Kernel::HLERequestContext& ctx) {
|
||||||
IPC::RequestParser rp(ctx, 0x06, 0, 0);
|
IPC::RequestParser rp(ctx, 0x06, 0, 0);
|
||||||
|
|
||||||
|
acquired_channel_mask = 0;
|
||||||
|
|
||||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||||
rb.Push(RESULT_SUCCESS);
|
rb.Push(RESULT_SUCCESS);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,45 @@ class System;
|
||||||
|
|
||||||
namespace Service::CSND {
|
namespace Service::CSND {
|
||||||
|
|
||||||
|
enum class Encoding : u8 {
|
||||||
|
Pcm8 = 0,
|
||||||
|
Pcm16 = 1,
|
||||||
|
Adpcm = 2,
|
||||||
|
Psg = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class LoopMode : u8 {
|
||||||
|
Manual = 0, // Play block 1 endlessly ignoring the size
|
||||||
|
Normal = 1, // Play block 1 once, then repeat with block 2. Block size is reloaded every time a
|
||||||
|
// new block is started
|
||||||
|
OneShot = 2, // Play block 1 once and stop
|
||||||
|
ConstantSize = 3, // Similar to Normal, but only load block size once at the beginning
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AdpcmState {
|
||||||
|
s16 predictor = 0;
|
||||||
|
u8 step_index = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Channel {
|
||||||
|
PAddr block1_address = 0;
|
||||||
|
PAddr block2_address = 0;
|
||||||
|
u32 block1_size = 0;
|
||||||
|
u32 block2_size = 0;
|
||||||
|
AdpcmState block1_adpcm_state;
|
||||||
|
AdpcmState block2_adpcm_state;
|
||||||
|
bool block2_adpcm_reload = false;
|
||||||
|
u16 left_channel_volume = 0;
|
||||||
|
u16 right_channel_volume = 0;
|
||||||
|
u16 left_capture_volume = 0;
|
||||||
|
u16 right_capture_volume = 0;
|
||||||
|
u32 sample_rate = 0;
|
||||||
|
bool linear_interpolation = false;
|
||||||
|
LoopMode loop_mode = LoopMode::Manual;
|
||||||
|
Encoding encoding = Encoding::Pcm8;
|
||||||
|
u8 psg_duty = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class CSND_SND final : public ServiceFramework<CSND_SND> {
|
class CSND_SND final : public ServiceFramework<CSND_SND> {
|
||||||
public:
|
public:
|
||||||
explicit CSND_SND(Core::System& system);
|
explicit CSND_SND(Core::System& system);
|
||||||
|
@ -26,10 +65,10 @@ private:
|
||||||
* Inputs:
|
* Inputs:
|
||||||
* 0 : Header Code[0x00010140]
|
* 0 : Header Code[0x00010140]
|
||||||
* 1 : Shared memory block size, for mem-block creation
|
* 1 : Shared memory block size, for mem-block creation
|
||||||
* 2 : Offset0 located in the shared-memory, region size=8
|
* 2 : offset to master state located in the shared-memory, region size=8
|
||||||
* 3 : Offset1 located in the shared-memory, region size=12*num_channels
|
* 3 : offset to channel state located in the shared-memory, region size=12*num_channels
|
||||||
* 4 : Offset2 located in the shared-memory, region size=8*num_capturedevices
|
* 4 : offset to capture state located in the shared-memory, region size=8*num_captures
|
||||||
* 5 : Offset3 located in the shared-memory.
|
* 5 : offset to type 1 commands (?) located in the shared-memory.
|
||||||
* Outputs:
|
* Outputs:
|
||||||
* 1 : Result of function, 0 on success, otherwise error code
|
* 1 : Result of function, 0 on success, otherwise error code
|
||||||
* 2 : Handle-list header
|
* 2 : Handle-list header
|
||||||
|
@ -166,15 +205,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void Reset(Kernel::HLERequestContext& ctx);
|
void Reset(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
struct Type0Command {
|
|
||||||
// command id and next command offset
|
|
||||||
u32 command_id;
|
|
||||||
u32 finished;
|
|
||||||
u32 flags;
|
|
||||||
u8 parameters[20];
|
|
||||||
};
|
|
||||||
static_assert(sizeof(Type0Command) == 0x20, "Type0Command structure size is wrong");
|
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
|
|
||||||
std::shared_ptr<Kernel::Mutex> mutex = nullptr;
|
std::shared_ptr<Kernel::Mutex> mutex = nullptr;
|
||||||
|
@ -182,6 +212,16 @@ private:
|
||||||
|
|
||||||
static constexpr u32 MaxCaptureUnits = 2;
|
static constexpr u32 MaxCaptureUnits = 2;
|
||||||
std::array<bool, MaxCaptureUnits> capture_units = {false, false};
|
std::array<bool, MaxCaptureUnits> capture_units = {false, false};
|
||||||
|
|
||||||
|
static constexpr u32 ChannelCount = 32;
|
||||||
|
std::array<Channel, ChannelCount> channels;
|
||||||
|
|
||||||
|
u32 master_state_offset = 0;
|
||||||
|
u32 channel_state_offset = 0;
|
||||||
|
u32 capture_state_offset = 0;
|
||||||
|
u32 type1_command_offset = 0;
|
||||||
|
|
||||||
|
u32 acquired_channel_mask = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Initializes the CSND_SND Service
|
/// Initializes the CSND_SND Service
|
||||||
|
|
Reference in New Issue