views:

46

answers:

1

I am using VB.net sockets to send a music file from one instance to another. It currently saves the file, but I do not want to be caught up in file-sharing, so I want to "save" it to a buffer, and when it gets to a particular percentage transferred, I want another thread to play the music file, deleting it from the buffer as it plays. I have the code to send it over the network, but I need to know how to keep it in a buffer and not save it as a file.

Thanks

Nick

A: 

Assuming you get a network stream you could do something like the code below.

The Main() sub gets a network stream and reads it until the end. It passed the bytes over to the PlayBuffer class which appends the bytes to the end of a MemoryStream. When you use the Read() method on the PlayBuffer, it reads from the start and keeps a pointer to where you have read to. The locking is there so that you can write and read to the stream from separate threads. I have not provided code which uses Read(). It's just to give an idea. If you are getting continous bytes, then you could allocate a large buffer and once at the end, you start writing at the beginning again (ring-buffer).

[Edit - modified code to write and read at the same time]

I have not completely tested the code, but the PlayBuffer class inherits a stream and should be able to write and read at the same time, as long as one thread writes and the other just reads.

Friend Class PlayProgram
    Public Sub Main()
        Dim playBuffer As New PlayBuffer
        Dim readData As New Thread(Function 
            Using client As TcpClient = New TcpClient("somehostname", 80)
                Using networkStream As NetworkStream = client.GetStream
                    Dim readBytes As Integer
                    Dim buffer As Byte() = New Byte(&H1000  - 1) {}
                    Do While (readBytes = networkStream.Read(buffer, 0, buffer.Length) > 0)
                        playBuffer.Write(buffer, 0, readBytes)
                        playBuffer.Flush
                    Loop
                End Using
            End Using
        End Function)
        readData.Start
        New SoundPlayer(playBuffer).Play
        readData.Join
    End Sub
End Class


Friend Class PlayBuffer
    Inherits Stream
    Public Sub AppendToBuffer(ByVal buffer As Byte(), ByVal bytesRead As Integer)
        SyncLock PlayBuffer._lock
            Me._buffer.Position = Me._buffer.Length
            Me._buffer.Write(buffer, 0, bytesRead)
        End SyncLock
    End Sub

    Public Overrides Sub Flush()
        SyncLock PlayBuffer._lock
            Me._buffer.Flush
        End SyncLock
    End Sub

    Public Overrides Function Read(ByVal internalBuffer As Byte(), ByVal offset As Integer, ByVal count As Integer) As Integer
        SyncLock PlayBuffer._lock
            Me._buffer.Position = Me._readPosition
            Dim bytesRead As Integer = Me._buffer.Read(internalBuffer, 0, internalBuffer.Length)
            Me._readPosition = (Me._readPosition + bytesRead)
            Return bytesRead
        End SyncLock
    End Function

    Public Overrides Function Seek(ByVal offset As Long, ByVal origin As SeekOrigin) As Long
        Throw New NotImplementedException
    End Function

    Public Overrides Sub SetLength(ByVal value As Long)
        Throw New NotImplementedException
    End Sub

    Public Overrides Sub Write(ByVal buffer As Byte(), ByVal offset As Integer, ByVal count As Integer)
        SyncLock PlayBuffer._lock
            Me._buffer.Position = Me._buffer.Length
            Me._buffer.Write(buffer, offset, count)
        End SyncLock
    End Sub


    Public Overrides ReadOnly Property CanRead As Boolean
        Get
            Return True
        End Get
    End Property

    Public Overrides ReadOnly Property CanSeek As Boolean
        Get
            Throw New NotImplementedException
        End Get
    End Property

    Public Overrides ReadOnly Property CanWrite As Boolean
        Get
            Return True
        End Get
    End Property

    Public Overrides ReadOnly Property Length As Long
        Get
            Return Me._buffer.Length
        End Get
    End Property

    Public Overrides Property Position As Long
        Get
            Return Me._readPosition
        End Get
        Set(ByVal value As Long)
            Throw New NotImplementedException
        End Set
    End Property

    Private ReadOnly _buffer As MemoryStream = New MemoryStream
    Private Shared ReadOnly _lock As Object = New Object
    Private _readPosition As Long
End Class
Mikael Svenson
Mikael, I would just like to say a HUGE thank you, that has helped so much!
Nick
This is new to me, could you provide code which uses the read function? thanks (also, it's just one song at a time)
Nick
Read will give you a byte array, so it depends on what you use to actually play your audio. http://stackoverflow.com/questions/184683/play-audio-from-a-stream-using-c has an example of using NAudio to play from a stream. If you use this you could pass the network stream directly to the constructor of the player lib. What are you using to play the audio?
Mikael Svenson
I edited the code where writing happens in a separate thread. Then I use SoundPlayer to play the data (if it's a wav file)
Mikael Svenson
I'll use that NAudio thing, thank you! However, Where do i copy the naudio.dll to for VB to use it? or am i being silly, i understand that naudio is written IN C#, but can all the .net apps still use it?
Nick
If not, i may use the VB.NET WPF player, which does mp3 and .wav
Nick
You just reference the dll like any other .net dll (or framework library) It doesn't matter if it has been created in c# or vb.net :) (The code I wrote was actually written i c# and I translated it to VB.Net via reflector, since I'm primarily a c# person).
Mikael Svenson
the NAudio didn't work too well for me, so i'll be using the WPF Media Element, however, how do i tell it to read from the byte array? Sorry, but i'm really bad when it comes to networking things :p
Nick
@Nick: I'll see if I get time for a whole complete sample later today.
Mikael Svenson
Thank you so much =]
Nick
Looked a bit at the MediaPlayer control, and it can only take a Uri in the Open argument. If you use it you would have to save the data to a file first, and then play that file. http://pastebin.com/5GMiP7vF
Mikael Svenson