HydrogenAudio

Hydrogenaudio Forum => General Audio => Topic started by: sn0wman on 2006-09-26 13:14:25

Title: help with Delphi code
Post by: sn0wman on 2006-09-26 13:14:25
hello.

trying to cover this topic (http://www.hydrogenaudio.org/forums/index.php?showtopic=48405), i decided to use directly pipes in delphi to support unicode files (+paths) in my program.
i am turning to you for help cause i have no more ideas what i can do wrong.
the code i paste below input file 'a.flac' to encoder through pipe, and receive the results through another one. function seems to work, but weirdly stops receiving data after about 98% done. the piece i save on the disc matches with respondent reference WAV file, except it is only a 98% piece. notice: whole flac file IS passed to encoder as i can see in in task manager.

('a.flac' -> within debugging, oryginally file is passed as parameter 'Param')

(i am sure its not text/binary mode issue cause 98% could not be encoded if it was, and, there are single \x0A characters in received output)

//edit3: if you think you can help, here (http://dl3.ohshare.com/v/2965689/evilpackage.rar.html) is the link to the packege with the code and everything needed to test it.

Code: [Select]
function Run_EN(App:Widestring; Param:Widestring) : string;

var

             SA : TSecurityAttributes;
             SI : TStartupInfo;
             PI : TProcessInformation;
StdOutPipeRead,
StdOutPipeWrite,
StdInPipeRead,
StdInPipeWrite,
StdErrPipeRead,
StdErrPipeWrite : THandle;
    WasOKwrite,
     WasOKread,
      ProcessOK : Boolean;
         Buffer : array[0..65535] of char; //or bytes, whatever
     BytesRead,
  BytesWritten,
           Code : Cardinal;
              F : TTntFileStream;
              M : TMemoryStream;
            Read : longword;
  
begin

//SAME1
{SA.nLength:=sizeof(SA);
SA.lpSecurityDescriptor:=nil;
SA.bInheritHandle:=TRUE;

if (CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 65536)and CreatePipe(StdInPipeRead, StdInPipeWrite, @SA, 65536) and CreatePipe(StdErrPipeRead, StdErrPipeWrite, @SA, 65536)) then
   begin
   ZeroMemory(@SI, sizeof(SI));
   with SI do
     begin
     cb := sizeof(SI);
     dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
     wShowWindow := SW_HIDE;
     hStdInput := StdInPipeRead;
     hStdOutput := StdOutPipeWrite;
     hStdError := StdErrPipeWrite;
     end;}
//\SAME1


//SAME_2: just longer
if (CreatePipe(StdOutPipeRead, StdOutPipeWrite, nil, 65536)and CreatePipe(StdInPipeRead, StdInPipeWrite, nil, 65536) and CreatePipe(StdErrPipeRead, StdErrPipeWrite, nil, 65536)) then
   begin

   DuplicateHandle(GetCurrentProcess(), StdOutPipeWrite,GetCurrentProcess(), @StdOutPipeWriteDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);
   DuplicateHandle(GetCurrentProcess(), StdInPipeRead,GetCurrentProcess(), @StdInPipeReadDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);
   DuplicateHandle(GetCurrentProcess(), StdErrPipeWrite,GetCurrentProcess(), @StdErrPipeWriteDup, 0, True,DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS);

   ZeroMemory(@SI, sizeof(SI));
   with SI do
     begin
     cb := sizeof(SI);
     dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
     wShowWindow := SW_HIDE;
     hStdInput := StdInPipeReadDup;
     hStdOutput := StdOutPipeWriteDup;
     hStdError := StdErrPipeWriteDup;
     end;
//\SAME_2


   ProcessOK:=CreateProcessW(nil,pwidechar('flac.exe -d -o - --totally-silent -'), nil, nil, TRUE, 0, nil, pwidechar(FWorkDir), SI, PI);

  

//SAME_1
{   CloseHandle(StdInPipeRead);StdInPipeRead:=0;
   CloseHandle(StdOutPipeWrite);StdOutPipeWrite:=0;
   CloseHandle(StdErrPipeWrite);StdErrPipeWrite:=0;}
//\SAME_1

//SAME_2
   CloseHandle(StdInPipeReadDup);StdInPipeReadDup:=0;
   CloseHandle(StdOutPipeWriteDup);StdOutPipeWriteDup:=0;
   CloseHandle(StdErrPipeWriteDup);StdErrPipeWriteDup:=0;
//\SAME_2

   F:=TFileStream.Create('a.flac', fmOpenRead or fmShareDenyWrite);
   M:=TMemoryStream.Create;

     WasOKwrite:=true;
   bytesWritten:=1;
   BytesToWrite := F.Read(FileBuffer, 65536);

   if ProcessOK then
      while (BytesToWrite>0)and(WasOKwrite)and(bytesWritten>0) do
        begin
        WasOKwrite:=WriteFile(StdInPipeWrite, FileBuffer, BytesToWrite, bytesWritten, nil);
        while WaitForSingleObject(PI.hProcess, 20)=WAIT_OBJECT_0 do Application.ProcessMessages;
          repeat
          if PeekNamedPipe(StdOutPipeRead,nil,0,nil,@BytesToRead,nil) and (BytesToRead>0) then
//Peek must be or hangs
//after ~98% it says '0' bytes left
             begin
             setlength(PipeBuffer,BytesToRead);
             WasOKread:=ReadFile(StdOutPipeRead, PipeBuffer[0], BytesToRead, bytesRead, nil);
             Written:=M.Write(PipeBuffer[0], bytesRead);
             end
          else Break;
          until not WasOKread or (BytesRead = 0);
          
          //there is no error anyway
  {        repeat
          if PeekNamedPipe(StdErrPipeRead,nil,0,nil,@BytesToRead,nil) and (BytesToRead>0) then
             WasOKread:=ReadFile(StdErrPipeRead, Buffer, BytesToRead, bytesRead, nil)
          else Break;
          until not WasOKread or (BytesRead = 0);}

        BytesToWrite := F.Read(FileBuffer, 65536);
        end;

//save result to compare externally
   M.SaveToFile('a.wav');

   M.Free;
   F.Free;
   SetLength(PipeBuffer,0);
   CloseHandle(PI.hThread);
   CloseHandle(PI.hProcess);
   TerminateProcess(PI.hProcess, Code);
   CloseHandle(StdOutPipeRead);
   CloseHandle(StdErrPipeRead);
   CloseHandle(StdInPipeWrite);
   end;
end;


thing that makes me wondering:

in a simplier version of this function, where i only capture the output and pass input to encoder normally
'flac.exe -d -c --totally-silent a.flac'
my 'reading' code looks like this (the simplier only-reading version works fine !!!):

Code: [Select]
   if ProcessOK then
      repeat
         WasOKread:=ReadFile(StdOutPipeRead, Buffer, 65536, BytesRead, nil);
         M.Write(Buffer, bytesRead);
      until not WasOKread or (BytesRead = 0);


no peek needed, and the more, when i only use it

Code: [Select]
   if ProcessOK then
      repeat
      if PeekNamedPipe(StdOutPipeRead,nil,0,nil,@BytesRead,nil) and (BytesRead>0) then
         begin
         WasOKread:=ReadFile(StdOutPipeRead, Buffer, 65536, BytesRead, nil);
         M.Write(Buffer, bytesRead);
         end
      else break;
      until not WasOKread or (BytesRead = 0);


file is NOT read. from all i know, this function with these parameters ONLY checks if something is waiting in a pipe, so teoretically it is even more proper. but this is just what i found trying to make working the 'full' version above.

i hope you are smarter than me  and will tell me whats wrong...!

//edit:
i noticed ~98% on two (all i tried, big~22MB and small~0,5MB one) FLAC files. when i tried same thing with lame and 52KB MP3 file, i got only 53KB output, instead of 517KB. i also noticed, that small changes in code, like now
      
Code: [Select]
while WaitForSingleObject(PI.hProcess, 20)=WAIT_OBJECT_0 do Application.ProcessMessages;

becomes->
      
Code: [Select]
while WaitForSingleObject(PI.hProcess, 10)=WAIT_OBJECT_0 do Application.ProcessMessages;

i got less output, so if before i got from 437KB FLAC file always 448KB output, now i have 432KB. but no matter how much time i give, its never more than that previous 448KB.

hmm...am i dumb or something ?

//edit2:
one more thing : PeekNamedPipe does not return any error.