Click here to Skip to main content
15,897,187 members
Articles / Programming Languages / C#

Sending and playing microphone audio over network

Rate me:
Please Sign up or sign in to vote.
4.92/5 (65 votes)
3 Aug 20072 min read 524.9K   42.9K   336  
Sending and playing microphone audio over network
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;

using LumiSoft.Net;

namespace LumiSoft.Data.lsDB
{
    /// <summary>
    /// Table what all columns are with fixed length.
    /// </summary>
    public class lsDB_FixedLengthTable : IDisposable
    {
        private LDB_DataColumnCollection m_pColumns           = null;
		private FileStream               m_pDbFile            = null;
		private string                   m_DbFileName         = "";
		private bool                     m_TableLocked        = false;
        private long                     m_RowsStartOffset    = -1;
        private long                     m_ColumnsStartOffset = -1;
        private int                      m_RowLength          = -1;
        private byte[]                   m_RowDataBuffer      = null;
        private lsDB_FixedLengthRecord   m_pCurrentRecord     = null;
        private long                     m_FileLength         = 0;
		private long                     m_FilePosition       = 0;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public lsDB_FixedLengthTable()
        {
            m_pColumns = new LDB_DataColumnCollection(this);
        }

		#region method Dispose

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		public void Dispose()
		{
			Close();
		}

		#endregion


        #region method Open

         /// <summary>
		/// Opens specified data file.
		/// </summary>
		/// <param name="fileName">File name.</param>
        public void Open(string fileName)
        {
            Open(fileName,0);
        }

        /// <summary>
		/// Opens specified data file.
		/// </summary>
		/// <param name="fileName">File name.</param>
		/// <param name="waitTime">If data base file is exclusively locked, then how many seconds to wait file to unlock before raising a error.</param>
		public void Open(string fileName,int waitTime)
		{            
			DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime);
			while(true){
				try{
					m_pDbFile = File.Open(fileName,FileMode.Open,FileAccess.ReadWrite,FileShare.ReadWrite);

					break;
				}
				catch(IOException x){
                    if(!File.Exists(fileName)){
                        throw new Exception("Specified database file '" + fileName + "' does not exists !");
                    }

					// Make this because to get rid of "The variable 'x' is declared but never used"
					string dummy = x.Message;

					System.Threading.Thread.Sleep(15);

					// Lock wait time timed out
                    if(DateTime.Now > lockExpireTime){
						throw new Exception("Database file is locked and lock wait time expired !");
					}
				}
			}

			/* Table structure:
				50    bytes         - version
				2     bytes         - CRLF
                4     bytes         - Free rows count
                2     bytes         - CRLF
				100 x 500 bytes     - 100 columns info store
				2     bytes         - CRLF
				... data pages
			*/

			m_DbFileName = fileName;

			// TODO: check if LDB file

			// Read version line (50 bytes + CRLF)	
			byte[] version = new byte[52];
            ReadFromFile(0,version,0,version.Length);

            // Read free rows count
            byte[] freeRows = new byte[6];
            ReadFromFile(0,freeRows,0,freeRows.Length);

            long currentColumnOffset = 58;
			// Read 100 column lines (500 + CRLF bytes each)
			for(int i=0;i<100;i++){
				byte[] columnInfo = new byte[102];                
				if(ReadFromFile(currentColumnOffset,columnInfo,0,columnInfo.Length) != columnInfo.Length){
					throw new Exception("Invalid columns data area length !");
				}

				if(columnInfo[0] != '\0'){					
					m_pColumns.Parse(columnInfo);
				}

                currentColumnOffset += 102;
			}

			// Header terminator \0
			m_pDbFile.Position++;
	
			// No we have rows start offset
			m_RowsStartOffset = m_pDbFile.Position;

			// Store file length and position
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_pDbFile.Position;

            // Calculate row length
            m_RowLength = 1 + 2;
            for(int i = 0;i < m_pColumns.Count;i++){
                m_RowLength += m_pColumns[i].ColumnSize;
            }

            m_RowDataBuffer = new byte[m_RowLength];
		}

		#endregion

        #region method Close

		/// <summary>
		/// Closes database file.
		/// </summary>
		public void Close()
		{
			if(m_pDbFile != null){
				m_pDbFile.Close();
				m_pDbFile = null;
				m_DbFileName = "";
				m_FileLength = 0;
				m_FilePosition = 0;
                m_RowLength = 0;
                m_ColumnsStartOffset = 0;
                m_RowsStartOffset = 0;
                m_TableLocked = false;
                m_RowDataBuffer = null;
                m_pCurrentRecord = null;
			}
		}

		#endregion

        #region method Create

        /// <summary>
		/// Creates new database file.
		/// </summary>
		/// <param name="fileName">File name.</param>
        public void Create(string fileName)
        {
            m_pDbFile = new FileStream(fileName,FileMode.CreateNew,FileAccess.ReadWrite,FileShare.None);

			/* Table structure:
				50    bytes         - version
				2     bytes         - CRLF
                4     bytes         - Free rows count
                2     bytes         - CRLF
				100 x 500 bytes     - 100 columns info store
				2     bytes         - CRLF
				... data pages
			*/

			// Version 50 + CRLF bytes
			byte[] versionData = new byte[52];
			versionData[0] = (byte)'1';
			versionData[1] = (byte)'.';
			versionData[2] = (byte)'0';
			versionData[50] = (byte)'\r';
			versionData[51] = (byte)'\n';
			m_pDbFile.Write(versionData,0,versionData.Length);

            // Free rows count
            byte[] freeRows = new byte[6];
            freeRows[4] = (byte)'\r';
            freeRows[5] = (byte)'\n';
            m_pDbFile.Write(freeRows,0,freeRows.Length);

            m_ColumnsStartOffset = m_pDbFile.Position;

			// 100 x 100 + CRLF bytes header lines
			for(int i=0;i<100;i++){
				byte[] data = new byte[100];
				m_pDbFile.Write(data,0,data.Length);
				m_pDbFile.Write(new byte[]{(int)'\r',(int)'\n'},0,2);
			}

			// Headers terminator char(0)
			m_pDbFile.WriteByte((int)'\0');

			// Rows start pointer
			m_RowsStartOffset = m_pDbFile.Position - 1;

			m_DbFileName = fileName;

			// Store file length and position
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_pDbFile.Position;
          
            // Calculate row length
            m_RowLength = 1 + 2;
            for(int i = 0;i < m_pColumns.Count;i++){
                m_RowLength += m_pColumns[i].ColumnSize;
            }

            m_RowDataBuffer = new byte[m_RowLength];
        }

        #endregion


        #region method LockTable
		
		/// <summary>
		/// Locks table.
		/// </summary>
		/// <param name="waitTime">If table is locked, then how many sconds to wait table to unlock, before teturning error.</param>
		public void LockTable(int waitTime)
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			// Table is locked already, just skip locking
			if(m_TableLocked){
				return;
			}

			DateTime lockExpireTime = DateTime.Now.AddSeconds(waitTime);
			while(true){
				try{
					// We just lock first byte
					m_pDbFile.Lock(0,1);
					m_TableLocked = true;

					break;
				}
				// Catch the IOException generated if the 
				// specified part of the file is locked.
				catch(IOException x){
					// Make this because to get rid of "The variable 'x' is declared but never used"
					string dummy = x.Message;

					System.Threading.Thread.Sleep(15);

					// Lock wait time timed out
                    if(DateTime.Now > lockExpireTime){
						throw new Exception("Table is locked and lock wait time expired !");
					}
				}
			}
		}

		#endregion

		#region method UnlockTable

		/// <summary>
		/// Unlock table.
		/// </summary>
		public void UnlockTable()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}

			if(m_TableLocked){
				// We just unlock first byte
				m_pDbFile.Unlock(0,1);
			}
		}

		#endregion


        #region method MoveFirstRecord

        /// <summary>
        /// Moves to first record.
        /// </summary>
        public void MoveFirstRecord()
        {
            m_pCurrentRecord = null;
        //    NextRecord();
        }

        #endregion

        #region method NextRecord

        /// <summary>
		/// Gets next record. Returns true if end of file reached and there are no more records.
		/// </summary>
		/// <returns>Returns true if end of file reached and there are no more records.</returns>
		public bool NextRecord()
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
                        
			//--- Find next record ---------------------------------------------------//
			long nextRowStartOffset = 0;
			if(m_pCurrentRecord == null){
				nextRowStartOffset = m_RowsStartOffset;
			}
			else{                
                nextRowStartOffset = m_pCurrentRecord.Pointer + m_RowLength;                
			}

			while(true){
				if(m_FileLength > nextRowStartOffset){
                    ReadFromFile(nextRowStartOffset,m_RowDataBuffer,0,m_RowLength);

                    // We want used row
                    if(m_RowDataBuffer[0] == 'u'){
                        if(m_pCurrentRecord == null){
                            m_pCurrentRecord = new lsDB_FixedLengthRecord(this,nextRowStartOffset,m_RowDataBuffer);
                        }
                        else{
                            m_pCurrentRecord.ReuseRecord(this,nextRowStartOffset,m_RowDataBuffer);
                        }
                        break;
                    }
				}
				else{
					return true;
				}
				
				nextRowStartOffset += m_RowLength;
			}
			//-------------------------------------------------------------------------//
			
			return false;
		}

		#endregion


        #region method AppendRecord

		/// <summary>
		/// Appends new record to table.
		/// </summary>
		public void AppendRecord(object[] values)
		{
			if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			if(m_pColumns.Count != values.Length){
				throw new Exception("Each column must have corresponding value !");
			}

			bool unlock = true;
			// Table is already locked, don't lock it
			if(this.TableLocked){
				unlock = false;
			}
			else{
				LockTable(15);
			}

            /* Fixed record structure:
                1 byte     - specified is row is used or free space
                             u - used
                             f - free space
                x bytes    - columns data
                2 bytes    - CRLF
            */

            int rowLength = 1 + 2;
            for(int i = 0;i < m_pColumns.Count;i++){
                rowLength += m_pColumns[i].ColumnSize;
            }

            int position = 1;
            byte[] record = new byte[rowLength];
            record[0] = (int)'u';
            record[rowLength - 2] = (int)'\r';
            record[rowLength - 1] = (int)'\n';
            for(int i = 0;i < values.Length;i++){
                byte[] columnData = LDB_Record.ConvertToInternalData(m_pColumns[i],values[i]);
                // Check that column won't exceed maximum length.
                if(columnData.Length > m_pColumns[i].ColumnSize){
                    throw new Exception("Column '" + m_pColumns[i] + "' exceeds maximum value length !");
                }

                Array.Copy(columnData,0,record,position,columnData.Length);
                position += columnData.Length; 
            }

            // Find free row
            byte[] freeRowsBuffer = new byte[4];
            ReadFromFile(52,freeRowsBuffer,0,freeRowsBuffer.Length);
            int freeRows = ldb_Utils.ByteToInt(freeRowsBuffer,0);
            // There are plenty free rows, find first
            
            if(freeRows > 100){
                //--- Find free record ---------------------------------------------------//
			    long nextRowStartOffset = m_RowsStartOffset;
                long rowOffset = 0;

                byte[] rowData = new byte[m_RowLength];
	    		while(true){
                    ReadFromFile(nextRowStartOffset,rowData,0,m_RowLength);

                    // We want used row
                    if(rowData[0] == 'f'){
                        rowOffset = nextRowStartOffset;
                        break;
                    }
				
				    nextRowStartOffset += m_RowLength;
			    }
			    //-------------------------------------------------------------------------//

                // Write new record to file
                WriteToFile(rowOffset,record,0,record.Length);

                // Update free rows count
                WriteToFile(52,ldb_Utils.IntToByte(freeRows - 1),0,4);
            }
            // There are few empty rows, just append it
            else{
                AppendToFile(record,0,record.Length);
            }

			if(unlock){
				UnlockTable();
			}
		}
	
		#endregion

        #region method DeleteRecord

		/// <summary>
		/// Deletes current record.
		/// </summary>
        public void DeleteCurrentRecord()
        {
            if(!this.IsDatabaseOpen){
				throw new Exception("Database isn't open, please open database first !");
			}
			if(m_pCurrentRecord == null){
				throw new Exception("There is no current record !");
			}

			bool unlock = true;
			// Table is already locked, don't lock it
			if(this.TableLocked){
				unlock = false;
			}
			else{
				LockTable(15);
			}
                        
            byte[] data = new byte[m_RowLength];
            data[0] = (byte)'f';
            data[m_RowLength - 2] = (byte)'\r';
            data[m_RowLength - 1] = (byte)'\n';
            WriteToFile(m_pCurrentRecord.Pointer,data,0,data.Length);

            // Update free rows count
            byte[] freeRowsBuffer = new byte[4];
            ReadFromFile(52,freeRowsBuffer,0,freeRowsBuffer.Length);
            int freeRows = ldb_Utils.ByteToInt(freeRowsBuffer,0);
            WriteToFile(52,ldb_Utils.IntToByte(freeRows + 1),0,4);

            if(unlock){
				UnlockTable();
			}

			// Activate next record **** Change it ???
			NextRecord();
        }

        #endregion


        #region method AddColumn

        /// <summary>
		/// Adds column to db file.
		/// </summary>
		/// <param name="column"></param>
		internal void AddColumn(LDB_DataColumn column)
		{
            if(column.ColumnSize < 1){
                throw new Exception("Invalid column size '" + column.ColumnSize + "' for column '" + column.ColumnName + "' !");
            }

			// Find free column data area

            long currentColumnOffset = m_ColumnsStartOffset;
			long freeColumnPosition = -1;
			// Loop all columns data areas, see it there any free left
			for(int i=0;i<100;i++){
                byte[] columnInfo = new byte[102];                
				if(ReadFromFile(currentColumnOffset,columnInfo,0,columnInfo.Length) != columnInfo.Length){
					throw new Exception("Invalid columns data area length !");
				}

				// We found unused column data area
				if(columnInfo[0] == '\0'){
					freeColumnPosition = currentColumnOffset - 102;
					break;
				}

                currentColumnOffset += 102;
			}

			if(freeColumnPosition != -1){
				// TODO: If there is data ???

				// Store column
				byte[] columnData = column.ToColumnInfo();
				WriteToFile(currentColumnOffset,columnData,0,columnData.Length);
			}
			else{
				throw new Exception("Couldn't find free column space ! ");
			}
		}

		#endregion

		#region method RemoveColumn

		/// <summary>
		/// Removes specified column from database file.
		/// </summary>
		/// <param name="column"></param>
		internal void RemoveColumn(LDB_DataColumn column)
		{
			throw new Exception("TODO:");
		}

		#endregion


        #region method ReadFromFile

		/// <summary>
		/// Reads data from file.
		/// </summary>
        /// <param name="readOffset">Offset in database file from where to start reading data.</param>
		/// <param name="data">Buffer where to store readed data.</param>
		/// <param name="offset">Offset in array to where to start storing readed data.</param>
		/// <param name="count">Number of bytes to read.</param>
		/// <returns></returns>
		internal int ReadFromFile(long readOffset,byte[] data,int offset,int count)
		{
            SetFilePosition(readOffset);

			int readed = m_pDbFile.Read(data,offset,count);
			m_FilePosition += readed;

			return readed;
		}

		#endregion

		#region method WriteToFile

		/// <summary>
		/// Writes data to file.
		/// </summary>
        /// <param name="writeOffset">Offset in database file from where to start writing data.</param>
		/// <param name="data">Data to write.</param>
		/// <param name="offset">Offset in array from where to start writing data.</param>
		/// <param name="count">Number of bytes to write.</param>
		/// <returns></returns>
		internal void WriteToFile(long writeOffset,byte[] data,int offset,int count)
		{
            SetFilePosition(writeOffset);

			m_pDbFile.Write(data,offset,count);
			m_FilePosition += count;
		}

		#endregion

        #region method AppendToFile

        /// <summary>
        /// Appends specified data at the end of file.
        /// </summary>
        /// <param name="data">Data to write.</param>
        /// <param name="offset">Offset in array from where to start writing data.</param>
        /// <param name="count">Number of bytes to write.</param>
        internal void AppendToFile(byte[] data,int offset,int count)
        {
            m_pDbFile.Position = m_pDbFile.Length;

            m_pDbFile.Write(data,offset,count);

			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_pDbFile.Position;
        }

        #endregion

        #region method GetFilePosition

        /// <summary>
		/// Gets current position in file.
		/// </summary>
		/// <returns></returns>
		internal long GetFilePosition()
		{
			return m_FilePosition;
		}

		#endregion

		#region method SetFilePosition

		/// <summary>
		/// Sets file position.
		/// </summary>
		/// <param name="position">Position in file.</param>
		private void SetFilePosition(long position)
		{
			if(m_FilePosition != position){
				m_pDbFile.Position = position;
				m_FilePosition = position;
			}
		}

		#endregion
// REMOVE ME:
		#region method GoToFileEnd
/*
		/// <summary>
		/// Moves position to the end of file.
		/// </summary>
		private void GoToFileEnd()
		{
			m_pDbFile.Position = m_pDbFile.Length;
			m_FileLength = m_pDbFile.Length;
			m_FilePosition = m_FileLength;
		}
*/
		#endregion


        #region Properties Implementation

		/// <summary>
		/// Gets if there is active database file.
		/// </summary>
		public bool IsDatabaseOpen
		{
			get{ return m_pDbFile != null; }
		}

		/// <summary>
		/// Gets open database file name. Throws exception if database isn't open.
		/// </summary>
		public string FileName
		{
			get{
				if(!this.IsDatabaseOpen){
					throw new Exception("Database isn't open, please open database first !");
				}

				return m_DbFileName; 
			}
		}

		/// <summary>
		/// Gets table columns. Throws exception if database isn't open.
		/// </summary>
		public LDB_DataColumnCollection Columns
		{
			get{ 
				if(!this.IsDatabaseOpen){
					throw new Exception("Database isn't open, please open database first !");
				}

				return m_pColumns; 
			}
		}

		/// <summary>
		/// Gets current record. Returns null if there isn't current record.
		/// </summary>
		public lsDB_FixedLengthRecord CurrentRecord
		{
			get{ return m_pCurrentRecord; }
		}

		/// <summary>
		/// Gets table is locked.
		/// </summary>
		public bool TableLocked
		{
			get{ return m_TableLocked; }
		}

		#endregion
        
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions