#!/usr/bin/newlisp
#------------------------------------------------------------------------------------------
#
# Program to play audio CD's on the CDROM drive. First argument may contain CDROM-device.
# Device default: /dev/cdrom.
#
# ---> LINUX ONLY <--- Requires GTK-server 2.0.11 or higher. (c) PvE. License: GPL.
#
# May 28, 2006 - version 1.0
# - initial release
#
# June 2, 2006 - version 1.1
# - added eject button
#
# July 1, 2006 - version 1.2
# - rewrote to embedded GTK :-)
#
# July 17, 2006 - version 1.3
# - fixed bug when tracks titles are empty
# - fixed bug with CDROM ejecting on 2.6 kernels
# - added CLOSE_TRAY functionality
# - quit is faster now
#
# September 8, 2007 - version 1.4
# - compliance with GTK-server 2.1.5 and higher
#
#--------------------------------------------------------------------------------------- GTK
(import "libgtk-server.so" "gtk")
(set 'cfgfile (open "/etc/gtk-server.cfg" "read"))
(cond ((not cfgfile)(println "No GTK-server configfile found! Exiting...")(exit)))
(while (read-line cfgfile)
(if (starts-with (current-line) "FUNCTION_NAME")
(begin
(set 'func (chop ((parse (current-line) " ") 2)))
(set 'lb (append {(lambda()(setq s "} func {")(dolist (x (args))(setq s (string s " " x)))(get-string (gtk s)))}))
(constant (global (sym func)) (eval-string lb))
)
)
)
(close cfgfile)
(set 'NULL "NULL")
#---------------------------------------------------------------------------------------- GUI
(context 'GUI)
# Setup XML IDE definition
(set 'Cdplayer [text]'
250
90
True
CD Player
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_NONE
False
True
False
True
False
False
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
True
False
0
True
False
0
True
False
0
30
True
<b>Status</b>: <i><span foreground="red">STOPPED</span></i>
False
True
GTK_JUSTIFY_CENTER
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
True
<b>Time:</b> <i><span foreground="blue">00:00:00</span></i>
False
True
GTK_JUSTIFY_RIGHT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
0
True
True
0
True
True
True
False
0
True
Pause / resume
True
GTK_RELIEF_NORMAL
True
False
False
True
gtk-media-pause
4
0.5
0.5
0
0
0
True
True
True
Stop playing
True
GTK_RELIEF_NORMAL
True
True
gtk-media-stop
4
0.5
0.5
0
0
0
True
True
True
Start playing
True
GTK_RELIEF_NORMAL
True
True
gtk-media-play
4
0.5
0.5
0
0
0
True
True
True
Previous track
True
GTK_RELIEF_NORMAL
True
True
gtk-media-rewind
4
0.5
0.5
0
0
0
True
True
True
Next track
True
GTK_RELIEF_NORMAL
True
True
gtk-media-forward
4
0.5
0.5
0
0
0
True
True
True
Eject CD
True
GTK_RELIEF_NORMAL
True
True
gtk-cdrom
4
0.5
0.5
0
0
0
True
True
True
Quit CD player
True
GTK_RELIEF_NORMAL
True
True
gtk-quit
4
0.5
0.5
0
0
0
True
True
0
True
True
0
True
True
True
True
False
GTK_POS_TOP
1
GTK_UPDATE_CONTINUOUS
True
50 0 100 1 0 0
0
False
True
'
[/text])
# Initialize GTK and load XML string which is surrounded with a ' (important!)
(gtk_init)
(set 'xml (glade_xml_new_from_buffer Cdplayer (length Cdplayer) "NULL" "NULL"))
# Get widgets and connect signals
(set 'main_window (glade_xml_get_widget xml "main_window"))
(gtk_server_connect main_window "delete_event main_quit")
(gtk_server_connect main_window "show main_window")
(set 'button_pause (glade_xml_get_widget xml "button_pause"))
(gtk_server_connect button_pause "clicked button_pause")
(set 'button_stop (glade_xml_get_widget xml "button_stop"))
(gtk_server_connect button_stop "clicked button_stop")
(set 'button_play (glade_xml_get_widget xml "button_play"))
(gtk_server_connect button_play "clicked button_play")
(set 'button_prev (glade_xml_get_widget xml "button_prev"))
(gtk_server_connect button_prev "clicked button_prev")
(set 'button_next (glade_xml_get_widget xml "button_next"))
(gtk_server_connect button_next "clicked button_next")
(set 'button_quit (glade_xml_get_widget xml "button_quit"))
(gtk_server_connect button_quit "clicked button_quit")
(set 'button_eject (glade_xml_get_widget xml "button_eject"))
(gtk_server_connect button_eject "clicked button_eject")
(set 'label_display (glade_xml_get_widget xml "label_display"))
(set 'label_time (glade_xml_get_widget xml "label_time"))
(set 'volume (glade_xml_get_widget xml "volume"))
(gtk_server_connect volume "value-changed volume")
(gtk_range_set_value volume 80)
# Combobox with titles of CD
(set 'vbox (glade_xml_get_widget xml "vbox"))
(set 'combo_tracks (gtk_combo_box_new_text))
(gtk_box_pack_end vbox combo_tracks 1 1 1)
(gtk_widget_show combo_tracks)
# Return every second from GTK main iteration - make sure to connect this signal earlier
(gtk_server_timeout 1000 main_window "show")
(define (label_text_display txt)
(gtk_label_set_markup_with_mnemonic label_display txt)
)
(define (label_text_time txt)
(gtk_label_set_markup_with_mnemonic label_time txt)
)
#---------------------------------------------------------------------------------------------
(context 'CD)
# Get IOCTL call
(import "libc.so.6" "ioctl")
# Get some constants from '/usr/include/linux/cdrom.h'
(constant 'CDROMPAUSE 0x5301)
(constant 'CDROMRESUME 0x5302)
(constant 'CDROMPLAYTRKIND 0x5304)
(constant 'CDROMREADTOCHDR 0x5305)
(constant 'CDROMREADTOCENTRY 0x5306)
(constant 'CDROMSTOP 0x5307)
(constant 'CDROMSTART 0x5308)
(constant 'CDROMEJECT 0x5309)
(constant 'CDROMCLOSETRAY 0x5319)
(constant 'CDROM_DRIVE_STATUS 0x5326)
(constant 'CDROMSUBCHNL 0x530b)
(constant 'CDS_NO_INFO 0)
(constant 'CDS_NO_DISC 1)
(constant 'CDS_TRAY_OPEN 2)
(constant 'CDS_DRIVE_NOT_READY 3)
(constant 'CDS_DISC_OK 4)
(constant 'CDROM_MSF 0x02)
(constant 'CDROM_AUDIO_PLAY 0x11)
(constant 'CDROM_AUDIO_PAUSED 0x12)
# Find OS mixer
(set 'mixer (exec "which rexima 2>/dev/null"))
(if (= mixer '())
(set 'mixer (exec "which aumix 2>/dev/null")))
# Find CDDA2WAV for CD recognition
(set 'cdda2wav (exec "which cdda2wav 2>/dev/null"))
(set 'CD_title "newLisp CD Player 1.4")
(set 'Position 0)
(set 'Text "\"Status: STOPPED\"")
(set 'Device "/dev/cdrom")
(set 'Start_Time nil)
(set 'Cur_Track nil)
(define (find_tracks, tochdr)
(set 'tochdr (pack "bb" 0 0))
(ioctl cdrom CDROMREADTOCHDR tochdr)
(unpack "bb" tochdr)
)
(define (current_track, subchnl)
(set 'subchnl (pack "bbbbbb ld ld" CDROM_MSF 0 0 0 0 0 0 0))
(ioctl cdrom CDROMSUBCHNL subchnl)
(nth 3 (unpack "bbbbbb ld ld" subchnl))
)
(define (cd_status, subchnl)
(set 'subchnl (pack "bbbbbb ld ld" CDROM_MSF 0 0 0 0 0 0 0))
(ioctl cdrom CDROMSUBCHNL subchnl)
(nth 1 (unpack "bbbbbb ld ld" subchnl))
)
(define (play_track, ti tracks no x y)
(set 'cdrom (open Device "read"))
(set 'Track_Range (find_tracks))
(set 'Status true)
(set 'ti (pack "bbbb" (first Track_Range) 0 (last Track_Range) 0))
(ioctl cdrom CDROMPLAYTRKIND ti)
(set 'Text "\"Status: PLAYING\"")
(gtk_toggle_button_set_active GUI:button_pause 0)
(if (!= cdda2wav '())
(begin
(set 'tracks (exec (append (first cdda2wav) " -D " Device " -J -v toc -L 1 -N -H -g 2>&1")))
(dolist (x tracks)
(if (= x "")
(begin
(set 'x " ")
(set 'Text "\"Status: UNKOWN CD - PLAYING\"")
)
)
(if (regex "Album" (first (parse x " ")))
(begin
(replace "\\'" x "`")
(replace "\"" x "`")
(set 'y (g_locale_to_utf8 (append {"} (slice x (+ (find "'" x) 1)) {"}) -1 "NULL NULL NULL"))
(set 'CD_title (slice y 0 (find "'" y)) )
(set 'y (slice y (+ (find "from" y) 6)))
(set 'CD_title (append CD_title " by " (slice y 0 (find "'" y))))
)
)
(if (regex "T[0-9]+:" (first (parse x " ")))
(begin
(set 'no (slice (first (parse x " ")) 1))
(replace "\\'" x "`")
(replace "\"" x "`")
(set 'y (g_locale_to_utf8 (append {"} (slice x (+ (find "'" x) 1)) {"}) -1 "NULL NULL NULL"))
(if (> (length y) 0) (gtk_combo_box_append_text GUI:combo_tracks (append {"} no " " (slice y 0 (find "'" y)) " - " ((parse x " +" 0) 2) {"})))
)
)
)
(gtk_combo_box_set_active GUI:combo_tracks 0)
)
)
(if (= (first Track_Range) 0)
(begin
(set 'Text "\"Status: STOPPED\"")
(set 'Status nil)
)
)
(set 'Start_Time (date-value))
(if cdrom (close cdrom))
)
(define (stop_playing, x)
(set 'cdrom (open Device "read" "n"))
(ioctl cdrom CDROMSTOP)
(set 'Text "\"Status: STOPPED\"")
(if Status
(for (x (first Track_Range) (last Track_Range))
(gtk_combo_box_remove_text GUI:combo_tracks 0)
)
)
(set 'Track_Range nil)
(set 'Status nil)
(set 'CD_title "newLisp CD Player")
(set 'Start_Time nil)
(set 'Cur_Track nil)
(gtk_toggle_button_set_active GUI:button_pause 0)
(if cdrom (close cdrom))
)
(define (prev_track, ti current)
(set 'cdrom (open Device "read"))
(set 'current (current_track))
(if (and Status (> current 1))
(begin
(set 'ti (pack "bbbb" (- current 1) 0 (last (find_tracks)) 0))
(ioctl cdrom CDROMPLAYTRKIND ti)
)
)
(gtk_toggle_button_set_active GUI:button_pause 0)
(if cdrom (close cdrom))
)
(define (next_track, ti current)
(set 'cdrom (open Device "read"))
(set 'current (current_track))
(if (and Status (!= (last Track_Range) -1) (< current (last Track_Range)) )
(begin
(set 'ti (pack "bbbb" (+ current 1) 0 (last (find_tracks)) 0))
(ioctl cdrom CDROMPLAYTRKIND ti)
)
)
(gtk_toggle_button_set_active GUI:button_pause 0)
(if cdrom (close cdrom))
)
(define (cd_pause)
(set 'cdrom (open Device "read"))
(if (= (cd_status) CDROM_AUDIO_PLAY)
(begin
(ioctl cdrom CDROMPAUSE)
(set 'Text "\"Status: PAUSED\"")
(set 'Pause_Time (date-value))
)
(if (= (cd_status) CDROM_AUDIO_PAUSED)
(begin
(ioctl cdrom CDROMRESUME)
(set 'Text "\"Status: PLAYING\"")
(set 'Start_Time (+ Start_Time (- (date-value) Pause_Time)))
)
)
)
(if cdrom (close cdrom))
)
(define (adjust_volume, vol)
(set 'vol (gtk_range_get_value GUI:volume))
(if (= mixer (exec "which rexima 2>/dev/null")) (exec (append (first mixer) " cd " (string (int vol)) )))
(if (= mixer (exec "which aumix 2>/dev/null")) (exec (append (first mixer) " -c " vol)))
)
(define (update_gui, ct)
(if Status (set 'cdrom (open Device "read")))
(set 'ct (current_track))
(if (= (cd_status) CDROM_AUDIO_PLAY)
(gtk_combo_box_set_active GUI:combo_tracks (- ct 1)) )
(GUI:label_text_display Text)
# Show elapsed time
(if Start_Time
(if (= (cd_status) CDROM_AUDIO_PLAY) (GUI:label_text_time (append "\"Time: " (date (- (date-value) Start_Time) -60 "%H:%M:%S") "\"")))
(GUI:label_text_time "\"Time: 00:00:00\"")
)
# Reset tracktime if needed
(if (and (= (cd_status) CDROM_AUDIO_PLAY)(!= Cur_Track ct))
(begin
(set 'Cur_Track ct)
(set 'Start_Time (date-value))
)
)
(gtk_window_set_title GUI:main_window (append {"} (slice CD_title (integer Position)) " - " CD_title " - " CD_title {"}))
(inc 'Position 2)
(if (> Position (length CD_title)) (set 'Position 0))
(if cdrom (close cdrom))
)
(define (change_track, ti current)
(set 'cdrom (open Device "read"))
(set 'current (+ (int (gtk_combo_box_get_active GUI:combo_tracks)) 1))
(if (!= current (current_track))
(begin
(set 'ti (pack "bbbb" current 0 (last Track_Range) 0))
(ioctl cdrom CDROMPLAYTRKIND ti)
)
)
(if cdrom (close cdrom))
)
(define (cd_eject)
(stop_playing)
(set 'cdrom (open Device "read" "n"))
(if (= (int (ioctl cdrom CDROM_DRIVE_STATUS)) CDS_TRAY_OPEN)
(ioctl cdrom CDROMCLOSETRAY)
(ioctl cdrom CDROMEJECT)
)
(if cdrom (close cdrom))
)
#---------------------------------------------------------------------------------------------
(context 'MAIN)
(if (= (length (main-args)) 3) (set 'CD:Device (last (main-args))))
# Mainloop
(until (or (= event "button_quit") (= event "main_quit"))
(set 'event (gtk_server_callback "wait"))
(case event
("button_play" (CD:play_track))
("button_stop" (CD:stop_playing))
("button_prev" (CD:prev_track))
("button_next" (CD:next_track))
("button_pause" (CD:cd_pause))
("button_eject" (CD:cd_eject))
("volume" (CD:adjust_volume))
("main_window" (CD:update_gui))
)
(if (= event GUI:combo_tracks) (CD:change_track))
)
(CD:stop_playing)
(gtk_exit)
(exit)