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 { | ||||
| 
 | ||||
| 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) { | ||||
|     IPC::RequestParser rp(ctx, 0x01, 5, 0); | ||||
|     const u32 size = Common::AlignUp(rp.Pop<u32>(), Memory::PAGE_SIZE); | ||||
|     const u32 offset0 = rp.Pop<u32>(); | ||||
|     const u32 offset1 = rp.Pop<u32>(); | ||||
|     const u32 offset2 = rp.Pop<u32>(); | ||||
|     const u32 offset3 = rp.Pop<u32>(); | ||||
|     master_state_offset = rp.Pop<u32>(); | ||||
|     channel_state_offset = rp.Pop<u32>(); | ||||
|     capture_state_offset = rp.Pop<u32>(); | ||||
|     type1_command_offset = rp.Pop<u32>(); | ||||
| 
 | ||||
|     using Kernel::MemoryPermission; | ||||
|     mutex = system.Kernel().CreateMutex(false, "CSND:mutex"); | ||||
|  | @ -32,8 +208,10 @@ void CSND_SND::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 
 | ||||
|     LOG_WARNING(Service_CSND, | ||||
|                 "(STUBBED) called, size=0x{:08X} " | ||||
|                 "offset0=0x{:08X} offset1=0x{:08X} offset2=0x{:08X} offset3=0x{:08X}", | ||||
|                 size, offset0, offset1, offset2, offset3); | ||||
|                 "master_state_offset=0x{:08X} channel_state_offset=0x{:08X} " | ||||
|                 "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) { | ||||
|  | @ -53,31 +231,178 @@ void CSND_SND::Shutdown(Kernel::HLERequestContext& ctx) { | |||
| void CSND_SND::ExecuteCommands(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x03, 1, 0); | ||||
|     const u32 addr = rp.Pop<u32>(); | ||||
|     LOG_WARNING(Service_CSND, "(STUBBED) called, addr=0x{:08X}", addr); | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     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"); | ||||
|     } else { | ||||
|         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); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     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) { | ||||
|     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); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.Push<u32>(0xFFFFFF00); | ||||
|     rb.Push(acquired_channel_mask); | ||||
| 
 | ||||
|     LOG_WARNING(Service_CSND, "(STUBBED) called"); | ||||
| } | ||||
|  | @ -85,6 +410,8 @@ void CSND_SND::AcquireSoundChannels(Kernel::HLERequestContext& ctx) { | |||
| void CSND_SND::ReleaseSoundChannels(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp(ctx, 0x06, 0, 0); | ||||
| 
 | ||||
|     acquired_channel_mask = 0; | ||||
| 
 | ||||
|     IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,6 +15,45 @@ class System; | |||
| 
 | ||||
| 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> { | ||||
| public: | ||||
|     explicit CSND_SND(Core::System& system); | ||||
|  | @ -26,10 +65,10 @@ private: | |||
|      *  Inputs: | ||||
|      *      0 : Header Code[0x00010140] | ||||
|      *      1 : Shared memory block size, for mem-block creation | ||||
|      *      2 : Offset0 located in the shared-memory, region size=8 | ||||
|      *      3 : Offset1 located in the shared-memory, region size=12*num_channels | ||||
|      *      4 : Offset2 located in the shared-memory, region size=8*num_capturedevices | ||||
|      *      5 : Offset3 located in the shared-memory. | ||||
|      *      2 : offset to master state located in the shared-memory, region size=8 | ||||
|      *      3 : offset to channel state located in the shared-memory, region size=12*num_channels | ||||
|      *      4 : offset to capture state located in the shared-memory, region size=8*num_captures | ||||
|      *      5 : offset to type 1 commands (?) located in the shared-memory. | ||||
|      *  Outputs: | ||||
|      *      1 : Result of function, 0 on success, otherwise error code | ||||
|      *      2 : Handle-list header | ||||
|  | @ -166,15 +205,6 @@ private: | |||
|      */ | ||||
|     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; | ||||
| 
 | ||||
|     std::shared_ptr<Kernel::Mutex> mutex = nullptr; | ||||
|  | @ -182,6 +212,16 @@ private: | |||
| 
 | ||||
|     static constexpr u32 MaxCaptureUnits = 2; | ||||
|     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
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue