MP3 played in separate thread - c#

One problem was solved, another followed: In a C#-program I use following method to set a labels color to green, then playing a mp3-file and finally setting the color back to black.
The problem is that the sound seems to be played in an extra thread, thus the time between the change of the two colors is too short (in fact, it should have the green color while the file is played).
private void playSound()
{
label1.ForeColor = Color.LimeGreen;
Application.DoEvents();
WMPLib.WindowsMediaPlayer wmp = new WMPLib.WindowsMediaPlayer();
wmp.URL = #"C:\examplesound.mp3"; // duration about 30s
wmp.controls.play();
label1.ForeColor = Color.Black;
}
Is there anything can be done to force the label to keep the green color whilst the mp3-file is played?

Don't set the colour back to black straight away as the playback is in another thread.
When the current track ends WMPLib sends out a PlayStateChange event.
So add a handler:
wmp.PlayStateChange += this.Player_PlayStateChange;
private void Player_PlayStateChange(int newState)
{
if ((WMPLib.WMPPlayState)newState == WMPLib.WMPPlayState.wmppsStopped)
{
label1.ForeColor = Color.Black;
}
}
The page for playState has a list of values:
8 - MediaEnded - Media item has completed playback.
You'll need to make sure this is done on the UI thread.

Try hooking the PlayStateChanged event and put the label1.ForeColor = Color.Black; in there.
At the moment there's nothing in your code saying that it should only change to black when it finishes, only after it has started to play.

Related

Paint refresh code flickering when refreshed

my code for my game had a paint system where is draws then redraws a car (or a box) every 50 ticks. i have really bad flickering when running the code (even when not moving) anything else you need i can grab :)
i have tried:
-Double buffering
-Putting everything into a panel and refreshing that
//My Paint Code
Bitmap greenCar = new
e.Graphics.FillRectangle(car.getDrawingbrushColor(),
car.getCarposition());
e.Graphics.DrawImage(greenCar,car2.getCarposition());
}
my timer code
scoreTimer.Tick += new EventHandler(Tick2);
scoreTimer.Interval = 100;
scoreTimer.Start();
DoubleBuffered = true;
My Refresh Code
{
this.Counter.Text = Scorecounter.ToString();
Scorecounter += 1;
panel1.Refresh();
}
i just dont want it to flicker, i is really holding me back.

How to create a smooth animated text marquee?

I know that there are lot of different threads about horizontal text animation/text scrolling, but unfortunately none of them give smooth scrolling with repeatable text. I have tried double/thickness animation using various WPF controls containing text. Also tried animating with visual brush which gives me by far the most elegant scrolling compared to other approaches (for e.g. playing with Canvas.Left property etc.) but that too goes blur the text, if the text length or the animation speed is too high.
I'm over to a pure DirectX C# implementation using SharpDX library. Should also mention that I'm a beginner with DirectX programming. Here is the code:
public void RunMethod()
{
// Make window active and hide mouse cursor.
window.PointerCursor = null;
window.Activate();
var str = "This is an example of a moving TextLayout object with no snapped pixel boundaries.";
// Infinite loop to prevent the application from exiting.
while (true)
{
// Dispatch all pending events in the queue.
window.Dispatcher.ProcessEvents(CoreProcessEventsOption.ProcessAllIfPresent);
// Quit if the users presses Escape key.
if (window.GetAsyncKeyState(VirtualKey.Escape) == CoreVirtualKeyStates.Down)
{
return;
}
// Set the Direct2D drawing target.
d2dContext.Target = d2dTarget;
// Clear the target.
d2dContext.BeginDraw();
d2dContext.Clear(Color.CornflowerBlue);
//float layoutXOffset = 0;
float layoutXOffset = layoutX;
// Create the DirectWrite factory objet.
SharpDX.DirectWrite.Factory fontFactory = new SharpDX.DirectWrite.Factory();
// Create a TextFormat object that will use the Segoe UI font with a size of 24 DIPs.
textFormat = new TextFormat(fontFactory, "Verdana", 100.0f);
textLayout2 = new TextLayout(fontFactory, str, textFormat, 2000.0f, 100.0f);
// Draw moving text without pixel snapping, thus giving a smoother movement.
// d2dContext.FillRectangle(new RectangleF(layoutXOffset, 1000, 1000, 100), backgroundBrush);
d2dContext.DrawTextLayout(new Vector2(layoutXOffset, 0), textLayout2, textBrush, DrawTextOptions.NoSnap);
d2dContext.EndDraw();
//var character = str.Substring(0, 1);
//str = str.Remove(0, 1);
//str += character;
layoutX -= 3.0f;
if (layoutX <= -1000)
{
layoutX = 0;
}
// Present the current buffer to the screen.
swapChain.Present(1, PresentFlags.None);
}
}
Basically it creates an endless loop and subtracts the horizontal offset. Here are the challenges: I need repeatable text similar to HTML marquee without any gaps, Would probably need to extend it to multiple monitors.
Please suggest.
I don't know neither how to use DirectX nor sharpdx, but if you want you can consider this solution
I had a similar problem a while ago, but with the text inside a combobox. After a bounty i got what i was looking for. I'm posting the relevant piece of code as an example, but you can check the complete answer here
Basically, whenever you have a textblock/textbox that contain a string that cannot be displayed completely, cause the length exceed the textblock/box lenght you can use this kind of approach. You can define a custom usercontrol derived from the base you need (e.g. SlidingComboBox : Combobox) and define an animation for you storyboard like the following
_animation = new DoubleAnimation()
{
From = 0,
RepeatBehavior = SlideForever ? RepeatBehavior.Forever : new RepeatBehavior(1), //repeat only if slide-forever is true
AutoReverse = SlideForever
};
In my example i wanted this behaviour to be active only when the mouse was on the combobox, so in my custom OnMouse enter i had this piece of code
if (_parent.ActualWidth < textBlock.ActualWidth)
{
_animation.Duration = TimeSpan.FromMilliseconds(((int)textBlock.Text?.Length * 100));
_animation.To = _parent.ActualWidth - textBlock.ActualWidth;
_storyBoard.Begin(textBlock);
}
Where _parent represent the container of the selected item. After a check on the text lenght vs combobox lenght i start the animation and end it at the end of the text to be displayed
Note that in the question i mentioned there are also other soltions. I'm posting the one that worked for me

How to resume picturebox image's gif winforms C#

When a specific condition is met I disable my picturebox and the image freeze's, whenever it runs again the gif starts all over again not from where it was stopped is there anyway to resume it ?
Stopping picturebox : http://prntscr.com/b6gg7a
"Resuming" picturebox : http://prntscr.com/b6gglb
I believe indeed this is a rather tedious/unlikely to perform with an animated gif.
What you could/should do IMO is the following:
Instead of making the background a gif, break it into multiple frames(pictures).
Set up a timer that triggers the background to change to the next image.
You can pause and resume that timer the same as you would enable and disable the picturebox.
Your background change function could look something like this:
int bgCount = 8; //Amount of frames your background uses
int bgCurrent = 0;
string[] bgImg = {
"bg1.png",
"bg2.png",
"bg3.png",
"bg4.png",
"bg5.png",
"bg6.png",
"bg7.png",
"bg8.png",
};
void changeBG()
{
if(bgCurrent > bgCount-1) //If not displaying the last background image go to the next one
{
bgCurrent++;
imgBox.image = bgImg[bgCurrent];
}else //If displaying the last background image -> Start with the first one
{
bgCurrent = 0;
imgBox.image = bgImg[bgCurrent];
}
}
}
And you just let the timer trigger this event. And you simply start and stop the timer.
Sidenote:
1* You might experiment with, instead of changing the source, just make 8 imageboxed and call/display them as they are needed.

Problems with Opacity

I'm trying to draw a semi-transparent background and then opaque elements on top of it.
How come I can't do something like this?
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
this.Opacity = 0.5;
pe.Graphics.FillRectangle(trans_black_brush, square_rect_big);
this.Opacity = 1;
pe.Graphics.FillRectangle(solid_red_brush, square_rect);
}
I'd appreciate if someone with better understanding of Form drawing could tell me why this doesn't work :)
Update:
The solution has 3 forms:
1) Main (program, buttons etc)
2) Semi-transparent background (screen size, using opacity)
3) Transparent background but solid brushes on top.
In Form2's constructor, I have this:
Foreground = new FormForeground(this);
and in Form3's constructor I have this:
private Form_FormBackground m_Parent;
public FormForeground(FormBackground parent)
{
InitializeComponent();
FormBackground m_Parent = parent;
...
}
Whenever the mouse is clicked and used to draw with in form 3,
I update the parent's rectangle like so:
private void _UpdateParent()
{
m_Parent.s_DrawArea = m_DrawArea;
m_Parent.Invalidate();
}
The parent, form 2 then does its OnPaint() where it draws the marked area.
It does work, however the drawing does lag a bit compared to drawing directly in form3 (which does not produce the desired results because the drawn area needs to be transparent across the forms).
This doesn't work because Opacity is a Property of the Form and will always make the whole form and all its content have the current Value. It is perfect for fading a form in or out, though..
You can't achieve what you want with only one form.
Instead you will need two sychronized forms.
One can be somewhat opaque and will let the desktop shine through; the other must be transparent by making use of the TransparencyKey property and you can draw onto it..
To synchronize the two forms code the Move and the ResizeEnd events.
For a first setup use code like this:
A dummy form to create the semi-transparent look:
Form form0 = new Form() { Opacity = 0.33f , BackColor = Color.Black};
In the Form1's Load event:
TransparencyKey = Color.FromArgb(255, 147, 151, 162);
BackColor = TransparencyKey;
DoubleBuffered = true;
form0.Enabled = false;
form0.BringToFront();
form0.Show();
form0.Size = Size;
form0.Location = Location;
BringToFront();
And in the Move and the ResizeEnd events maybe code like this:
private void Form1_Move(object sender, EventArgs e)
{
form0.Size = Size;
form0.Location = Location;
}
private void Form1_ResizeEnd(object sender, EventArgs e)
{
form0.Size = Size;
form0.Location = Location;
}
You also may want to study this post that also shows a way to sandwich two forms.
Note that I picked a rather random color instead of the more common named color Fuchsia or any named colors. This is because I
Don't want to accidentally use it in the drawing, thius breaking making wrong spots transparent, but also
Don't want to make the form transparent for mouse actions, aka 'click-through'. This happens when using Fuchsia (and possibly some other colors) for some weird legacy reasons..

PictureBox double buffer keypress lag?

Sometimes, If I hold down for example the left arrow key, the pictureBox will stop drawing the coordinates of my character. A prime example is the yellow dot encircled in red. By holding down the left arrow key, the pictureBox did not constantly draw my characters location, until I hit another key/let go of the arrow key, then the picturebox started updating again (as you can see encircled in purple).
Also, another error I am experiencing is the fact that it should only be drawing one yellow dot and one 'blue whirlpool' per location; while it is drawing multiple (as you can see).
The code below is what I am using to replicate the picture results.
private Thread grabValues;
private Bitmap bm;
private Image image1;
private Image image2;
private Graphics gM;
void RGraphics()
{
image1 = Properties.Resources.image1;
image2 = Properties.Resources.image2;
}
void RInvokeMe()
{
while (true)
{
if (this.InvokeRequired)
{
this.Invoke(new UpdateControlsDelegate(MUpdate));
this.Invoke(new UpdateControlsDelegate(CUpdate));
}
else
{
MUpdate();
CUpdate();
}
}
}
void MUpdate()
{
pictureBox1.ImageLocation = "http://urlexample/r/we.png";
}
void CUpdate()
{
bm = new Bitmap(pictureBox1.Image);
gM = Graphics.FromImage(bm);
gM.CompositingMode = CompositingMode.SourceOver;
MDraw.DrawBlueDot(gM, image2, X2, Y2);
MDraw.DrawYellowDot(gM, image1, X1, Y1);
pictureBox1.Image = bm;
pictureBox1.Refresh();
}
public Form2()
{
InitializeComponent();
RGraphics();
grabValues = new Thread(new ThreadStart(RInvokeMe));
grabValues.IsBackground = true;
grabValues.Start();
}
Basically you start a thread (grabValues, implemented by function RInvokeMe) which endlessly invoke on your form methods MUpdate and CUpdate. When you invoke a method then you post a message on the windows message queue in order to let the window execute the given function. But when you press a key even the keypress message is posted to the windows queue... but maybe it is not served quickly because of the tons of invoke requests you are already executing on the form.
You could try to introduce a small delay (Thread.Sleep(500);) in your RInvokeMe loop just to see if the problem is there...
Then if so, I would consider to completely re-design your project in order to avoid such a scenario (if your characters are only moved only when pressing keys, why you simply do not redraw the scene only upon keypress but you do it continuously?)

Categories