mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	CSND: handle command processing
This commit is contained in:
		
							parent
							
								
									7ea82e7941
								
							
						
					
					
						commit
						64641cf958
					
				
					 2 changed files with 398 additions and 31 deletions
				
			
		|  | @ -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
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue