Q&A

  • GSM녹음이 안되네요.
안녕하세요.

gsm으로 음성을 녹음시키려 하는데 제대로 되지 않네요
녹음은 정상적으로 되는것 같은데 플레이를 해보면
처음 0.5초가량만 빼놓고는 엄청난 노이즈음이 섞여 들려
음성을 분간할수가 없습니다. gsm이 아닌 pcm등의
고용량의 포멧으로 녹음할때에는 잘 들리는것도 이상합니다.

아래는 재생과 녹음의 소스인데
제가 잘못한 부분이 어디인지 알려주시면 정말 감사하겠습니다.
(음성 컴포넌트로는 ACMIn, ACMOut, ACMConvertor를 사용하였습니다)


ps. 혹시 gsm보다 음성압축률이 더 좋은 코덱이 있나요?
그것을 녹음및 플레이할수 있는 컴포넌트도 있다면 좋을텐데..
알고 계신분 있으시다면 리플좀 부탁드립니다.

{------------------------------------------------------------------}
[녹음]

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ACMOut, ACMIn, ACMConvertor, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ACMConvertor1: TACMConvertor;
    ACMIn1: TACMIn;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure ACMIn1BufferFull(Sender: TObject; Data: Pointer;
      Size: Integer);
  private
    { Private declarations }
  public
    { Public declarations }
    SaveFile:TFileStream;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
try
  ACMConvertor1.ChooseFormatIn(True);
except
  showmessage('지원하지 않는 파일형식');
end;

ACMIn1.Open(ACMConvertor1.FormatIn);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
ACMIn1.Close;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
SaveFile := TFileStream.Create('c:test.dat',fmCreate or fmOpenReadWrite);
SaveFile.Position := 0;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
try
  SaveFile.Free;
except
end;
end;

procedure TForm1.ACMIn1BufferFull(Sender: TObject; Data: Pointer;
  Size: Integer);
begin
SaveFile.Write(Data^,Size);
end;

end.

{------------------------------------------------------------------}
[재생]

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ACMOut, ACMIn, ACMConvertor, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    ACMConvertor1: TACMConvertor;
    ACMOut1: TACMOut;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    OpenFile:TFileStream;
  end;

procedure PlayWaveStream(Stream:TStream;Size:integer;aFormat:TACMWaveFormat);

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure PlayWaveStream(Stream:TStream;Size:integer;aFormat:TACMWaveFormat);
var
data:PChar;
gauage,buffersize:integer;
begin
Stream.Position := 0;
buffersize := 1024;
gauage := 0;
GetMem(data,buffersize);

With TACMOut.Create(nil) Do
begin
  Open(aFormat);

  while gauage <= Stream.Size do
  begin
   Stream.Read(data^,buffersize);
   Play(data^,buffersize);
   gauage := gauage + buffersize;
  end;
end;

FreeMem(data,buffersize);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
try
  ACMConvertor1.ChooseFormatOut(True);
  PlayWaveStream(OpenFile,1024,ACMConvertor1.FormatOut);
except
  showmessage('지원하지 않는 파일형식');
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
OpenFile := TFileStream.Create('c:test.dat', fmOpenRead);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
try
  OpenFile.Free;
except
end;
end;

end.

{-------------------------------------------------------------------}
3  COMMENTS
  • Profile
    이광수 2002.06.07 23:51
    GSM이라면 마이크로 소프트 코덱인것 같군요.

    제가 알기로는 MS에서 사용할수 없게끔 프로텍션을 걸어 놓은 것으로압니다.
    따라서 일련의 특정한 초기화 방법을 사용해야 제대로 돌아가게 됩니다.

    그방법은 물론 MS가 안가르쳐 줍니다.
    아마도 MS가 GSM에 압축방식에 대한 특허료를 지불하고
    코덱을 만들어서 일겁니다.

    G723.1인가 하고 Wma도 역시 마찬가지 이유로 공개되지 않았습니다.

    하지만 방법은 존재하죠, 어떤분이 쓰신 사용법 입니다.
    저도 안해봤습니다.

    라이센스에 위배되는지도 모릅니다. 주의해서 쓰시길 바랍니다.

    ------------------------------------------------------------
    GSM610, G723.1 사용법

    type
    TSelectCodec = (PCM, ADPCM, GSM610, G723);

    type
    TACMWaveFormat = packed record
       case integer of
         0 : (Format  :TWaveFormatEx);
         1 : (RawData :Array[0..128] of byte);
    end;

    TG723WaveFormat = record
       wf    :TWaveFormatEx;
       extra :array[0..128] of BYTE;
    end;


    function TACMConfig.ACMConfig: Pointer;
    Var
    GSMWaveFmtEx    :array[0..49] of Byte;
    G723WaveFormat  :TG723WaveFormat;
    outp            :PChar;
    begin
    getmem(outp,50);
    if codec = GSM610 then begin
      GSMWaveFmtEx[0] := $31;
      GSMWaveFmtEx[1] := $00;
      GSMWaveFmtEx[2] := $01;
      GSMWaveFmtEx[3] := $00;
      GSMWaveFmtEx[4] := $40;
      GSMWaveFmtEx[5] := $1F;
      GSMWaveFmtEx[6] := $00;
      GSMWaveFmtEx[7] := $00;

      GSMWaveFmtEx[8] := $59;
      GSMWaveFmtEx[9] := $06;
      GSMWaveFmtEx[10] := $00;
      GSMWaveFmtEx[11] := $00;
      GSMWaveFmtEx[12] := $41;
      GSMWaveFmtEx[13] := $00;
      GSMWaveFmtEx[14] := $00;
      GSMWaveFmtEx[15] := $00;

      GSMWaveFmtEx[16] := $02;
      GSMWaveFmtEx[17] := $00;
      GSMWaveFmtEx[18] := $40;
      GSMWaveFmtEx[19] := $01;
      GSMWaveFmtEx[20] := $2A;
      GSMWaveFmtEx[21] := $2E;
      GSMWaveFmtEx[22] := $66;
      GSMWaveFmtEx[23] := $6D;

      GSMWaveFmtEx[24] := $74;
      GSMWaveFmtEx[25] := $29;
      GSMWaveFmtEx[26] := $00;
      GSMWaveFmtEx[27] := $2A;
      GSMWaveFmtEx[28] := $2E;
      GSMWaveFmtEx[29] := $66;
      GSMWaveFmtEx[30] := $6D;
      GSMWaveFmtEx[31] := $74;

      GSMWaveFmtEx[32] := $00;
      GSMWaveFmtEx[33] := $41;
      GSMWaveFmtEx[34] := $6E;
      GSMWaveFmtEx[35] := $79;
      GSMWaveFmtEx[36] := $20;
      GSMWaveFmtEx[37] := $66;
      GSMWaveFmtEx[38] := $69;
      GSMWaveFmtEx[39] := $6C;

      GSMWaveFmtEx[40] := $65;
      GSMWaveFmtEx[41] := $28;
      GSMWaveFmtEx[42] := $2A;
      GSMWaveFmtEx[43] := $2E;
      GSMWaveFmtEx[44] := $2A;
      GSMWaveFmtEx[45] := $29;
      GSMWaveFmtEx[46] := $00;
      GSMWaveFmtEx[47] := $2A;

      GSMWaveFmtEx[48] := $2E;
      GSMWaveFmtEx[49] := $2A;
       move(GSMWaveFmtEx[0],outp^,50) ;
    end;


    if codec = G723 then begin
       G723WaveFormat.wf.wFormatTag      := 66;
       G723WaveFormat.wf.nChannels       := 1;
       G723WaveFormat.wf.nSamplesPerSec  := 8000;
       G723WaveFormat.wf.nAvgBytesPerSec := 800;
       G723WaveFormat.wf.nBlockAlign     := 24;
       G723WaveFormat.wf.wBitsPerSample  := 0;
       G723WaveFormat.wf.cbSize          := 10;

       G723WaveFormat.extra[0]           := 2;
       G723WaveFormat.extra[1]           := 0;
       G723WaveFormat.extra[2]           := $CE;
       G723WaveFormat.extra[3]           := $9A;
       G723WaveFormat.extra[4]           := $32;
       G723WaveFormat.extra[5]           := $F7;
       G723WaveFormat.extra[6]           := $A2;
       G723WaveFormat.extra[7]           := $AE;
       G723WaveFormat.extra[8]           := $DE;
       G723WaveFormat.extra[9]           := $AC;

       move(G723WaveFormat, outp^, 50);
    end;

    if codec = PCM then begin
       getmem(fc.pwfx, 50);
       fc.pwfx.wFormatTag      := 1;
       fc.pwfx.nChannels       := 1;
       fc.pwfx.nSamplesPerSec  := 8000;
       fc.pwfx.nAvgBytesPerSec := 8000;
       fc.pwfx.nBlockAlign     := 1;
       fc.pwfx.wbitspersample  := 8;
       fc.pwfx.cbSize          := 129;
      
       move(fc.pwfx^,outp^,50);
    end;

    Result := outp;
    end;


  • Profile
    이은호 2002.06.08 01:25
    답변 감사드립니다.

    근데 제가 내공이 짧아서 그런지 소스를 봐도 어떻게 써야하는지
    이해가 안되는군요. ACMIn과 ACMOut, ACMConvertor를 이용한
    예제 하나 부탁드려도 될런지요. 어렵다면 어찌 사용해야하는지
    간단히 방법이라도 ㅜ_ㅜ.........

  • Profile
    이광수 2002.06.08 01:35
    ACMConvertor1.ChooseFormatIn(True);
    아마도 포멧 선택 Dialog를 사용하시나 보군요.
    저도 컴포넌트를 사용해보지 않아서 답을 드리기 어렵군요.

    즉 아래 소스는 ACMConvertor1.ChooseFormatIn(True);
    의 다이얼로그가 하는 역할과 동일합니다.
    즉 어떤 포맷으로 설정하는 가 죠.

    아마 컴포넌트 소스에 보면 수동으로 포맷을 설정하는 방법이 있을것 같군요.

    참고로 설명드리기가 조금 벅찹니다. 저도 정확한 지식이 모자르는군요.
    자료를 찾아서 해보시길 바랍니다.

    >답변 감사드립니다.
    >
    >근데 제가 내공이 짧아서 그런지 소스를 봐도 어떻게 써야하는지
    >이해가 안되는군요. ACMIn과 ACMOut, ACMConvertor를 이용한
    >예제 하나 부탁드려도 될런지요. 어렵다면 어찌 사용해야하는지
    >간단히 방법이라도 ㅜ_ㅜ.........
    >
    >>GSM이라면 마이크로 소프트 코덱인것 같군요.
    >>
    >>제가 알기로는 MS에서 사용할수 없게끔 프로텍션을 걸어 놓은 것으로압니다.
    >>따라서 일련의 특정한 초기화 방법을 사용해야 제대로 돌아가게 됩니다.
    >>
    >>그방법은 물론 MS가 안가르쳐 줍니다.
    >>아마도 MS가 GSM에 압축방식에 대한 특허료를 지불하고
    >>코덱을 만들어서 일겁니다.
    >>
    >>G723.1인가 하고 Wma도 역시 마찬가지 이유로 공개되지 않았습니다.
    >>
    >>하지만 방법은 존재하죠, 어떤분이 쓰신 사용법 입니다.
    >>저도 안해봤습니다.
    >>
    >>라이센스에 위배되는지도 모릅니다. 주의해서 쓰시길 바랍니다.
    >>
    >>------------------------------------------------------------
    >>GSM610, G723.1 사용법
    >>
    >>type
    >> TSelectCodec = (PCM, ADPCM, GSM610, G723);
    >>
    >>type
    >> TACMWaveFormat = packed record
    >>   case integer of
    >>     0 : (Format  :TWaveFormatEx);
    >>     1 : (RawData :Array[0..128] of byte);
    >> end;
    >>
    >> TG723WaveFormat = record
    >>   wf    :TWaveFormatEx;
    >>   extra :array[0..128] of BYTE;
    >> end;
    >>
    >>
    >>function TACMConfig.ACMConfig: Pointer;
    >>Var
    >> GSMWaveFmtEx    :array[0..49] of Byte;
    >> G723WaveFormat  :TG723WaveFormat;
    >> outp            :PChar;
    >>begin
    >> getmem(outp,50);
    >> if codec = GSM610 then begin
    >>  GSMWaveFmtEx[0] := $31;
    >>  GSMWaveFmtEx[1] := $00;
    >>  GSMWaveFmtEx[2] := $01;
    >>  GSMWaveFmtEx[3] := $00;
    >>  GSMWaveFmtEx[4] := $40;
    >>  GSMWaveFmtEx[5] := $1F;
    >>  GSMWaveFmtEx[6] := $00;
    >>  GSMWaveFmtEx[7] := $00;
    >>
    >>  GSMWaveFmtEx[8] := $59;
    >>  GSMWaveFmtEx[9] := $06;
    >>  GSMWaveFmtEx[10] := $00;
    >>  GSMWaveFmtEx[11] := $00;
    >>  GSMWaveFmtEx[12] := $41;
    >>  GSMWaveFmtEx[13] := $00;
    >>  GSMWaveFmtEx[14] := $00;
    >>  GSMWaveFmtEx[15] := $00;
    >>
    >>  GSMWaveFmtEx[16] := $02;
    >>  GSMWaveFmtEx[17] := $00;
    >>  GSMWaveFmtEx[18] := $40;
    >>  GSMWaveFmtEx[19] := $01;
    >>  GSMWaveFmtEx[20] := $2A;
    >>  GSMWaveFmtEx[21] := $2E;
    >>  GSMWaveFmtEx[22] := $66;
    >>  GSMWaveFmtEx[23] := $6D;
    >>
    >>  GSMWaveFmtEx[24] := $74;
    >>  GSMWaveFmtEx[25] := $29;
    >>  GSMWaveFmtEx[26] := $00;
    >>  GSMWaveFmtEx[27] := $2A;
    >>  GSMWaveFmtEx[28] := $2E;
    >>  GSMWaveFmtEx[29] := $66;
    >>  GSMWaveFmtEx[30] := $6D;
    >>  GSMWaveFmtEx[31] := $74;
    >>
    >>  GSMWaveFmtEx[32] := $00;
    >>  GSMWaveFmtEx[33] := $41;
    >>  GSMWaveFmtEx[34] := $6E;
    >>  GSMWaveFmtEx[35] := $79;
    >>  GSMWaveFmtEx[36] := $20;
    >>  GSMWaveFmtEx[37] := $66;
    >>  GSMWaveFmtEx[38] := $69;
    >>  GSMWaveFmtEx[39] := $6C;
    >>
    >>  GSMWaveFmtEx[40] := $65;
    >>  GSMWaveFmtEx[41] := $28;
    >>  GSMWaveFmtEx[42] := $2A;
    >>  GSMWaveFmtEx[43] := $2E;
    >>  GSMWaveFmtEx[44] := $2A;
    >>  GSMWaveFmtEx[45] := $29;
    >>  GSMWaveFmtEx[46] := $00;
    >>  GSMWaveFmtEx[47] := $2A;
    >>
    >>  GSMWaveFmtEx[48] := $2E;
    >>  GSMWaveFmtEx[49] := $2A;
    >>   move(GSMWaveFmtEx[0],outp^,50) ;
    >> end;
    >>
    >>
    >> if codec = G723 then begin
    >>   G723WaveFormat.wf.wFormatTag      := 66;
    >>   G723WaveFormat.wf.nChannels       := 1;
    >>   G723WaveFormat.wf.nSamplesPerSec  := 8000;
    >>   G723WaveFormat.wf.nAvgBytesPerSec := 800;
    >>   G723WaveFormat.wf.nBlockAlign     := 24;
    >>   G723WaveFormat.wf.wBitsPerSample  := 0;
    >>   G723WaveFormat.wf.cbSize          := 10;
    >>
    >>   G723WaveFormat.extra[0]           := 2;
    >>   G723WaveFormat.extra[1]           := 0;
    >>   G723WaveFormat.extra[2]           := $CE;
    >>   G723WaveFormat.extra[3]           := $9A;
    >>   G723WaveFormat.extra[4]           := $32;
    >>   G723WaveFormat.extra[5]           := $F7;
    >>   G723WaveFormat.extra[6]           := $A2;
    >>   G723WaveFormat.extra[7]           := $AE;
    >>   G723WaveFormat.extra[8]           := $DE;
    >>   G723WaveFormat.extra[9]           := $AC;
    >>
    >>   move(G723WaveFormat, outp^, 50);
    >> end;
    >>
    >> if codec = PCM then begin
    >>   getmem(fc.pwfx, 50);
    >>   fc.pwfx.wFormatTag      := 1;
    >>   fc.pwfx.nChannels       := 1;
    >>   fc.pwfx.nSamplesPerSec  := 8000;
    >>   fc.pwfx.nAvgBytesPerSec := 8000;
    >>   fc.pwfx.nBlockAlign     := 1;
    >>   fc.pwfx.wbitspersample  := 8;
    >>   fc.pwfx.cbSize          := 129;
    >>  
    >>   move(fc.pwfx^,outp^,50);
    >> end;
    >>
    >> Result := outp;
    >>end;
    >>
    >>
    >>>안녕하세요.
    >>>
    >>>gsm으로 음성을 녹음시키려 하는데 제대로 되지 않네요
    >>>녹음은 정상적으로 되는것 같은데 플레이를 해보면
    >>>처음 0.5초가량만 빼놓고는 엄청난 노이즈음이 섞여 들려
    >>>음성을 분간할수가 없습니다. gsm이 아닌 pcm등의
    >>>고용량의 포멧으로 녹음할때에는 잘 들리는것도 이상합니다.
    >>>
    >>>아래는 재생과 녹음의 소스인데
    >>>제가 잘못한 부분이 어디인지 알려주시면 정말 감사하겠습니다.
    >>>(음성 컴포넌트로는 ACMIn, ACMOut, ACMConvertor를 사용하였습니다)
    >>>
    >>>
    >>>ps. 혹시 gsm보다 음성압축률이 더 좋은 코덱이 있나요?
    >>>그것을 녹음및 플레이할수 있는 컴포넌트도 있다면 좋을텐데..
    >>>알고 계신분 있으시다면 리플좀 부탁드립니다.
    >>>
    >>>{------------------------------------------------------------------}
    >>>[녹음]
    >>>
    >>>unit Unit1;
    >>>
    >>>interface
    >>>
    >>>uses
    >>>  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    >>>  Dialogs, ACMOut, ACMIn, ACMConvertor, StdCtrls;
    >>>
    >>>type
    >>>  TForm1 = class(TForm)
    >>>    Button1: TButton;
    >>>    Button2: TButton;
    >>>    ACMConvertor1: TACMConvertor;
    >>>    ACMIn1: TACMIn;
    >>>    procedure Button1Click(Sender: TObject);
    >>>    procedure Button2Click(Sender: TObject);
    >>>    procedure FormCreate(Sender: TObject);
    >>>    procedure FormDestroy(Sender: TObject);
    >>>    procedure ACMIn1BufferFull(Sender: TObject; Data: Pointer;
    >>>      Size: Integer);
    >>>  private
    >>>    { Private declarations }
    >>>  public
    >>>    { Public declarations }
    >>>    SaveFile:TFileStream;
    >>>  end;
    >>>
    >>>var
    >>>  Form1: TForm1;
    >>>
    >>>implementation
    >>>
    >>>{$R *.dfm}
    >>>
    >>>procedure TForm1.Button1Click(Sender: TObject);
    >>>begin
    >>> try
    >>>  ACMConvertor1.ChooseFormatIn(True);
    >>> except
    >>>  showmessage('지원하지 않는 파일형식');
    >>> end;
    >>>
    >>> ACMIn1.Open(ACMConvertor1.FormatIn);
    >>>end;
    >>>
    >>>procedure TForm1.Button2Click(Sender: TObject);
    >>>begin
    >>> ACMIn1.Close;
    >>>end;
    >>>
    >>>procedure TForm1.FormCreate(Sender: TObject);
    >>>begin
    >>> SaveFile := TFileStream.Create('c:test.dat',fmCreate or fmOpenReadWrite);
    >>> SaveFile.Position := 0;
    >>>end;
    >>>
    >>>procedure TForm1.FormDestroy(Sender: TObject);
    >>>begin
    >>> try
    >>>  SaveFile.Free;
    >>> except
    >>> end;
    >>>end;
    >>>
    >>>procedure TForm1.ACMIn1BufferFull(Sender: TObject; Data: Pointer;
    >>>  Size: Integer);
    >>>begin
    >>> SaveFile.Write(Data^,Size);
    >>>end;
    >>>
    >>>end.
    >>>
    >>>{------------------------------------------------------------------}
    >>>[재생]
    >>>
    >>>unit Unit1;
    >>>
    >>>interface
    >>>
    >>>uses
    >>>  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
    >>>  Dialogs, ACMOut, ACMIn, ACMConvertor, StdCtrls;
    >>>
    >>>type
    >>>  TForm1 = class(TForm)
    >>>    Button1: TButton;
    >>>    Button2: TButton;
    >>>    ACMConvertor1: TACMConvertor;
    >>>    ACMOut1: TACMOut;
    >>>    procedure Button1Click(Sender: TObject);
    >>>    procedure FormCreate(Sender: TObject);
    >>>    procedure FormDestroy(Sender: TObject);
    >>>  private
    >>>    { Private declarations }
    >>>  public
    >>>    { Public declarations }
    >>>    OpenFile:TFileStream;
    >>>  end;
    >>>
    >>>procedure PlayWaveStream(Stream:TStream;Size:integer;aFormat:TACMWaveFormat);
    >>>
    >>>var
    >>>  Form1: TForm1;
    >>>
    >>>implementation
    >>>
    >>>{$R *.dfm}
    >>>
    >>>
    >>>procedure PlayWaveStream(Stream:TStream;Size:integer;aFormat:TACMWaveFormat);
    >>>var
    >>> data:PChar;
    >>> gauage,buffersize:integer;
    >>>begin
    >>> Stream.Position := 0;
    >>> buffersize := 1024;
    >>> gauage := 0;
    >>> GetMem(data,buffersize);
    >>>
    >>> With TACMOut.Create(nil) Do
    >>> begin
    >>>  Open(aFormat);
    >>>
    >>>  while gauage <= Stream.Size do
    >>>  begin
    >>>   Stream.Read(data^,buffersize);
    >>>   Play(data^,buffersize);
    >>>   gauage := gauage + buffersize;
    >>>  end;
    >>> end;
    >>>
    >>> FreeMem(data,buffersize);
    >>>end;
    >>>
    >>>procedure TForm1.Button1Click(Sender: TObject);
    >>>begin
    >>> try
    >>>  ACMConvertor1.ChooseFormatOut(True);
    >>>  PlayWaveStream(OpenFile,1024,ACMConvertor1.FormatOut);
    >>> except
    >>>  showmessage('지원하지 않는 파일형식');
    >>> end;
    >>>end;
    >>>
    >>>procedure TForm1.FormCreate(Sender: TObject);
    >>>begin
    >>> OpenFile := TFileStream.Create('c:test.dat', fmOpenRead);
    >>>end;
    >>>
    >>>procedure TForm1.FormDestroy(Sender: TObject);
    >>>begin
    >>> try
    >>>  OpenFile.Free;
    >>> except
    >>> end;
    >>>end;
    >>>
    >>>end.
    >>>
    >>>{-------------------------------------------------------------------}
    >>
    >