unit UMusic; interface uses Classes, MPlayer, Windows, Messages, SysUtils, Forms, ULog, USongs, Bass;//, DXSounds; procedure InitializeSound; type TSoundCard = record Name: string; Source: array of string; end; TFFTData = array [0..256] of Single; TCustomSoundEntry = record Filename: String; Handle: hStream; end; TMusic = class private // MediaPlayer: TMediaPlayer; // It will be replaced by another component; { MediaPlayerStart: TMediaPlayer; // or maybe not if done this way ;) MediaPlayerBack: TMediaPlayer; MediaPlayerSwoosh: TMediaPlayer; MediaPlayerChange: TMediaPlayer; MediaPlayerOption: TMediaPlayer; MediaPlayerClick: TMediaPlayer; MediaPlayerDrum: TMediaPlayer; MediaPlayerHihat: TMediaPlayer; MediaPlayerClap: TMediaPlayer; MediaPlayerShuffle: TMediaPlayer;} BassStart: hStream; // Wait, I've replaced this with BASS BassBack: hStream; // It has almost all features we need BassSwoosh: hStream; BassChange: hStream; // Almost? It aleady has them all :) BassOption: hStream; BassClick: hStream; BassDrum: hStream; BassHihat: hStream; BassClap: hStream; BassShuffle: hStream; //Custom Sounds CustomSounds: array of TCustomSoundEntry; Loaded: boolean; Loop: boolean; // DXSound: TDXSound; // Player: TcmxMp3; public Bass: hStream; // SoundCard: array of TSoundCard; procedure InitializePlayback; procedure InitializeRecord; procedure SetVolume(Volume: integer); procedure SetMusicVolume(Volume: integer); procedure SetLoop(Enabled: boolean); function Open(Name: string): boolean; // true if succeed procedure Rewind; procedure MoveTo(Time: real); procedure Play; procedure Pause; //Pause Mod procedure Stop; procedure Close; function Finished: boolean; function Length: real; function Position: real; procedure PlayStart; procedure PlayBack; procedure PlaySwoosh; procedure PlayChange; procedure PlayOption; procedure PlayClick; procedure PlayDrum; procedure PlayHihat; procedure PlayClap; procedure PlayShuffle; procedure StopShuffle; procedure CaptureStart; procedure CaptureStop; procedure CaptureCard(RecordI, PlayerLeft, PlayerRight: byte); procedure StopCard(Card: byte); function LoadPlayerFromFile(var MediaPlayer: TMediaPlayer; Name: string): boolean; function LoadSoundFromFile(var hStream: hStream; Name: string): boolean; //Equalizer function GetFFTData: TFFTData; //Custom Sounds function LoadCustomSound(const Filename: String): Cardinal; procedure PlayCustomSound(const Index: Cardinal); end; const RecordSystem = 1; type TMuzyka = record Path: string; Start: integer; // start of song in ms // BPM: array of TBPM; // Gap: real; IlNut: integer; DlugoscNut: integer; // WartoscNut: integer; end; TCzesci = record Akt: integer; // aktualna czesc utworu do rysowania High: integer; Ilosc: integer; Resolution: integer; NotesGAP: integer; Wartosc: integer; Czesc: array of record Start: integer; StartNote: integer; Lyric: string; LyricWidth: real; Koniec: integer; BaseNote: integer; HighNut: integer; IlNut: integer; TotalNotes: integer; Nuta: array of record Color: integer; Start: integer; Dlugosc: integer; Ton: integer; TonGamy: integer; Tekst: string; FreeStyle: boolean; Wartosc: integer; // zwykla nuta x1, zlota nuta x2 end; end; end; TCzas = record // wszystko, co dotyczy aktualnej klatki // BajtowTotal: integer; // BajtowTeraz: integer; // BajtowNaSek: integer; OldBeat: integer; // poprzednio wykryty beat w utworze AktBeat: integer; // aktualny beat w utworze MidBeat: real; // dokladny AktBeat // should not be used // OldHalf: integer; // poprzednio wykryta polowka // AktHalf: integer; // aktualna polowka w utworze // MidHalf: real; // dokladny AktHalf // now we use this for super synchronization! // only used when analyzing voice OldBeatD: integer; // poprzednio wykryty beat w utworze AktBeatD: integer; // aktualny beat w utworze MidBeatD: real; // dokladny AktBeatD FracBeatD: real; // fractional part of MidBeatD // we use this for audiable clicks OldBeatC: integer; // poprzednio wykryty beat w utworze AktBeatC: integer; // aktualny beat w utworze MidBeatC: real; // dokladny AktBeatC FracBeatC: real; // fractional part of MidBeatC OldCzesc: integer; // poprzednio wyswietlana czesc // akt jest w czesci.akt Teraz: real; // aktualny czas w utworze Razem: real; // caly czas utworu // TerazSek: integer; end; var Form: TForm; Music: TMusic; // muzyka Muzyka: TMuzyka; // czesci z nutami; Czesci: array of TCzesci; // czas Czas: TCzas; fHWND: Thandle; const ModeStr: array[TMPModes] of string = ('Not ready', 'Stopped', 'Playing', 'Recording', 'Seeking', 'Paused', 'Open'); implementation uses UGraphic, URecord, UFiles, UIni, UMain, UThemes; procedure InitializeSound; begin Log.LogStatus('Initializing Playback', 'InitializeSound'); Music.InitializePlayback; Log.LogStatus('Initializing Record', 'InitializeSound'); Music.InitializeRecord; end; procedure TMusic.InitializePlayback; var Pet: integer; S: integer; begin Log.BenchmarkStart(4); Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize'); Loaded := false; Loop := false; fHWND := AllocateHWND( nil); if not BASS_Init(1, 44100, 0, fHWND, nil) then begin Application.MessageBox ('Could not initialize BASS', 'Error'); Exit; end; Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4); // config playing buffer // BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10); // BASS_SetConfig(BASS_CONFIG_BUFFER, 100); { MediaPlayer := TMediaPlayer.Create( nil ); MediaPlayer.ParentWindow := fHWND; MediaPlayer.Wait := true;} Log.LogStatus('Loading Sounds', 'Music Initialize'); { LoadPlayerFromFile(MediaPlayerStart, SoundPath + 'Common Start.mp3'); LoadPlayerFromFile(MediaPlayerBack, SoundPath + 'Common Back.mp3'); LoadPlayerFromFile(MediaPlayerSwoosh, SoundPath + 'menu swoosh.mp3'); LoadPlayerFromFile(MediaPlayerChange, SoundPath + 'select music change music.mp3'); LoadPlayerFromFile(MediaPlayerOption, SoundPath + 'option change col.mp3'); LoadPlayerFromFile(MediaPlayerClick, SoundPath + 'rimshot022b.mp3'); LoadPlayerFromFile(MediaPlayerDrum, SoundPath + 'bassdrumhard076b.mp3'); LoadPlayerFromFile(MediaPlayerHihat, SoundPath + 'hihatclosed068b.mp3'); LoadPlayerFromFile(MediaPlayerClap, SoundPath + 'claps050b.mp3'); LoadPlayerFromFile(MediaPlayerShuffle, SoundPath + 'Shuffle.mp3');} Log.BenchmarkStart(4); LoadSoundFromFile(BassStart, SoundPath + 'Common Start.mp3'); LoadSoundFromFile(BassBack, SoundPath + 'Common Back.mp3'); LoadSoundFromFile(BassSwoosh, SoundPath + 'menu swoosh.mp3'); LoadSoundFromFile(BassChange, SoundPath + 'select music change music 50.mp3'); LoadSoundFromFile(BassOption, SoundPath + 'option change col.mp3'); LoadSoundFromFile(BassClick, SoundPath + 'rimshot022b.mp3'); // LoadSoundFromFile(BassDrum, SoundPath + 'bassdrumhard076b.mp3'); // LoadSoundFromFile(BassHihat, SoundPath + 'hihatclosed068b.mp3'); // LoadSoundFromFile(BassClap, SoundPath + 'claps050b.mp3'); // LoadSoundFromFile(BassShuffle, SoundPath + 'Shuffle.mp3'); Log.BenchmarkEnd(4); Log.LogBenchmark('--> Loading Sounds', 4); end; procedure TMusic.InitializeRecord; var S: integer; device: integer; descr: string; input: integer; input2: integer; flags: integer; mic: array[0..15] of integer; SC: integer; // soundcard SCI: integer; // soundcard input begin if RecordSystem = 1 then begin SetLength(Sound, 6 {max players});//Ini.Players+1); for S := 0 to High(Sound) do begin //Ini.Players do begin Sound[S] := TSound.Create; Sound[S].Num := S; Sound[S].BufferNew := TMemoryStream.Create; SetLength(Sound[S].BufferLong, 1); Sound[S].BufferLong[0] := TMemoryStream.Create; Sound[S].n := 4*1024; end; // check for recording devices; {device := 0; descr := BASS_RecordGetDeviceDescription(device); SetLength(SoundCard, 0); while (descr <> '') do begin SC := High(SoundCard) + 1; SetLength(SoundCard, SC+1); Log.LogAnalyze('Device #'+IntToStr(device)+': '+ descr); SoundCard[SC].Description := Descr; // check for recording inputs mic[device] := -1; // default to no change input := 0; BASS_RecordInit(device); Log.LogAnalyze('Input #' + IntToStr(Input) + ': ' + BASS_RecordGetInputName(input)); flags := BASS_RecordGetInput(input); SetLength(SoundCard[SC].Input, 0); while (flags <> -1) do begin SCI := High(SoundCard[SC].Input) + 1; SetLength(SoundCard[SC].Input, SCI+1); Log.LogAnalyze('Input #' + IntToStr(Input) + ': ' + BASS_RecordGetInputName(input)); SoundCard[SC].Input[SCI].Name := BASS_RecordGetInputName(Input); if (flags and BASS_INPUT_TYPE_MASK) = BASS_INPUT_TYPE_MIC then begin mic[device] := input; // auto set microphone end; Inc(Input); flags := BASS_RecordGetInput(input); end; if mic[device] <> -1 then begin Log.LogAnalyze('Found the mic at input ' + IntToStr(Mic[device])) end else begin Log.LogAnalyze('Mic not found'); mic[device] := 0; // setting to the first one (for kxproject) end; SoundCard[SC].InputSeleceted := Mic[Device]; BASS_RecordFree; inc(Device); descr := BASS_RecordGetDeviceDescription(Device); end; // while} end; // if end; procedure TMusic.SetVolume(Volume: integer); begin //Old Sets Wave Volume //BASS_SetVolume(Volume); //New: Sets Volume only for this Application BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume); BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume); BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, Volume); end; procedure TMusic.SetMusicVolume(Volume: Integer); begin //Max Volume Prevention if Volume > 100 then Volume := 100; //Set Volume BASS_ChannelSetAttributes (Bass, -1, Volume, -101); end; procedure TMusic.SetLoop(Enabled: boolean); begin Loop := Enabled; end; function TMusic.Open(Name: string): boolean; begin Loaded := false; if FileExists(Name) then begin { MediaPlayer.FileName := Name; MediaPlayer.Open;} Bass := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0); Loaded := true; //Set Max Volume SetMusicVolume (100); end; Result := Loaded; // Player := TcmxMp3.Create(Name); end; procedure TMusic.Rewind; begin if Loaded then begin // MediaPlayer.Position := 0; end; end; procedure TMusic.MoveTo(Time: real); var bytes: integer; begin // if Loaded then begin // MediaPlayer.StartPos := Round(Time); bytes := BASS_ChannelSeconds2Bytes(Bass, Time); BASS_ChannelSetPosition(Bass, bytes); // end; end; procedure TMusic.Play; begin if Loaded then begin // MediaPlayer.Play; BASS_ChannelPlay(Bass, False); // for setting position before playing end; end; procedure TMusic.Pause; //Pause Mod begin if Loaded then begin BASS_ChannelPause(Bass); // Pauses Song end; end; procedure TMusic.Stop; begin Bass_ChannelStop(Bass); // Bass_StreamFree(Bass); // if ModeStr[MediaPlayer.Mode] = 'Playing' then begin // MediaPlayer.Stop; // end; end; procedure TMusic.Close; begin Bass_StreamFree(Bass); // Player.Free; // MediaPlayer.Close; end; function TMusic.Length: real; var bytes: integer; begin Result := 60; bytes := BASS_StreamGetLength(Bass); Result := BASS_ChannelBytes2Seconds(Bass, bytes); { if Assigned(MediaPlayer) then begin if Loaded then Result := MediaPlayer.Length / 1000; end;} // if Assigned(Player) then // Result := Player.LengthInSeconds; end; function TMusic.Position: real; var bytes: integer; begin Result := 0;//MediaPlayer.Position / 1000; bytes := BASS_ChannelGetPosition(BASS); Result := BASS_ChannelBytes2Seconds(BASS, bytes); end; function TMusic.Finished: boolean; begin Result := false; // if ModeStr[MediaPlayer.Mode] = 'Stopped' then Result := true; if BASS_ChannelIsActive(BASS) = BASS_ACTIVE_STOPPED then begin // beep; Result := true; end; end; {function myeffect( chan : integer; stream : Pointer; len : integer; udata : Pointer ): Pointer; cdecl; var dane: pwordarray; pet: integer; Prev: smallint; PrevNew: smallint; begin dane := stream; Prev := 0; for pet := 0 to len div 2 -1 do begin PrevNew := Dane[Pet]; // Dane[pet] := Round(PrevNew*1/8 + Prev*7/8); Prev := Dane[Pet]; end; end;} procedure TMusic.PlayStart; {var Music: PMix_Chunk;} begin { Mix_OpenAudio(44100, 16, 1, 16*1024); Music := Mix_LoadWAV('D:\Rozne\UltraStar\Old\Boys - Hej Sokoly 30s.wav'); Mix_RegisterEffect(0, myeffect, nil, 0); Mix_PlayChannel(0, Music, 0);} // MediaPlayerStart.Rewind; // MediaPlayerStart.Play; BASS_ChannelPlay(BassStart, True); end; procedure TMusic.PlayBack; begin // MediaPlayerBack.Rewind; // MediaPlayerBack.Play; // if not BASS_ChannelPlay(BassBack, True);// then // Application.MessageBox ('Error playing stream!', 'Error'); end; procedure TMusic.PlaySwoosh; begin // MediaPlayerSwoosh.Rewind; // MediaPlayerSwoosh.Play; BASS_ChannelPlay(BassSwoosh, True); end; procedure TMusic.PlayChange; begin // MediaPlayerChange.Rewind; // MediaPlayerChange.Play; BASS_ChannelPlay(BassChange, True); end; procedure TMusic.PlayOption; begin // MediaPlayerOption.Rewind; // MediaPlayerOption.Play; BASS_ChannelPlay(BassOption, True); end; procedure TMusic.PlayClick; begin // MediaPlayerClick.Rewind; // MediaPlayerClick.Play; BASS_ChannelPlay(BassClick, True); end; procedure TMusic.PlayDrum; begin // MediaPlayerDrum.Rewind; // MediaPlayerDrum.Play; BASS_ChannelPlay(BassDrum, True); end; procedure TMusic.PlayHihat; begin // MediaPlayerHihat.Rewind; // MediaPlayerHihat.Play; BASS_ChannelPlay(BassHihat, True); end; procedure TMusic.PlayClap; begin // MediaPlayerClap.Rewind; // MediaPlayerClap.Play; BASS_ChannelPlay(BassClap, True); end; procedure TMusic.PlayShuffle; begin // MediaPlayerShuffle.Rewind; // MediaPlayerShuffle.Play; BASS_ChannelPlay(BassShuffle, True); end; procedure TMusic.StopShuffle; begin BASS_ChannelStop(BassShuffle); end; procedure TMusic.CaptureStart; var S: integer; SC: integer; P1: integer; P2: integer; begin for S := 0 to High(Sound) do Sound[S].BufferLong[0].Clear; { case PlayersPlay of 1: begin CaptureCard(0, 0, 1, 0); end; 2: begin if Ini.TwoPlayerMode = 0 then begin CaptureCard(0, 0, 1, 2); end else begin CaptureCard(0, 0, 1, 0); CaptureCard(1, 1, 2, 0); end; end; 3: begin CaptureCard(0, 0, 1, 2); CaptureCard(1, 1, 3, 0); end; end; // case} // CaptureCard(0, 0, 0, 0); // end; {for SC := 0 to High(SoundCard) do begin P1 := Ini.SoundCard[SC, 1]; P2 := Ini.SoundCard[SC, 2]; if P1 > PlayersPlay then P1 := 0; if P2 > PlayersPlay then P2 := 0; CaptureCard(SC, P1, P2); end; } // 0.5.2: new for SC := 0 to High(Ini.CardList) do begin P1 := Ini.CardList[SC].ChannelL; P2 := Ini.CardList[SC].ChannelR; if P1 > PlayersPlay then P1 := 0; if P2 > PlayersPlay then P2 := 0; if (P1 > 0) or (P2 > 0) then CaptureCard(SC, P1, P2); end; end; procedure TMusic.CaptureStop; var SC: integer; P1: integer; P2: integer; begin { if RecordSystem = 1 then begin case PlayersPlay of 1: begin StopCard(0); end; 2: begin if Ini.TwoPlayerMode = 0 then begin StopCard(0); end else begin StopCard(0); StopCard(1); end; end; 3: begin StopCard(0); StopCard(1); end; end; end;} {for SC := 0 to High(SoundCard) do begin StopCard(SC); end; } // 0.5.2 for SC := 0 to High(Ini.CardList) do begin P1 := Ini.CardList[SC].ChannelL; P2 := Ini.CardList[SC].ChannelR; if P1 > PlayersPlay then P1 := 0; if P2 > PlayersPlay then P2 := 0; if (P1 > 0) or (P2 > 0) then StopCard(SC); end; end; //procedure TMusic.CaptureCard(RecordI, SoundNum, PlayerLeft, PlayerRight: byte); procedure TMusic.CaptureCard(RecordI, PlayerLeft, PlayerRight: byte); var Error: integer; ErrorMsg: string; begin if not BASS_RecordInit(RecordI) then begin Error := BASS_ErrorGetCode; ErrorMsg := IntToStr(Error); if Error = BASS_ERROR_DX then ErrorMsg := 'No DX5'; if Error = BASS_ERROR_ALREADY then ErrorMsg := 'The device has already been initialized'; if Error = BASS_ERROR_DEVICE then ErrorMsg := 'The device number specified is invalid'; if Error = BASS_ERROR_DRIVER then ErrorMsg := 'There is no available device driver'; {Log.LogAnalyze('Error initializing record [' + IntToStr(RecordI) + ', ' + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: ' + ErrorMsg);} Log.LogError('Error initializing record [' + IntToStr(RecordI) + ', ' + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: ' + ErrorMsg); Log.LogError('Music -> CaptureCard: Error initializing record: ' + ErrorMsg); end else begin //SoundCard[RecordI].BassRecordStream := BASS_RecordStart(44100, 2, MakeLong(0, 20) , @GetMicrophone, PlayerLeft + PlayerRight*256); Recording.SoundCard[RecordI].BassRecordStream := BASS_RecordStart(44100, 2, MakeLong(0, 20) , @GetMicrophone, PlayerLeft + PlayerRight*256); {if SoundCard[RecordI].BassRecordStream = 0 then begin Error := BASS_ErrorGetCode; ErrorMsg := IntToStr(Error); if Error = BASS_ERROR_INIT then ErrorMsg := 'Not successfully called'; if Error = BASS_ERROR_ALREADY then ErrorMsg := 'Recording is already in progress'; if Error = BASS_ERROR_NOTAVAIL then ErrorMsg := 'The recording device is not available'; if Error = BASS_ERROR_FORMAT then ErrorMsg := 'The specified format is not supported'; if Error = BASS_ERROR_MEM then ErrorMsg := 'There is insufficent memory'; if Error = BASS_ERROR_UNKNOWN then ErrorMsg := 'Unknown'; Log.LogError('Error creating record stream [' + IntToStr(RecordI) + ', ' + IntToStr(PlayerLeft) + ', '+ IntToStr(PlayerRight) + ']: ' + ErrorMsg); end; } end; end; procedure TMusic.StopCard(Card: byte); begin BASS_RecordSetDevice(Card); BASS_RecordFree; end; function TMusic.LoadPlayerFromFile(var MediaPlayer: TMediaPlayer; Name: string): boolean; begin Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadPlayerFromFile'); if FileExists(Name) then begin try MediaPlayer := TMediaPlayer.Create( nil ); except Log.LogError('Failed to create MediaPlayer', 'LoadPlayerFromFile'); end; try MediaPlayer.ParentWindow := fHWND; MediaPlayer.Wait := true; MediaPlayer.FileName := Name; MediaPlayer.DeviceType := dtAutoSelect; MediaPlayer.Display := nil; except Log.LogError('Failed setting MediaPlayer: ' + MediaPlayer.ErrorMessage, 'LoadPlayerFromFile'); end; try MediaPlayer.Open; except Log.LogError('Failed to open using MediaPlayer', 'LoadPlayerFromFile'); end; end else begin Log.LogError('Sound not found: "' + Name + '"', 'LoadPlayerFromFile'); exit; end; end; function TMusic.LoadSoundFromFile(var hStream: hStream; Name: string): boolean; var L: Integer; begin if FileExists(Name) then begin Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadPlayerFromFile'); try hStream := BASS_StreamCreateFile(False, pchar(Name), 0, 0, 0); //Add CustomSound L := High(CustomSounds) + 1; SetLength (CustomSounds, L + 1); CustomSounds[L].Filename := Name; CustomSounds[L].Handle := hStream; except Log.LogError('Failed to open using BASS', 'LoadPlayerFromFile'); end; end else begin Log.LogError('Sound not found: "' + Name + '"', 'LoadPlayerFromFile'); exit; end; end; //Equalizer function TMusic.GetFFTData: TFFTData; var Data: TFFTData; begin //Get Channel Data Mono and 256 Values BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512); //Result := Data; end; function TMusic.LoadCustomSound(const Filename: String): Cardinal; var S: hStream; I: Integer; F: String; begin //Search for Sound in already loaded Sounds F := UpperCase(SoundPath + FileName); For I := 0 to High(CustomSounds) do begin if (UpperCase(CustomSounds[I].Filename) = F) then begin Result := I; Exit; end; end; if LoadSoundFromFile(S, SoundPath + Filename) then Result := High(CustomSounds) else Result := 0; end; procedure TMusic.PlayCustomSound(const Index: Cardinal); begin if Index <= High(CustomSounds) then BASS_ChannelPlay(CustomSounds[Index].Handle, True); end; end.