Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Lyrics sync out after the first minute 1

Status
Not open for further replies.

doctorjellybean

Programmer
May 5, 2003
145
It all started on this post, and I'm very grateful to Daddy and Glenn for their input on that.


I had to make some changes, because the original code in that post created and read the timestamps in the [000000] format, and I discovered later that it needed to be done in the [00:00.00] format.

The amended code works, but I've discovered that it is perfectly in sync for the first minute. After that the lyrics seems to appear a millisecond/second before it's due. It doesn't make sense to me, as it uses the same code to create and read back.

The following is the Time creation code

Code:
function TExistingLyricsForm.PostionToTime(const vPos : Integer) : String;

var
 dt : TDateTime;
begin

  Result := '00:00.00';
  if vPos <= 0 then Exit;
  dt := vPos / MSecsPerSec / SecsPerDay;
  //  milliseconds can be displayed with leading zeros
  // and without leading zeros but can have 3 digits
  // so default it to milliseconds with leading zeros
  // and get rid of the last digit
  Result := Copy(FormatDateTime('nn:ss.zzz', dt), 1, 8);
end;

function MinutesToMilli(vMinutes : String) : Integer;
var
iPos, iMinutes, iSeconds : Integer;
sTmp : String;
begin
try
  sTmp := vMinutes;
  iPos := Pos(':', sTmp);
  iMinutes := (StrToInt(Copy(sTmp, 1, iPos - 1)) * 60000);
  sTmp := Copy(sTmp, iPos+1, Length(sTmp));
  iPos := Pos('.', sTmp);
  iSeconds := (StrToInt(Copy(sTmp, 1, iPos - 1)) * 100);
  if iMinutes > 0 then
  iSeconds := iSeconds * 10;
  sTmp := Copy(sTmp, iPos+1, Length(sTmp));
  Result := (iMinutes + iSeconds + StrToInt(sTmp));
  if Result < 60000 then
  Result := (Result * 10);
  except
     Result := 0;
  end;
end;

procedure LoadLyricFile(LFile: string);
  var
    LTextfile: Text;
    LTextString: String;
    RPos: integer;
  begin
    assign(LTextFile, LFile);
    Reset(LTextFile);
    LyricCount := 1;
    while not eof(LTextFile) do
      begin
        readln(LTextFile, LTextString);
        RPos := Pos(']', LTextString);
        LyricRecord[LyricCount].LyricLine :=
                Copy(LTextString, RPos+1, Length(LTextString)-RPos);
         LyricRecord[LyricCount].OffsetStr :=
                Copy(LtextString, 2, RPos-2);
        LyricRecord[LyricCount].OffsetStr :=
                Copy(LtextString, 2, RPos-2);
        LyricRecord[LyricCount].Offset :=
                MinutesToMilli(LyricRecord[LyricCount].OffsetStr);
        inc(lyriccount);
      end;
    { to signal the logic we're done with the data }
    LyricRecord[LyricCount].Offset := 999999999;
    close(LTextFile);
end;

procedure TExistingLyricsForm.Btn_CreateTimeStampsClick(Sender: TObject);
begin
if Btn_CreateTimeStamps.Caption='Create Blank' then begin
memo_lyrics.lines.Add('['+PostionToTime(MediaPlayer1.Position)+']');
btn_createtimestamps.Caption:='Create Time';
btn_createtimestamps.Enabled:=false;
exit end else
if Btn_ClearTimeStamps.Enabled=false then Btn_ClearTimeStamps.Enabled:=true;
if x = 0 then begin
memo_lyrics.lines.Delete(x);
memo_lyrics.lines.Insert(x,'['+PostionToTime(MediaPlayer1.Position)+']'+lyricslist[x]);
{memo_timestamps.items.Add('['+inttostr(mediaplayer1.Position)+']'+lyricslist[x]);}
x:= x+1;
exit end else
{lyricslist.Count;
if lyricslist.Count=x then begin
Btn_CreateStamps.Enabled:=false;
exit end else}
memo_lyrics.lines.Delete(x);
memo_lyrics.lines.Insert(x,'['+PostionToTime(MediaPlayer1.Position)+']'+lyricslist[x]);
x:=x+1;
if lyricslist.Count=x then Btn_CreateTimeStamps.Caption:='Create Blank';
if lyricslist.Count=x then Btn_TestTimeStamps.Enabled:=true;
if lyricslist.Count=x then Btn_SaveTimeStamps.Enabled:=true;
end;

The code in the Media Player for playback

Code:
function TMediaPlayerForm.PostionToTime(const vPos : Integer) : String;

var
  dt : TDateTime;
begin
  Result := '00:00.00';
  if vPos <= 0 then Exit;
  dt := vPos / MSecsPerSec / SecsPerDay;
  //  milliseconds can be displayed with leading zeros
  // and without leading zeros but can have 3 digits
  // so default it to milliseconds with leading zeros
  // and get rid of the last digit
  Result := Copy(FormatDateTime('nn:ss.zzz', dt), 1, 8);
end;

function MinutesToMilli(vMinutes : String) : Integer;
var
iPos, iMinutes, iSeconds : Integer;
sTmp : String;
begin
try
sTmp := vMinutes;
iPos := Pos(':', sTmp);
iMinutes := (StrToInt(Copy(sTmp, 1, iPos - 1)) * 60000);
sTmp := Copy(sTmp, iPos+1, Length(sTmp));
iPos := Pos('.', sTmp);
iSeconds := (StrToInt(Copy(sTmp, 1, iPos - 1)) * 100);
if iMinutes > 0 then
iSeconds := iSeconds * 10;
sTmp := Copy(sTmp, iPos+1, Length(sTmp));
Result := (iMinutes + iSeconds + StrToInt(sTmp));
if Result < 60000 then
Result := (Result * 10);
except
Result := 0;
end;
end;

procedure LoadLyricFile(LFile: string);
  var
    LTextfile: Text;
    LTextString: String;
    RPos: integer;
  begin
    assign(LTextFile, LFile);
    Reset(LTextFile);
    LyricCount := 1;
    while not eof(LTextFile) do
      begin
        readln(LTextFile, LTextString);
        RPos := Pos(']', LTextString);
        LyricRecord[LyricCount].LyricLine :=
                Copy(LTextString, RPos+1, Length(LTextString)-RPos);
         LyricRecord[LyricCount].OffsetStr :=
                Copy(LtextString, 2, RPos-2);
        LyricRecord[LyricCount].OffsetStr :=
                Copy(LtextString, 2, RPos-2);
        LyricRecord[LyricCount].Offset :=
                MinutesToMilli(LyricRecord[LyricCount].OffsetStr);
        inc(lyriccount);
      end;
    { to signal the logic we're done with the data }
    LyricRecord[LyricCount].Offset := 999999999;
    close(LTextFile);
  end;

procedure TMediaPlayerForm.Timer_TrackPlayerTimer(Sender: TObject);
begin
if MediaPlayer1.Position >= LyricRecord[LyricPos].Offset then
    begin
      label_lyrics.caption:= LyricRecord[LyricPos].LyricLine;
      inc(LyricPos);
    end;
  if MediaPlayer1.Position = MediaPlayer1.TrackLength[1] then
    begin
      Timer_TrackPlayer.Enabled := false;
      LyricPos := 1;
end;
end;

I've uploaded the project and an example (2MB), as it might be easier to try it than just reading the code.

Just as a matter of interest. I've also tried this amended code in D6, because D6 doesn't recognize MSecsPerSec.

Code:
function TLyricsMediaPlayerForm.PostionToTime(const vPos : Integer) : String;
var
dt : TDateTime;
begin
Result := '00:00.00';
if vPos <= 0 then Exit;
Dt := vPos / (24*60*60*1000) / (24*60*60);
//  milliseconds can be displayed with leading zeros
// and without leading zeros but can have 3 digits
// so default it to milliseconds with leading zeros
// and get rid of the last digit
Result := Copy(FormatDateTime('nn:ss:zzz', dt), 1, 8);
end;

The example project was created in D2010, and wether I use dt := vPos / MSecsPerSec / SecsPerDay; or Dt := vPos / (24*60*60*1000) / (24*60*60); doesn't appear to make any difference.

I hope someone can figure out this very strange behaviour.

Thanks in advance.
 
From glancing at the project you posted in the link, what seems to be indicated is that you need to look closely at your conversion calculations. You may not be getting what you are expecting for truncation or the like from one case to the other (div is different than / when it comes to division as well).

In the thread you referenced, look at my post of 10 Apr 08 16:46 for a good example of converting a ms value to hours, minutes, and seconds.

I'm waiting for the white paper entitled "Finding Employment in the Era of Occupational Irrelevancy
 
Thanks Glenn.

I've had a look at your code

Code:
function TForm1.mstotime(inms: longint): string;
  var
    minute, second, ms: longint;
  begin
    minute := (inms div 1000) div 60;
    second := (inms div 1000) mod 60;
    ms := inms mod 1000;
    Result := IntToStr(minute) + ':' + IntToStr(second) + ':' + InttoStr(ms);
  end;

and I've looked at the code in the sample I've uploaded. I'm not sure in which section of my code I should use your code.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top