|
Could you share some code? I cannot for the life of me find any C# code that handles getting and setting the speaker volume.
Everything makes sense in someone's mind
|
|
|
|
|
I've had a play and this seems to work. This is the extent of my testing but I hope it helps!
It's quite a lot of code - welcome to the world of PInvoke. There is more that needs doing such as checking the result of each API call and raising a custom exception if != MMSYSERR_NOERROR etc...
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
internal struct WAVEFORMATEX
{
public short wFormatTag;
public short nChannels;
public int nSamplesPerSec;
public int nAvgBytesPerSec;
public short nBlockAlign;
public short wBitsPerSample;
public short cbSize;
}
using System;
using System.Runtime.InteropServices;
internal delegate void waveOutProc(
IntPtr hwo,
int uMsg,
IntPtr dwInstance,
IntPtr dwParam1,
IntPtr dwParam2);
internal static class NativeMethods
{
public const int MMSYSERR_NOERROR = 0;
public const int CALLBACK_FUNCTION = 0x00030000;
[DllImport("winmm.dll", SetLastError = true)]
public static extern int waveOutOpen(
out IntPtr phwo,
int uDeviceID,
ref WAVEFORMATEX pwfx,
waveOutProc dwCallback,
IntPtr dwCallbackInstance,
int fdwOpen);
[DllImport("winmm.dll", SetLastError = true)]
public static extern int waveOutClose(IntPtr hwo);
[DllImport("winmm.dll", SetLastError = true)]
public static extern int waveOutGetVolume(
IntPtr hwo,
out uint pdwVolume);
[DllImport("winmm.dll", SetLastError = true)]
public static extern int waveOutSetVolume(
IntPtr hwo,
uint dwVolume);
}
using System;
public class WaveOut : IDisposable
{
private waveOutProc callback;
private IntPtr handle;
private int id;
public WaveOut(int id)
{
callback = new waveOutProc(Callback);
this.id = id;
}
~WaveOut()
{
Dispose(false);
}
public int ID
{
get { return id; }
}
private void Callback(IntPtr hwo, int uMsg, IntPtr dwInstance,
IntPtr dwParam1, IntPtr dwParam2)
{
}
public void Close()
{
if (handle != IntPtr.Zero)
NativeMethods.waveOutClose(handle);
handle = IntPtr.Zero;
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
}
if (handle != IntPtr.Zero)
Close();
}
public uint GetVolume()
{
uint volume = uint.MaxValue;
if (handle != IntPtr.Zero)
NativeMethods.waveOutGetVolume(handle, out volume);
return volume;
}
public void Open()
{
if (handle == IntPtr.Zero)
{
WAVEFORMATEX waveFormatEx = new WAVEFORMATEX();
waveFormatEx.wFormatTag = 1;
waveFormatEx.nChannels = 2;
waveFormatEx.nSamplesPerSec = 44100;
waveFormatEx.wBitsPerSample = 16;
waveFormatEx.nBlockAlign = (short)(waveFormatEx.nChannels * (waveFormatEx.wBitsPerSample / 8));
waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * waveFormatEx.nBlockAlign;
NativeMethods.waveOutOpen(out handle, id,
ref waveFormatEx, callback, IntPtr.Zero,
NativeMethods.CALLBACK_FUNCTION);
}
}
public void SetVolume(uint volume)
{
if (handle != IntPtr.Zero)
NativeMethods.waveOutSetVolume(handle, volume);
}
}
using System;
internal class Test
{
private static void ConvertFromVolume(uint volume, out int left, out int right)
{
left = (int)volume & 0xFFFF;
right = (int)(volume >> 16) & 0xFFFF;
}
private static uint ConvertToVolume(int left, int right)
{
return (uint)right << 16 | (ushort)left;
}
public void Run()
{
WaveOut waveOut = new WaveOut(0);
waveOut.Open();
int originalLeft;
int originalRight;
ConvertFromVolume(waveOut.GetVolume(), out originalLeft, out originalRight);
Console.WriteLine(
"Left: {0}, Right: {1}",
originalLeft, originalRight);
int newLeft = 32768;
int newRight = 32768;
waveOut.SetVolume(ConvertToVolume(newLeft, newRight));
ConvertFromVolume(waveOut.GetVolume(), out newLeft, out newLeft);
Console.WriteLine(
"Left: {0}, Right: {1}",
newLeft, newLeft);
waveOut.SetVolume(ConvertToVolume(originalLeft, originalRight));
ConvertFromVolume(waveOut.GetVolume(), out newLeft, out newLeft);
Console.WriteLine(
"Left: {0}, Right: {1}",
newLeft, newLeft);
waveOut.Close();
}
}
using System;
class Program
{
static void Main(string[] args)
{
new Test().Run();
Console.ReadKey();
}
}
|
|
|
|
|
Thanks, I'll have a go at it.
Everything makes sense in someone's mind
|
|
|
|
|
Ok, I've coded it.
What should be happening when I run this?
Everything makes sense in someone's mind
|
|
|
|
|
When I run it on my system it
1. retrieves the original volume
2. then changes it and retrieves it agin to show it is changed.
3. It then sets it back to the original and retrieves it again to it has been changed back.
If you remove step 3 it should remain changed to whatever you set it to in 2. Check out the code in Test.Run() , I have commented it all.
|
|
|
|
|
Ok, that's what I figred should be happening.
I'm listening to music right now via headphones. When I run this, the sound level doesn't change.
Everything makes sense in someone's mind
|
|
|
|
|
I haven't actually tested it with audio - just the values from GetVolume so I see that the driver has responded. There could be one of two things here...
1. The device ID needs to be the correct one for the device who's volume you want to adjust - I'm not sure without further research how to determine the correct ID for the main audio out device/master
2. Maybe the API only adjusts the volume for the audio stream you play directly - unlikely though.
Edit: you can use waveOutGetNumDevs to get the number of devices and waveOutGetDevCaps in conjuction with WAVEOUTCAPS to get the correct device. Check out this[^] page. If you can't resolve this, let me know and I'll have another look tomorrow, bedtime for me here in the UK - good luck!
|
|
|
|
|
Are you using Vista or Windows 7? If so, this[^] may explain why it's not working and a possible workaround?
Edit: Tested demo app and it works!
|
|
|
|
|
Yes, I'm Windows 7.
This app worked. Thank you
Everything makes sense in someone's mind
|
|
|
|
|
If the above doesn't help - a search for Audio Mixer C# yielded this[^] - looks good...
|
|
|
|
|
I saw that but it looked far more comples than I wanted to get into just for setting the master volume. But hey, I might have to go there at some point.
Everything makes sense in someone's mind
|
|
|
|
|
Ok so I seem to have my backgroundWorkers worked out. However I can't figure out how to report progress back to the original form. I'll try to pice together the code to have this make sense. FYI I don't have anything in the progressUpdated even yet.
namespace MailTest
{
public partial class Form1 : Form
{
List<BGW> BGWs = new List<BGW>();
public Form1()
{
InitializeComponent();
for (int i = 0; i < 3; i++)
{
BGWs.Add(new BGW());
BGWs[i].ProgressChanged += BGW_ProgressChanged;
BGWs[i].RunWorkerCompleted += BGW_WorkerCompleted;
}
btnGetMessageInfo.Enabled = false;
}
void BGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
BGW bgw = (BGW)sender;
}
void BGW_WorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
BGW bgw = (BGW)sender;
}
class BGW : BackgroundWorker
{
public BGW()
{
this.WorkerReportsProgress = true;
this.WorkerSupportsCancellation = true;
}
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
WriteMail.WriteAndParseMail();
}
}
private void btnGetMessageInfo_Click(object sender, EventArgs e)
{
processNumberOfWorkers.howMayWorkers();
MessageBox.Show("There are " + GlobalVars.intHowManyWorkers + " workers needed!");
btnGetMessageInfo.Enabled = false;
backgroundWorker2.RunWorkerAsync();
BGWs[0].RunWorkerAsync();
}
class WriteMail
{
public static void WriteAndParseMail()
{
int intEmail;
bool bolSrvMsg;
string txtLogString = null;
TcpClient tcpClient = new TcpClient();
if (percentComplete <= highestPercentageReached)
{
if (percentComplete == 0)
{
percentComplete = 1;
}
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
}
I know I'm probably missing something obvious but still new to backgroundWorkers in C# and not seeing many examples for doing this from a class. When I do it from the main form with a different backgroundWorker it works fine, but when I changed it to this "type" it didn't like the function on the same class so I had to create a new one (which I wanted to do anyways).
Thanks in advance!!!!
|
|
|
|
|
MacRaider4 wrote: I'm probably missing something obvious
For some obscure reason there is a BackgroundWorker.WorkerReportsProgress property, and in good Microsoft tradition its default value is the least useful one.
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
I thought the only two choices for that are true and false? which I have in :
public BGW()
{
this.WorkerReportsProgress = true;
this.WorkerSupportsCancellation = true;
}
is this where I get a lot of snickering behind my back
|
|
|
|
|
Sorry, I initially didn't read all your code, I just reported the most likely oversight.
I looked through your code now, and I would say:
1. your ReportProgress method should get called, however it is empty. What makes you say it doesn't work?
2. your Mail stuff is static, having multiple BGW's active in WriteAndParseMail() is not safe, it requires data synchronization.
3. I'm not sure you can have multiple TcpClient instances operating concurrently like that.
4. your percentage stuff is incomplete, so I can't be sure it is correct.
5. for overall progress, I often use a timer (Windows.Forms.Timer) which just gathers the number and updates the GUI at fixed intervals, say once a second. That turns out to be easier than having to check for actual percentage changes (which is however more economical).
Luc Pattyn [Forum Guidelines] [My Articles] Nil Volentibus Arduum
Please use <PRE> tags for code snippets, they preserve indentation, improve readability, and make me actually look at the code.
|
|
|
|
|
When I use the hard coded version I have:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbWrite.Value = e.ProgressPercentage;
lblCurrentCount.Text = e.UserState.ToString();
}
Of which does work (when it's not calling from the class), sorry I should have included that.
What makes me say it doesn't work is when I uncomment that section of code out I get a error saying it's not in the current context.
I have had 2 TcpClients running at the same time, so that doesn't seem to be a issue.
Is this what you mean by percentage stuff being incomplete?
percentComplete = (int)((float)currentMail / (float)intEmail * 100);
if (percentComplete <= highestPercentageReached)
{
if (percentComplete == 0)
{
percentComplete = 1;
}
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
BGWs[0].ReportProgress(percentComplete, strCounter);
}
|
|
|
|
|
Easy... Your BGWs are defined in Form1, while you're trying to access them from within WriteMail.
I would suggest adding a "sender" parameter to WriteAndParseMail(), so you can pass the worker into it.
protected override void OnDoWork(DoWorkEventArgs e)
{
base.OnDoWork(e);
WriteMail.WriteAndParseMail(this);
}
And in the worker thread...
highestPercentageReached = percentComplete + 1;
string strCounter = currentMail + " of " + intEmail;
(sender as BackgroundWorker).ReportProgress(percentComplete, strCounter);
|
|
|
|
|
I tried that but no luck, though I'm sure it's me. This was so much easier in VB...
|
|
|
|
|
Oh, ya know what... I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork... Technically, your routine is running AFTER DoWork completes... I really don't think it's intended to be run that way.
The standard way to use a BackgroundWorker is to hook the DoWork EVENT, and put your code there. No need to subclass it, unless you really want to centralize those two boolean property settings.
Just guessing here, of course... I've never seen the BW subclassed... Never had any need to do that.
|
|
|
|
|
Ok I've been doing some more reading, would this be better using a "thread" vs a BGW? Now granted I don't need to put this in a seperate class, however for what ever reason when I use BGWs[0].RunWorkerAsync(); vs backgroundWorker1.RunWorkerAsync(); I'm not able to have the "function" in the same class (the Form) thus why I moved it to a seperate class. Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference? Where I work there are two of us, and he does all the C++ stuff and I do the VB (6+ years, though .net in only the last year) and just starting with C# (about 6 months ago).
I have been able to subclass a BGW in VB but obviously not in C#, yet again if I don't have to not a big deal.
Basically what I need to do is be able to run the same function up to 3 times concurrently. I believe if my memory serves me correctally each "occurance" of the function will have it's own local variables. I also believe that if I use a thread vs a bgw I can lock what I'm writing to and instead of throwing a error, the threads will wait until the current one is done updating. Lets say it's just lblOutput.Text = e.UserState.ToString(); (as if it were a bgw perhaps in a thread you can use the variable directally)?
Does this help to explain what I'm trying to accomplish and maybe give you all a better idea of what path I should be taking?
|
|
|
|
|
MacRaider4 wrote: (as if it were a bgw perhaps in a thread you can use the variable directally)?
Nope, that limitation is universal to all types of multithreading in WinForms (And WPF, for that matter). No touching the GUI controls except from the GUI thread.
MacRaider4 wrote: Plus when I was getting into more advanced someone reccomended keeping as little as you can on your main form and using classes for as much as possible. Is this a good habbit, bad habbit or just personal preference?
That's generally a good idea, except in really small tools when it would be complete overkill. The form is your connection to the GUI, and business logic goes elsewhere... In your particular situation, I think I would probably handle it something like this:
(Consider this C#ish pseudocode, as I don't have VS installed on my home machine at the moment)
public static void SendMessagesAsync(ProgressChangedHandler progressCallback, params SomeClass[] messages)
{
for (int idx = 0; idx < messages.Length; idx++)
{
BackgroundWorker wkr = new BackgroundWorker();
wkr.WorkerSupportsProgress = true;
wkr.ProgressChanged += progressCallback;
wkr.DoWorkEventArgs += wkr_DoWork;
wkr.RunWorkerAsync(messages[idx]);
}
}
private static void wkr_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker wkr = sender as BackgroundWorker;
SomeClass args = e.Argument as SomeClass;
}
private void SomethingCalledFromYourForm()
{
MyStaticClass.SendMessagesAsync(wkr_ProgressChanged, new SomeClass[] {
new SomeClass() { Whatever = parameters, YouWant = toset },
new SomeClass() { Subject = someone, Body = "something else?", Recipient = "someone@somewhere.sometime" }
});
}
I don't know what you need to pass to the WriteMail function, but that's generally how you get information into a background worker... Make a class to hold it, and pass it as the argument to RunWorkerAsync().
|
|
|
|
|
Ian Shlasko wrote: I wonder if the BackgroundWorker only allows ReportProgress to be called during the DoWork
No, internally it uses a SendOrPostCallback delegate and an AsyncOperation . If the AsyncOperation is null (there is no active worker thread) then the delegate is called directly. If it's not null then the AsyncOperation 's Post method is called with the delegate and args as parameters which automatically invokes it on the syncronization context that RunWorkerAsync existed in.
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
}
ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)
{
this.asyncOperation.Post(this.progressReporter, arg);
}
else
{
this.progressReporter(arg);
}
}
|
|
|
|
|
Hey guys,
is it possible to load a .jpg on a form and make it transparently to sketch it in paint or somewhere else?
The picture should always stay in the foreground, but I want interact to the window under it.
any suggestions?
|
|
|
|
|
How about...
- Set jpg as form BackgroundImage
- Set form Opacity to 50% (or whatever)
- Set FormBorderStyle to None (but you will have to handle Move another way)
- Set form TopMost to true
Illogical thoughts make me ill
|
|
|
|
|
yeah, thank you !
But 1 Problem remain...
How could I draw under it without set the focus to the Form?
|
|
|
|
|