Tổng hợp tiếng nói bằng phương pháp trực tiếp

Tổng hợp tiếng nói bằng phương pháp trực tiếp với đơn vị ghi âm là các âm vị kép.

Các âm vị kép cần phải ghi âm trước là:

  • Ti
  • In
  • Mi

Từ các âm vị kép trên hãy tổng hợp thành các từ có nghĩa có thể có

Yêu cầu:

  • Ghi âm bằng Micro

Và tạo lại tín hiệu ghi âm ở loa

  • Ghép các âm vị kép đã ghi âm sao cho chất lượng tiếng nói tổng hợp tốt nhất.
  • Hiển thị tín hiệu tiếng nói của từ đã tổng hợp.

Phần II:

BÁO CÁO CHƯƠNG TRÌNH

  1. Cơ sở lý thuyết

  1. File RIFF (RIFF: Resource Interchange File Format)

 

          File Wave là tập tin chứa các dữ liệu của mẫu âm thanh đã được số hoá. Phương pháp số hoá âm thanh hiện nay là phương pháp PCM. Phương pháp này sẽ lấy mẫu âm thanh với khoảng tần số từ 11.025kHz cho đến 44.1 kHz. Mỗi lần lấy mẫu, số liệu lại được lượng tử hoá bằng 1 hay 2 byte cho 1 mẫu âm thanh. Như vậy tần số lấy mẫu càng cao, số byte dùng lượng tử hoá càng nhiều thì âm thanh phát lại càng trung thực, nhưng lại tăng số byte cần lưu trữ. Với một mẫu âm thanh phát ra trong một phút cần phải lưu trữ ít nhất 660 kB. Đó là lý do tại sao các wave file luôn có kích thước khá lớn so với file midi.

          Cấu trúc của file wave thuộc vào lớp file được sử dụng bởi các hàm Multimedia của Windows, cụ thể là: file RIFF (dạng file trao đổi tài nguyên).

Một file RIFF gồm một hoặc nhiều chunk, trong mỗi chunk lại chứa con trỏ chỉ đến chunk kế tiếp.

Mỗi chuck bao gồm:

  • Loại chunk
  • Dữ liệu theo sau loại chunk đó.

Một ứng dụng muốn đọc file RIFF có thể đi qua lần lượt từng chunk, đọc dữ liệu ở chunk nó quan tâm và có thể bỏ qua các chunk mà nó không quan tâm. Một chunk của file RIFF luôn bắt đầu bằng một header có cấu trúc như sau:

Typedef struct

{

FOURCC ckid;

DWORD ckSize;

} CK;

Trong đó:

  1. Trường FOURCC:
  • Kích thước 4 byte, xác đinhl loại chunk
  • Mang giá trị “WAVE”, nếu là file WAVE.
  • Chú ý: Nếu loại chunk ít hơn 4 ký tự thì các ký tự còn lại bên phải sẽ được thêm vào các khoảng trắng. Trong trường FOURCC phân biệt giữa chữ hoa và chữ thường.
  • Trường DWORD:
  • Chứa kích thước vùng dữ liệu của chunk

Vùng dữ liệu này nằm ngay sau header và có kích thước là: ckSize byte. Chunk có thể chứa các subchunk. Subchunk cũng là một chunk. Một file RIFF luôn bắt đầu bằng một chunk loại “RIFF”.

  1. Cấu trúc file Wave

          File Wave bắt đầu  là chunk loại “RIFF”. Hai subchunk “fmt” và “data” trong wave chunk đặc tả thông tin về âm thanh của file wave, tiếp đó là dữ liệu của từng subchunk.Cụ thể:

  1. Subchunk “fmt”

          Dữ liệu của fmt chunk là đối tượng WAVEFORMAT có cấu trúc như sau:

Typedef struct waveformat_tag

{

WORD wFormatTag;

WORD nChannels;

DWORD nSamplesPerSec;

DWORD nAvgBytesPerSec;

WORD nBlockAlign;

} WAVEFORMAT;

Trong đó:

  • wFormatTag thường có giá trị là WAVE_FORMAT_PCM được định nghĩa trong tập tin  MMSYSTEM.H  như sau:

#define WAVE_FORMAT_PCM 1

Giá trị này báo cho phần mềm đang đọc Wave file biết kiểu mã hoá dữ liệu âm thanh sang dữ liệu số là kiểu mã hoá PCM. Hiện nay đây là kiểu mã hoá duy nhất của Wave file.

  • nChannels: có hai giá trị : bằng 1 cho âm thanh mono và bằng 2 cho âm thanh stereo.
  • nSamplesPerSec: cho biết tốc độ lấy mẫu, có các giá trị:

11025 — 11.025 kHz

22050 — 22.050 kHz

44100 — 44.100 kHz

  • nAvgBytesPerSec: cho biết số byte yêu cầu trung bình trong một giây để phát lại mẫu dữ liệu của sóng âm.
  • nBlockAlign: cho biết số byte dùng để chứa một mẫu âm thanh. Như vậy mẫu 8 bit hay ít hơn sẽ yêu cầu 1 byte, mẫu 9 đến 16 bit sẽ yêu cầu 2 byte. Nếu âm thanh là stereo thì yêu cầu số byte gấp 2 lần âm thanh mono.

Ta thấy trong WAVEFORMAT chưa có thông tin về số bit dùng để lượng tử hoá một mẫu dữ liệu của sóng âm. Thực tế Wave file sẽ xác lập số bit dùng cho một mẫu dữ liệu bằng một trường gắn vào cuối cấu trúc của WAVEFORMAT. Cấu trúc đó như sau:

Typedef struct pcmwaveformat_tag

{

WAVEFORMAT wf;

WORD wBitsPerSample;

} PCMWAVEFORMAT;

Trong đó:

  • wBitsPerSample: cho biết số bit trong một mẫu dữ liệu.

Chú ý:

Các mẫu dữ liệu vẫn phải lưu trữ ở dạng byte hoặc word. Do đó, nếu một wave file dùng 12 bit để lượng tử hoá một mẫu sóng âm thì sẽ phải lưu trữ 4 bit thừa không dùng đến.

  1. Subchunk “data”

 

Dữ liệu của data subchunk của wave file chứa các số liệu của âm thanh đã được số hoá. Đối với mẫu âm thanh 8 bit, dữ liệu của data subchunk bao gồm các giá trị 1 byte (có giá trị trong khoảng 0-255) của các mẫu âm thanh. Đối với mẫu âm thanh 16 bit, mỗi mẫu dữ liệu gồm 2 byte (có giá trị trong khoảng từ -32768 đến 32767). Điều này không có nghĩa là file wave 16 bit sẽ nghe to hơn 256 lần file wave 8 bit, mà nó có nghĩa là âm thanh được lượng tử hoá chính xác hơn, nghe trung thực hơn.

Trong mẫu mono 8 bit, dữ liệu của data subchunk gồm chuỗi các giá trị 1 byte. Với stereo 8 bit, mỗi mẫu gồm 2 byte, dữ liệu sẽ được sắp xếp xen kẽ (interleave), với byte đầu (byte chẵn) là mẫu âm thanh của kênh bên trái, byte sau (byte lẻ) là của kênh bên phải

_Cấu trúc file Wave_

  1. Đọc File RIFF

          Để làm việc với file RIFF, ta phải mở nó và “descend” vào chunk mà ta cần. Điều này có nghĩa là ta cần phải định vị được chunk này, rồi chuyển con trỏ file vào đầu khối dữ liệu của chunk. Khi làm việc xong với 1 chunk ta phải “ascend” ra khỏi chunk và “descend” xuống chunk khác.

          Các hàm dùng xử lý RIFF file đều có tiền tố là mmio và làm việc với file handle dạng HMMIO. Để bắt đầu, ta phải mở file bằng đoạn mã sau:

HMMIO h;

if ((h=mmioOpen(path,NULL,MMIO_READ))==NULL)

{

/*báo lỗi*/

return(0);

}

Trong đó:

  • Thông số path chứa đường dẫn của file wave.
  • Cờ MMIO_READ báo cho mmioOpen mở file để đọc. Ta cũng có thể mở nó để ghi bằng thông số MMIO_WRITE hay cả đọc và ghi bằng thông số MMIO_READWRITE.
  • Nếu mở file thành công, mmioOpen sẽ trả về một handle loại HMMIO.

Nếu thất bại, nó sẽ trả về trị NULL.

Sau khi mở file xong, ta bắt đầu định vị Wave chunk bằng đoạn mã sau:

MMCKINFO mmParent;

MmParent.fccType=mmioFOURCC(‘W’,’A’,’V’,’E’);

if (mmioDescend(h,(LPMMCKINFO)& mmParent, NULL,MMIO_FINDRIFF))

{

mmioClose(h,0);

/* báo lỗi */

return(0);

}

           Cấu trúc của MMCKINFO chứa các thông tin về chunk. Nó được định nghĩa trong MMSYSTEM.H như sau:

Typedef struct

{

FOURCC ckid;

DWORD cksize;

FOURCC fcctype;

DWORD dwDataOffset;

DWORD dwFlags;

} MMCKINFO;

Trong đó:

  • Để “đi vào” một chunk, ta cho trường ckid của MMCKINFO ở loại chunk mà ta muốn định vị. Có một macro thực hiện việc này là mmioFOURCC. Sau đó gọi hàm mmioDescend để định vị chunk. Nếu định vị thành công, hàm này trả về giá trị 0 và đối tượng MMCKINFO truyền cho hàm sẽ được điền vào các thông tin về chunk.
  • Trường cksize định nghĩa kích thước tính bằng byte của chunk.
  • Đối số thứ ba của mmioDescend là cờ MMIO_FINDRIFF.

Cờ này chỉ thị cho mmioDescend tìm một file có ID là RIFF với loại chunk được xác định bởi ckid. Nếu muốn tìm một chunk trong Wave file ta cho cờ này là MMIO_FINDCHUNK.

Sau khi đi vào Wave chunk, ta bắt đầu đi vào fmt subchunk của nó:

MMIOCKINFO mmSub;

MmSub.ckid=mmioFOURCC(‘f’,’m’,’t’);

if (mmioDescend(h,(LPMMCKINFO)& mmSub,(LPMMCKINFO)&

mmParent,MMIO_FINDCHUNK))

{

mmioClose(h,0);

/* báo lỗi */

return(0);

}

          Đến đây ta đã có thể bắt đầu đọc dữ liệu từ Wave File. Đoạn mã sau đọc đối tượng PCMWAVEFORMAT từ fmt subchunk:

PCMWAVEFORMAT waveformat;

Int n;

n = min ((unsigned int)mmSub.cksize,sizeof(PCMWAVEFORMAT));

if(mmioRead(h,(LPSTR)&waveformat,(long)n) !=(long)n)

{

/* báo lỗi */

return(0L);

}

if(waveformat.wf.wFormatTag !=WAVE_FORMAT_PCM)

{

/* BÁO LỖI */

mmioClose(h,0);

return(0L);

}

Trong đó:

  • Đối số đầu tiên của mmioRead là handle của file đang đọc.
  • Đối số thứ hai là con trỏ xa trỏ tới vùng đệm để chứa dữ liệu.
  • Đối số thứ ba là số byte cần đọc. Hàm này sẽ trả về số byte thực sự đọc được.

Sau khi đã đọc nội dung của chunk, ta đi ra khỏi chunk để chuẩn bị đọc chunk kế tiếp:

mmAscend(h,(LPMMCKINFO)&mmSub,0);

Trong đó:

  • Đối số thứ hai của mmAscend là đối tượng MMCKINFO của chunk mà ta “đi ra”.
  • Đối số thứ ba là đối số giả.

Công việc còn lại là đọc dữ liệu mã hoá âm thanh của Wave file vào bộ nhớ. Chú ý rằng giá trị cksize trả về bởi mmioDescend được sử dụng để xác định kích thước vùng đệm cần cấp phát để chứa dữ liệu.

GOBALHANDLE wavehandle;

HPSTR wavepointer;

MmSub.ckid=mmioFOURCC(‘d’,’a’,’t’,’a’);

if(mmioDescend(h,(LPMMCKINFO)&mmSub,(LPMMCKINFO)&mmParent, MMIO_FINDCHUNK))

{

mmioClose(h,0);

/* báo lỗi */

return(0);

}

if((wavehandle=GlobalAlloc(GMEM_MOVEBLEIGMEM_SHARE, mmSub.cksize))==NULL)

{

mmioClose(h,0);

/* báo lỗi */

return(0);

}

if(wavepointer=(HPSTR)gLOBALlOCK(WAVEHANDLE))==null)

{

GlobalFree(wavehandle);

mmioClose(h,0);

/* báo lỗi */

return(0);

}

if(mmioRead(h,wavepointer,mSub.cksize) !=mSub.cksize)

{

GlobalUnlock(wavehandle);

GlobalFree(wavehandle);

mmioClose(h,0);

/* báo lỗi */

return(0);

}

GlobalUnlock(wavehandle);

 

  1. Phương pháp tổng hợp tiếng nói từ các âm vị kép

          Dùng micro đọc các âm vị kép cho trước vào 2 file

  • Ti/Mi đọc vào một file
  • In đọc vào một file

Sau đó dùng hai con trỏ trỏ vào vùng dữ liệu của hai file trên, hai con trỏ được sử dụng gồm:

  • m_pcData1 trỏ vào vùng data của file Ti/Mi
  • m_pcData2 trỏ vào vùng data của file In

Hai con trỏ trên dùng để ghép hai vùng dữ liệu với nhau, sau đó ghi

vào file Synthetize. wav

  1. Chương trình

 

  1. Đọc file Wave

/*

      Open wave file with file name is @strFileName and window receives wave

                                        out message is *pParent.

                  Function return 0 if open succesfully. otherwise return -1

*/

int CWave::OpenFile(LPCTSTR strFileName)

{

          HMMIO hmmio;       //Handle multimedia input/output

MMRESULT   result; //Multimedia input/output result

          MMCKINFO   *pmmcki,*pmmckiParent;  //Multimedia chunk information

LONG           sizeread;       //Number byte are read

          DWORD                 dataread;      //Data bytes count are read of wave file

          //Open multimedia file does not have buffer or input/output procedure

          //Open for read data

          hmmio = mmioOpen((LPTSTR)strFileName,NULL,MMIO_READ);

          if(hmmio == NULL)

          {

                   AfxMessageBox(“Khong the mo duoc file nay”,MB_ICONSTOP);

                   return -1;

          }

          //Give memory for pmmckifParent and pmmckif

          pmmckiParent = new MMCKINFO;

          pmmcki                  = new MMCKINFO;

          /*if do not enough memory then

                   – report error to user

                   – close MMIO file Which you are opened

                   – exit function

          */

          if((pmmckiParent==NULL)||(pmmcki==NULL))

          {

                   AfxMessageBox(“Khong du bo nho”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   return -1;

          }

          //Find riff chunk

          pmmckiParent->ckid = mmioFOURCC(‘R’,’I’,’F’,’F’);

          pmmckiParent->fccType = mmioFOURCC(‘W’,’A’,’V’,’E’);

          result = mmioDescend(hmmio,pmmckiParent,NULL,MMIO_FINDRIFF);

          //if chunk not found then report and exit function

          if(result == MMIOERR_CHUNKNOTFOUND)

          {

                   AfxMessageBox(“Day khong phai la RIFF file”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Find format chunk

          pmmcki->ckid = mmioFOURCC(‘f’,’m’,’t’,’ ‘);

          result = mmioDescend(hmmio,pmmcki,pmmckiParent,MMIO_FINDCHUNK);

          //if chunk not found then report and exit function

          if(result == MMIOERR_CHUNKNOTFOUND)

          {

                   AfxMessageBox(“File nay khong co doan format”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Read data of format chunk and store in m_stWaveFormatEx

          sizeread = mmioRead(hmmio,(char *)&m_stWaveFormatEx,sizeof(WAVEFORMATEX));//Doc cau truc format cua file

          if(sizeread != sizeof(WAVEFORMATEX))

          {

                   AfxMessageBox(“Co loi khi doc file”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //If file format is not WAVE_FORMAT_PCM then report and exit function

          if(m_stWaveFormatEx.wFormatTag != WAVE_FORMAT_PCM)

          {

                   AfxMessageBox(“File nay khong phai la file PCM”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Go to end of format chunk

          result = mmioAscend(hmmio,pmmcki,0);

          //if the end of format chunk is not found then report and exit function

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Co loi trong cau truc file”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Find data chunk

          pmmcki->ckid = mmioFOURCC(‘d’,’a’,’t’,’a’);

          result = mmioDescend(hmmio,pmmcki,pmmckiParent,MMIO_FINDCHUNK);

          //If chunk not found then report and exit function

          if(result == MMIOERR_CHUNKNOTFOUND)

          {

                   AfxMessageBox(“Khong tim thay doan data”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Go to position that store data chunk size

          sizeread = mmioSeek(hmmio,-4,SEEK_CUR);

          if(sizeread == -1)

          {

                   AfxMessageBox(“Co loi khi tim kich thuoc doan du lieu”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Read data chunk size

        sizeread = mmioRead(hmmio,(char* )&m_dwDataSize,sizeof(DWORD));

          //if size read is not equance size of DWORD then report and exit function

          if(sizeread != sizeof(DWORD))

          {

                   AfxMessageBox(“Co loi khi doc kich thuoc doan du lieu”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Allocate mamory to store data

          //If has allocated then free and reallocate

          if(m_pcData != NULL)

          {

                   delete m_pcData;

                   m_pcData = NULL;

          }

          m_pcData = new char[m_dwDataSize];

          if(m_pcData == NULL)

          {

                   AfxMessageBox(“Thieu bo nho de doc du lieu”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   return -1;

          }

          //Read data of data chunk. If has error then report and exit function

          dataread = mmioRead(hmmio,m_pcData,m_dwDataSize);

          if(dataread != m_dwDataSize)

          {

                   AfxMessageBox(“Co loi khi doc du lieu cua file”,MB_ICONSTOP);

                   mmioClose(hmmio,MMIO_FHOPEN);

                   delete pmmcki;

                   delete pmmckiParent;

                   delete m_pcData;

                   m_pcData = NULL;

                   return -1;

          }

          //Free memory and close multimedia input/ouput procedure

          delete pmmcki;

          delete pmmckiParent;

          mmioClose(hmmio,MMIO_FHOPEN);

          return 0;

}

  1. Hiển thị tín hiệu tiếng nói

Xây dựng hàm Draw() để vẽ dạng sóng hiển thị tiếng nói, hàm Draw

được thiết kế như sau:

void CWndView::Draw()

{

     int i;

     int x,y;

     Eraser();

     CDC* pDC=GetDC();

     DrawAxis(pDC);

     CPen* pOldPen=pDC->SelectObject(&m_LinePen);

     y=(int)floor(m_deltaY*m_pData[0]);

     pDC->MoveTo(Xpos,Ypos-y);

     if(m_iType==3)

     {

          pDC->Ellipse(Xpos-4,Ypos-y-4,Xpos+4,Ypos-y+4);

         for(i=0;i<m_iSize;i++)

         {

                x=(int)(m_deltaX)*i;

                y=(int)floor(m_deltaY*m_pData[i]);

                pDC->LineTo(Xpos+x,Ypos-y);

                pDC->Ellipse(Xpos+x-2,Ypos-y-2,Xpos+x+2,Ypos-y+2);

         }

     }

     else

    {

         for(i=0;i<m_iSize;i++)

         {

                 x=(int)floor(m_deltaX*i);

                 y=(int)floor(m_deltaY*m_pData[i]);

                 pDC->LineTo(Xpos+x,Ypos-y);

         }

     }

    pDC->SelectObject(pOldPen);

    ReleaseDC(pDC);

}

  1. Ghép dữ liệu hai file Ti/Mi và In

 

int CWave::Save(LPCTSTR strFileName)

{

          MMRESULT   result;

          HMMIO                  hmmio;

          MMCKINFO   mmParckif,mmSubckif;

          LONG           sizewrite;

          hmmio = mmioOpen((char*)strFileName,NULL,MMIO_WRITE|MMIO_CREATE);

          if(hmmio == NULL)

          {

                   AfxMessageBox(“Khong the tao file moi”);

                   return -1;

          }

          m_dwDataSize = m_dwDataSize1+ m_dwDataSize2;

          mmParckif.cksize = 4+8+sizeof(WAVEFORMATEX)+8+m_dwDataSize;

          mmParckif.fccType = mmioFOURCC(‘W’,’A’,’V’,’E’);

          mmParckif.dwFlags = MMIO_DIRTY;

          //Create RIFF chunk

          result = mmioCreateChunk(hmmio,&mmParckif,MMIO_CREATERIFF);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Loi khi tao doan RIFF”);

                   return -1;

          }

          mmSubckif.ckid = mmioFOURCC(‘f’,’m’,’t’,’ ‘);

          mmSubckif.cksize = sizeof(WAVEFORMATEX);

          result = mmioCreateChunk(hmmio,&mmSubckif,NULL);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Loi khi tao doan format”);

                   return -1;

          }

          sizewrite = mmioWrite(hmmio,(char*)&m_stWaveFormatEx,sizeof(WAVEFORMATEX));

          if(sizewrite != sizeof(WAVEFORMATEX))

          {

                   AfxMessageBox(“Loi khi ghi du lieu cho doan format”);

                   return -1;

          }

          //Go to end of format chunk

          result = mmioAscend(hmmio,&mmSubckif,0);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Khong tim thay cuoi doan format”);

                   return -1;

          }

          //Create data chunk

          mmSubckif.ckid = mmioFOURCC(‘d’,’a’,’t’,’a’);

          mmSubckif.cksize = m_dwDataSize;

          result = mmioCreateChunk(hmmio,&mmSubckif,NULL);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Loi khi tao doan data”);

                   return -1;

          }

          int i;

          if(m_pcData != NULL)

          {

                   delete m_pcData;

                   m_pcData = NULL;

          }

          m_pcData = new char[m_dwDataSize];

          for(i=0;i<=m_dwDataSize1-1;i++)

          {

                   m_pcData[i] = m_pcData1[i];

          }

          for(i=m_dwDataSize1;i<=m_dwDataSize-1;i++)

          {

                   int j=i-m_dwDataSize1;

                   m_pcData[i] = m_pcData2[j];

          }

          sizewrite = mmioWrite(hmmio,m_pcData,m_dwDataSize);

          if(sizewrite != (LONG)m_dwDataSize)

          {

                   AfxMessageBox(“Loi khi ghi du lieu cho doan data”);

                   return -1;

          }

          result = mmioAscend(hmmio,&mmSubckif,0);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Khong tim thay cuoi doan format”);

                   return -1;

          }

          result = mmioClose(hmmio,MMIO_FHOPEN);

          if(result != MMSYSERR_NOERROR)

          {

                   AfxMessageBox(“Loi khi dong file”);

                   return -1;

          }

          return 0;

}

Leave a Reply