Compare commits

..
Author SHA1 Message Date
visionmercer
1d83475666 oi 2026-10-02 11:26:00 +02:00
visionmercer
499fcff6dd remove excessive use of space. 2026-10-02 11:22:56 +02:00
visionmercer
4d5522f1bf Remove useless code 2026-10-01 13:15:43 +02:00
visionmercer
eb50085052 Use whole width if needed. 2026-09-18 11:55:46 +02:00
visionmercer
670a19aae6 Repeat off in ipc. 2026-09-17 12:50:45 +02:00
visionmercer
321b0a3424 configurable
added the ability to use a config file.
2026-09-15 13:16:56 +02:00
visionmercer
662907254a Update readme 2026-09-10 13:59:06 +02:00
2 changed files with 260 additions and 188 deletions

407
cimp.bas
View file

@ -4,7 +4,7 @@ declare library"terminkey"
sub echoon() sub echoon()
function termwidth() function termwidth()
function ipc_init&() function ipc_init&()
function ipc_check_message%(byval buf as _offset, byval max_len as long) function ipc_check_message%(byval buf as _offset,byval max_len as long)
sub ipc_send_message(buf as string) sub ipc_send_message(buf as string)
sub ipc_cleanup() sub ipc_cleanup()
end declare end declare
@ -25,89 +25,93 @@ $end if
redim file(0) as string redim file(0) as string
chdir _startdir$ chdir _startdir$
if command$=""then if command$=""then
print"please specify file to play." print"please specify file to play."
goto quit goto quit
end if end if
dim ipc_status as long dim ipc_status as long
ipc_status = ipc_init ipc_status=ipc_init
if ipc_status = 0 then if ipc_status=0 then
' --- CLIENT MODE --- ' --- CLIENT MODE ---
dim cmd_msg as string dim cmd_msg as string
Select Case command$(1) select case command$(1)
Case "--soundbank" case "--soundbank"
cmd_msg = "SOUNDBANK:" + _cwd$ + slash + command$(2) cmd_msg="SOUNDBANK:"+_cwd$ +slash+command$(2)
Case "--next" case "--next"
cmd_msg = "NEXT" cmd_msg="NEXT"
Case "--prev" case "--prev"
cmd_msg = "PREV" cmd_msg="PREV"
Case "-v", "--volume" case "-v","--volume"
cmd_msg = "VOL:" + command$(2) cmd_msg="VOL:"+command$(2)
case "--pause" case "--pause"
cmd_msg = "PAUSE" cmd_msg="PAUSE"
case "--stop" case "--stop"
cmd_msg = "STOP" cmd_msg="STOP"
case "--play" case "--play"
cmd_msg = "PLAY" cmd_msg="PLAY"
case "--quit" case "--quit"
cmd_msg = "QUIT" cmd_msg="QUIT"
case "--shuffle" case "--shuffle"
cmd_msg = "SHUFFLE" cmd_msg="SHUFFLE"
case "--repeat" case "--repeat"
cmd_msg = "REPEAT" cmd_msg="REPEAT"
case "--repeat-1" case "--repeat-1"
cmd_msg = "REPEAT1" cmd_msg="REPEAT1"
case "--repeat-off"
cmd_msg="REPEATOFF"
case "--nyan" case "--nyan"
cmd_msg = "NYAN" cmd_msg="NYAN"
Case "--add" case "--nyan-off"
For i = 2 To _commandcount cmd_msg="NYANOFF"
cmd_msg = "ADD:" + _cwd$ + slash + command$(i) case "--add"
for i=2 to _commandcount
cmd_msg="ADD:"+_cwd$ +slash+command$(i)
ipc_send_message cmd_msg ipc_send_message cmd_msg
Next i next i
System system
Case "--playlist" case "--playlist"
cmd_msg = "GET_PLAYLIST" cmd_msg="GET_PLAYLIST"
Case Else case else
' Default behavior: Resolve target file/playlist and replace active queue ' Default behavior: Resolve target file/playlist and replace active queue
cmd_msg = "PLAY:" + _cwd$ + slash + command$(1) cmd_msg="PLAY:"+_cwd$ +slash+command$(1)
ipc_send_message cmd_msg ipc_send_message cmd_msg
If _commandcount > 1 Then if _commandcount>1 then
For i = 2 To _commandcount for i=2 to _commandcount
cmd_msg = "ADD:" + _cwd$ + slash + command$(i) cmd_msg="ADD:"+_cwd$ +slash+command$(i)
ipc_send_message cmd_msg ipc_send_message cmd_msg
Next i next i
System system
End If end if
End Select end select
' Send instruction to the main player instance ' Send instruction to the main player instance
ipc_send_message cmd_msg ipc_send_message cmd_msg
' --- Two-way Client feedback for --playlist --- ' --- Two-way Client feedback for --playlist ---
if cmd_msg = "GET_PLAYLIST" then if cmd_msg="GET_PLAYLIST" then
ipc_cleanup ipc_cleanup
_delay 0.1 _delay 0.1
dim client_listen as long dim client_listen as long
client_listen = ipc_init client_listen=ipc_init
if client_listen = 1 then if client_listen=1 then
dim reply_buf as string dim reply_buf as string
reply_buf = space$(4096) + chr$(0) reply_buf=space$(4096)+chr$(0)
dim start_wait as double dim start_wait as double
start_wait = timer start_wait=timer
do while timer - start_wait < 2 do while timer-start_wait<2
dim reply_len as long dim reply_len as long
reply_len = ipc_check_message(_offset(reply_buf), 4096) reply_len=ipc_check_message(_offset(reply_buf),4096)
if reply_len > 0 then if reply_len>0 then
print left$(reply_buf, reply_len) print left$(reply_buf,reply_len)
exit do exit do
end if end if
_limit 30 _limit 30
@ -117,7 +121,7 @@ if ipc_status = 0 then
end if end if
system ' Exit client instance completely system ' Exit client instance completely
elseif ipc_status = -1 then elseif ipc_status=-1 then
print "Error initializing Inter-Process Communication." print "Error initializing Inter-Process Communication."
goto quit goto quit
end if end if
@ -133,6 +137,8 @@ dim shuffle as integer
dim nooutput as integer dim nooutput as integer
dim timevis as integer dim timevis as integer
dim nyan as integer dim nyan as integer
dim server as integer
dim marqueeoffset as integer dim marqueeoffset as integer
dim i as integer dim i as integer
@ -167,6 +173,40 @@ timevis=1
nyan=0 nyan=0
marqueeoffset=0 marqueeoffset=0
dim config as string
config=GetConfig("cimp.conf")
if _fileexists(config) then
dim li as string
dim fh as integer
fh=freefile
open config for input as fh
while not eof(fh)
line input #fh,li
if left$(li,1)="#" then _continue
select case _trim$(left$(li,instr(li,"=")-1))
case "volume"
volume=val(_trim$(mid$(li,instr(li,"=")+1)))/100
case "shuffle"
if _trim$(mid$(li,instr(li,"=")+1))="true" then shuffle=-1 else shuffle=0
case "repeat"
if _trim$(mid$(li,instr(li,"=")+1))="true" then repeat=-1 else repeat=0
if _trim$(mid$(li,instr(li,"=")+1))="1" then repeat=1
case "server"
if _trim$(mid$(li,instr(li,"=")+1))="true" then server=-1 else server=0
case "nyan"
if _trim$(mid$(li,instr(li,"=")+1))="true" then nyan=-1 else nyan=0
case "nooutput"
if _trim$(mid$(li,instr(li,"=")+1))="true" then nooutput=-1 else nooutput=0
case ""
case else
print "Config not understood:"
print li
end select
wend
close fh
end if
for i=1 to _commandcount for i=1 to _commandcount
select case command$(i) select case command$(i)
case "-v","--volume" case "-v","--volume"
@ -176,31 +216,31 @@ for i=1 to _commandcount
shuffle=-1 shuffle=-1
case "-r","--repeat" case "-r","--repeat"
if command$(i+1)="1"then if command$(i+1)="1"then
repeat=1 repeat=1
else else
repeat=-1 repeat=-1
end if end if
case "-n","--nooutput" case "-n","--nooutput"
nooutput=-1 nooutput=-1
case "-N","--nyan" case "-N","--nyan"
nyan=-1 nyan=-1
case "-b","--soundbank" case "-b","--soundbank"
if _commandcount>i and _fileexists(command$(i+1)) then if _commandcount>i and _fileexists(command$(i+1)) then
loadsoundbank command$(i+1) loadsoundbank command$(i+1)
else else
print "error loading soundbank" print "error loading soundbank"
system system
end if end if
case else case else
if _fileexists(command$(i)) then if _fileexists(command$(i)) then
if lcase$(right$(command$(i),4))=".m3u"then if lcase$(right$(command$(i),4))=".m3u"then
parsem3u command$(i),file() parsem3u command$(i),file()
else else
file(ubound(file))=command$(i) file(ubound(file))=command$(i)
redim _preserve file(ubound(file)+1) redim _preserve file(ubound(file)+1)
end if end if
end if end if
end select end select
next next
redim _preserve file(ubound(file)-1) redim _preserve file(ubound(file)-1)
@ -209,7 +249,7 @@ if shuffle=-1 then shufflearray file()
i=0 i=0
musichandle=_sndopen(file(i)) musichandle=_sndopen(file(i))
if musichandle=0 then if musichandle=0 then
print"Error: could not open file "; file(i) print"Error: could not open file ";file(i)
goto quit goto quit
end if end if
_sndvol musichandle,volume _sndvol musichandle,volume
@ -219,93 +259,97 @@ songname=beforelast(".",afterlast("/",file(i)))
while keyin<>27 while keyin<>27
keyin=terminkey keyin=terminkey
' ---Poll Client Messages--- ' ---Poll Client Messages---
dim incoming_buf as string dim incoming_buf as string
incoming_buf = space$(512) + chr$(0) incoming_buf=space$(512)+chr$(0)
dim msg_len as long dim msg_len as long
msg_len = ipc_check_message(_offset(incoming_buf), 512) msg_len=ipc_check_message(_offset(incoming_buf),512)
if msg_len > 0 then if msg_len>0 then
dim client_cmd as string dim client_cmd as string
client_cmd = left$(incoming_buf, msg_len) client_cmd=left$(incoming_buf,msg_len)
if client_cmd = "NEXT" then if client_cmd="NEXT" then
playnext = 1 playnext=1
elseif client_cmd = "PLAY" then elseif client_cmd="PLAY" then
keyin=asc("x") keyin=asc("x")
elseif client_cmd = "PAUSE" then elseif client_cmd="PAUSE" then
keyin=asc("c") keyin=asc("c")
elseif client_cmd = "STOP" then elseif client_cmd="STOP" then
keyin=asc("v") keyin=asc("v")
elseif client_cmd = "QUIT" then elseif client_cmd="QUIT" then
keyin=27 keyin=27
elseif client_cmd = "REPEAT" then elseif client_cmd="REPEAT" then
repeat=-1 repeat=-1
elseif client_cmd = "REPEAT1" then elseif client_cmd="REPEAT1" then
repeat=1 repeat=1
elseif client_cmd = "SHUFFLE" then elseif client_cmd="REPEATOFF" then
repeat=0
elseif client_cmd="SHUFFLE" then
keyin=asc("s") keyin=asc("s")
elseif client_cmd = "NYAN" then elseif client_cmd="NYAN" then
nyan = not nyan nyan=not nyan
elseif left$(client_cmd, 10) = "SOUNDBANK:" then elseif client_cmd="NYANOFF" then
if client_cmd="default" then loadsoundbank "" else loadsoundbank mid$(client_cmd,11) nyan=0
elseif left$(client_cmd,10)="SOUNDBANK:" then
if client_cmd="default" then loadsoundbank "" else loadsoundbank mid$(client_cmd,11)
if _SNDPLAYING(musichandle) then if _sndplaying(musichandle) then
' a hack to make qb64pe load the midi file that is already loaded. ' a hack to make qb64pe load the midi file that is already loaded.
' qb64 can't open the same file again with a new sound bank for some reason ' qb64 can't open the same file again with a new sound bank for some reason
' so it has to be closed, freed and given a short delay before reloading ' so it has to be closed, freed and given a short delay before reloading
' which gives a short break in the playing of music, very annoying. ' which gives a short break in the playing of music, very annoying.
' this should be reverted if qb64pe ever gets the ability to reload the same ' this should be reverted if qb64pe ever gets the ability to reload the same
' file more than once. ' file more than once.
tmppos=_sndgetpos(musichandle) tmppos=_sndgetpos(musichandle)
_sndstop musichandle _sndstop musichandle
_sndclose musichandle _sndclose musichandle
_delay .1 _delay .1
musichandle=_sndopen(file(i)) musichandle=_sndopen(file(i))
_sndvol musichandle,volume _sndvol musichandle,volume
_sndsetpos musichandle,tmppos _sndsetpos musichandle,tmppos
_sndplay musichandle _sndplay musichandle
end if end if
elseif left$(client_cmd, 4) = "VOL:" then elseif left$(client_cmd,4)="VOL:" then
volume = val(mid$(client_cmd, 5)) / 100 volume=val(mid$(client_cmd,5))/100
if volume > 1 then volume = 1 if volume>1 then volume=1
if volume < 0 then volume = 0 if volume<0 then volume=0
_sndvol musichandle, volume _sndvol musichandle,volume
elseif left$(client_cmd, 4) = "ADD:" then elseif left$(client_cmd,4)="ADD:" then
dim new_file as string dim new_file as string
new_file = _trim$(mid$(client_cmd, 5)) new_file=_trim$(mid$(client_cmd,5))
if _fileexists(new_file) then if _fileexists(new_file) then
if lcase$(right$(new_file, 4)) = ".m3u" then if lcase$(right$(new_file,4))=".m3u" then
parsem3u new_file, file() parsem3u new_file,file()
else else
redim _preserve file(ubound(file) + 1) as string redim _preserve file(ubound(file)+1) as string
file(ubound(file)) = new_file file(ubound(file))=new_file
end if end if
end if end if
elseif left$(client_cmd, 5) = "PLAY:" then elseif left$(client_cmd,5)="PLAY:" then
dim replace_file as string dim replace_file as string
replace_file = _trim$(mid$(client_cmd, 6)) replace_file=_trim$(mid$(client_cmd,6))
if _fileexists(replace_file) then if _fileexists(replace_file) then
redim file(0) as string redim file(0) as string
if lcase$(right$(replace_file, 4)) = ".m3u" then if lcase$(right$(replace_file,4))=".m3u" then
parsem3u replace_file, file() parsem3u replace_file,file()
else else
file(0) = replace_file file(0)=replace_file
end if end if
i = -1 i=-1
playnext = 1 playnext=1
end if end if
elseif client_cmd = "GET_PLAYLIST" then elseif client_cmd="GET_PLAYLIST" then
dim p_idx as long dim p_idx as long
dim playlist_payload as string dim playlist_payload as string
playlist_payload = "=== Current Playlist ===" + chr$(10) playlist_payload="=== Current Playlist ==="+chr$(10)
for p_idx = lbound(file) to ubound(file) for p_idx=lbound(file) to ubound(file)
if p_idx = i then if p_idx=i then
playlist_payload = playlist_payload + "-> " + file(p_idx) + chr$(10) playlist_payload=playlist_payload+"-> "+file(p_idx)+chr$(10)
else else
playlist_payload = playlist_payload + " " + file(p_idx) + chr$(10) playlist_payload=playlist_payload+" "+file(p_idx)+chr$(10)
end if end if
next p_idx next p_idx
@ -314,7 +358,7 @@ while keyin<>27
ipc_send_message playlist_payload ipc_send_message playlist_payload
_delay 0.1 _delay 0.1
dim reinit as long dim reinit as long
reinit = ipc_init reinit=ipc_init
end if end if
end if end if
' --- End Poll Client Messages --- ' --- End Poll Client Messages ---
@ -416,7 +460,7 @@ while keyin<>27
if nooutput=0 then if nooutput=0 then
tw=termwidth tw=termwidth
fixedwidth=uwidth(state)+uwidth(progress) fixedwidth=uwidth(state)+uwidth(progress)
maxtitlewidth=tw-fixedwidth-2 maxtitlewidth=tw-fixedwidth
currentsongwidth=uwidth(songname) currentsongwidth=uwidth(songname)
if currentsongwidth>maxtitlewidth and maxtitlewidth>4 then if currentsongwidth>maxtitlewidth and maxtitlewidth>4 then
@ -454,20 +498,20 @@ while keyin<>27
' Print the text line ' Print the text line
if nyan=-1 then if nyan=-1 then
print termcolor(7); state; animatedrainbowtext(visibletitle); termcolor(7); progress; clearrest print termcolor(7);state;animatedrainbowtext(visibletitle);termcolor(7);progress;clearrest
else else
print termcolor(7); state; termcolor(3); visibletitle; termcolor(7); progress; clearrest print termcolor(7);state;termcolor(3);visibletitle;termcolor(7);progress;clearrest
end if end if
' Generate and print progress bar matching the layout width ' Generate and print progress bar matching the layout width
progressbar=bar(uwidth(state)+uwidth(visibletitle)+uwidth(progress),(_sndgetpos(musichandle)/_sndlen(musichandle))*100,11,7) progressbar=bar(uwidth(state)+uwidth(visibletitle)+uwidth(progress),(_sndgetpos(musichandle)/_sndlen(musichandle))*100,11,7)
print progressbar; clearrest; cursorback; print progressbar;clearrest;cursorback;
end if end if
_limit 30 _limit 30
if _exit then goto quit if _exit then goto quit
wend wend
quit: quit:
_sndclose musichandle _sndclose musichandle
@ -494,42 +538,42 @@ sub parsem3u (filename$,array$())
dim basepath$,l$,resolvedpath$ dim basepath$,l$,resolvedpath$
for i=len(filename$) to 1 step -1 for i=len(filename$) to 1 step -1
if mid$(filename$,i,1)="/"or mid$(filename$,i,1)="\"then if mid$(filename$,i,1)="/"or mid$(filename$,i,1)="\"then
basepath$=left$(filename$,i) basepath$=left$(filename$,i)
exit for exit for
end if end if
next next
f=freefile f=freefile
open filename$ for input as #f open filename$ for input as #f
count=0 count=0
do until eof(f) do until eof(f)
line input #f,l$ line input #f,l$
l$=_trim$(l$) l$=_trim$(l$)
if len(l$)>0 and left$(l$,1)<>"#"then if len(l$)>0 and left$(l$,1)<>"#"then
resolvedpath$=l$ resolvedpath$=l$
if not _fileexists(resolvedpath$) then if not _fileexists(resolvedpath$) then
resolvedpath$=basepath$+l$ resolvedpath$=basepath$+l$
end if end if
if _fileexists(resolvedpath$) then if _fileexists(resolvedpath$) then
array$(ubound(array$))=resolvedpath$ array$(ubound(array$))=resolvedpath$
redim _preserve array$(ubound(array$)+1) redim _preserve array$(ubound(array$)+1)
end if end if
end if end if
loop loop
close #f close #f
end sub end sub
function timeleft$ (handle&) function timeleft$ (handle&)
dim seconds as integer dim seconds as integer
seconds=_sndlen(handle&)-_sndgetpos(handle&) seconds=_sndlen(handle&)-_sndgetpos(handle&)
if seconds<0 then seconds=0 if seconds<0 then seconds=0
timeleft$=right$("0"+ltrim$(str$(seconds \ 60)),2)+":"+right$("0"+ltrim$(str$(seconds mod 60)),2) timeleft$=right$("0"+ltrim$(str$(seconds\60)),2)+":"+right$("0"+ltrim$(str$(seconds mod 60)),2)
end function end function
function timeelapsed$ (handle&) function timeelapsed$ (handle&)
dim seconds as integer dim seconds as integer
seconds=_sndgetpos(handle&) seconds=_sndgetpos(handle&)
if seconds<0 then seconds=0 if seconds<0 then seconds=0
timeelapsed$=right$("0"+ltrim$(str$(seconds \ 60)),2)+":"+right$("0"+ltrim$(str$(seconds mod 60)),2) timeelapsed$=right$("0"+ltrim$(str$(seconds\60)),2)+":"+right$("0"+ltrim$(str$(seconds mod 60)),2)
end function end function
function termcolor$ (colorvalue as _unsigned long) function termcolor$ (colorvalue as _unsigned long)
@ -625,12 +669,9 @@ end function
function ulen% (txt$) function ulen% (txt$)
dim count%,i%,b% dim count%,i%,b%
count%=0
for i%=1 to len(txt$) for i%=1 to len(txt$)
b%=asc(txt$,i%) b%=asc(txt$,i%)
if (b% and &h80)=0 or (b% and &hc0)=&hc0 then if (b% and &h80)=0 or (b% and &hc0)=&hc0 then count%=count%+1
count%=count%+1
end if
next next
ulen%=count% ulen%=count%
end function end function
@ -708,5 +749,37 @@ function getcodepoint& (utf8char$)
end function end function
sub loadsoundbank (filename as string) sub loadsoundbank (filename as string)
_midisoundbank filename _midisoundbank filename
end sub end sub
function GetConfig$ (FileName as string)
select case left$(_os$,instr(_os$,"]"))
case "[WINDOWS]"
dim appdata as string
appdata=environ$("APPDATA")
if appdata="" then appdata=environ$("USERPROFILE")+"\AppData\Roaming"
if right$(appdata,1)<>"\" then appdata=appdata+"\"
GetConfig=appdata+FileName
case "[MACOSX]"
dim homemac as string
homemac=environ$("HOME")
if homemac="" then homemac="/tmp"
if right$(homemac,1)="/" then homemac=left$(homemac,len(homemac)-1)
GetConfig=homemac+"/Library/Application Support/"+FileName
case else ' Linux and other Unix-like systems
dim xdg as string,baseDir as string,homelinux as string
xdg=environ$("XDG_CONFIG_HOME")
if xdg<>"" then
baseDir=xdg
else
homelinux=environ$("HOME")
if homelinux="" then homelinux="/tmp"
baseDir=homelinux+"/.config"
end if
if right$(baseDir,1)="/" then baseDir=left$(baseDir,len(baseDir)-1)
GetConfig=baseDir+"/"+FileName
end select
end function

View file

@ -129,9 +129,8 @@ When interacting directly with the player interface terminal, use these keys:
## Supported Audio Formats ## Supported Audio Formats
All formats supported by QB64/QB64PE are supported by cimp, as QB64 handles the All formats supported by QB64PE are supported by cimp, as it handles the
decoding. The following formats are supported by the most recent versions of decoding. The following formats are supported by the most recent version:
both compilers:
Audio files: Audio files:
`mod`, `xm`, `s3m`, `it`, `mid`, `mp3`, `ogg`, and `flac` `mod`, `xm`, `s3m`, `it`, `mid`, `mp3`, `ogg`, and `flac`
@ -143,7 +142,7 @@ Sound banks:
| File | Description | | File | Description |
| ------------- | --------------------------------------------------------------------------------------- | | ------------- | --------------------------------------------------------------------------------------- |
| `cimp.bas` | Main player — QB64-PE source handling client/server states and UI | | `cimp.bas` | Main player - QB64 source handling client/server states and UI |
| `terminkey.h` | C library for cross-platform raw keyboard input, IPC pipes/sockets, and terminal sizing | | `terminkey.h` | C library for cross-platform raw keyboard input, IPC pipes/sockets, and terminal sizing |
## Notes ## Notes