Forum > Audio and Video
ZxTune chiptunes player
(1/1)
Guva:
After several days of struggling, with the help of "Мата" and "Изоленты", I finally managed to build ZXTune using CMake.
--- Quote ---(Note: "Мат" is Russian slang for strong swear words, and "изолента" refers to duct tape—often used humorously to imply a rough, improvised solution.)
--- End quote ---
"The chip tune chapter is finally coming to a close."
I'm not releasing the demo yet - it plays perfectly, but I haven't fully figured out what parameters need to be passed to `ZXTune_GetPlayerParameterInt` and `ZXTune_SetPlayerParameterInt` functions. Also wrote a cross-compilation script for Linux.
This project is based on ZXTune version "zxtune-r4310". Regarding Z80 emulation, the project includes most of the original source code, including third-party dependencies. Other third-party emulators (etc.) have been removed (since I already have separate standalone players or don't need them), and I didn't want this player to be larger than necessary (e.g., FLAC, mp3, ogg, sidplayfp, vorbis, xmp support removed). Some unused "boosting" components were also removed, along with RAR support.
Supporting tracker formats such as:
Chip Tracker v1.xx, Digital Music Maker, Digital Studio AY/Covox, Extreme Tracker v1.xx,
ProDigiTracker v0.xx, SQ Digital Tracker, Sample Tracker and other. https://zxart.ee/rus/muzyka/
https://github.com/GuvaCode/zxTunePascal
compiled libraries
https://github.com/GuvaCode/zxTunePascal/releases/download/r4310_V1/binray_windows_linux.zip
Guva:
I wrote an example player, tested only under Linux.
--- Code: Pascal [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program ZxPlay; {$mode objfpc}{$H+} uses {$IFDEF UNIX} cthreads, {$ENDIF} Classes, SysUtils, CTypes, raylib, libZxTune, Contnrs, SyncObjs, Crt; const DEFAULT_FREQ = 44100; DEFAULT_BITS = 16; DEFAULT_CHANNELS = 2; BUFFER_SIZE = 8192; DEFAULT_FRAME_DURATION = 20000; // 20ms in microseconds POSITION_UPDATE_INTERVAL = 100; // ms PROGRESS_BAR_WIDTH = 30; // Ширина полоски прогресса var ZxTunePlayer: ZXTuneHandle = nil; ZxTuneData: ZXTuneHandle = nil; ZxTuneModule: ZXTuneHandle = nil; ModuleInfo: ZXTuneModuleInfo; ShouldExit: Boolean = False; PositionLock: TCriticalSection; InfoShown: Boolean = False; procedure LoadLibs();var folder: PChar;begin folder := ''; {$IFDEF CPUX86_64} {$IFDEF LINUX} folder := 'dlls/lin64/'; {$ENDIF} {$IFDEF WINDOWS} folder := 'dlls/win64/'; {$ENDIF} {$ENDIF} {$IFDEF CPU386} {$IFDEF LINUX} folder := 'dlls/lin32/'; {$ENDIF} {$IFDEF WINDOWS} folder := 'dlls/win32/'; {$ENDIF} {$ENDIF} LoadZXTuneLibrary(folder + libZxTune.library_name);end; procedure LoadModuleFile(const MusicFile: string);var FileStream: TFileStream; FFileData: Pointer; FFileSize: NativeUInt;begin try FileStream := TFileStream.Create(MusicFile, fmOpenRead or fmShareDenyWrite); try FFileSize := FileStream.Size; GetMem(FFileData, FFileSize); FileStream.ReadBuffer(FFileData^, FFileSize); finally FileStream.Free; end; ZxTuneData := ZXTune_CreateData(FFileData, FFileSize); if ZxTuneData = nil then raise Exception.Create('Failed to create ZXTune data'); ZxTuneModule := ZXTune_OpenModule(ZxTuneData); if ZxTuneModule = nil then raise Exception.Create('Failed to open ZXTune module'); if not ZXTune_GetModuleInfo(ZxTuneModule, ModuleInfo) then raise Exception.Create('Failed to get module info'); ZxTunePlayer := ZXTune_CreatePlayer(ZxTuneModule); if ZxTunePlayer = nil then raise Exception.Create('Failed to create ZXTune player'); except on E: Exception do begin WriteLn('Error: ', E.Message); raise; end; end;end; function GetPosition: Integer;var Samples: NativeUInt; Frequency: Integer;begin Result := 0; Frequency := 0; PositionLock.Enter; try if ZxTunePlayer <> nil then begin Samples := ZXTune_GetCurrentPosition(ZxTunePlayer); if not ZXTune_GetPlayerParameterInt(ZxTunePlayer, 'sound.frequency', Frequency) then Frequency := DEFAULT_FREQ; Result := Round((Samples / DEFAULT_CHANNELS) / Frequency * 1000)*2; end; finally PositionLock.Leave; end;end; function GetDuration: Integer;var FrameDuration: Integer;begin Result := 0; PositionLock.Enter; try if ZxTuneModule <> nil then begin // Get frame duration in microseconds (default to 20ms if not available) FrameDuration := ZXTune_GetDuration(ZxTunePlayer); if FrameDuration <= 0 then FrameDuration := DEFAULT_FRAME_DURATION; // Default 20ms frame duration // Calculate duration: (frames * frame_duration) / 1000 Result := Round((ModuleInfo.Frames * FrameDuration) / 1000); end; finally PositionLock.Leave; end;end; procedure FormatTime(ms: Integer; out Minutes, Seconds: Integer);begin Minutes := ms div 60000; Seconds := (ms div 1000) mod 60;end; procedure ShowPosition;var PosMs, DurationMs: Integer; PosMin, PosSec: Integer; DurMin, DurSec: Integer; ProgressPos: Integer; ProgressBar: string; i: Integer;begin // Выводим информацию о модуле только один раз if not InfoShown then begin ClrScr; WriteLn('ZX Tune Player. ', ZXTune_GetVersion); WriteLn(''); WriteLn('Module: ',ExtractFileName(ParamStr(1))); WriteLn('Tempo: ', ModuleInfo.Frames); WriteLn('Channels: ', ModuleInfo.Channels); WriteLn; InfoShown := True; end; PosMs := GetPosition; DurationMs := GetDuration; FormatTime(PosMs, PosMin, PosSec); FormatTime(DurationMs, DurMin, DurSec); // Рассчитываем позицию для полоски прогресса if DurationMs > 0 then ProgressPos := Round((PosMs / DurationMs) * PROGRESS_BAR_WIDTH) else ProgressPos := 0; // Строим полоску прогресса ProgressBar := '['; for i := 1 to PROGRESS_BAR_WIDTH do begin if i <= ProgressPos then ProgressBar := ProgressBar + '=' else ProgressBar := ProgressBar + '-'; end; ProgressBar := ProgressBar + ']'; // Выводим только изменяемую часть (позицию и прогресс-бар) GotoXY(1, 7); // Перемещаем курсор на строку после статической информации Write(Format('Position: %d:%.2d / %d:%.2d %s', [PosMin, PosSec, DurMin, DurSec, ProgressBar]), ' '); // Добавляем пробелы для очисткиend; procedure FillAudio(bufferData: Pointer; frames: LongWord); cdecl;begin ZXTune_RenderSound(ZxTunePlayer, bufferData, frames);end; procedure PlayFile(Filename: string);var Stream: TAudioStream; LastUpdate: QWord;begin InitAudioDevice(); SetAudioStreamBufferSizeDefault(BUFFER_SIZE); Stream := LoadAudioStream(DEFAULT_FREQ, DEFAULT_BITS, DEFAULT_CHANNELS); if not IsAudioStreamValid(Stream) then raise Exception.Create('Failed to initialize audio stream'); SetAudioStreamCallback(Stream, @FillAudio); PlayAudioStream(Stream); LoadModuleFile(Filename); LastUpdate := GetTickCount64; while not ShouldExit do begin if IsAudioStreamProcessed(Stream) then ResumeAudioStream(Stream); if GetTickCount64 - LastUpdate >= POSITION_UPDATE_INTERVAL then begin ShowPosition; LastUpdate := GetTickCount64; end; if KeyPressed then begin ReadKey; ShouldExit := True; end; Sleep(1); end; StopAudioStream(Stream); CloseAudioDevice; ZXTune_CloseData(ZxTuneData); ZXTune_CloseModule(ZxTuneModule); ZXTune_DestroyPlayer(ZxTunePlayer); WriteLn;end; begin PositionLock := TCriticalSection.Create; ExitCode := 1; LoadLibs; if ParamCount = 1 then begin if SysUtils.FileExists(ParamStr(1)) then begin PlayFile(ParamStr(1)); ExitCode := 0; end else WriteLn('File ', ParamStr(1), ' does not exist'); end else WriteLn('Usage: ', ExtractFileName(ParamStr(0)), ' <supported file (pt2, pt3, etc.)>'); PositionLock.Free;end.
Gigatron:
Good work @guva , and many thanx , we can now listen more and more module file type format !
Also, the site you mention is excellent!! I've been listening to chip music for hours.
https://zxart.ee/eng/mainpage/
Guva:
--- Quote from: Gigatron on August 14, 2025, 09:15:26 pm ---Good work @guva , and many thanx , we can now listen more and more module file type format !
https://zxart.ee/eng/mainpage/
--- End quote ---
Tnx :)
I've been working in C code for a few days and I've added a few functions. The position is now displayed correctly.
--- Code: C [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---ZXTUNE_API long ZXTune_GetDuration(ZXTuneHandle player);ZXTUNE_API size_t ZXTune_GetCurrentPosition(ZXTuneHandle player); ZXTUNE_API long ZXTune_GetDurationMs(ZXTuneHandle player, const ZXTuneModuleInfo* info);ZXTUNE_API long ZXTune_GetPositionMs(ZXTuneHandle player, const ZXTuneModuleInfo* moduleInfo); ZXTUNE_API long ZXTune_GetPlayerLoopTrack(ZXTuneHandle player);ZXTUNE_API bool ZXTune_SetPlayerLoopTrack(ZXTuneHandle player, int paramValue); ZXTUNE_API long ZXTune_GetSoundFrequency(ZXTuneHandle player); ZXTUNE_API bool ZXTune_SetDoneSamples(ZXTuneHandle player, const ZXTuneModuleInfo* moduleInfo);
It remains to figure out why the library is not loaded in windows :(
Navigation
[0] Message Index