사운드 컨트롤을 제어해서 지정된 어플에서만 음성을 받아오도록 해야하는 과제가 주어졌다.
일단, 대전제로 아래와같이 Mute 는 사용하지 않으면서, 해당어플에서 내보내는 경고음을 유저에게 내보내야한다.
자, 여기서부터 문제
어떻게 제어를 해야할까??
일단, 한번 검색부터 해보자.
라는 글이 보인다.
그리고, 아래의 글도 많은 도움이 되었다.
C# (CSharp) MIXERLINECONTROLS Code Examples
시스템 비프음을 내고, 시스템사운드를 이용한 음향효과를 내보내는 긴 버튼
그리고, 해당 어플에서 음량을 100%로 효과발산(?) 해주는 버튼
스피커 음량의 초기셋팅은 아래와같이 지정해둔것을 전제로 한다.
스피커는 MAX 100% 지정되어있으며, 나머지는 모두 0% 로 지정 (소리를 받지않겠다는 강한의지!!)
일단, 여기저기 있는 함수를 끌어~ 끌어~모아서 baseClass 작성
baseClass는 아래와 같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | using System; using System.IO; using System.Runtime.InteropServices; namespace WindowsFormsApplication1 { public sealed class SoundUtils { public const int MMSYSERR_NOERROR = 0; public const int MAXPNAMELEN = 32; public const int MIXER_LONG_NAME_CHARS = 64; public const int MIXER_SHORT_NAME_CHARS = 16; public const int MIXER_GETLINEINFOF_COMPONENTTYPE = 0x3; public const int MIXER_GETCONTROLDETAILSF_VALUE = 0x0; public const int MIXER_GETLINECONTROLSF_ONEBYTYPE = 0x2; public const int MIXER_SETCONTROLDETAILSF_VALUE = 0x0; public const int MIXERLINE_COMPONENTTYPE_DST_FIRST = 0x0; public const int MIXERLINE_COMPONENTTYPE_SRC_FIRST = 0x1000; public const int MIXERLINE_COMPONENTTYPE_DST_SPEAKERS = (MIXERLINE_COMPONENTTYPE_DST_FIRST + 4); public const int MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE = (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 3); public const int MIXERLINE_COMPONENTTYPE_SRC_LINE = (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 2); public const int MIXERCONTROL_CT_CLASS_FADER = 0x50000000; public const int MIXERCONTROL_CT_UNITS_UNSIGNED = 0x30000; public const int MIXERCONTROL_CONTROLTYPE_FADER = (MIXERCONTROL_CT_CLASS_FADER | MIXERCONTROL_CT_UNITS_UNSIGNED); public const int MIXERCONTROL_CONTROLTYPE_VOLUME = (MIXERCONTROL_CONTROLTYPE_FADER + 1); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerClose(int hmx); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetControlDetailsA(int hmxobj, ref MIXERCONTROLDETAILS pmxcd, int fdwDetails); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetDevCapsA(int uMxId, MIXERCAPS pmxcaps, int cbmxcaps); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetID(int hmxobj, int pumxID, int fdwId); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetLineControlsA(int hmxobj, ref MIXERLINECONTROLS pmxlc, int fdwControls); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetLineInfoA(int hmxobj, ref MIXERLINE pmxl, int fdwInfo); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerGetNumDevs(); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerMessage(int hmx, int uMsg, int dwParam1, int dwParam2); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerOpen(out int phmx, int uMxId, int dwCallback, int dwInstance, int fdwOpen); [DllImport("winmm.dll", CharSet = CharSet.Ansi)] private static extern int mixerSetControlDetails(int hmxobj, ref MIXERCONTROLDETAILS pmxcd, int fdwDetails); [DllImport("winmm.dll", EntryPoint = "sndPlaySoundA")] private static extern int sndPlaySound(string lpszSoundName, int uFlags); public enum SND { SND_SYNC = 0x0000,/* play synchronously (default) */ SND_ASYNC = 0x0001, /* play asynchronously */ SND_NODEFAULT = 0x0002, /* silence (!default) if sound not found */ SND_MEMORY = 0x0004, /* pszSound points to a memory file */ SND_LOOP = 0x0008, /* loop the sound until next sndPlaySound */ SND_NOSTOP = 0x0010, /* don't stop any currently playing sound */ SND_NOWAIT = 0x00002000, /* don't wait if the driver is busy */ SND_ALIAS = 0x00010000,/* name is a registry alias */ SND_ALIAS_ID = 0x00110000, /* alias is a pre d ID */ SND_FILENAME = 0x00020000, /* name is file name */ SND_RESOURCE = 0x00040004, /* name is resource name or atom */ SND_PURGE = 0x0040, /* purge non-static events for task */ SND_APPLICATION = 0x0080 /* look for application specific association */ } public struct MIXERCAPS { public int wMid; public int wPid; public int vDriverVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)] public string szPname; public int fdwSupport; public int cDestinations; } public struct MIXERCONTROL { public int cbStruct; public int dwControlID; public int dwControlType; public int fdwControl; public int cMultipleItems; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)] public string szShortName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)] public string szName; public int lMinimum; public int lMaximum; [MarshalAs(UnmanagedType.U4, SizeConst = 10)] public int reserved; } public struct MIXERCONTROLDETAILS { public int cbStruct; public int dwControlID; public int cChannels; public int item; public int cbDetails; public IntPtr paDetails; } public struct MIXERCONTROLDETAILS_UNSIGNED { public int dwValue; } public struct MIXERLINE { public int cbStruct; public int dwDestination; public int dwSource; public int dwLineID; public int fdwLine; public int dwUser; public int dwComponentType; public int cChannels; public int cConnections; public int cControls; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_SHORT_NAME_CHARS)] public string szShortName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MIXER_LONG_NAME_CHARS)] public string szName; public int dwType; public int dwDeviceID; public int wMid; public int wPid; public int vDriverVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXPNAMELEN)] public string szPname; } public struct MIXERLINECONTROLS { public int cbStruct; public int dwLineID; public int dwControl; public int cControls; public int cbmxctrl; public IntPtr pamxctrl; } private static bool GetVolumeControl(int hmixer, int componentType, int ctrlType, out MIXERCONTROL mxc, out int vCurrentVol) { // This function attempts to obtain a mixer control. // Returns True if successful. MIXERLINECONTROLS mxlc = new MIXERLINECONTROLS(); MIXERLINE mxl = new MIXERLINE(); MIXERCONTROLDETAILS pmxcd = new MIXERCONTROLDETAILS(); MIXERCONTROLDETAILS_UNSIGNED du = new MIXERCONTROLDETAILS_UNSIGNED(); mxc = new MIXERCONTROL(); int rc; bool retValue; vCurrentVol = -1; mxl.cbStruct = Marshal.SizeOf(mxl); mxl.dwComponentType = componentType; rc = mixerGetLineInfoA(hmixer, ref mxl, MIXER_GETLINEINFOF_COMPONENTTYPE); if (MMSYSERR_NOERROR == rc) { int sizeofMIXERCONTROL = 152; int ctrl = Marshal.SizeOf(typeof(MIXERCONTROL)); mxlc.pamxctrl = Marshal.AllocCoTaskMem(sizeofMIXERCONTROL); mxlc.cbStruct = Marshal.SizeOf(mxlc); mxlc.dwLineID = mxl.dwLineID; mxlc.dwControl = ctrlType; mxlc.cControls = 1; mxlc.cbmxctrl = sizeofMIXERCONTROL; // Allocate a buffer for the control mxc.cbStruct = sizeofMIXERCONTROL; // Get the control rc = mixerGetLineControlsA(hmixer, ref mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE); if (MMSYSERR_NOERROR == rc) { retValue = true; // Copy the control into the destination structure mxc = (MIXERCONTROL)Marshal.PtrToStructure( mxlc.pamxctrl, typeof(MIXERCONTROL)); } else { retValue = false; } int sizeofMIXERCONTROLDETAILS = Marshal.SizeOf(typeof(MIXERCONTROLDETAILS)); int sizeofMIXERCONTROLDETAILS_UNSIGNED = Marshal.SizeOf(typeof(MIXERCONTROLDETAILS_UNSIGNED)); pmxcd.cbStruct = sizeofMIXERCONTROLDETAILS; pmxcd.dwControlID = mxc.dwControlID; pmxcd.paDetails = Marshal.AllocCoTaskMem(sizeofMIXERCONTROLDETAILS_UNSIGNED); pmxcd.cChannels = 1; pmxcd.item = 0; pmxcd.cbDetails = sizeofMIXERCONTROLDETAILS_UNSIGNED; rc = mixerGetControlDetailsA(hmixer, ref pmxcd, MIXER_GETCONTROLDETAILSF_VALUE); du = (MIXERCONTROLDETAILS_UNSIGNED)Marshal.PtrToStructure( pmxcd.paDetails, typeof(MIXERCONTROLDETAILS_UNSIGNED)); vCurrentVol = du.dwValue; return retValue; } retValue = false; return retValue; } private static bool SetVolumeControl(int hmixer, MIXERCONTROL mxc, int volume) { // This function sets the value for a volume control. // Returns True if successful bool retValue; int rc; MIXERCONTROLDETAILS mxcd = new MIXERCONTROLDETAILS(); MIXERCONTROLDETAILS_UNSIGNED vol = new MIXERCONTROLDETAILS_UNSIGNED(); mxcd.item = 0; mxcd.dwControlID = mxc.dwControlID; mxcd.cbStruct = Marshal.SizeOf(mxcd); mxcd.cbDetails = Marshal.SizeOf(vol); // Allocate a buffer for the control value buffer mxcd.cChannels = 1; vol.dwValue = volume; // Copy the data into the control value buffer mxcd.paDetails = Marshal.AllocCoTaskMem(Marshal.SizeOf( typeof(MIXERCONTROLDETAILS_UNSIGNED))); Marshal.StructureToPtr(vol, mxcd.paDetails, false); // Set the control value rc = mixerSetControlDetails(hmixer, ref mxcd, MIXER_SETCONTROLDETAILSF_VALUE); if (MMSYSERR_NOERROR == rc) { retValue = true; } else { retValue = false; } return retValue; } /// <summary> /// 薄仙税 瑳拳聖 亜閃身 /// </summary> /// <returns></returns> public static int GetVolume() { int mixer; int currentVol; MIXERCONTROL volCtrl = new MIXERCONTROL(); mixerOpen(out mixer, 0, 0, 0, 0); int type = MIXERCONTROL_CONTROLTYPE_VOLUME; GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out currentVol); mixerClose(mixer); return currentVol; } /// <summary> /// ボリューム設定 /// </summary> /// <param name="vVolume"></param> public static void SetVolume(int vVolume) { int mixer; MIXERCONTROL volCtrl = new MIXERCONTROL(); int currentVol; mixerOpen(out mixer, 0, 0, 0, 0); int type = MIXERCONTROL_CONTROLTYPE_VOLUME; GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out currentVol); if (vVolume > volCtrl.lMaximum) vVolume = volCtrl.lMaximum; if (vVolume < volCtrl.lMinimum) vVolume = volCtrl.lMinimum; SetVolumeControl(mixer, volCtrl, vVolume); GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out currentVol); if (vVolume != currentVol) { throw new Exception("Cannot Set Volume"); } mixerClose(mixer); } /// <summary> /// ボリュームをパーセントで設定 /// </summary> /// <param name="iPercent"></param> public static void SetVolumePercent(int iPercent) { int iVolumn = 0; int mixer; int currentVol; if (iPercent < 0) iPercent = 0; else if (iPercent > 100) iPercent = 100; MIXERCONTROL volCtrl = new MIXERCONTROL(); mixerOpen(out mixer, 0, 0, 0, 0); int type = MIXERCONTROL_CONTROLTYPE_VOLUME; GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out currentVol); iVolumn = volCtrl.lMaximum * iPercent / 100; SetVolumeControl(mixer, volCtrl, iVolumn); GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, out volCtrl, out currentVol); if (iVolumn != currentVol) { throw new Exception("Cannot Set Volume"); } mixerClose(mixer); } /// <summary> /// WAVファイルを流す /// </summary> /// <param name="filename">Wavファイルプルパス</param> /// <param name="loop">true:再生成功, false:再生失敗</param> public static void PlaySound(string filename, bool loop) { if (File.Exists(filename)) { if (loop) sndPlaySound(filename, (int)SND.SND_ASYNC | (int)SND.SND_LOOP); else sndPlaySound(filename, (int)SND.SND_ASYNC); } } private Microsoft.VisualBasic.Devices.Audio audio = new Microsoft.VisualBasic.Devices.Audio(); /// <summary> /// 指定したファイルを流す /// </summary> /// <param name="wavFilePath"></param> public void PlaySoundWav(string wavFilePath) { //バックグラウンドでWAVを再生する audio.Play(wavFilePath, Microsoft.VisualBasic.AudioPlayMode.Background); //次のようにすると、ループ再生される //audio.Play(wavFilePath, // Microsoft.VisualBasic.AudioPlayMode.BackgroundLoop); //次のようにすると、最後まで再生し終えるまで待機する //audio.Play(wavFilePath, // Microsoft.VisualBasic.AudioPlayMode.WaitToComplete); //再生しているWAVを停止する audio.Stop(); } } } | cs |
화면의 코드는 아래와같다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | using System; using System.Windows.Forms; using System.Media; using Microsoft.VisualBasic; namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Console.Beep(); } private void button2_Click(object sender, EventArgs e) { Interaction.Beep(); } private void button3_Click(object sender, EventArgs e) { Console.Beep(10000, 500); } private void button4_Click(object sender, EventArgs e) { //SystemSounds.Beep.Play(); //SystemSounds.Exclamation.Play(); SystemSounds.Question.Play(); //SystemSounds.Asterisk.Play(); //SystemSounds.Hand.Play(); } private void button5_Click(object sender, EventArgs e) { SoundUtils.SetVolumePercent(100); } private void button6_Click(object sender, EventArgs e) { SoundUtils.SetVolumePercent(50); } private void button7_Click(object sender, EventArgs e) { SoundUtils.SetVolumePercent(0); } private void button8_Click(object sender, EventArgs e) { SoundUtils.GetVolume(); SoundUtils.SetVolume(100); } } } | cs |
실행시켜보면, 사운드 믹서에 해당어플이 등록되어서 볼륨제어가 가능해진다.
[실행전]
[실행후] & 100% 눌렀을때,
[실행후] & 50% 눌렀을때,
[실행후] & 0% 눌렀을때,
다른어플에 대해서도 어플ID 및 ProcessName 등을 통해서 사운드를 제어할수 있다.
추후과제
어플의 초기 기동시에 전체 권한을 부여받아서 Mute 를 해제하고,
Speaker 의 Sound Volume을 MAX 로 지정하고,
타어플에 대해서는 모든 Volume를 Min 0% 로 지정한 후,
지금의 어플동작을 실시하면, 모든 과제는 해결될 것으로 보여진다.
지금까지 만든 소스는 아래에 첨부한다.
password = toshu1203
'C#' 카테고리의 다른 글
ShowDialog로 호출되던 폼을 Show로 바꾸면서 화면숨김 (0) | 2019.03.11 |
---|---|
비동기처리에 대한 이해와 설명 (0) | 2019.02.20 |
서로다른 두개의폼의 값 제어하기 (0) | 2018.10.09 |
C# DataGridView 에 입력된 내용을 CSV출력하는 방법 (0) | 2018.09.11 |
[질답] C# MSSQL 연동 (0) | 2018.08.27 |