I have been making a game for my games development class but due to the limitations in college we have to create a game using Visual Basic and no plugins, so I only have GDI+ to work with.
I have run into an error where it will run out of memory and the game stops running, the error is at line 312 - "_backBufferGr.DrawImage(_backbuffer, 0, 0, _resWidth, _resHeight)"
I think it may be due to the images that are being spawned aren't being cleared but I'm not sure as I have only been coding for about 3 months. If anyone can help that would be very appreciated. I have attached the code below - the classes are in separate files in my project.
Here is an image of my error:
https://imgur.com/WEAwSb4
and as text:
System.OutOfMemory: Out of memory.
at System.Drawing.Graphics.CheckErrorStatus(Int32 Status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y, Int32 Width, Int32 height)
at SpaceInvaders.Spaceinvaders.DrawGraphics()
Imports System.Drawing.Imaging
Imports System.IO
Public Class Spaceinvaders
'Star Generation Variables
ReadOnly _random As New Random
Private ReadOnly _r As New Random
Private ReadOnly _stars As New List(Of Point)
'Sound Variabless
Public Shared Intsound As Integer = 0
Public Shared Snd As New Sounds
'Graphics varibles
Dim _backbuffer As Bitmap
Dim _backBufferGr As Graphics
Public Shared Gr As Graphics
Shared _sourceRec As Rectangle
'View Port Variables
Dim _resWidth As Int16 = 700
Dim _resHeight As Int16 = 650
Dim _paused As Boolean = False
Dim _pauseNum As Int16 = 0
Dim _pausedText As Int16 = 40
Dim _mouseX, _mouseY As Int16
'Key Detection
Public Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Int16) As Int16
Public Function GetKeyState(ByVal key1 As Int16) As Boolean
Dim s As Int16
s = GetAsyncKeyState(key1)
If s = 0 Then Return False
Return True
End Function
'Character Variables
Dim _bmpPlayer As Bitmap
Public Shared PlayerW, PlayerH As Int16
Public Shared XPos As Int16 = 0
Public Shared YPos As Int16 = 0
Dim _movementSpeed As Int16 = 8
Dim _moveDir As Int16 = 0
Dim _lastDir As Int16 = 0
'Fire Variables
Dim _fire As Boolean
Dim _bulletArray(100000) As Bullet
Dim _bulletNum As Int16 = 0
Dim _cooldown As Int16
Public Shared Points As Int32 = 0
Public Shared EnemiesKilled As Int16 = 0
Public Shared ExploArray(100000) As Explo
Public Shared ExploNum As Int16 = 0
'Enemy Variables
Dim _spawnNum As Int16
Public Shared EnemyArray(100000) As Enemies
Public Shared EnemyNum As Int16 = 0
Public Shared Lives As Int16 = 3
Dim SpawnSpd As Int16 = 30
'Other Variables
Dim _isRunning As Boolean = True
Public Shared CollitionDetc As New StreamWriter(Application.StartupPath() & "\" & "Detection" & ".Log")
Public Function FadeInImage(ByVal bmp As Bitmap, ByVal opacity As Single) As Bitmap
Dim bmp2 As New Bitmap(bmp.Width, bmp.Height, PixelFormat.Format32bppArgb)
opacity = Math.Max(0, Math.Min(opacity, 1.0F))
Using ia As New ImageAttributes
Dim cm As New ColorMatrix
cm.Matrix33 = opacity
ia.SetColorMatrix(cm)
Dim destpoints() As PointF = {New Point(0, 0), New Point(bmp.Width, 0), New Point(0, bmp.Height)}
Using g As Graphics = Graphics.FromImage(bmp2)
g.DrawImage(bmp, destpoints, New RectangleF(Point.Empty, bmp.Size), GraphicsUnit.Pixel, ia)
End Using
End Using
Return bmp2
End Function
'Form Events
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Show()
Focus()
'Create Stars - 300 is the Number of Stars
CreateStarField(300)
'Start Music
Intsound += 1
With Snd
.Name = "Sound" & Intsound
.PlaySound(1, True)
End With
'This creates the graphics and the backbuffer, along with drawing the player to the screen
Gr = CreateGraphics()
_backbuffer = New Bitmap(_resWidth, _resHeight)
_bmpPlayer = New Bitmap(My.Resources.Ship)
XPos = (Width / 2)
YPos = 500
Gr.DrawImage(_bmpPlayer, XPos, YPos, _sourceRec, GraphicsUnit.Pixel)
StartGameLoop()
End Sub
Private Sub Spaceinvaders_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
Do Until Intsound = 0
Snd.Kill("Sound" & Intsound)
Intsound -= 1
Loop
Dispose()
End
End Sub
Private Sub Spaceinvaders_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove
_mouseX = e.X
_mouseY = e.Y
End Sub
Private Sub Spaceinvaders_MouseDown(sender As Object, e As MouseEventArgs) Handles MyBase.MouseDown
If (290 + 110 < _mouseX Or _mouseX < 290 Or 336 + 63 < _mouseY Or _mouseY < 336) Then
Else
_paused = False
End If
End Sub
'Starfield Background Generation
Private Sub CreateStarField(numStars As Integer)
_stars.Clear()
For i = 1 To numStars
_stars.Add(New Point(_r.Next(0, Width), _r.Next(0, Height)))
Next
End Sub
'Runs the actual game over and over again until it is stopped
Sub StartGameLoop()
Do While _isRunning = True
Application.DoEvents()
LivesCheck()
SetMoveDir()
MovePlayer(_moveDir)
'Start the drawing events & FPS Counter
DrawGraphics()
Loop
Do While _isRunning = False
Application.DoEvents()
Loop
End Sub
'Subs to do with player creation and movement
Private Sub GetPlayer(ByVal dir As Int16)
Select Case dir
Case 1 'Upfacing Direction
_bmpPlayer = New Bitmap(My.Resources.Ship)
_sourceRec = New Rectangle(0, 0, 85, 50)
PlayerH = 50
PlayerW = 85
Case 2 'Downfacing Direction
_bmpPlayer = New Bitmap(My.Resources.Ship)
_sourceRec = New Rectangle(0, 0, 85, 50)
PlayerH = 50
PlayerW = 85
Case 3 'Left Facing Direction
_bmpPlayer = New Bitmap(My.Resources.ShipLeft)
_sourceRec = New Rectangle(0, 0, 96, 76)
PlayerH = 76
PlayerW = 96
Case 4 'Right Facing Direction
_bmpPlayer = New Bitmap(My.Resources.ShipRight)
_sourceRec = New Rectangle(0, 0, 96, 76)
PlayerH = 76
PlayerW = 96
End Select
End Sub
Sub SetMoveDir()
If GetKeyState(Keys.W) = True Then _moveDir = 1
If GetKeyState(Keys.A) = True Then _moveDir = 3
If GetKeyState(Keys.S) = True Then _moveDir = 2
If GetKeyState(Keys.D) = True Then _moveDir = 4
If GetKeyState(Keys.Space) = True Then _fire = True
If GetKeyState(Keys.P) = True Then
If _pauseNum = 0 Then
_paused = True
_pauseNum = 1
ElseIf _pauseNum = 1 Then
_paused = False
_pauseNum = 0
End If
End If
If GetKeyState(Keys.W) = False And
GetKeyState(Keys.A) = False And
GetKeyState(Keys.S) = False And
GetKeyState(Keys.D) = False Then
_moveDir = 0
End If
If _moveDir <> 0 Then _lastDir = _moveDir
End Sub
Private Sub MovePlayer(ByVal dir As Int16)
Select Case dir
Case 1
If YPos <= 0 Then
Else
YPos -= _movementSpeed
End If
Case 2
If YPos >= 544 Then
Else
YPos += _movementSpeed
End If
Case 3
If XPos <= -2 Then
Else
XPos -= _movementSpeed
End If
Case 4
If XPos >= 606 Then
Else
XPos += _movementSpeed
End If
End Select
End Sub
'Draw the stuff to the screen
Sub DrawGraphics()
If _paused = True Then
Gr.DrawString("Paused", New Font("Verdana", _pausedText), New SolidBrush(Color.White), New Point(235, 256))
If (290 + 110 < _mouseX Or _mouseX < 290 Or 336 + 63 < _mouseY Or _mouseY < 336) Then
Gr.DrawString("Play", New Font("Verdana", 25), New SolidBrush(Color.White), New Point(290, 336))
Else
Gr.DrawString("Play", New Font("Verdana", 25), New SolidBrush(Color.Red), New Point(290, 336))
End If
'Copy BackBuffer To Graphics Object
Gr = Graphics.FromImage(_backbuffer)
'Draw BackBuffer to the screen
Try
_backBufferGr = CreateGraphics()
_backBufferGr.DrawImage(_backbuffer, 0, 0, _resWidth, _resHeight)
Catch ex As Exception
MsgBox(ex)
_isRunning = False
Exit Sub
End Try
Gr.Clear(Color.Black)
'Runs when the game is unpaused
ElseIf _paused = False Then
Gr.Clear(Color.Black)
'Draws Stars to the screen
DrawStars()
'Draws Enemies to the screen
EnemyDraw()
'Draws bullets to the screen
BulletDraw()
'Draws Explosions
Expslostion()
'Draw the player
DrawPlayer()
'Draws lives to the screen
DrawHUD()
'Copy BackBuffer To Graphics Object
Gr = Graphics.FromImage(_backbuffer)
'Draw BackBuffer to the screen
Try
_backBufferGr = CreateGraphics()
_backBufferGr.DrawImage(_backbuffer, 0, 0, _resWidth, _resHeight)
Catch ex As Exception
MsgBox("ERROR: " & vbCrLf & ex.ToString)
_isRunning = False
Exit Sub
End Try
Gr.Clear(Color.Black)
GC.Collect()
_fire = False
WriteLog()
End If
End Sub
Sub BulletDraw()
If _bulletNum = 0 Then
_bulletNum = 0
Else
For i = 1 To _bulletNum
_bulletArray(i).Move(i)
Next
End If
If _cooldown < 2 Then
_cooldown += 1
Else : If _fire = True Then
_bulletNum += 1
_bulletArray(_bulletNum) = New Bullet
_bulletArray(_bulletNum).Spawn(_bulletNum, 4)
_cooldown = 0
End If : End If
End Sub
Sub EnemyDraw()
If EnemyNum = 0 Then
EnemyNum = 0
Else
For i = 1 To EnemyNum
EnemyArray(i).Move()
Next
End If
If _spawnNum < SpawnSpd Then
_spawnNum += 1
Else
Points += 5
EnemyNum += 1
EnemyArray(EnemyNum) = New Enemies
EnemyArray(EnemyNum).Spawn()
_spawnNum = 0
End If
End Sub
Sub Expslostion()
If ExploNum = 0 Then
ExploNum = 0
Else
For i = 1 To ExploNum
ExploArray(i).Animation()
Next
End If
End Sub
Sub DrawStars()
For Each pt As Point In _stars 'Loops until all the stars are added to the form background
Dim num = _random.Next(1, 6) 'Randomly Picks a number
Dim numSize = _random.Next(1, 3)
If num = 1 Then 'Picks a colour based on the number picked
Gr.FillEllipse(Brushes.White, New Rectangle(pt, New Size(numSize, numSize)))
ElseIf num = 2 Then
Gr.FillEllipse(Brushes.Blue, New Rectangle(pt, New Size(numSize, numSize)))
ElseIf num = 3 Then
Gr.FillEllipse(Brushes.DimGray, New Rectangle(pt, New Size(numSize, numSize)))
ElseIf num = 4 Then
Gr.FillEllipse(Brushes.DarkOrange, New Rectangle(pt, New Size(numSize, numSize)))
ElseIf num = 5 Then
Gr.FillEllipse(Brushes.Red, New Rectangle(pt, New Size(numSize, numSize)))
End If
Next
End Sub
Sub DrawPlayer()
If _moveDir = 0 Then
_bmpPlayer = New Bitmap(My.Resources.Ship)
Else
GetPlayer(_lastDir)
End If
_bmpPlayer.MakeTransparent(Color.Fuchsia)
Gr.DrawImage(_bmpPlayer, XPos, YPos, _sourceRec, GraphicsUnit.Pixel)
End Sub
Sub DrawHUD()
Select Case Lives
Case 3
Gr.FillRectangle(Brushes.Red, 510, 5, 150, 10)
Case 2
Gr.FillRectangle(Brushes.Red, 510, 5, 100, 10)
Case 1
Gr.FillRectangle(Brushes.Red, 510, 5, 50, 10)
End Select
Gr.DrawString("Ships Destroyed: " & EnemiesKilled, New Font("Verdana", 10), New SolidBrush(Color.White), New Point(5, 5))
Gr.DrawString("Score: " & Points, New Font("Verdana", 10), New SolidBrush(Color.White), New Point(5, 20))
End Sub
Sub WriteLog()
Dim sw As New StreamWriter(Application.StartupPath() & "\" & "Variables" & ".Log")
sw.WriteLine("--------Variables Log--------")
sw.WriteLine("")
sw.WriteLine("Sounds Playing: " & Intsound)
sw.WriteLine("")
sw.WriteLine("Window Resolution: " & _resWidth & " " & _resHeight)
sw.WriteLine("Game Running: " & _isRunning)
sw.WriteLine("")
sw.WriteLine("Player Postion: " & XPos & " " & YPos)
sw.WriteLine("Player Size: " & PlayerW & " " & PlayerH)
sw.WriteLine("Player Movement Speed: " & _movementSpeed)
sw.WriteLine("PLayer Last Direction: " & _lastDir)
sw.WriteLine("")
sw.WriteLine("Bullets Being Fired?: " & _fire)
sw.WriteLine("Number Of bullets spawned: " & _bulletNum)
sw.WriteLine("")
sw.WriteLine("Number of enemies spawned: " & EnemyNum)
sw.WriteLine()
sw.WriteLine("--------Bullet Variables--------")
sw.WriteLine("Bullet Number X Y")
For i = 1 To _bulletNum
sw.WriteLine(i & " " & _bulletArray(i).X & " " & _bulletArray(i).Y)
Next
sw.WriteLine("--------Enemy Variables--------")
sw.WriteLine("Enemy Number X Y")
For i = 1 To EnemyNum
sw.WriteLine(i & " " & EnemyArray(i).X & " " & EnemyArray(i).Y)
Next
sw.Close()
sw.Dispose()
End Sub
Sub LivesCheck()
If Lives = 0 Then
_isRunning = False
Gameover.Show()
End If
End Sub
End Class
Public Class Bullet
Dim _bulletX, _bulletY As Int16
Dim _bmpBullet As Bitmap = My.Resources.bullet1
Dim _bulletRec As New Rectangle
Dim _bulletSpd As Int16 = 4
Dim _enemyNum As Int16
Dim _active As Boolean = True
Function X()
Return _bulletX
End Function
Function Y()
Return _bulletY
End Function
Sub Spawn(ByVal i As Int16, ByVal s As Int16)
Spaceinvaders.Intsound += 1
With Spaceinvaders.Snd
.Name = "Sound" & Spaceinvaders.Intsound
.PlaySound(2, False)
End With
_bulletSpd = s
_bulletX = Spaceinvaders.XPos + (Spaceinvaders.PlayerW / 2)
_bulletY = Spaceinvaders.YPos + (Spaceinvaders.PlayerH / 2)
_bmpBullet = My.Resources.bullet1
_bmpBullet.MakeTransparent(Color.Fuchsia)
Spaceinvaders.Gr.DrawImage(_bmpBullet, _bulletX, _bulletY, _bulletRec, GraphicsUnit.Pixel)
End Sub
Sub Move(ByVal bulletNum As Int16)
If _active = False Then
Me.Finalize()
Else
_enemyNum = Spaceinvaders.EnemyNum
For i = 1 To _enemyNum
Dim EnemyRect As Rectangle
EnemyRect = Spaceinvaders.EnemyArray(i).Rectangle
If (_bulletRec.IntersectsWith(EnemyRect)) Then
If Spaceinvaders.EnemyArray(i).Invc >= 40 Then
Spaceinvaders.EnemyArray(i).Kill(-10, -10)
_active = False
Spaceinvaders.Points += 500
Spaceinvaders.CollitionDetc.WriteLine("Enemy Num: " & i & " & " & "Bullet Num: " & bulletNum & " - HIT")
_bulletX = -100
_bulletY = -100
_bulletRec = New Rectangle(-100, 100, 1, 1)
Spaceinvaders.Intsound += 1
With Spaceinvaders.Snd
.Name = "Sound" & Spaceinvaders.Intsound
.PlaySound(3, False)
End With
Dim enemyX, enemyY As Int16
enemyX = Spaceinvaders.EnemyArray(i).X
enemyY = Spaceinvaders.EnemyArray(i).Y
Spaceinvaders.ExploNum += 1
Spaceinvaders.ExploArray(Spaceinvaders.ExploNum) = New Explo()
Spaceinvaders.ExploArray(Spaceinvaders.ExploNum).Spawn(enemyX, enemyY)
Me.Finalize()
End If
End If
Next
If _bulletY <= 0 Then
_bulletX = -100
_bulletY = -100
Else
_bulletY -= _bulletSpd
_bmpBullet.MakeTransparent(Color.Fuchsia)
Spaceinvaders.Gr.DrawImage(_bmpBullet, _bulletX, _bulletY)
_bulletRec = New Rectangle(_bulletX, _bulletY, 16, 16)
End If
End If
End Sub
End Class
Public Class Enemies
Dim _enemyX, _enemyY As Int16
Dim _bmpEnemy As Bitmap = My.Resources.InvaderSkullWhite
Dim _moveNum As Int16 = 0
Dim _active As Boolean = True
Dim _tempInvc As Int16 = 0
Dim EnemyRect As Rectangle
Function X()
Return _enemyX
End Function
Function Y()
Return _enemyY
End Function
Function Kill(ByVal x, ByVal y)
_enemyX = x
_enemyY = y
_active = False
EnemyRect = New Rectangle(x, y, 1, 1)
Me.Finalize()
End Function
Function Invc()
Return _tempInvc
End Function
Function Rectangle()
Return EnemyRect
End Function
Sub Spawn()
Dim rand As New Random
_enemyY = 3
_enemyX = rand.Next(10, 600)
Select Case rand.Next(1, 5)
Case 1
_bmpEnemy = My.Resources.InvaderSkullWhite
Case 2
_bmpEnemy = My.Resources.InvaderSkullRed
Case 3
_bmpEnemy = My.Resources.InvaderSkullGreen
Case 4
_bmpEnemy = My.Resources.InvaderSkullYellow
End Select
_bmpEnemy.MakeTransparent(Color.Fuchsia)
Spaceinvaders.Gr.DrawImage(_bmpEnemy, _enemyX, _enemyY)
Move()
End Sub
Sub Move()
If _active = False Then
Me.Finalize()
Else
If _tempInvc < 40 Then
_tempInvc += 1
End If
If _moveNum < 10 Then
_moveNum += 1
_bmpEnemy.MakeTransparent(Color.Fuchsia)
Spaceinvaders.Gr.DrawImage(_bmpEnemy, _enemyX, _enemyY)
Else
If _enemyY >= 700 Then
Spaceinvaders.Lives -= 1
_enemyX = -5
_enemyY = -5
Else
Dim randX As New Random
_enemyY += 5
Select Case _enemyX
Case _enemyX <= 5
_enemyX = _enemyX + randX.Next(1, 4)
Case _enemyX >= 600
_enemyX = _enemyX + randX.Next(-4, -1)
Case Else
_enemyX = _enemyX + randX.Next(-4, 4)
End Select
_bmpEnemy.MakeTransparent(Color.Fuchsia)
Spaceinvaders.Gr.DrawImage(_bmpEnemy, _enemyX, _enemyY)
EnemyRect = New Rectangle(_enemyX, _enemyY, 64, 64)
End If
_moveNum = 0
End If
End If
End Sub
End Class
Public Class Sounds
Public Declare Function MciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Integer, ByVal hwndCallback As Integer) As Integer
Dim _appPath As String = Application.StartupPath()
Private _oName As String = Nothing
Public Property Name As String
Set(value As String)
_oName = value
End Set
Get
Return _oName
End Get
End Property
Public Sub PlaySound(ByVal id As Integer, ByVal repeat As Boolean, Optional vol As Integer = 35)
If repeat = True Then
MciSendString("Open " & GetFile(id) & " alias " & _oName, 0, 0, 0)
MciSendString("Play " & _oName & " repeat", CStr(0), 0, 0)
Else
MciSendString("Open " & GetFile(id) & " alias " & _oName, CStr(0), 0, 0)
MciSendString("Play " & _oName, CStr(0), 0, 0)
End If
'Set Vol
MciSendString("Open " & GetFile(id) & " alias " & _oName, CStr(0), 0, 0)
MciSendString("setaudio " & _oName & " volume to " & vol, CStr(0), 0, 0)
End Sub
Private Function GetFile(ByVal id As Integer) As String
Dim path As String = ""
'Here is where you put the sound paths so that your game can play sounds
Select Case id
Case 0 'Menu Background Music
path = _appPath & "\Audio\Menu.mp3"
Case 1 'Ingame Background Music
path = _appPath & "\Audio\InGame.mp3"
Case 2 'Fire Spund
path = _appPath & "\Audio\Lazer.mp3"
Case 3 'Expolsion sound
path = _appPath & "\Audio\Expolsion.mp3"
End Select
path = Chr(34) & path & Chr(34)
Return path
End Function
Public Sub Kill(ByVal song As String)
MciSendString("close " & song, CStr(0), 0, 0)
_oName = Nothing
End Sub
End Class
Whenever you're done with an object that implements IDisposable in almost all cases you should probably call it (there are exceptions outside the scope of this answer). For note, A "using" statement always calls Dispose when it's done (so you're Graphics calls that are using a using are good on that front).
Where I see potential problems are the places you're using a class wide variable and resetting new Bitmap's onto it (I don't think the old one's get disposed and as a result I think they're hanging out there and slowly eating up your memory).
_bmpPlayer = New Bitmap(My.Resources.Ship)
See if something like this helps:
If _bmpPlayer IsNot Nothing Then
_bmpPlayer.Dispose()
End If
_bmpPlayer = new Bitmap(My.Resources.Ship)
That said, if you're using these same images over and over I would probably store them and re-use them as opposed to re-writing a new Bitmap from the resource every time.
Thanks to everyone trying to help me fix the problem. I fixed the out of memory error by changing this bit of code:
If _bulletNum = 0 Then
_bulletNum = 0
Else
For i = 1 To _bulletNum
_bulletArray(i).Move(i)
Next
End If
To this:
If _bulletNum = 0 Then
_bulletNum = 0
Else
Dim skipbullet As Int16
skipbullet = _bulletNum - 50
If skipbullet >= 1 Then
For i = skipbullet To _bulletNum
_bulletArray(i).Move(i)
Next
For i = 1 To skipbullet
_bulletArray(i).Kill()
Next
Else
For i = 1 To _bulletNum
_bulletArray(i).Move(i)
Next
End If
End If
Related
I am using the following code to print but every time I print to a laser printer, the right and bottom margins get cut off regardless of what I set my margins at. Could anyone shed some light on this situation? Note, I have tried using PrintDoc.OriginAtMargins = True/False but it doesn't appear to be working either.
/code/
Public MarginSize As Integer = 15
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'temp = Nothing
'With dgView
' For Each row In dgView.Rows
' temp += row.Cells(0).Value & " " & row.Cells(1).Value & " " & row.Cells(2).Value & " " & row.Cells(3).Value & vbNewLine
' Next
'End With
PrintDialog.PrinterSettings = PrintDoc.PrinterSettings
If PrintDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then
PrintDoc.PrinterSettings = PrintDialog.PrinterSettings
Dim PageSetup As New PageSettings
With PageSetup
.Margins.Left = MarginSize
.Margins.Right = MarginSize
.Margins.Top = MarginSize
.Margins.Bottom = MarginSize
.Landscape = False
End With
PrintDoc.DefaultPageSettings = PageSetup
End If
' PrintDoc.OriginAtMargins = False
PrintPreviewDialog.Document = PrintDoc
PrintPreviewDialog.WindowState = FormWindowState.Maximized
PrintPreviewDialog.PrintPreviewControl.Zoom = 1
PrintPreviewDialog.ShowDialog()
End Sub
Private Sub PrintDoc_PrintPage(sender As Object, e As Printing.PrintPageEventArgs) Handles PrintDoc.PrintPage
Static intStart As Integer
Dim fntText As Font = txtDrawnBy.Font
Dim txtHeight As Integer
Dim LeftMargin As Integer = PrintDoc.DefaultPageSettings.Margins.Left
Dim RightMargin As Integer = PrintDoc.DefaultPageSettings.PaperSize.Width - MarginSize
Dim TopMargin As Integer = PrintDoc.DefaultPageSettings.Margins.Top
Dim BottomMargin As Integer = PrintDoc.DefaultPageSettings.PaperSize.Height - MarginSize
txtHeight = PrintDoc.DefaultPageSettings.PaperSize.Height - PrintDoc.DefaultPageSettings.Margins.Top - PrintDoc.DefaultPageSettings.Margins.Bottom
Dim LinesPerPage As Integer = CInt(Math.Round(txtHeight / (fntText.Height + 0.025)))
'Draw Rectangle for Margin
e.Graphics.DrawRectangle(Pens.Red, e.MarginBounds)
Dim y1 As Integer = e.PageBounds.Height.ToString / 3
Dim y2 As Integer = e.PageBounds.Height.ToString / 3 * 2
'Draw line 1/4 way down
e.Graphics.DrawLine(Pens.Orange, LeftMargin, y1, RightMargin, y1)
'Draw line 3/4 way down
e.Graphics.DrawLine(Pens.Orange, LeftMargin, y2, RightMargin, y2)
Dim intLineNumber As Integer
Dim sf As New StringFormat
Dim LineStep As Integer = 0
For intCounter = intStart To 66
'Print line numbers
e.Graphics.DrawString(intLineNumber.ToString & ": ", fntText, Brushes.Black, LeftMargin, fntText.Height * intLineNumber + TopMargin)
intLineNumber += 1
If intLineNumber > LinesPerPage Then
intStart = intCounter
e.HasMorePages = True
Exit For
End If
Next
End Sub
I have also attached an image of my results.
Image of print results
Given a string, how do you generate all partitions of it (shown as smaller strings separated by commas)?
Also, what is the total number of partitions for a string of length n?
The following will give the result, but is not good on long strings.
String: CODE
C,O,D,E
C,O,DE
C,OD,E
C,ODE
CO,D,E
CO,DE
COD,E
String: PEACE
P,E,A,C,E
P,E,A,CE
P,E,AC,E
P,E,ACE
P,EA,C,E
P,EA,CE
P,EAC,E
PE,A,C,E
PE,A,CE
PE,AC,E
PE,ACE
PEA,C,E
PEA,CE
Sub getAllComb()
oriStr = TextBox1.Text
Dim tmp = ""
Dim k = 0
For i = 0 To oriStr.Length
For j = 1 To 3
'tmp = Mid(oriStr, i, j)
Try
tmp1(k) = oriStr.Substring(i, j)
k = k + 1
'tmp = oriStr.Substring(i, j)
'Debug.Print(tmp)
Catch ex As Exception
'Debug.Print("Error>>>>" + ex.Message)
Exit For
End Try
Next
Next
tmp = ""
For i = 0 To k
Debug.Print(i.ToString + "<i " + tmp1(i))
tmp = tmp & tmp1(i) & vbCrLf
Next
'MessageBox.Show(tmp)
Dim tmpAll1 = ""
tmpAll1 = addFunclen4(k)
MessageBox.Show(tmpAll1)
Debug.Print(tmpAll1)
TextBox1.Text = oriStr & vbCrLf & vbCrLf & tmpAll1
End Sub
Function addFunclen4(k As Integer) As String
Dim retVal = ""
Dim tmp = ""
Dim tmpAll = ""
Dim tmpStr = ""
Dim tmpAll1 = ""
For i = 0 To k
For i1 = 0 To k
For i2 = 0 To k
For i3 = 0 To k
For i4 = 0 To k
tmp = Form1.tmp1(i) + Form1.tmp1(i1) + Form1.tmp1(i2) + Form1.tmp1(i3) + Form1.tmp1(i4)
If Form1.tmp1(i) <> "" Then
If tmp = Form1.oriStr Then
tmpStr = Form1.tmp1(i) + "," + Form1.tmp1(i1) + "," + Form1.tmp1(i2) + "," + Form1.tmp1(i3) + "," + Form1.tmp1(i4)
Do While tmpStr.Contains(",,") = True
tmpStr = Replace(tmpStr, ",,", ",")
Loop
If Mid(tmpStr, tmpStr.Length, 1) = "," Then
tmpStr = Mid(tmpStr, 1, tmpStr.Length - 1)
End If
If tmpAll1.Contains(tmpStr) = False Then
tmpAll1 = tmpAll1 + tmpStr + vbCrLf
End If
End If
End If
Next
Next
Next
Next
Next
retVal = tmpAll1
Return retVal
End Function
I reckon [2^(n-1) - 1] in total:
(n-1) positions to put a comma, 2 "states" (comma or not comma), -1 for the trivial case with no commas.
A simpler algorithm would be to iterate through the number of cases and use the binary representation to determine whether to put a comma in each position.
For example (simple form with TextBox, Button and ListBox):
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
ListBox1.Items.Clear()
Dim s As String = TextBox1.Text
If s.Length < 2 Then
MessageBox.Show("Enter a longer string")
Return
End If
For i = 1 To Math.Pow(2, s.Length - 1) - 1
Dim result As String = s(0)
For j = 1 To s.Length - 1
result = result & CommaOrNot(i, j) & s(j)
Next
ListBox1.Items.Add(result)
Next
End Sub
Private Function CommaOrNot(i As Integer, j As Integer) As String
If (i And Math.Pow(2, j - 1)) = Math.Pow(2, j - 1) Then
Return ","
Else
Return ""
End If
End Function
I really liked Fruitbat's approach. Here's an alternate version using a slightly different mechanism for the representation of the binary number and how to determine if the comma should be included or not:
Public Class Form1
Private combinations As List(Of String)
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim s As String = TextBox1.Text
If s.Length < 2 Then
MessageBox.Show("Enter a longer string")
Exit Sub
End If
Button1.Enabled = False
ListBox1.DataSource = Nothing
ListBox1.Items.Clear()
ListBox1.Items.Add("Generating combinations...")
BackgroundWorker1.RunWorkerAsync(s)
End Sub
Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim s As String = e.Argument
Dim combinations As New List(Of String)
Dim binary() As Char
Dim values() As Char = s.ToCharArray
Dim max As Integer = Convert.ToInt32(New String("1", s.Length - 1), 2)
Dim sb As New System.Text.StringBuilder
For i As Integer = 0 To max
sb.Clear()
binary = Convert.ToString(i, 2).PadLeft(values.Length, "0").ToCharArray
For j As Integer = 0 To values.Length - 1
sb.Append(If(binary(j) = "0", "", ","))
sb.Append(values(j))
Next
combinations.Add(sb.ToString)
Next
e.Result = combinations
End Sub
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
combinations = e.Result
ListBox1.Items.Clear()
ListBox1.Items.Add("Generating combinations...Done!")
ListBox1.Items.Add("Adding Results...one moment please!")
Application.DoEvents()
ListBox1.DataSource = Nothing
ListBox1.DataSource = combinations
Button1.Enabled = True
MessageBox.Show("Done!")
End Sub
End Class
Recently, I've been doing this coursework for my college titled "SpellingBee project". This is where a student will take a test loaded from an Access 2010 database and output it on the form.
I have an algorithm which checks the accuracy of the spelling inputted by a student, and it will give either 2 points, 1 point or 0 points depending on the conditions. <-- This can be found in 'testword' private function.
The algorithm will be used in a procedure called 'btnMarkIt', and essentially it just takes all the answers and calculate the total score out of 20.
Here's the code:
Imports System.Data
Imports System.Data.OleDb
Public Class frmTakeTest
Dim TestConnection As New OleDbConnection
Dim DtasetTest As New DataSet
Dim DtaadpTest As New OleDbDataAdapter
Dim SqlCmdBldTest As New OleDbCommandBuilder(DtaadpTest)
Dim CurrentRowNo As Integer = -1
Dim MarkTest As Boolean = False
Dim TakeTestNow As Boolean = False
Dim ViewOnly As Boolean
Dim totalMarks As Integer
Private Sub frmTakeTest_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim DriveLetter As Char = Application.StartupPath.Substring(0, 1)
TestConnection.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & DriveLetter & ":\Coursework - Computing\prj_computer-course\Jamie - Coursework\CourseworkDB.accdb"
DtaadpTest.SelectCommand = New OleDbCommand
DtaadpTest.SelectCommand.Connection = TestConnection
DtaadpTest.SelectCommand.CommandText = "SELECT * FROM tbl_test"
DtaadpTest.Fill(DtasetTest, "tblTakeTest")
ViewOnly = True
Protect()
End Sub
Private Sub DigitsOnly(ByRef Character As Char)
'Validate character input: digit keys only
If Char.IsDigit(Character) = False And Char.IsControl(Character) = False Then
MessageBox.Show("Digits only.", "Validation Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
'Stop invalid character appearing in field
Character = Nothing
End If
End Sub
Private Sub DisplayAccount()
'Purpose: Display a test when the user adds or edit a new record.
If DtasetTest.Tables("tblTakeTest").Rows.Count > 0 Then
txtDef1.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def1").ToString
txtDef2.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def2").ToString
txtDef3.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def3").ToString
txtDef4.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def4").ToString
txtDef5.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def5").ToString
txtDef6.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def6").ToString
txtDef7.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def7").ToString
txtDef8.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def8").ToString
txtDef9.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def9").ToString
txtDef10.Text = DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Def10").ToString
End If
End Sub
Private Sub Protect()
'Purpose: To enable/disable screen objects depending on whether ViewOnly is true or false
If TakeTestNow = True Then
txtDef1.ReadOnly = True
txtDef2.ReadOnly = True
txtDef3.ReadOnly = True
txtDef4.ReadOnly = True
txtDef5.ReadOnly = True
txtDef6.ReadOnly = True
txtDef7.ReadOnly = True
txtDef8.ReadOnly = True
txtDef9.ReadOnly = True
txtDef10.ReadOnly = True
Else
txtDef1.ReadOnly = ViewOnly
txtDef2.ReadOnly = ViewOnly
txtDef3.ReadOnly = ViewOnly
txtDef4.ReadOnly = ViewOnly
txtDef5.ReadOnly = ViewOnly
txtDef6.ReadOnly = ViewOnly
txtDef7.ReadOnly = ViewOnly
txtDef8.ReadOnly = ViewOnly
txtDef9.ReadOnly = ViewOnly
txtDef10.ReadOnly = ViewOnly
End If
txtAns1.ReadOnly = ViewOnly
txtAns2.ReadOnly = ViewOnly
txtAns3.ReadOnly = ViewOnly
txtAns4.ReadOnly = ViewOnly
txtAns5.ReadOnly = ViewOnly
txtAns6.ReadOnly = ViewOnly
txtAns7.ReadOnly = ViewOnly
txtAns8.ReadOnly = ViewOnly
txtAns9.ReadOnly = ViewOnly
txtAns10.ReadOnly = ViewOnly
txtSearch.ReadOnly = Not ViewOnly
btnLoadTest.Enabled = ViewOnly
btnMarkIt.Enabled = Not ViewOnly
btnSubmit.Enabled = Not ViewOnly
btnPrintPreview.Enabled = Not ViewOnly
btnPrint.Enabled = Not ViewOnly
End Sub
Private Sub CloseToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CloseToolStripMenuItem.Click
Dim Result = MessageBox.Show("Are you sure you want to quite? Any information entered will not be saved.", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Warning)
If Result = Windows.Forms.DialogResult.Yes Then
Me.Close()
Else
'Do Nothing
End If
End Sub
Private Sub btnLoadTest_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnLoadTest.Click
'Purpose: Load an existing test from the database and output it to the user
If txtSearch.Text = Nothing Then
MessageBox.Show("Please enter an ID number.", "Search Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Else
If IsNumeric(txtSearch.Text) = True Then
DtaadpTest.SelectCommand.CommandText = "SELECT * FROM tbl_test WHERE TestID = " & txtSearch.Text
End If
DtasetTest.Tables("tblTakeTest").Clear()
DtaadpTest.Fill(DtasetTest, "tblTakeTest")
If DtasetTest.Tables("tblTakeTest").Rows.Count = 0 Then
MessageBox.Show("Test not found.", "Search Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
ViewOnly = True
Else
Dim Result = MessageBox.Show("Test found! Would you like to take it now?", "Question", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If Result = Windows.Forms.DialogResult.Yes Then
CurrentRowNo = 0
TakeTestNow = True
DisplayAccount()
ViewOnly = False
Protect()
Else
'Do Nothing
End If
End If
End If
End Sub
Private Sub txtSearch_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtSearch.KeyPress
DigitsOnly(e.KeyChar)
End Sub
Private Sub btnMarkIt_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnMarkIt.Click
Dim marks(9) As Integer
For i = 0 To 9
marks(i) = testWord(Controls("txtAns" & i + 1).Text, DtasetTest.Tables("tblTakeTest").Rows(0).Item("Ans" & i + 1))
Next i
Dim msgText As String = ""
For i = 0 To 9
msgText += DtasetTest.Tables("tblTakeTest").Rows(CurrentRowNo).Item("Ans" & i + 1) & " " & marks(i) & " points" & vbNewLine
Next
MsgBox(msgText)
totalMarks = CInt(marks(0) + marks(1) + marks(2) + marks(3) + marks(4) + marks(5) + marks(6) + marks(7) + marks(8) + marks(9))
txtTotalMark.Text = totalMarks
End Sub
Private Function testWord(ByVal inputWord As String, ByVal actualWord As String)
Dim lengthScore, accuracyScore, accuracyTally As Integer
inputWord = inputWord.ToLower
actualWord = actualWord.ToLower
'Length
If inputWord.Length = actualWord.Length Then
lengthScore = 2
ElseIf inputWord.Length = actualWord.Length + 1 Or inputWord.Length = actualWord.Length - 1 Then
lengthScore = 1
Else
lengthScore = 0
End If
'Accuracy
Dim inputArray() As Char = inputWord.ToCharArray
Dim actualArray() As Char = actualWord.ToCharArray
Dim found As Boolean
For i = 0 To inputArray.Length - 1
found = False
If actualArray.Length > i Then
If inputArray(i) = actualArray(i) Then
accuracyTally = accuracyTally + 2
found = True
End If
End If
If found = False And i > 0 And i <= (actualArray.Length) Then
If inputArray(i) = actualArray(i - 1) Then
accuracyTally = accuracyTally + 1
found = True
End If
End If
If found = False And i < (actualArray.Length - 1) Then
If inputArray(i) = actualArray(i + 1) Then
accuracyTally = accuracyTally + 1
found = True
End If
End If
Next i
'Add up
Dim accMax As Integer = inputWord.Length * 2
Dim accPerc As Integer
If accuracyTally > 0 Then
accPerc = CInt((accuracyTally / accMax) * 100)
Else
accPerc = 0
End If
If accPerc = 100 Then
accuracyScore = 2
ElseIf accPerc > 70 Then
accuracyScore = 1
Else
accuracyScore = 0
End If
If lengthScore = 2 And accuracyScore = 2 Then
Return 2
ElseIf lengthScore > 0 And accuracyScore > 0 Then
Return 1
Else
Return 0
End If
End Function
End Class
*Note: The error occurs in the 'btnMarkIt' procedure:
For i = 0 To 9
marks(i) = testWord(Controls("txtAns" & i + 1).Text, DtasetTest.Tables("tblTakeTest").Rows(0).Item("Ans" & i + 1))
Next i
Please try:
For i as Integer = 0 To 9
marks(i) = testWord(Controls("txtAns" & i + 1).Text, DtasetTest.Tables("tblTakeTest").Rows(0).Item("Ans" & i + 1))
Next i
I am printing records from an ODBC connection but I am not able to print more than the first page. The code I have below generates multiple copies of the same first page. How can I iterate through my records and still create page breaks when necessary?
Private Sub btnPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click
'MsgBox("Printing functionality is still under construction.", MsgBoxStyle.Information)
Dim RecordDoc As Drawing.Printing.PrintDocument
RecordDoc = New Drawing.Printing.PrintDocument
With RecordDoc.DefaultPageSettings
.Landscape = False
.Margins.Left = 50
.Margins.Right = 50
.Margins.Top = 50
.Margins.Bottom = 50
End With
RecordDoc.DocumentName = "Print Records"
AddHandler RecordDoc.PrintPage, AddressOf Me.printrecords
dlgPreview.Document = RecordDoc
dlgPreview.ShowDialog()
RecordDoc.Dispose()
End Sub
Private Sub PrintRecords(ByVal sender As Object, ByVal e As Drawing.Printing.PrintPageEventArgs)
Dim lvi As ListViewItem
Dim Conn As OdbcConnection
Dim Reader As OdbcDataReader
Dim strFname As String = ""
Dim strLname As String = ""
Dim strReportHdr As String = ""
Dim strMainCategory As String = ""
Dim strColumnHeader As String = ""
Dim intLinesPerPage As Integer = 0
Dim x, y As Single
Dim myfont As Font = New Font("Arial", 12, FontStyle.Regular)
Dim myPen As New Pen(Color.Black, 3)
'DEFINE THE REPORT HEADER BASED ON LISTVIEW SELECTIONS
'FIRST, GET THE COLUMN NUMBER SELECTED. YOU'LL USE THIS FOR THE REPORT HEADER AS WELL AS WHEN PRINTING DATA
Dim PrintColHeader As ColumnHeader = ListView1.Columns(intLViewColSort)
'REMOVE THE < OR > AS NECESSARY, IF PRESENT
If PrintColHeader.Text.StartsWith("> ") Then
strColumnHeader = Mid(PrintColHeader.Text, 3) & ", in descending order"
ElseIf PrintColHeader.Text.StartsWith("< ") Then
strColumnHeader = Mid(PrintColHeader.Text, 3) & ", in ascending order"
End If
If rdoCustInfo.Checked = True Then
strMainCategory = "Customer Information"
ElseIf rdoCustVendPrefs.Checked = True Then
strMainCategory = "Customer Vendor Preferences"
ElseIf rdoPricePts.Checked = True Then
strMainCategory = "Customer Price Points"
ElseIf rdoSalesHist.Checked = True Then
strMainCategory = "Customer Sales History"
ElseIf rdoSpecific.Checked = True Then
strMainCategory = "Other"
Else
MsgBox("System error with print function. Have a glass of wine.", MsgBoxStyle.Critical)
Exit Sub
End If
y = e.MarginBounds.Y
x = e.MarginBounds.X
e.Graphics.DrawRectangle(myPen, e.MarginBounds.X, e.MarginBounds.Y + 20, 500, 1)
e.Graphics.DrawString(strMainCategory & " - " & strColumnHeader, myfont, Brushes.Black, x, y)
y += CInt(2 * myfont.GetHeight(e.Graphics))
myfont = New Font("Arial", 10, FontStyle.Regular)
intLinesPerPage = e.MarginBounds.Height / myfont.GetHeight(e.Graphics)
'Open Connection TO ASC
Conn = New OdbcConnection(ConnString)
Conn.Open()
For Each lvi In ListView1.Items
'Execute Query
cmdString = "select lastname,firstname,street1,street2,city,state,zipcode,phonenum,emailaddress from customer where customernum=" & lvi.Text
Dim Cmd As New OdbcCommand(cmdString, Conn)
Reader = Cmd.ExecuteReader()
'Process The Result Set
While (Reader.Read())
Dim tempy As Integer
tempy = y
Dim CustName As String = Trim(Reader("firstname")) & " " & Trim(Reader("lastname"))
CustName = StrConv(CustName, VbStrConv.ProperCase)
e.Graphics.DrawString(CustName, myfont, Brushes.Black, e.MarginBounds.X, y)
y += CInt(myfont.GetHeight(e.Graphics))
Dim street As String = ""
If Trim(Reader("street2").ToString) <> "" And Not (IsDBNull(Reader("street2"))) Then
street += Trim(Reader("street1").ToString) & vbCrLf & Trim(Reader("street2"))
Else
street += Trim(Reader("street1").ToString)
End If
street = StrConv(street, VbStrConv.ProperCase)
e.Graphics.DrawString(street, myfont, Brushes.Black, e.MarginBounds.X, y)
y += CInt(myfont.GetHeight(e.Graphics))
Dim CityStateZip As String = ""
CityStateZip = StrConv(Trim(Reader("city")), VbStrConv.ProperCase) & ", " & Trim(Reader("state")) & ", " & Reader("zipcode")
e.Graphics.DrawString(CityStateZip, myfont, Brushes.Black, e.MarginBounds.X, y)
x += 200
e.Graphics.DrawString(Trim(Reader("phonenum")), myfont, Brushes.Black, x, tempy)
tempy += CInt(myfont.GetHeight(e.Graphics))
y += CInt(2 * myfont.GetHeight(e.Graphics))
e.Graphics.DrawString(Trim(Reader("emailaddress")), myfont, Brushes.Black, x, tempy)
x = e.MarginBounds.X
'If intPrintLineCount1 > intLinesPerPage Then
' e.HasMorePages = True
' intPrintLineCount1 = 0
'Else
' e.HasMorePages = False
'End If
If y + myfont.Height > e.MarginBounds.Bottom Then
e.HasMorePages = True
End If
'intPrintLineCount1 += 10
End While
Cmd.Dispose()
Next
Reader.Close()
Conn.Close()
Conn.Dispose()
End Sub
EDIT: Solved in self-answer below.
I've looked all over but I can't find anything useful for playing audio files with volume control.
I tried XNA; SLIMDX and "Microsoft.VisualBasic.Devices.Audio" but nothing helped.
The options I found which had volume control were too complex and I couldn't figure out how to use, and the method I have currently doesn't let you do anything more than play(background with or without loop, or pause execution until end of play) and stop.
Here's my current code:
Dim AD As New Microsoft.VisualBasic.Devices.Audio
Sub Play()
Dim af() As Byte = IO.File.ReadAllBytes("music.wav")
AD.Play(af, AudioPlayMode.BackgroundLoop)
End Sub
This loops "music.wav" in the background, but i cannot pause/seek it or control the volume. Is there any simple way(like the above) to play audio files from a buffer and control the audio volume? I've looked all over but nothing I've found works for my project.
System: Win7 64-bit
VS version: 2010
Language: VB.net
Oh one more thing, buffering the audio first is something I need for my solution as well(as you can see in my current code)
Does anyone have a solution to this? :)
I found the answer after a bit of searching around so here's the solution I found for my question:
Download Naudio and add the references to your project.
Then the following code is how to use it for loading audio from a buffer:
Dim Wave1 As New NAudio.Wave.WaveOut 'Wave out device for playing the sound
Dim xa() As Byte = IO.File.ReadAllBytes("C:\YourPath\YourWave.wav") 'Your Buffer
Sub PlaySound()
Dim data As New IO.MemoryStream(xa) 'Data stream for the buffer
Wave1.Init(New NAudio.Wave.BlockAlignReductionStream(NAudio.Wave.WaveFormatConversionStream.CreatePcmStream(New NAudio.Wave.WaveFileReader(data))))
Wave1.Volume = 0.1 'Sets the Volume to 10%
Wave1.Play()
End Sub
WaveFileReader can be changed to whichever reader you need(i.e. the MP3 one for ".mp3" files) to load your audio file, but the code works as-is for loading ".wav" files.
oh and also, don't forget to
WaveOut.Dispose()
when you're done to avoid errors.
I hope my research helps somebody :)
Have you tried using the Media Player control?
hey i hav make a class to handle wave(pcm) file hope this will help u.. its not completed yet but might be helpful.
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.ComponentModel
Public Structure WaveHeader
Public Chunk As Char()
Public ChunkSize As Int32
Public Format As Char()
Public SubChunk1 As Char()
Public SubChunk1Size As Int32
Public AudioFormat As Int16
Public Channels As Int16
Public SampleRate As Int32
Public ByteRate As Int32
Public BlockAlign As Int16
Public BitsPerSample As Int16
Public SubChunk2 As Char()
Public SubChunk2Size As Int32
End Structure
Public Enum State
None
Playing
Paused
Stopped
Finished
End Enum
Public Class wav
Private watch As New Stopwatch
Private WithEvents timer As New Timer
Private mystate As State = State.None
Private myheader As WaveHeader
Private myurl As String = Nothing
Private mytotaltime As Double = 0
Private Declare Function SetProcessWorkingSetSize Lib "kernel32.dll" (ByVal process As IntPtr, ByVal minimumWorkingSetSize As Integer, ByVal maximumWorkingSetSize As Integer) As Integer
Event OnPlayStateChange(ByVal e As State)
Public Shared Sub FlushMemory()
GC.Collect()
GC.WaitForPendingFinalizers()
If (Environment.OSVersion.Platform = PlatformID.Win32NT) Then
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1)
End If
End Sub
Sub New()
timer.Interval = 1
timer.Start()
End Sub
Function readheader(ByVal url As String)
Dim fh As WaveHeader
Dim stream As New FileStream(url, FileMode.Open)
Dim br As New BinaryReader(stream)
fh.Chunk = br.ReadChars(4)
fh.ChunkSize = br.ReadInt32
fh.Format = br.ReadChars(4)
fh.SubChunk1 = br.ReadChars(4)
fh.SubChunk1Size = br.ReadInt32
fh.AudioFormat = br.ReadInt16
fh.Channels = br.ReadInt16
fh.SampleRate = br.ReadInt32
fh.ByteRate = br.ReadInt32
fh.BlockAlign = br.ReadInt16
fh.BitsPerSample = br.ReadInt16
For i = 1 To fh.SubChunk1Size - 16
br.ReadByte()
Next
fh.SubChunk2 = br.ReadChars(4)
fh.SubChunk2Size = br.ReadInt32
br.Close()
stream.Close()
Return Header2String(fh)
End Function
Function Header2String(ByVal fh As WaveHeader)
Dim t As String = ""
t &= "Chunk " & fh.Chunk & Environment.NewLine
t &= "Chunksize " & fh.ChunkSize & Environment.NewLine
t &= "Format " & fh.Format & Environment.NewLine
t &= "subChunk1 " & fh.SubChunk1 & Environment.NewLine
t &= "subchunk1size " & fh.SubChunk1Size & Environment.NewLine
t &= "PCM " & fh.AudioFormat & Environment.NewLine
t &= "Channels " & fh.Channels & Environment.NewLine
t &= "Samplerate " & fh.SampleRate & Environment.NewLine
t &= "ByteRate " & fh.ByteRate & Environment.NewLine
t &= "Block Align " & fh.BlockAlign & Environment.NewLine
t &= "Bits/Sample " & fh.BitsPerSample & Environment.NewLine
t &= "subChunk2 " & fh.SubChunk2 & Environment.NewLine
t &= "subChunk2size " & fh.SubChunk2Size & Environment.NewLine
Return t
End Function
Function StopAudio()
My.Computer.Audio.Stop()
watch.Stop()
watch.Reset()
If PlayState = State.Playing Or PlayState = State.Paused Then
mystate = State.Stopped
End If
Return 0
End Function
Function playAudio(ByVal url As String)
If My.Computer.FileSystem.FileExists(url) Then
Try
My.Computer.Audio.Play(SongStream(url, 0), AudioPlayMode.Background)
'My.Computer.Audio.Play(fast(url, 0, CDbl(form1.TextBox4.Text)), AudioPlayMode.Background)
watch.Restart()
mystate = State.Playing
RaiseEvent OnPlayStateChange(State.Playing)
myurl = url
Catch ex As Exception
Throw New Exception("Error! Can't Play The File.")
'MsgBox(ex.Message)
End Try
Else
Throw New Exception("File Not Exist.")
End If
Return 0
End Function
Function PauseAudio()
If PlayState = State.Playing Then
My.Computer.Audio.Stop()
watch.Stop()
mystate = State.Paused
RaiseEvent OnPlayStateChange(State.Paused)
End If
Return 0
End Function
Function ResumeAudio()
If PlayState = State.Paused And IsNothing(URL) = False Then
Try
My.Computer.Audio.Play(SongStream(URL, time), AudioPlayMode.Background)
watch.Start()
mystate = State.Playing
RaiseEvent OnPlayStateChange(State.Playing)
Catch : End Try
End If
Return 0
End Function
Private Function fast(ByVal url As String, ByVal position As Double, ByVal speed As Single)
Dim fh As New WaveHeader
Dim stream As New FileStream(url, FileMode.Open)
Dim br As New BinaryReader(stream)
fh.Chunk = br.ReadChars(4)
fh.ChunkSize = br.ReadInt32
fh.Format = br.ReadChars(4)
fh.SubChunk1 = br.ReadChars(4)
fh.SubChunk1Size = br.ReadInt32
fh.AudioFormat = br.ReadInt16
fh.Channels = br.ReadInt16
fh.SampleRate = br.ReadInt32
fh.ByteRate = br.ReadInt32
fh.BlockAlign = br.ReadInt16
fh.BitsPerSample = br.ReadInt16
fh.SampleRate *= speed
fh.ByteRate *= speed
For i = 1 To fh.SubChunk1Size - 16
br.ReadChar()
Next
stream.Position = fh.SubChunk1Size + 20
fh.SubChunk2 = br.ReadChars(4)
fh.SubChunk2Size = br.ReadInt32
If fh.Channels = 6 Then
fh.Channels = 2
fh.BlockAlign = fh.Channels * fh.BitsPerSample / 8
fh.SampleRate = fh.SampleRate * (6 / fh.Channels)
End If
position = Math.Round(CInt(position / 1000) * fh.ByteRate)
If position >= fh.SubChunk2Size Then
Throw New Exception("Songs isn't that long")
End If
mytotaltime = Math.Round(fh.SubChunk2Size / fh.ByteRate)
fh.SubChunk2Size -= position
Dim header() As Byte = {Asc("R"), Asc("I"), Asc("F"), Asc("F"), 0, 0, 0, 0, Asc("W"), Asc("A"), Asc("V"), Asc("E"), Asc("f"), Asc("m"), Asc("t"), Asc(" "), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Asc("d"), Asc("a"), Asc("t"), Asc("a"), 0, 0, 0, 0}
BitConverter.GetBytes(fh.SubChunk2Size).CopyTo(header, 40)
BitConverter.GetBytes(fh.BitsPerSample).CopyTo(header, 34)
BitConverter.GetBytes(fh.BlockAlign).CopyTo(header, 32)
BitConverter.GetBytes(fh.ByteRate).CopyTo(header, 28)
BitConverter.GetBytes(fh.SampleRate).CopyTo(header, 24)
BitConverter.GetBytes(fh.Channels).CopyTo(header, 22)
BitConverter.GetBytes(fh.AudioFormat).CopyTo(header, 20)
BitConverter.GetBytes(16).CopyTo(header, 16)
BitConverter.GetBytes(fh.SubChunk2Size + 36).CopyTo(header, 4)
myheader = fh
Dim audio(fh.SubChunk2Size + 44) As Byte
header.CopyTo(audio, 0)
stream.Position = position
br.ReadBytes(fh.SubChunk2Size).CopyTo(audio, 44)
br.Dispose()
stream.Dispose()
br = Nothing
stream = Nothing
Return audio
End Function
Private Function SongStream(ByVal url As String, ByVal position As Double)
Dim fh As New WaveHeader
Dim stream As New FileStream(url, FileMode.Open)
Dim br As New BinaryReader(stream)
fh.Chunk = br.ReadChars(4)
fh.ChunkSize = br.ReadInt32
fh.Format = br.ReadChars(4)
fh.SubChunk1 = br.ReadChars(4)
fh.SubChunk1Size = br.ReadInt32
fh.AudioFormat = br.ReadInt16
fh.Channels = br.ReadInt16
fh.SampleRate = br.ReadInt32
fh.ByteRate = br.ReadInt32
fh.BlockAlign = br.ReadInt16
fh.BitsPerSample = br.ReadInt16
For i = 1 To fh.SubChunk1Size - 16
br.ReadChar()
Next
stream.Position = fh.SubChunk1Size + 20
fh.SubChunk2 = br.ReadChars(4)
fh.SubChunk2Size = br.ReadInt32
If fh.Channels = 6 Then
fh.Channels = 2
fh.BlockAlign = fh.Channels * fh.BitsPerSample / 8
fh.SampleRate = fh.SampleRate * (6 / fh.Channels)
End If
position = Math.Round(CInt(position / 1000) * fh.ByteRate)
If position >= fh.SubChunk2Size Then
Throw New Exception("Songs isn't that long")
End If
mytotaltime = Math.Round(fh.SubChunk2Size / fh.ByteRate)
fh.SubChunk2Size -= position
Dim header() As Byte = {Asc("R"), Asc("I"), Asc("F"), Asc("F"), 0, 0, 0, 0, Asc("W"), Asc("A"), Asc("V"), Asc("E"), Asc("f"), Asc("m"), Asc("t"), Asc(" "), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Asc("d"), Asc("a"), Asc("t"), Asc("a"), 0, 0, 0, 0}
BitConverter.GetBytes(fh.SubChunk2Size).CopyTo(header, 40)
BitConverter.GetBytes(fh.BitsPerSample).CopyTo(header, 34)
BitConverter.GetBytes(fh.BlockAlign).CopyTo(header, 32)
BitConverter.GetBytes(fh.ByteRate).CopyTo(header, 28)
BitConverter.GetBytes(fh.SampleRate).CopyTo(header, 24)
BitConverter.GetBytes(fh.Channels).CopyTo(header, 22)
BitConverter.GetBytes(fh.AudioFormat).CopyTo(header, 20)
BitConverter.GetBytes(16).CopyTo(header, 16)
BitConverter.GetBytes(fh.SubChunk2Size + 36).CopyTo(header, 4)
myheader = fh
Dim audio(fh.SubChunk2Size + 44) As Byte
header.CopyTo(audio, 0)
stream.Position = position
br.ReadBytes(fh.SubChunk2Size).CopyTo(audio, 44)
br.Dispose()
stream.Dispose()
br = Nothing
stream = Nothing
Return audio
End Function
Region "Property"
<Browsable(False)> ReadOnly Property PlayState
Get
Return mystate
End Get
End Property
<Browsable(False)> ReadOnly Property URL
Get
Return myurl
End Get
End Property
ReadOnly Property TotalTime
Get
Return mytotaltime
End Get
End Property
ReadOnly Property time
Get
Return watch.ElapsedMilliseconds
End Get
End Property
ReadOnly Property timestamp
Get
Return watch.Elapsed.ToString
End Get
End Property
End Region
Private Sub timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles timer.Tick
If Not TotalTime = 0 Then
If TotalTime <= time / 1000 Then
watch.Stop()
watch.Reset()
mystate = State.Finished
RaiseEvent OnPlayStateChange(State.Finished)
End If
End If
FlushMemory()
End Sub
ReadOnly Property SongHeader As WaveHeader
Get
Return myheader
End Get
End Property
End Class
Public Class spactrum
Dim h As WaveHeader
Function readheader(ByVal url As String)
Dim fh As WaveHeader
Dim stream As New FileStream(url, FileMode.Open)
Dim br As New BinaryReader(stream)
fh.Chunk = br.ReadChars(4)
fh.ChunkSize = br.ReadInt32
fh.Format = br.ReadChars(4)
fh.SubChunk1 = br.ReadChars(4)
fh.SubChunk1Size = br.ReadInt32
fh.AudioFormat = br.ReadInt16
fh.Channels = br.ReadInt16
fh.SampleRate = br.ReadInt32
fh.ByteRate = br.ReadInt32
fh.BlockAlign = br.ReadInt16
fh.BitsPerSample = br.ReadInt16
For i = 1 To fh.SubChunk1Size - 16
br.ReadByte()
Next
fh.SubChunk2 = br.ReadChars(4)
fh.SubChunk2Size = br.ReadInt32
h = fh
Return br.ReadBytes(fh.SubChunk2Size)
End Function
Function showit()
Dim b As New Bitmap(500, 200)
Dim g As Graphics = Graphics.FromImage(b)
Dim d() As Byte = readheader("songs\s.wav")
'Dim t As Integer = d.Count
For i = 0 To d.Count - 1
Dim x = CInt((i / d.Count) * 500)
Dim y = CInt(d(i).ToString) - 200
g.DrawLine(Pens.Black, x, 0, x, y)
Next
'g.FillEllipse(Brushes.Black, 0, 0, 500, 300)
Return b.Clone
End Function
End Class
As there seems to be little documentation on using naudio in VB.NET, rather than the C# examples found everywhere, and further to #user1666788's comments, this is a simple way of making it play an MP3 file for VB.NET, rather than a WAV.
Public Shared Wave1 As New NAudio.Wave.WaveOut 'Wave out device for playing the sound
Public Sub btn_PlayPause_Click(sender As Object, e As EventArgs) Handles btn_PlayPause.Click
Dim file As String = "C:\test.mp3"
Dim data As New NAudio.Wave.Mp3FileReader(file)
Wave1.Init(data)
Wave1.Play()
End Sub