How to set microphone boost in C# using WASAPI - c#

I am trying to use WASAPI in C# but I could not even find which dll to reference in Visual Studio. Should I reference a dll in COM assemblies or download one from Microsoft website and reference it? Is there any documentation how to use the WASAPI in C#?
I want to use it to set microphone boost level. I have been using NAudio for that, but in Windows 8.1 it does not function properly, see this. It sets the boost level via winmm calls. I thought I could use WASAPI directly.
Edit
I have tried CSCore which has a wrapper for WASAPI calls for setting microphone boost. It successfully sets the value, but the program crashes everytime with Access Violation exception after setting the value. Here is the code for CSCore:
MMDeviceEnumerator deviceEnumerator = new MMDeviceEnumerator();
MMDeviceCollection deviceCollection = deviceEnumerator.EnumerateAudioEndPoints(EDataFlow.eCapture, DEVICE_STATE.DEVICE_STATE_ACTIVE);
MMDevice microphone = null;
for (int i = 0; i < deviceCollection.Count; i++)
{
MMDevice device = deviceCollection[i];
if (device.FriendlyName.Contains("Plantronics"))
{
microphone = device;
}
}
if (microphone != null && microphone.AudioSessionManager2.Sessions.Count < 1)
{
return;
}
AudioSessionControl2 activeSession = null;
for (int i = 0; i < microphone.AudioSessionManager2.Sessions.Count; i++)
{
if (microphone.AudioSessionManager2.Sessions[i].State == AudioSessionState.AudioSessionStateActive)
{
activeSession = microphone.AudioSessionManager2.Sessions[i];
}
}
if (activeSession == null)
{
return;
}
activeSession.SimpleAudioVolume.MasterVolume += 0.1f;
I have also found AudioSwitcher library which seems to have a wrapper too, however the microphone volume cannot be changed. It is always -1. Here is the code for AudioSwitcher:
CoreAudioController audioController = new CoreAudioController();
var devices = audioController.GetCaptureDevices(DeviceState.Active);
foreach (CoreAudioDevice device in devices)
{
if (device.FullName.Contains("Plantronics"))
{
device.Volume = 49;
}
}

Related

AxMSTSCLib how to redirect camera from client to remote session?

I am using AxMSTSCLib to develop a Windows application for establishing RDP connections. But I don't know how to redirect a physical camera from client to remote RDP session.
I tried to add camerastoredirect:s:* and devicestoredirect:s:* in .rdp file to redirect camera, it worked, I can use the camera in remote desktop via RDP.
But I can't find the correct property in AxMSTSCLib library to enable this feature.
I've tried to redirect all possible/dynamic devices, but it didn't work at all. Here is the code:
public AxMSTSCLib.AxMsRdpClient7NotSafeForScripting oRemote;
oRemote = new AxMSTSCLib.AxMsRdpClient7NotSafeForScripting();
oRemote.AdvancedSettings7.RedirectDevices = true;
oRemote.AdvancedSettings7.RedirectPorts = true;
oRemote.AdvancedSettings7.RedirectPOSDevices = true;
MSTSCLib.IMsRdpClientNonScriptable5 ocx = (MSTSCLib.IMsRdpClientNonScriptable5)oRemote.GetOcx();
ocx.RedirectDynamicDevices = true;
for (int i = 0; i < ocx.DeviceCollection.DeviceCount; i++)
{
MSTSCLib.IMsRdpDevice device = ocx.DeviceCollection.get_DeviceByIndex((uint)i);
device.RedirectionState = true;
}
I found IMsRdpCameraRedirConfigCollection in newer API documents, need to upgrade IMsRdpClientNonScriptable from 5 to 7.
I tried to print its count before setting Redirected as true, like this:
MSTSCLib.IMsRdpClientNonScriptable7 ocx = (MSTSCLib.IMsRdpClientNonScriptable7)oRemote.GetOcx();
Logger.Log("[ocx.CameraRedirConfigCollection.Count]:" + (ocx.CameraRedirConfigCollection.Count).ToString());
for (int i = 0; i < ocx.CameraRedirConfigCollection.Count; i++)
{
MSTSCLib.IMsRdpCameraRedirConfig camera = ocx.CameraRedirConfigCollection.get_ByIndex((uint)i);
Logger.Log("[camera.FriendlyName]:" + camera.FriendlyName);
camera.Redirected = true;
}
Unfortunately, I got zero.
According to the Docs, it looks like I have to add the camera symbolic link to the collection using AddConfig method. I stuck at this point, how do I add a camera symbolic link to the IMsRdpCameraRedirConfigCollection? How to get camera symbolic link?
Why IMsRdpCameraRedirConfigCollection is empty in the beginning? Since you didn't Rescan connected camera devices.
MSTSCLib.IMsRdpClientNonScriptable7 ocx = (MSTSCLib.IMsRdpClientNonScriptable7)oRemote.GetOcx();
// enumerates connected camera devices
ocx.CameraRedirConfigCollection.Rescan();
for (int i = 0; i < ocx.CameraRedirConfigCollection.Count; i++)
{
MSTSCLib.IMsRdpCameraRedirConfig camera = ocx.CameraRedirConfigCollection.get_ByIndex((uint)i);
camera.Redirected = true;
}
That's it, all connected camera devices will be redirected to the remote desktop.

Trouble with transfer function in WIA C#

I have a problem with the below code. I want to scan a document by clicking a button in a WinForms C# application.
I use WIA, Visual studio and the scanner Fujitsu N7100A working with Windows 8. I am following a tutorial online for using WIA.
But the program doesn't run as expected. It seems to break down at the Transfer method.
// Create a DeviceManager instance
var deviceManager = new DeviceManager();
// Create an empty variable to store the scanner instance
DeviceInfo firstScannerAvailable = null;
// Loop through the list of devices to choose the first available
AddLogs(deviceManager.DeviceInfos.Count.ToString(), filename);
foreach (DeviceInfo d in deviceManager.DeviceInfos)
{
if (d.Type == WiaDeviceType.ScannerDeviceType)
{
firstScannerAvailable = d;
}
}
// Connect to the first available scanner
var device = firstScannerAvailable.Connect();
// Select the scanner
var scannerItem = device.Items[0];
// Retrieve a image in JPEG format and store it into a variable
var imageFile = (ImageFile)scannerItem.Transfer(FormatID.wiaFormatPNG);
//Save the image in some path with filename
var path = #"C:\Documents\scan.png";
if (File.Exists(path))
{
File.Delete(path);
}
// Save image !
imageFile.SaveFile(path);
I just have to remove the addition of lines in the file of log.
This is much more of a workaround since i have no idea about your scanner.
I would assume that all scanners has a drive where they store their scanned documents, like mine, So i would suggest that you read all available drives loop through them check for DriveType and VolumeLabel and then read it's files and copy the document where you want
Something like this :
foreach (var item in DriveInfo.GetDrives())
{
//VolumeLabel differs from a scanner to another
if (item.VolumeLabel == "Photo scan" && item.DriveType == DriveType.Removable)
{
foreach (var obj in Directory.GetFiles(item.Name))
{
File.Copy(obj, "[YOUR NEW PATH]");
break;
}
break;
}
}
Finaly a TWAIN application work with this scanner. I will work with that. I don't said why do that work with TWAIN and not with WIA but that the reality. Sorry for this waste of time. Thank you for the answers. Have a nice day.
I am currently solving this very problem. It seems the N7100A driver sets the Pages property of the device to 0, which should mean continous scanning, but the transfer method is unable to handle this value. You must set that property to 1:
var pages = 1;
// Not all devices have this property, but Fujitsu N7100A has.
device.Properties["Pages"]?.set_Value(ref pages);
I think the problem is here
var scannerItem = device.Items[0];
as WIA indexes are NOT zero based so it should be 1 instead
var scannerItem = device.Items[1];

DirectShow Audio Device has no pins

I want to mux webcam capture with audio capture in an avi mux and write that file to disk.
This works in graphedit.
I try to recreate this in c# with directshowlib. This works so far but the only without the microphone capture. My microphone filter is created but has no pins. I tried this on two different laptops. My code for the microphone filter:
Guid microphonFilter = new Guid("{E30629D2-27E5-11CE-875D-00608CB78066}");
IBaseFilter pMikrofonRealtekHighDefinitionAudio = (IBaseFilter)Activator.CreateInstance(Type.GetTypeFromCLSID(microphonFilter));
hr = pGraph.AddFilter(pMikrofonRealtekHighDefinitionAudio, "Mikrofon (Realtek High Definition Audio) ");
I also tried:
IBaseFilter microphonFilter = (IBaseFilter) new AudioRecord();
My code for finding pins:
static IPin GetPin(IBaseFilter filter, string pinname)
{
IEnumPins epins;
int hr = filter.EnumPins(out epins);
checkHR(hr, "Can't enumerate pins");
IntPtr fetched = Marshal.AllocCoTaskMem(4);
IPin[] pins = new IPin[1];
while (epins.Next(1, pins, fetched) == 0)
{
PinInfo pinfo;
pins[0].QueryPinInfo(out pinfo);
bool found = (pinfo.name == pinname);
DsUtils.FreePinInfo(pinfo);
if (found)
return pins[0];
}
checkHR(-1, "Pin not found");
return null;
}
Audio (and video) capture devices like this cannot be instantiated using CoCreateInstance (using CLSID - Activator.CreateInstance in C#). You have to create them using monikers, typically through enumeration, as described on MSDN (with source code snippet): Selecting a Capture Device.
Below is code snippet from DirectShow.NET samples for video capture, you need similar for audio device category.
// This version of FindCaptureDevice is provide for education only.
// A second version using the DsDevice helper class is define later.
public IBaseFilter FindCaptureDevice()
{
// ...
// Create the system device enumerator
ICreateDevEnum devEnum = (ICreateDevEnum) new CreateDevEnum();
// Create an enumerator for the video capture devices
hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0);
DsError.ThrowExceptionForHR(hr);
// The device enumerator is no more needed
Marshal.ReleaseComObject(devEnum);
// If there are no enumerators for the requested type, then
// CreateClassEnumerator will succeed, but classEnum will be NULL.
if (classEnum == null)
{
throw new ApplicationException("No video capture device was detected.\r\n\r\n" +
"This sample requires a video capture device, such as a USB WebCam,\r\n" +
"to be installed and working properly. The sample will now close.");
}

Playing NAudio .mp3 through an Audio Source in Unity

I'm using a package called Visualizer Studio (http://www.alteredr.com/visualizer-studio/) to make a game where objects react to music. My goal is to have the user be able to load a song at any time during the game. It's going to be standalone/PC, not WebPlayer.
My problem is that Visualizer Studio takes the audio data from an Audio Source in the scene. So when I use NAudio to load and stream MP3s, visualizer studio doesn't hear them, and my objects don't react to the music.
I'm using IWavePlayer right now in my code. I've tried adding audio.clip = www.GetAudioClip and similar functions to have the audio clip load the music that is being played, but to no avail.
I got the code I'm using to select and stream .mp3s from this blog post (the source code is at the bottom): (http://denis-potapenko.blogspot.com/2013/04/task-6-loading-mp3-audio-via-www-class.html). It's unchanged at the moment, because nothing I was trying was working.
To be clear, the .mp3s DO play when I select them from a file browser. I just need them to play via an audio source in my scene. Please, there has to be a way to do this. I've contacted visualizer studio support and asked on the unity forums, but nobody can help so far.
Please keep in mind that I'm not exactly a great programmer, so you might have to dumb answers down for me. Thanks for you patience in advance. Anyway,
Here's my code I'm using to open a file browser and stream audio:
using UnityEngine;
using System.Collections;
using System.IO;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
using NAudio;
using NAudio.Wave;
public class AppRoot : MonoBehaviour
{
///////////////////////////////////////////////////////////////////////////
#region Variables
private static AppRoot mInstance = null;
private const string cLocalPath = "file://localhost/";
private IWavePlayer mWaveOutDevice;
private WaveStream mMainOutputStream;
private WaveChannel32 mVolumeStream;
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Interface
public AppRoot()
{
mInstance = this;
}
public void Start()
{
}
public void Update()
{
if(Input.GetKeyDown(KeyCode.F))
{
UnloadAudio();
LoadAudio();
}
}
public void OnGUI()
{
if (GUI.Button(new Rect(100, Screen.height - 200 + 100, Screen.width - 200, 35), "Load audio"))
{
UnloadAudio();
LoadAudio();
}
}
public void OnApplicationQuit()
{
UnloadAudio();
}
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Implementation
private void LoadAudio()
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Title = "Open audio file";
ofd.Filter = "MP3 audio (*.mp3) | *.mp3";
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
WWW www = new WWW(cLocalPath + ofd.FileName);
Debug.Log("path = " + cLocalPath + ofd.FileName);
while (!www.isDone) { };
if (!string.IsNullOrEmpty(www.error))
{
System.Windows.Forms.MessageBox.Show("Error! Cannot open file: " + ofd.FileName + "; " + www.error);
return;
}
byte[] imageData = www.bytes;
if (!LoadAudioFromData(imageData))
{
System.Windows.Forms.MessageBox.Show("Cannot open mp3 file!");
return;
}
mWaveOutDevice.Play();
Resources.UnloadUnusedAssets();
}
}
private bool LoadAudioFromData(byte[] data)
{
try
{
MemoryStream tmpStr = new MemoryStream(data);
mMainOutputStream = new Mp3FileReader(tmpStr);
mVolumeStream = new WaveChannel32(mMainOutputStream);
mWaveOutDevice = new WaveOut();
mWaveOutDevice.Init(mVolumeStream);
return true;
}
catch (System.Exception ex)
{
Debug.LogWarning("Error! " + ex.Message);
}
return false;
}
private void UnloadAudio()
{
if (mWaveOutDevice != null)
{
mWaveOutDevice.Stop();
}
if (mMainOutputStream != null)
{
// this one really closes the file and ACM conversion
mVolumeStream.Close();
mVolumeStream = null;
// this one does the metering stream
mMainOutputStream.Close();
mMainOutputStream = null;
}
if (mWaveOutDevice != null)
{
mWaveOutDevice.Dispose();
mWaveOutDevice = null;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
#region Properties
private static AppRoot Instance
{
get
{
return mInstance;
}
}
#endregion
///////////////////////////////////////////////////////////////////////////
}
Sincerely,
Jonathan
http://answers.unity3d.com/answers/1128204/view.html
With the "latest" version of NAudio.dll and NAudio.WindowsMediaFormat.dll inserted into your Resources folder utilize this code to do what you described:
var musicInput : GameObject;
private var aud : AudioFileReader;
private var craftClip : AudioClip;
private var AudioData : float[];
private var readBuffer : float[];
private var soundSystem : AudioSource;
private var musicPath : String[];
//Check if there's a pref set for the music path. Use it AND add all the files from it
function CheckMusic()
{
var pathname = musicInput.GetComponent.<InputField>();
if(PlayerPrefs.HasKey("musicpath") == false)
{
PlayerPrefs.SetString("musicpath", "Enter Music Directory");
}
else
{
pathname.text = PlayerPrefs.GetString("musicpath");
musicPath = Directory.GetFiles(PlayerPrefs.GetString("musicpath"),"*.mp3");
}
}
function LoadSong(songToPlay : int)
{
//Download the song via WWW
var currentSong : WWW = new WWW(musicPath[songToPlay]);
//Wait for the song to download
if(currentSong.error == null)
{
//Set the title of the song
playingSong.text = Path.GetFileNameWithoutExtension(musicPath[songToPlay]);
//Parse the file with NAudio
aud = new AudioFileReader(musicPath[songToPlay]);
//Create an empty float to fill with song data
AudioData = new float[aud.Length];
//Read the file and fill the float
aud.Read(AudioData, 0, aud.Length);
//Create a clip file the size needed to collect the sound data
craftClip = AudioClip.Create(Path.GetFileNameWithoutExtension(musicPath[songToPlay]),aud.Length,aud.WaveFormat.Channels,aud.WaveFormat.SampleRate, false);
//Fill the file with the sound data
craftClip.SetData(AudioData,0);
//Set the file as the current active sound clip
soundSystem.clip = craftClip;
}
}
And I quote
The "songToPlay" variable that is passed to the function is a simple int that is acquired from the array created under the CheckMusic function. I search a chosen directory entered from a GUI Inputfield for a specific file type (MP3) which can be changed to WAV or OGG and then input those files to an array. Other code chooses the number of the song on the array to play and you can change that to anything you like. The important part is that the NAudio,dll does all the heavy lifting. All you need to do is use the aud.Read(float[] to send data to, song starting point(usually 0),length of song data (aud.length). The Float[] here is the same length as aud.length so create the float of the same length, read the file, fill the float, create the clip, then dump the float data in with AudioClip.SetData()
Right now this code works and it does the job. Downside is it takes
2-3 seconds to fill the float in this way and is a noticeable drag. It
also tends to chew up memory pretty fast, but it gets the job done.
Hope it helps as a starting point for those who are looking to do
this. I know I needed it.
When you use a WaveOut instance you are playing (relatively) directly on the soundcard, outside of the Unity environment. You need a way to introduce that data into Unity.
I haven't worked with Unity so this might not be the best answer,but...
You can introduce wave data into the scene using OnAudioFilterRead. You can use this to create procedural sound, and with a little coding it can presumably be hooked up to a NAudio wave source. This will introduce the audio data directly into the game for your in-game code to deal with.
Here's an article I found that might help: Procedural Audio with Unity
mWaveOutDevice = new WaveOut();
Maybe causing the issue, it is better practice to use WaveOutEvent() which supports running in all non GUI threads.
mWaveOutDevice = new WaveOutEvent();
Related StackOverFlow Question

Pin *.lnk file to Windows 7 Taskbar using C#

Even the programmatic pinning of icons in Windows 7 seems it's not permitted (like it says here: http://msdn.microsoft.com/en-us/library/dd378460(v=VS.85).aspx), there are some methods for doing this by using some VB scripts.
Someone found a way of doing this in C# like this:
private static void PinUnpinTaskBar(string filePath, bool pin)
{
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
// create the shell application object
dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
dynamic directory = shellApplication.NameSpace(path);
dynamic link = directory.ParseName(fileName);
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
string verbName = verb.Name.Replace(#"&", string.Empty).ToLower();
if ((pin && verbName.Equals("pin to taskbar")) || (!pin && verbName.Equals("unpin from taskbar")))
{
verb.DoIt();
}
}
shellApplication = null;
}
As can be seen, the code makes use of .NET Framework 4.0 features. The question I want to ask is: can this function be transformed so it would make the same thing, but using just 3.5 Framework? Any ideas?
Simple...
private static void PinUnpinTaskBar(string filePath, bool pin) {
if (!File.Exists(filePath)) throw new FileNotFoundException(filePath);
// create the shell application object
Shell shellApplication = new ShellClass();
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
Folder directory = shellApplication.NameSpace(path);
FolderItem link = directory.ParseName(fileName);
FolderItemVerbs verbs = link.Verbs();
for (int i = 0; i < verbs.Count; i++) {
FolderItemVerb verb = verbs.Item(i);
string verbName = verb.Name.Replace(#"&", string.Empty).ToLower();
if ((pin && verbName.Equals("pin to taskbar")) || (!pin && verbName.Equals("unpin from taskbar"))) {
verb.DoIt();
}
}
shellApplication = null;
}
Be sure to add a COM reference to "Microsoft Shell Controls And Automation".
If you want to keep the existing method of using Activator.CreateInstance so you don't have to have the extra COM interop DLL then you'll have to use reflection. But that would make the code a lot uglier.
regardless what localization the Windows user was using:
int MAX_PATH = 255;
var actionIndex = pin ? 5386 : 5387; // 5386 is the DLL index for"Pin to Tas&kbar", ref. http://www.win7dll.info/shell32_dll.html
StringBuilder szPinToStartLocalized = new StringBuilder(MAX_PATH);
IntPtr hShell32 = LoadLibrary("Shell32.dll");
LoadString(hShell32, (uint)actionIndex, szPinToStartLocalized, MAX_PATH);
string localizedVerb = szPinToStartLocalized.ToString();
// create the shell application object
dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
string path = Path.GetDirectoryName(filePath);
string fileName = Path.GetFileName(filePath);
dynamic directory = shellApplication.NameSpace(path);
dynamic link = directory.ParseName(fileName);
dynamic verbs = link.Verbs();
for (int i = 0; i < verbs.Count(); i++)
{
dynamic verb = verbs.Item(i);
if ((pin && verb.Name.Equals(localizedVerb)) || (!pin && verb.Name.Equals(localizedVerb)))
{
verb.DoIt();
break;
}
}
In windows 10 the above methods don't work. The "Pin to Taskbar" verb doesn't appear in the list in your program, only in explorer. For this to work in windows 10, you have two options. Either rename you program to explorer.exe, or you have to fool the object in to thinking your program is called explorer.exe. You have to find the PEB and change the Image Path Field. I wrote a post here on how to do it.
Update for UWP apps running on Windows 10 with the Fall Creators Update (Build 16299 or higher):
You can programmatically pin your own app to the taskbar, just like you can pin your app to the Start menu. And you can check whether your app is currently pinned, and whether the taskbar allows pinning.
You can access the TaskbarManager API directly, as described in MS docs.
I wanted to do this with a WinForms application, but it does not seem like it is going to work because I cannot reference the Windows.Foundation APIs.

Categories