diff options
Diffstat (limited to 'kscd')
139 files changed, 30963 insertions, 0 deletions
diff --git a/kscd/CDQuery.txt b/kscd/CDQuery.txt new file mode 100644 index 00000000..51ae2ecc --- /dev/null +++ b/kscd/CDQuery.txt @@ -0,0 +1,21 @@ + +Purchases: +http://cdnow.com/switch/from=sr-288025/target=buyweb_products/artfs=Seal +http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=Seal/frm=lk_DiscPlay/ + +Information: +http://musiccentral.msn.com/MCGen/ReEntry?theurl=http%3A//musiccentral.msn.com/Search/DoFullSearch%3FArtists%3Don%26Albums%3D%26Songs%3D%26ArticleTitles%3D%26FullText%3D%26strInput%3DSeal +http://www.ubl.com/find/form?SEARCH=Seal +http://www.excite.com/search.gw?c=web&search=Seal&trace=a +http://www.search.hotbot.com/hResult.html?SW=web&SM=MC&MT=Seal&DC=10&DE=2&RG=NA&_v=2 +http://www.infoseek.com/Titles?qt=Seal&col=WW&sv=IS&lk=ip-noframes&nh=10 +http://www.lycos.com/cgi-bin/pursuit?cat=lycos&query=Seal +http://www.mckinley.com/search.gw?search=Seal&c=web&look=magellan +http://search.yahoo.com/bin/search?p=Seal + +Performances: +http://www.tourdates.com/cgi-bin/search.cgi?type=Artist&search=Seal + + + + diff --git a/kscd/ChangeLog b/kscd/ChangeLog new file mode 100644 index 00000000..24eeb3ee --- /dev/null +++ b/kscd/ChangeLog @@ -0,0 +1,266 @@ +Wed May 31 06:18:21 2000 Dirk Frsterling <milliByte@gmx.de> + + * Switched from stand alone WorkMan code to libworkman. + * Fixed Bugs 296, 298, 299, 301, 371, 815, 1186, 2278, 2354, + * 2429, 2559 and 2586 or verified that they were already fixed. + * updated ChangeLog. + +Sat May 27 09:43:58 2000 Dirk Frsterling <milliByte@gmx.de> + + * Updated Kscd Magic from Synaesthesia 1.3 to Synaesthesia 2.0 + * Fixed missing slot in CDDB configuration + * Added (and fixed) loop mode display. + * updated and sorted libWorkMan API + * addressed the 100dpi font problem (Thanks to John R + MacMillan <john@weirdways.com> for sending an idea for a fix). + +Sun Mar 5 10:54:40 2000 Dirk Frsterling <milliByte@gmx.de> + + * Completed shuffle mode (some patch data was missing) and + added corresponding "shuffle" Display string. + * Slightly changed "loop" semantics: "loop" is no longer a single + playing mode but a modifier. You can now loop "playing" mode and + "shuffle" mode. + +Mon Feb 28 07:27:04 2000 Dirk Frsterling <milliByte@gmx.de> + + * Added "random is shuffle" patch from + Bernard Kozioziemski <koz@tdl.com> + * Started some source formatting my better understanding. Sorry, + but I'm a Qt/KDE-programming-newbie and need some visual hints yet + to understand the code. (And I _love_ source documentation + (comments) ...) + +Tue Jan 18 17:27:26 2000 Dirk Frsterling <milliByte@DeathsDoor.com> + + * Added the libWorkMan directory and inserted it into the make + process. Please note, that libWorkMan will be built and installed, + but is not currently used. + +1999-03-17 Harri Porten <porten@kde.org> + + * kscd.cpp: applied patch from Petter Reinholdtsen <pere@hungry.com> + (bug#302) that prevents a segfault if cddb directory is invalid. + * CDDialog.cpp: fixed length of last title (bug#304). + +1999-03-09 Harri Porten <porten@kde.org> + + * ported to Qt 2.0 strings. Haven't tested if everything still works. + +1999-02-18 Harri Porten <porten@kde.org> + + * cddb.cpp: work around snprintf() for non-GNUish systems + +1998-12-22 Sam Maloney <thufir@illogic.ml.org> + + * Added to last commit, as so the 'autodock' checkbox would be + disabled when 'docking' was unchecked, and vice versa. + +1998-12-20 Sam Maloney <thufir@illogic.ml.org> + + * Just simple bugfixes to make more stable like...: + * Fixed so that when one turns on or off 'enable docking' it + adds/removes the dockwidget in the panel (instead of before one + would have to restart kscd). + * Fixed so that if one turned off 'enable docking' but still had + 'autodock enabled' on, then it would NOT dock (and remove the + widget). + +1998-12-12 Christian Esken <esken@kde.org> + + * Tweaked docking a bit more. Under certain circumstances, + dock_widget->dock() correctly does not get called at the start + of the program. So if you docked, the window just vanished. + I added a call to dock_widget->dock() at "appropiate" places. + +1998-11-28 Bernd Wuebben <wuebben@petit.cornell.edu> + + * kscd.cpp: resinstated old look and behavior + Sam, we should talk ... + +1998-11-20 Sam Maloney <thufir@illogic.ml.org> + + * Fixed bug/"missing thing" were it would just crash when loading + up if it couldn't find the cddb base dir. Now it will use + InexactDialog to prompt the user for the directory. (cancel button + will force it to get from remote). + * NOTE: Im just going fix the inexact dialog box so it can be + single line input, (for the cddb dialog), then ill probably + release 1.2.4, unless anyone has any objections. (to lazy to fix + tonite) + +1998-11-11 Sam Maloney <thufir@illogic.ml.org> + + * Fixed bug with debug flag being messed up when docking + (incorrect extern statement) + * Worked around a wierd bug thing with docking. (consult + docking.cpp in toggleState() ) + +1998-11-08 Robert Bossecker <Robert.Bossecker@fresenius.de> + + * Fixed problem with editing EXT info, where if you edited track + x, it would (stupidly) modify x-1. If editing track 1, it would + modify the extinfo for the Title. + +1998-11-06 Sam Maloney <thufir@illogic.ml.org> + + * Made smtpMailer a global widget, so it could mail in background. + * Fixed problem were after submitting a record via SMTP, and + having an error, if one tried to exit Kscd, it would close all + widgets, but be stuck in while loop. + * Added error/success reporting of SMTP submissions. + * Fixed problem with Local Cache, where it would ignore leading + 0's in record file names, thus not finding them when looking for + that record. + * Added a timeout when waiting for server interaction. + +1998-11-04 Sam Maloney <thufir@illogic.ml.org> + + * Fixed problem with 'dock' button, when AutoDock is turned on. + * Implimented SMTP code, so Kscd no longer requires sendmail/unix + mail program(s) to submit CDDB records. + * Added VERY requested feature, of having it retract the tray if + its ejected, when one hits the eject button. + +1998-10-14 Alexei Dets <dets@services.ru> + + * Fixed CD Database Editor close handling. + +1998-10-13 Sam Maloney <thufir@illogic.ml.org> + + * Fixed compilation problems some compilers will notice related to + last CDDB setup modifs + +1998-10-09 Sam Maloney <thufir@illogic.ml.org> + + * Added "play on tray close" feature + * Many little fixes + +1998-09-23 Sam Maloney <thufir@illogic.ml.org> + + * Fixed submission of CDDB records, where it would not include a + 'PLAYORDER=' at the bottom of the email. + * Switched it over from using 'mail -s <subject> < record', to + using sendmail. (currently adding SMTP suppport, so it uses + no external program for this) + +1998-09-21 Sam Maloney <thufir@illogic.ml.org> + + * Added automatic storing of CDDB records downloaded from CDDB + server to local harddrive. + * Fixed bug with routine connecting signals, over and over again, + causing routines to be called too many time, annoying CDDB + server admins. Happens when a new CDDB (remote) request was + issued. + * Added snazyness of having the pulldown track selection box + display ("%02d: %s", tracknumber, trackname) for each item, + instead of just the number. + +Fri May 15 18:28:40 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * docking.cpp: fixed up the docking behaviour + + * Some Layout improvements. -- still not 100% satisfied with the + way the config dialogs look now. + +1998-05-12 Vadim Zaliva <lord@crocodile.org> + + * Options dialog made wider + * In CDDB setup dialog added list of email serves. + * Getting SMTP servers from list of servers. + +1998-04-29 Vadim Zaliva <lord@crocodile.org> + + * Corrected problem with not saving "UnixMailCommand" option + which was disabling CDDB submissions. + +1998-04-28 Vadim Zaliva <lord@crocodile.org> + + * Corrected bug in cddb with current CD information disappearing + after some time of playing. + +1998-04-26 Vadim Zaliva <lord@crocodile.org> + + * Removed potetial concurency problem in CDDB connection procedure. + * Added support for protocol level 3 in CDDB connections. + +1998-04-17 Vadim Zaliva <lord@crocodile.org> + + * User and domain name now calculated once, not per each request. + +1998-04-16 Vadim Zaliva <lord@crocodile.org> + + * Tested with Netscape Proxy + + * Tested with Squid-1.1.18 HTTP proxy + * Tested with HTTP proxy, w/o HTTP proxy, using CDDBP and HTTP. + * Small bug in parsing corrected. + + * State automata and parsing reimplemented + * Works over HTTP with and without HTTP proxy. + * Tested with M>Wall HTTP proxy + + * Implemented more sophisticated HTTP encoding for parameters. + * kscd.cpp reformated. + + * CDDB over HTTP works, w/o proxy now. Proxy support to be implemeted to-morrow. + +1998-04-15 Vadim Zaliva <lord@crocodile.org> + + * cddb.h, cddb.cpp reformated + * tested to work with and without HTTP proxy + * tested with squid 1.1.18 + + * Added one more default server, with HTTP protocol + * Code for getting servers list via HTTP done and works. + * Changed 'cddb' object allocation. + * (this is intermediate commit, HTTP only partialy works) + +1998-04-14 Vadim Zaliva <lord@crocodile.org> + + * GUI added for setting HTTP proxy to access CDDB. + * Config options added for HTTP proxy stuff + * most includes from cddb.h moved to cddb.cpp,kscd.cpp + * new version #1.1.1 + +Tue Apr 14 05:17:36 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * layout corrections for kscd magic dialog + +Sat Jan 17 21:36:27 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * Enabled CDDB support. A couple of clean ups and fixes. + +Tue Jan 13 03:24:49 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * applied Sebestyen Zoltan <szoli@digo.inf.elte.hu> FreeBSD patch. + +Thu Jan 1 19:08:22 1998 Bernd Johannes Wuebben <wuebben@petit.cornell.edu> + + * Monster commit: Commit cddb and web integration additions + Let the games begin! + +Fri Dec 26 13:48:56 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu> + + * headerfile changes in plat_bsd.c + +Sat Oct 25 11:13:40 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu> + + * fixed the segfault problem when you didn't have acess permissions + to the cdrom device + +Thu Aug 14 19:49:18 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu> + + * added irix support + * added KSCD configuration script + +Wed Jun 25 01:24:39 1997 Bernd Johannes Wuebben <wuebben@petit.cornell.edu> + + * added Marc van Kempen's FreeBSD patches + He reports also strange behaviour of kscd when reloading a CD + Is this with the code or with his CD drive ? + + * Set proper defines that should allow for a smooth compile + on a SUN/ SUN SOLARIS + + diff --git a/kscd/Makefile.am b/kscd/Makefile.am new file mode 100644 index 00000000..f0b8d50b --- /dev/null +++ b/kscd/Makefile.am @@ -0,0 +1,83 @@ +# $Id$ + +mimedir = $(kde_mimedir)/text +picsdir = $(kde_datadir)/kscd/pics + +INCLUDES= -I$(top_srcdir) -I$(top_builddir)/libkcddb $(all_includes) +METASOURCES = AUTO + +####### Files + +noinst_HEADERS = kscd.h bwlednum.h ledlamp.h \ + version.h docking.h configWidget.h + +kscd_SOURCES = panel.ui kscd.cpp kscd.skel ledlamp.cpp \ + docking.cpp bwlednum.cpp \ + configWidget.cpp configWidgetUI.ui prefs.kcfgc cddbdlg.cpp + +noinst_LTLIBRARIES = libkcompactdisc.la +libkcompactdisc_la_SOURCES = kcompactdisc.cpp + +libkcompactdisc_la_LIBADD = $(top_builddir)/kscd/libwm/libworkman.la + +kde_kcfg_DATA = kscd.kcfg + +kscd_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kscd_LDADD = $(LIBCDROM) $(LIBCDAUDIO) \ + $(LIBFPE) $(LIB_KIO) -lkutils $(top_builddir)/libkcddb/libkcddb.la \ + libkcompactdisc.la + +profiledata_DATA = kscd.profile.xml +profiledatadir = $(kde_datadir)/profiles + +servicedata_DATA = audiocd_play.desktop +servicedatadir = $(kde_datadir)/konqueror/servicemenus + +DISTCLEANFILES = config.h + +bin_PROGRAMS = kscd +# Don't Build cddaslave. It doesn't work. +# cddaslave + +#config.h: kscd-script +#$(SHELL) $(srcdir)/kscd-script + +SUBDIRS = bitmaps libwm + +bin_SCRIPTS = workman2cddb.pl + +kscdicondir = $(kde_datadir)/kscd/icons +kscdicon_ICON = cdsmall +KDE_ICON = kscd + +xdg_apps_DATA = kscd.desktop + +mime_DATA = xmcd.desktop + +messages: rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/kscd.pot + +cdrom.c:config.h +plat_bsd386.c:config.h +plat_freebsd.c:config.h +plat_hpux.c:config.h +plat_irix.c:config.h +plat_linux.c:config.h +plat_news.c:config.h +plat_osf1.c:config.h +plat_scor5.c:config.h +plat_sun.c:config.h +plat_sun_audio.c:config.h +plat_sun_cdda.c:config.h +plat_svr4.c:config.h +plat_ultrix.c:config.h + +KDE_OPTIONS = nofinal + + +cddbdlg.o: ../libkcddb/configbase.h ../libkcddb/cdinfodialogbase.h +configWidget.o: ../libkcddb/configbase.h +docking.o: ../libkcddb/configbase.h +kscd.o: ../libkcddb/configbase.h +kscd_skel.o: ../libkcddb/configbase.h panel.h prefs.h configWidgetUI.h + diff --git a/kscd/README b/kscd/README new file mode 100644 index 00000000..e1366867 --- /dev/null +++ b/kscd/README @@ -0,0 +1,59 @@ +KSCD 1.2.3 +========== + + * Fixed DOCKING! + +Basically, this version finishes a whole lot more that has been +missing/broken since the last version that bernd did, (1.2.0). + + Sam Maloney, <thufir@illogic.ml.org> + +KSCD 1.2.0t1 +============ + + * Fixed CDDB code big time. + * Submissions now fixed. + * Server requests fixed. + * Local storing of CDDB records. (automatic) + +KSCD have been broken and NOT updated, since the release of KDE 1.0, and +that is a long time ago, so i decided to fix MAJOR problems with the CDDB +code, now it is permitted to use and upload to the CDDB database. + +Sam Maloney, <thufir@illogic.ml.org> + +KSCD 0.8 +======== + +Sat Jan 17 17:50:37 1998 Bernd Johannes Wuebben <wuebben@math.cornell.edu> + + * KSCD 0.8 + * CDDB submissions enabled. + + +Remember if kscd seems to have trouble with your cd drive, do a 'make clean' +then 'make' and answer the questions posed by the kscd-script to configure +kscd suitable for your cdrom drive. + +Bernd + +KSCD 0.6 +======== + + CDDB support, web integration + +Bernd + +KSCD 0.4 +======== + +This is version 0.4 of kscd. Kscd is now easier to build than +ever with its new configuration script. Supprt was added +for Irix and Sun/Solaris cdda. + +Please see the html documentation in the docs directory for +information about kscd. + +Thank you, +Bernd Wuebben +wuebben@kde.org diff --git a/kscd/TODO b/kscd/TODO new file mode 100644 index 00000000..e10c01c8 --- /dev/null +++ b/kscd/TODO @@ -0,0 +1,9 @@ +* fix html encoding +* Session Management +* change the GUI + +AJS: +o fix eject button +o don't show "no disc" error on startup +o direct digital playback +o better GUI layout for LCD panel diff --git a/kscd/audiocd_play.desktop b/kscd/audiocd_play.desktop new file mode 100644 index 00000000..4200b488 --- /dev/null +++ b/kscd/audiocd_play.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +ServiceTypes=media/audiocd +Actions=Play; +X-KDE-Priority=TopLevel + +[Desktop Action Play] +Name=Play +Name[bg]=Старт +Name[bn]=চালাও +Name[br]=Seniñ +Name[bs]=Sviraj +Name[ca]=Reproducció +Name[cs]=Přehrát +Name[cy]=Chwarae +Name[da]=Spil +Name[de]=Abspielen +Name[el]=Αναπαραγωγή +Name[eo]=Ludi +Name[et]=Esitus +Name[fa]=پخش +Name[fi]=Soita +Name[fr]=Lecture +Name[ga]=Seinn +Name[gl]=Reproducir +Name[he]=נגן +Name[hu]=Lejátszás +Name[is]=Spila +Name[it]=Riproduci +Name[kk]=Ойнату +Name[km]=ចាក់ +Name[ko]=재생 +Name[lt]=Groti +Name[mk]=Пушти +Name[nb]=Spill +Name[nds]=Afspelen +Name[ne]=प्ले +Name[nl]=Afspelen +Name[nn]=Spel +Name[pa]=ਵਜਾਓ +Name[pl]=Odtwarzaj +Name[pt]=Tocar +Name[pt_BR]=DjPlay +Name[ru]=Воспроизведение +Name[sl]=Predvajaj +Name[sv]=Spela +Name[ta]=தொடங்கு +Name[th]=เล่น +Name[tr]=Çal +Name[uk]=Пуск +Name[wa]=Djouwer +Name[zh_CN]=播放 +Name[zh_HK]=播放 +Name[zh_TW]=播放 +Exec=kscd -s -caption "%c" %i %m %u +Icon=kscd diff --git a/kscd/bitmaps/AUTHORS b/kscd/bitmaps/AUTHORS new file mode 100644 index 00000000..3db9dacb --- /dev/null +++ b/kscd/bitmaps/AUTHORS @@ -0,0 +1,37 @@ +The following copyright notice applies to the following files found in +this directory: + eject.xbm + ff.xbm + lock.xbm + logo.xbm + nexttrk.xbm + options.xbm + playpaus.xbm + poweroff.xbm + prevtrk.xbm + repeat.xbm + rew.xbm + shuffle.xbm + stop.xbm + +/* + * xmcd - Motif(tm) CD Audio Player + * + * Copyright (C) 1993-1996 Ti Kan + * E-mail: ti@amb.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + */ diff --git a/kscd/bitmaps/CompactDisc.xpm b/kscd/bitmaps/CompactDisc.xpm new file mode 100644 index 00000000..cf7e4c00 --- /dev/null +++ b/kscd/bitmaps/CompactDisc.xpm @@ -0,0 +1,68 @@ +/* XPM */ +static char * image_name[] = { +"56 46 19 1", +" c #F7F7F3F3F7F7", +". c #B6B6B2B2B6B6", +"X c #000000000000", +"o c #A6A6A2A2A6A6", +"O c #969692929696", +"+ c #61618282B6B6", +"@ c #EFEFAEAEA6A6", +"# c #B6B6B6B6B6B6", +"$ c #F7F7F7F7F7F7", +"% c #9E9E96969E9E", +"& c #FFFFFBFBFFFF", +"* c #696971718E8E", +"= c #B6B6AEAEB6B6", +"- c #AEAEAAAA8E8E", +"; c #000000001010", +": c #080804040808", +"> c #A6A69E9EA6A6", +", c #B6B6BABAC7C7", +"< c #96969A9AAEAE", +" .", +" ......................................................X", +" ......................................................X", +" ......................................................X", +" o...o...o...o...o...o...o...o...o...o...o...o...o...o.X", +" ......................................................X", +" o.o.o.o.o.o.o.o...........................o.o.o.o.o.o.X", +" .....o.......o...OXO.o+X+OXOXOX++X+OXO+o.....o.......oX", +" o.o.o.o.o.o.o.o..X X.oOOOOOOOOOOOOOOOOOo..o.o.o.o.o.o.X", +" ...o...o...o.....X X.......................o...o...o..X", +" o.o.o.o.o.o..OXXXX Xo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X", +" .o...o.o.o...X. X.X X.X. X.X. .X.o...o.o.o...oX", +" o.o.o.o.o.o..X +X+ XoX XoX XXXXOoX XXX X..o.o.o.o.o.o.X", +" .o.o.o.o.o.o.X X.X X.X X.X X+++..X X.XXX.o.o.o.o.o.o.oX", +" o.o.o.o.o.o..X X.X XoX XoX. .XoX X......o.o.o.o.o.o.X", +" oo.ooo.ooo.o.X X.X XoX Xo.++XX XoX X.XXX.o.ooo.ooo.oooX", +" o.o.o.o.o.o..X +X+ XoX XoOXXXX XoX XXX X..o.o.o.o.o.o.X", +" oooooooooooo.X. XoX XoX .XoX. .X.oooooooooooooX", +" o.o.o.o.o.o..OXXXXXOo+X+oOXXXXXOoOXXXXXO..o.o.o.o.o.o.X", +" ooooooooooooo...........................ooooooooooooooX", +" ooo.ooo.ooooooooooooooooooooooooooooooooooo.ooo.ooo.ooX", +" ooooooooooooooooooooooooooooooooooooooooooooooooooooooX", +" ooooooooooooooooo...@.@#@.@@$@$@$@..oo.ooo.ooo.oooooooX", +" ooooooooooooooo#@@. O$O + %@@$@ #$@$@.o@oo.oo@.oooooooX", +" OoooOoooOoooO.@$$$ + + + + @&@$# + # . .@. .ooOoooOoX", +" oooooooooooo@ . + + %@@$# + O # .$@ .oooooooooX", +" ooOoOoOoooO@$@$@$ . + @$.$*$O . . @$ @#..ooOoOoX", +" oooooooooo@$@$@$@$@ @ .$O@# +$# .$@$@$@ #oooooooooX", +" OoOoOoOoO.$@$@$@$@$@$@$ $$$@$# @$@$$$$$$ oooOoOoOoX", +" ooooooooo@$#$#$#$#$#$# .XXX#$@$@ $ #@ooooooooX", +" OoOoOoOoO O$O$O$O$=$.$ .+ooo+# @$@$@$@$@$@$@#o#oOoOoOoX", +" oOoooOooO@ +$*$=$. .++*# #@#@#$#$.$.$#$+OoooOoooOX", +" OoOoOoOoO@$ +$# . - @$ $$$@$#@##=....@#$=+OOoOoOoOoX", +" oOoOoOoOOO . +$*$*$+#@$o$@$#@#.....=$.++OoOoOoOoOX", +" OoOoOoOoOo+ . + + +$+$+..$o o$@$#@#...@$.*;oOOoOoOoOoX", +" OOoOOOoOOOo+ . + + +$+.. O o o$@$#@#@$$O+:oOOoOOOoOOOX", +" OoOoOoOoOoOo+. . + +## O o o o$@$$$$=+X+oOOoOoOoOoOoX", +" OOOOOOOOOOOOoO+.. @$@ @ . . o o$>$@,*XX+OoOOOOOOOOOOOOX", +" OoOoOoOoOoOoOooO+++..o.o.o.o.o#>#+XX+<>ooOOoOoOoOoOoOoX", +" OOOOOOOOOOOOOOOoooO++X+XXXXXXXX+X+OoooOOOOOOOOOOOOOOOOX", +" OOOoOOOoOOOoOOOoOOoooooooooooooooooOOOOoOOOoOOOoOOOoOOX", +" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX", +" OoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOOOOoOOOOX", +" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX", +" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOX", +".XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"}; diff --git a/kscd/bitmaps/Image3.gif b/kscd/bitmaps/Image3.gif Binary files differnew file mode 100644 index 00000000..a2a72c4c --- /dev/null +++ b/kscd/bitmaps/Image3.gif diff --git a/kscd/bitmaps/Image3.tif b/kscd/bitmaps/Image3.tif Binary files differnew file mode 100644 index 00000000..4fd8fcb9 --- /dev/null +++ b/kscd/bitmaps/Image3.tif diff --git a/kscd/bitmaps/Image3.xpm b/kscd/bitmaps/Image3.xpm Binary files differnew file mode 100644 index 00000000..488cb3b0 --- /dev/null +++ b/kscd/bitmaps/Image3.xpm diff --git a/kscd/bitmaps/Image4.gif b/kscd/bitmaps/Image4.gif Binary files differnew file mode 100644 index 00000000..7fcf837f --- /dev/null +++ b/kscd/bitmaps/Image4.gif diff --git a/kscd/bitmaps/Image4.xpm b/kscd/bitmaps/Image4.xpm new file mode 100644 index 00000000..611290ec --- /dev/null +++ b/kscd/bitmaps/Image4.xpm @@ -0,0 +1,31 @@ +/* XPM */ +static char * Image4_xpm[] = { +"20 20 8 1", +" c #C71BC30BC71B", +". c #000000000000", +"X c #79E779E779E7", +"o c #FFFFFFFF0000", +"O c #BEFBBEFBBEFB", +"+ c #0000FFFFFFFF", +"@ c #0000FFFF0000", +"# c #FFFFFFFFFFFF", +" .. ", +" ... ", +" XXXXXX .... ", +" XXooOOOOXX.O.. ", +" X+oooOOOOOO.OO. ", +" X@++ooOOOOO..XO. ", +" X@@+ooOOOO...XO. ", +" XOO@@+oOOO#...O.. ", +" XOOO@+XXXXOOOO... ", +" XOOOO@X##XOOOO... ", +" XOOOO@X##XOOOO X ", +" XOOOO#XXXX+OOOOX ", +" XOOO#OO#@@o+OOOX ", +" XO#OOOO@@o++OX ", +" X#OOOOO@@oo++X ", +" XOOOOO@@@ooX ", +" XXOOO@@@XX ", +" XXXXXX ", +" ", +" "}; diff --git a/kscd/bitmaps/Makefile.am b/kscd/bitmaps/Makefile.am new file mode 100644 index 00000000..4785fec0 --- /dev/null +++ b/kscd/bitmaps/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = *.xpm *.gif *.xbm + diff --git a/kscd/bitmaps/cd3d.xpm b/kscd/bitmaps/cd3d.xpm new file mode 100644 index 00000000..627e76a0 --- /dev/null +++ b/kscd/bitmaps/cd3d.xpm @@ -0,0 +1,62 @@ +/* XPM */ +static char * image_name[] = { +"56 46 13 1", +" c #F7F7F7F7F7F7", +". c #B6B6B2B2B6B6", +"X c #B6B6B6B6B6B6", +"o c #000000001010", +"O c #A6A6A2A2A6A6", +"+ c #969692929696", +"@ c #3838FBFB3030", +"# c #717182829696", +"$ c #FFFFFFFF0000", +"% c #B6B620202020", +"& c #51515151FFFF", +"* c #FFFF0000FFFF", +"= c #FFFF14144141", +" .", +" .X..X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.o", +" ......................................................o", +" .X..X.................................................o", +" O...O.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.X.OX.XO.o", +" X..X..................................................o", +" O.+.O.+XO.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.+.OX+.O.O.OXo", +" .X...O....X..O.X..X..O.X..X..O.X..X..O.X..X..O.X..X..Oo", +" O.O.OXO.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.o", +" ...O...OX..O...O...OX..O...OX..O...OX..O...OX..O...O..o", +" OXO.O.O.O.OXO.OXO.O.O.O.OXO.O.O.OXO.O.O.OXO.O.O.OXO.OXo", +" .O..XO.O.O...O.O.OX..O.O@#@####O.O...OXO.O...OXO.O...Oo", +" O.O.O.O.O.O.OXO.O.O.O$#@#@########O.O.O.O.O.O.O.O.OXO.o", +" .OXO.OXO.OXO.O.O.O.%&#$@@###########.O.O.OXO.O.O.O.O.Oo", +" O.O.O.O.O.O.O.O.O.#*%&@$@@##O.#######.O.O.O.O.OXO.O.O.o", +" OO.OOO.OOO.OOO.OO@@#*%&$@##OO##OO#####XOOO.OOO.OOO.OOOo", +" OXO.O.OXO.OXO.OX&$$@#*%$@@#.O##.#.#####.O.OXO.O.O.OXO.o", +" OOOOOOOOOOOOOOOO%%&$$#**@@##OOOO#OO####OOOOOOOOOOOOOOOo", +" O.O.O.O.O.O.O.O*=*%%&$@$=@#####.#X######O.O.O.O.O.O.O.o", +" OOOOOOOOOOOOOOO#*#=*%&$$&@OOO##OO#######OOOOOOOOOOOOOOo", +" OOOXOOO.OOO.OO######=*%&$O O###########OO.OOOXOOO.OOo", +" OOOOOOOOOOOOOO@@@@@@@@@@O O##########OOOOOOOOOOOOOo", +" OOOOOOOOOOOOOO#########O #XX O#########OOOOOOOOOOOOOo", +" OOOOOOOOOOOOOO#########O #. +X O#########OOOOOOOOOOOOOo", +" +OOO+OOO+OOO+O#########O #X. O#########OOO+OOO+OOO+Oo", +" OOOOOOOOOOOOOO##########O O**########OOOOOOOOOOOOOo", +" OO+O+O+OOO+O+O####+######O O@&%***#####O+O+O+OOO+O+Oo", +" OOOOOOOOOOOOOOO##OO#######OOO#$@@&%%**##OOOOOOOOOOOOOOo", +" +O+O+O+O+O+O+O+####O##########=$@@$&%%**+O+O+O+O+O+O+Oo", +" OOOOOOOOOOOOOOOO##O#O##########$$@@$&&&OOOOOOOOOOOOOOOo", +" +O+O+O+O+O+O+O+O####+##########$=$@@@$&O+O+O+O+O+O+O+Oo", +" O+OOO+OOO+OOO+OOO####+O#########$#$@$@OOO+OOO+OOO+OOO+o", +" +O+O+O+O+O+O+O+O+O####+#+#######$=#$$O+O+O.O+O+O+O+O+Oo", +" O+O+O+O+O+O+O+O+O+O####+#########$=#O+O+..O+O+O+O+O+O+o", +" +O+O+O+O+O+O+O+O+O+O+############$+O+#...++O+O+O+O+O+Oo", +" ++O+++O+++O+++O+++O+++O+#######+++##...X#+O+++O+++O+++o", +" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+#........+O+O+O+O+O+O+Oo", +" ++++++++++++++++++++++++++++++++++OOOO++++++++++++++++o", +" +O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+O+Oo", +" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o", +" +++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O+++O++o", +" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o", +" +O+++++++O+++++++O+++++++O+++++++O+++++++O+++++++O++++o", +" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o", +" ++++++++++++++++++++++++++++++++++++++++++++++++++++++o", +"Xooooooooooooooooooooooooooooooooooooooooooooooooooooooo"}; diff --git a/kscd/bitmaps/cdsmallpause.xpm b/kscd/bitmaps/cdsmallpause.xpm new file mode 100644 index 00000000..7ade7d0b --- /dev/null +++ b/kscd/bitmaps/cdsmallpause.xpm @@ -0,0 +1,34 @@ +/* XPM */ +static char * cdsmallpause_xpm[] = { +"15 24 7 1", +" c none", +". c #FFFFFFFF0000", +"X c #C71BC30BC71B", +"o c #0000FFFFFFFF", +"O c #0000FFFF0000", +"+ c #FFFFFFFFFFFF", +"@ c #000000000000", +" ", +" ..XXX ", +" o...XXXXX ", +" Ooo..XXXX+X ", +" OOo..XXX+XX ", +" XXOOo.XX+XXXX ", +" XXXOo @ XXXXX ", +" XXXXO@ @XXXXX ", +" XXXX+ @ oXXXX ", +" XXX+XX+O.oXXX ", +" X+XXXXO.ooX ", +" +XXXXXO..oo ", +" XXXXXOO.. ", +" XXXOO ", +" ", +" ", +" @@ @@ ", +" @@ @@ ", +" @@ @@ ", +" @@ @@ ", +" @@ @@ ", +" @@ @@ ", +" @@ @@ ", +" "}; diff --git a/kscd/bitmaps/cdsmallplay.xpm b/kscd/bitmaps/cdsmallplay.xpm new file mode 100644 index 00000000..1622ebce --- /dev/null +++ b/kscd/bitmaps/cdsmallplay.xpm @@ -0,0 +1,33 @@ +/* XPM */ +static char * cdsmallplay_xpm[] = { +"15 24 6 1", +" c #FFFFFFFF0000", +". c #C71BC30BC71B", +"X c #0000FFFFFFFF", +"o c #0000FFFF0000", +"O c #FFFFFFFFFFFF", +"+ c #000000000000", +" ", +" ... ", +" X ..... ", +" oXX ....O. ", +" ooX ...O.. ", +" ..ooX ..O.... ", +" ...oX + ..... ", +" ....o+ +..... ", +" ....O + X.... ", +" ...O..Oo X... ", +" .O....o XX. ", +" O.....o XX ", +" .....oo ", +" ...oo ", +" ", +" ", +" ++ ", +" ++++ ", +" ++++++ ", +" ++++++++ ", +" ++++++ ", +" ++++ ", +" ++ ", +" "}; diff --git a/kscd/bitmaps/cdsmallstop.xpm b/kscd/bitmaps/cdsmallstop.xpm new file mode 100644 index 00000000..9bbc477a --- /dev/null +++ b/kscd/bitmaps/cdsmallstop.xpm @@ -0,0 +1,34 @@ +/* XPM */ +static char * cdsmallstop_xpm[] = { +"15 24 7 1", +" c none", +". c #FFFFFFFF0000", +"X c #C71BC30BC71B", +"o c #0000FFFFFFFF", +"O c #0000FFFF0000", +"+ c #FFFFFFFFFFFF", +"@ c #000000000000", +" ", +" ..XXX ", +" o...XXXXX ", +" Ooo..XXXX+X ", +" OOo..XXX+XX ", +" XXOOo.XX+XXXX ", +" XXXOo @ XXXXX ", +" XXXXO@ @XXXXX ", +" XXXX+ @ oXXXX ", +" XXX+XX+O.oXXX ", +" X+XXXXO.ooX ", +" +XXXXXO..oo ", +" XXXXXOO.. ", +" XXXOO ", +" ", +" ", +" @@@@@@@ ", +" @@@@@@@ ", +" @@@@@@@ ", +" @@@@@@@ ", +" @@@@@@@ ", +" @@@@@@@ ", +" @@@@@@@ ", +" "}; diff --git a/kscd/bitmaps/db.xbm b/kscd/bitmaps/db.xbm new file mode 100644 index 00000000..57f8514a --- /dev/null +++ b/kscd/bitmaps/db.xbm @@ -0,0 +1,8 @@ +#define db_width 20 +#define db_height 16 +static unsigned char db_bits[] = { + 0xff, 0xff, 0x0f, 0x01, 0x00, 0x08, 0xfd, 0xff, 0x0b, 0x01, 0x00, 0x08, + 0x01, 0x0f, 0x08, 0x01, 0x09, 0x08, 0x01, 0x0f, 0x08, 0x01, 0x00, 0x08, + 0x01, 0x00, 0x08, 0x81, 0x1f, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, + 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0x01, 0x00, 0x08, 0xff, 0xff, 0x0f, + }; diff --git a/kscd/bitmaps/db.xpm b/kscd/bitmaps/db.xpm Binary files differnew file mode 100644 index 00000000..56f82078 --- /dev/null +++ b/kscd/bitmaps/db.xpm diff --git a/kscd/bitmaps/eject.xbm b/kscd/bitmaps/eject.xbm new file mode 100644 index 00000000..709fc709 --- /dev/null +++ b/kscd/bitmaps/eject.xbm @@ -0,0 +1,7 @@ +#define eject_width 16 +#define eject_height 16 +static unsigned char eject_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0xc0, 0x03, 0xe0, 0x07, + 0xf0, 0x0f, 0xf8, 0x1f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, + 0xfc, 0x3f, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/ff.xbm b/kscd/bitmaps/ff.xbm new file mode 100644 index 00000000..76fdcaa4 --- /dev/null +++ b/kscd/bitmaps/ff.xbm @@ -0,0 +1,7 @@ +#define ff_width 16 +#define ff_height 16 +static unsigned char ff_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x02, 0x18, 0x06, 0x38, 0x0e, + 0x78, 0x1e, 0xf8, 0x3e, 0xf8, 0x3e, 0x78, 0x1e, 0x38, 0x0e, 0x18, 0x06, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/info.xbm b/kscd/bitmaps/info.xbm new file mode 100644 index 00000000..7359938e --- /dev/null +++ b/kscd/bitmaps/info.xbm @@ -0,0 +1,6 @@ +#define info_width 16 +#define info_height 16 +static unsigned char info_bits[] = { + 0x80, 0x01, 0x80, 0x01, 0x00, 0x00, 0xe0, 0x01, 0xe0, 0x01, 0x80, 0x01, + 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, + 0xe0, 0x07, 0xe0, 0x07, 0x00, 0x00, 0x00, 0x00, }; diff --git a/kscd/bitmaps/kscdlogo.xpm b/kscd/bitmaps/kscdlogo.xpm new file mode 100644 index 00000000..559994e5 --- /dev/null +++ b/kscd/bitmaps/kscdlogo.xpm @@ -0,0 +1,137 @@ +/* XPM */ +static char *kscdlogo[] = { +/* width height num_colors chars_per_pixel */ +" 90 120 10 1", +/* colors */ +". c #000000", +"# c #1d1d1d", +"a c #393939", +"b c #555555", +"c c #727272", +"d c #8e8e8e", +"e c #aaaaaa", +"f c #c7c7c7", +"g c #e3e3e3", +"h c #ffffff", +/* pixels */ +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhbbbbbdhhhhcbbbbbhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhdbbbbfhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhhf.....dhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhha....#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhd.....ehhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhh#....ahhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhd.....fhhhhhhhhheeeghhhhhebbbehhhhhhhhhhgdbbdb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bg#....bhhhhhhhhc....ehhhe#.....#ehhhhhhhc....bb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bc.....ghhhhhhhb.....ehhe.........bhhhhg#.....bb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....a.....chhhhhhhf......ehe...........ehhhb......bb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....#.....ehhhhhhhb......eh#...........#hhf.......bb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....ba....ahhhhhhh#......ee.....beb.....ehb.....#bbb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bd.....fhhhhhh.....#dgb....bhhheeeeghh.....ahhgb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bh.....ahhhhhh.....ehhb....ehhhhhhhhhh.....ehhhb....ehhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhc.....fhhhhd.....fhhb....bhhha....ch.....chhh#....hhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhf.....ahhda......hhhe.....#b#.....ehb.....cea....#hhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhha.....fhb......ahhhg............#hhf............chhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhe.....ahb......fhhhhd...........dhhhb..........#ghhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhh#.....fb.....bhhhhhhb.........bhhhhg#.........ehhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhh.....bhhhc.....cb....chhhhhhhhd#.....#dhhhhhhhc......aghhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhheeeeefhhhgeeeeeedbbdghhhhhhhhhhhebbcehhhhhhhhhhgebbcfhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh", +"hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" +}; diff --git a/kscd/bitmaps/lock.xbm b/kscd/bitmaps/lock.xbm new file mode 100644 index 00000000..0f2e6e80 --- /dev/null +++ b/kscd/bitmaps/lock.xbm @@ -0,0 +1,7 @@ +#define lock_width 16 +#define lock_height 16 +static unsigned char lock_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x20, 0x04, 0x20, 0x04, + 0x20, 0x04, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/logo.xbm b/kscd/bitmaps/logo.xbm new file mode 100644 index 00000000..16c293bb --- /dev/null +++ b/kscd/bitmaps/logo.xbm @@ -0,0 +1,28 @@ +#define logo_width 48 +#define logo_height 48 +static unsigned char logo_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x63, 0xa2, 0x23, 0xe6, 0x00, 0x9f, 0x94, 0xb6, 0x54, 0x49, + 0x00, 0x91, 0x90, 0xaa, 0x54, 0x41, 0x00, 0x91, 0x94, 0xaa, 0x73, 0x49, + 0x00, 0x11, 0x63, 0xa2, 0x50, 0x46, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x91, 0x0f, 0xff, 0xc3, 0x3f, 0x02, 0x90, 0x88, 0x00, 0x22, 0x40, + 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86, + 0x11, 0x91, 0x48, 0xfc, 0x13, 0x89, 0x11, 0x91, 0x48, 0x02, 0x10, 0x89, + 0x11, 0x91, 0x48, 0xfc, 0x10, 0x89, 0x11, 0x91, 0x48, 0x00, 0x11, 0xf9, + 0x11, 0x91, 0x48, 0x00, 0x12, 0x01, 0x11, 0x91, 0x88, 0x00, 0x12, 0xf9, + 0x11, 0x91, 0x08, 0x3f, 0x12, 0x89, 0x11, 0x91, 0x08, 0x40, 0x12, 0x89, + 0x11, 0x91, 0xc8, 0x3f, 0x12, 0x89, 0xe1, 0x90, 0x48, 0x00, 0x12, 0x86, + 0x01, 0x90, 0x48, 0x00, 0x12, 0x80, 0x02, 0x90, 0x48, 0x00, 0x21, 0x40, + 0xfc, 0x9f, 0xcf, 0xff, 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0xd7, 0x49, 0x20, 0xe9, 0x64, 0xa9, 0x90, 0x54, 0x50, 0x29, 0x95, + 0xa9, 0x96, 0x54, 0x50, 0x29, 0x95, 0xa9, 0x94, 0x5c, 0x70, 0x29, 0x95, + 0x27, 0x97, 0xd4, 0x51, 0xe6, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/magic.xbm b/kscd/bitmaps/magic.xbm new file mode 100644 index 00000000..9b19811e --- /dev/null +++ b/kscd/bitmaps/magic.xbm @@ -0,0 +1,7 @@ +/* Created with The GIMP */ +#define magicxbm_width 16 +#define magicxbm_height 16 +static unsigned char magicxbm_bits[] = { + 0x00, 0x10, 0x00, 0x38, 0x80, 0xfe, 0x80, 0x38, 0x80, 0x10, 0xc0, 0x11, + 0xc0, 0x01, 0xf0, 0x07, 0xfe, 0x3f, 0xf0, 0x07, 0xc0, 0x01, 0xc0, 0x01, + 0x84, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x00, 0x00 }; diff --git a/kscd/bitmaps/nexttrk.xbm b/kscd/bitmaps/nexttrk.xbm new file mode 100644 index 00000000..6e36d666 --- /dev/null +++ b/kscd/bitmaps/nexttrk.xbm @@ -0,0 +1,7 @@ +#define nexttrk_width 16 +#define nexttrk_height 16 +static unsigned char nexttrk_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x86, 0x61, 0x8e, 0x63, + 0x9e, 0x67, 0xbe, 0x6f, 0xbe, 0x6f, 0x9e, 0x67, 0x8e, 0x63, 0x86, 0x61, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/options.xbm b/kscd/bitmaps/options.xbm new file mode 100644 index 00000000..760ac205 --- /dev/null +++ b/kscd/bitmaps/options.xbm @@ -0,0 +1,7 @@ +#define options_width 16 +#define options_height 16 +static unsigned char options_bits[] = { + 0x00, 0x00, 0x08, 0x00, 0x04, 0x00, 0x04, 0x00, 0x8e, 0x7f, 0xfe, 0x7f, + 0x8e, 0x7f, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x7d, 0xfc, 0x7f, + 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/playpaus.xbm b/kscd/bitmaps/playpaus.xbm new file mode 100644 index 00000000..a2494705 --- /dev/null +++ b/kscd/bitmaps/playpaus.xbm @@ -0,0 +1,10 @@ +#define playpause_width 32 +#define playpause_height 16 +static unsigned char playpause_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x06, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71, + 0xfe, 0x01, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71, 0xfe, 0x07, 0xc0, 0x71, + 0xfe, 0x01, 0xc0, 0x71, 0x7e, 0x00, 0xc0, 0x71, 0x1e, 0x00, 0xc0, 0x71, + 0x06, 0x00, 0xc0, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/poweroff.xbm b/kscd/bitmaps/poweroff.xbm new file mode 100644 index 00000000..f332ef22 --- /dev/null +++ b/kscd/bitmaps/poweroff.xbm @@ -0,0 +1,7 @@ +#define poweroff_width 16 +#define poweroff_height 16 +static unsigned char poweroff_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0xfe, 0x18, 0xfe, 0x10, 0xfe, 0x10, 0xfe, 0x10, + 0xfe, 0x10, 0x82, 0x00, 0x82, 0x38, 0x82, 0x44, 0x82, 0x44, 0x82, 0x44, + 0xfe, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/prevtrk.xbm b/kscd/bitmaps/prevtrk.xbm new file mode 100644 index 00000000..1cf3b803 --- /dev/null +++ b/kscd/bitmaps/prevtrk.xbm @@ -0,0 +1,7 @@ +#define prevtrk_width 16 +#define prevtrk_height 16 +static unsigned char prevtrk_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x86, 0x61, 0xc6, 0x71, + 0xe6, 0x79, 0xf6, 0x7d, 0xf6, 0x7d, 0xe6, 0x79, 0xc6, 0x71, 0x86, 0x61, + 0x00, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/repeat.xbm b/kscd/bitmaps/repeat.xbm new file mode 100644 index 00000000..3e6272cf --- /dev/null +++ b/kscd/bitmaps/repeat.xbm @@ -0,0 +1,7 @@ +#define repeat_width 16 +#define repeat_height 16 +static unsigned char repeat_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x13, + 0x0c, 0x31, 0x04, 0x20, 0x04, 0x20, 0x8c, 0x30, 0xc8, 0x1f, 0x80, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/rew.xbm b/kscd/bitmaps/rew.xbm new file mode 100644 index 00000000..79aa0db3 --- /dev/null +++ b/kscd/bitmaps/rew.xbm @@ -0,0 +1,7 @@ +#define rew_width 16 +#define rew_height 16 +static unsigned char rew_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x60, 0x18, 0x70, 0x1c, + 0x78, 0x1e, 0x7c, 0x1f, 0x7c, 0x1f, 0x78, 0x1e, 0x70, 0x1c, 0x60, 0x18, + 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/shuffle.xbm b/kscd/bitmaps/shuffle.xbm new file mode 100644 index 00000000..7214cd0a --- /dev/null +++ b/kscd/bitmaps/shuffle.xbm @@ -0,0 +1,7 @@ +#define shuffle_width 16 +#define shuffle_height 16 +static unsigned char shuffle_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xcc, 0x00, + 0x84, 0x0c, 0xc0, 0x3c, 0x60, 0x3c, 0x30, 0x0c, 0x00, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bitmaps/stop.xbm b/kscd/bitmaps/stop.xbm new file mode 100644 index 00000000..1f176d13 --- /dev/null +++ b/kscd/bitmaps/stop.xbm @@ -0,0 +1,7 @@ +#define stop_width 16 +#define stop_height 16 +static unsigned char stop_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x0f, 0xf0, 0x0f, + 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/kscd/bwlednum.cpp b/kscd/bwlednum.cpp new file mode 100644 index 00000000..a8f828cc --- /dev/null +++ b/kscd/bwlednum.cpp @@ -0,0 +1,591 @@ +/* + * BW_LED_Number a very very primitive LED + * + * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu + * + * $Id$ + * + */ + + +#include "bwlednum.h" +#include "qbitarray.h" +#include "qpainter.h" +#include <stdio.h> + +#include "bwlednum.moc" + +#define NUM_OF_SEGMENTS 8 +#define STOP_CHAR 25 + + +static char segs[14][8] = +{ { 0, 1, 2, 4, 5, 6,25, 0}, // 0 + { 2, 5,25, 0, 0, 0, 0, 0}, // 1 + { 1, 2, 3, 4, 6,25 ,0, 0}, // 2 + { 1, 2, 3, 5, 6,25, 0, 0}, // 3 + { 0, 3, 2, 5 ,25, 0, 0, 0}, // 4 + { 0, 1, 3, 5, 6,25, 0, 0}, // 5 + { 0, 1, 3, 4, 5, 6,25, 0}, // 6 + { 1,2 , 5,25, 0, 0, 0, 0}, // 7 + { 0, 1, 2, 3, 4, 5, 6,25}, // 8 + { 0, 1, 2, 3, 5, 6,25, 0}, // 9 + { 3,25, 0, 0, 0, 0, 0, 0}, // - + { 7,25, 0, 0, 0, 0, 0, 0}, // . + { 8, 9,25, 0, 0, 0, 0, 0}, // : + {25, 0, 0, 0, 0, 0, 0, 0} }; // blank + + +BW_LED_Number::BW_LED_Number( QWidget *parent, const char *name ) + : QFrame( parent, name ){ + + + offcolor = QColor(100,0,0); + showOffColon(FALSE); + smallLED = false; + current_symbol = ' '; + old_segments = &segs[13][0]; // nothing + current_segments = &segs[13][0]; // nothing + setLEDColor(yellow,black); + +} + +void dump_segments(char * segs){ + + printf("dumping segments:"); + for (int i = 0; i <=7; i++){ + printf("%d:",segs[i]); + } + printf("\n"); +} + + +BW_LED_Number::~BW_LED_Number(){ + +} + +void BW_LED_Number::resizeEvent( QResizeEvent * ){ + +} + +void BW_LED_Number::mouseReleaseEvent( QMouseEvent * /* e */ ) +{ + emit(clicked()); +} + +void BW_LED_Number::showOffColon(bool off){ + + show_off_colon = off; + +} + +void BW_LED_Number::setLEDColor( const QColor& fgColor, const QColor& bgColor ){ + + fgcolor = fgColor; + bgcolor = bgColor; + + QColorGroup old_cg = this->colorGroup(); + + + QColorGroup new_cg( fgColor, bgColor, + fgColor, fgColor, fgColor, + fgColor, fgColor ); + + + this->setPalette(QPalette(new_cg, new_cg, new_cg)); + +} + + + + +static char *getSegments( char s) + +{ + if (s >= '0' && s <= '9'){ + return segs[s - '0']; + } + + int j; + + switch ( s ) { + case '-': + j = 10; + break; + case 'O': + j = 0; + break; + case '.': + j = 11; + break; + case ':': + j = 12; + break; + default: + j = 13; + break; + } + + return segs[j]; +} + +void BW_LED_Number::drawContents( QPainter *p ){ + + drawSymbol( p, current_symbol,TRUE ); + +} + + +void BW_LED_Number::display(int i ){ + + if( (i<0) || (i> 9)) + return; + display( (char)('0'+ i)); + +} + + +void BW_LED_Number::display(char s){ + + QPainter p; + + p.begin( this ); + + old_segments = current_segments; + current_symbol = s; + current_segments = getSegments(s); + + drawSymbol(&p,s,FALSE); + + p.end(); + +} + +void BW_LED_Number::setSmallLED(bool a_boolean){ + + smallLED = a_boolean; + +} + + +void BW_LED_Number::drawSymbol( QPainter *p,char ,bool repaint ){ + + // printf("drawSymbol repaint = %d\n",repaint); + + QPoint pos; + + int xSegment_Length, ySegment_Length, Segment_Length, xAdvance; + int Xoffset, Yoffset, space; + + space = 1; + + xSegment_Length = width()*5/((5 + space) + space) ; + + ySegment_Length = height()*5/12; + + Segment_Length = ySegment_Length > xSegment_Length ? xSegment_Length : ySegment_Length; + + xAdvance = Segment_Length*( 5 + space )/5 +1 ; + + // Xoffset = ( width() - xAdvance + Segment_Length/5 )/2; // origininal + Xoffset = ( width() - xAdvance + Segment_Length/4 )/2; + Yoffset = ( height() - Segment_Length*2 )/2; + + pos = QPoint( Xoffset , Yoffset ); + + + if(repaint){ + + // this draw the non-illumintated segments + + if(show_off_colon){// we want to show the colon which is actually ugly and + // by default not shown. + + for(int l = 0; l <= NUM_OF_SEGMENTS +1; l++){ + drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment + } + } + else{ + for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){ + drawSegment(pos,(char) l,*p,Segment_Length,TRUE); //erase segment + } + } + + // now draw the illuminated segments + + for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){ + if(current_segments[l] != STOP_CHAR){ + drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment + } + else{ + break; + } + } + } + else{ // we are not repainting ourselves due to a repaint event but rather + // genuinely changing the symbol that is to be displayed + + for(int l = 0; l <= NUM_OF_SEGMENTS -1; l++){ + + if(current_segments[l] != STOP_CHAR){ + if(!seg_contained_in(current_segments[l],old_segments)) + drawSegment(pos,current_segments[l],*p,Segment_Length,FALSE); // draw segment + } + else{ + break; + } + } + + + for(int k = 0; k <= NUM_OF_SEGMENTS -1; k++){ + + if(old_segments[k] != STOP_CHAR){ + if(!seg_contained_in(old_segments[k],current_segments)) + drawSegment(pos,old_segments[k],*p,Segment_Length,TRUE); //erase segment + } + else{ + break; + } + } + } +} + + + +bool BW_LED_Number::seg_contained_in( char c, char* seg){ + + bool result = FALSE; + + while ( *seg != STOP_CHAR){ + // printf("Comparing %d with %d\n",c,*seg); + if ( c == *seg ) + result = TRUE; + seg++; + } + + return result; +} + +void BW_LED_Number::setLEDoffColor(QColor color){ + + offcolor = color; +} + + +void BW_LED_Number::drawSegment( const QPoint &pos, char seg_number, QPainter &p, + int Segment_Length, bool erase){ + + + QPoint pt = pos; + QColorGroup g = colorGroup(); + QColor lightColor,darkColor; + if ( erase ){ + + lightColor = offcolor; + darkColor = offcolor; + + } else { + lightColor = g.light(); + darkColor = g.dark(); + } + + // int Width = (int) Segment_Length/5 ; // original + int Width = (int) Segment_Length/4; + + + QBrush brush(g.light()); + QPointArray pts; + + + pt.ry() += (QCOORD)Width/2; + + + if (erase){ + + p.setBrush(offcolor); + brush.setColor(offcolor); + + } + else + p.setBrush(g.light()); + + if(!smallLED){ + + switch ( seg_number ) { + case 0 : + + + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x(), pt.y() , + pt.x(), pt.y()-Width +1, + pt.x() + Width-1, pt.y()); + p.drawPolygon(pts); + pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 , + pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1, + pt.x() , pt.y() + Segment_Length - 3*Width/4 -1); + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + + p.fillRect(pt.x(),pt.y()+ Width/2 -1, Width , + Segment_Length - Width -Width +1 ,brush); + + break; + case 1 : + + p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush); + + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x()+1, pt.y()-Width , + pt.x()+Width, pt.y()-Width , + pt.x() + Width, pt.y() -1 ); + p.drawPolygon(pts); + + pts.setPoints(3,pt.x()+ Segment_Length - Width , pt.y() - Width, + pt.x()+ Segment_Length -1, pt.y() - Width, + pt.x() + Segment_Length - Width , pt.y() -1 ); + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + break; + case 2 : + pt.rx() += (QCOORD)(Segment_Length); + + + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x() , pt.y() , + pt.x() , pt.y() - Width + 1, // changes from 1 to 2 + pt.x() - Width +1, pt.y() ); + + p.drawPolygon(pts); + + pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1, + pt.x() , pt.y() + Segment_Length - 3*Width/4 - 1, + pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1); + + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + + p.fillRect(pt.x() - Width+1 ,pt.y() + Width/2- 1, Width , + Segment_Length - Width - Width + 1 ,brush); + + break; + case 3 : + + pt.ry() += (QCOORD)Segment_Length; + + p.setPen(g.background()); + + pts.setPoints(3,pt.x()-1 , pt.y() - Width/2 -1, + pt.x() + Width+2, pt.y()-Width -1 ,// + pt.x() + Width+2, pt.y() ); + p.drawPolygon(pts); + pts.setPoints(3,pt.x() + Segment_Length + 1, pt.y() - Width/2 -1 , + pt.x() + Segment_Length - Width - 2 , + pt.y() - Width -1, + pt.x() + Segment_Length - Width - 2, pt.y() ); + p.drawPolygon(pts); + + p.setPen(g.light()); + p.fillRect(pt.x() + Width -1 ,pt.y() - Width, Segment_Length- 2* Width + 3, + Width ,brush); + + break; + case 4 : + pt.ry() += (QCOORD)(Segment_Length +1); + p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush); + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x(), pt.y(), + pt.x(), pt.y()-Width+1, + pt.x() + Width-1, pt.y()); + p.drawPolygon(pts); + pts.setPoints(3,pt.x(), pt.y() + Segment_Length -Width - Width/2 -1 , + pt.x() + Width -1 , pt.y() -Width +Segment_Length - Width/2 -1 , + pt.x() , pt.y() + Segment_Length - 3*Width/4 -1); + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + + break; + case 5 : + pt.rx() += (QCOORD)(Segment_Length ); + pt.ry() += (QCOORD)(Segment_Length +1); + p.fillRect(pt.x() - Width +1 ,pt.y(), Width , + Segment_Length - Width - Width/2 ,brush); + + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x() , pt.y(), + pt.x() , pt.y() - Width +1, + pt.x() - Width +1, pt.y()); + + p.drawPolygon(pts); + + pts.setPoints(3,pt.x() , pt.y() + Segment_Length - Width - Width/2 -1, + pt.x() , pt.y() + Segment_Length - 3*Width/4 -1, + pt.x() - Width +1, pt.y() + Segment_Length - Width - Width/2 -1); + + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + + break; + case 6 : + pt.ry() += (QCOORD)(Segment_Length*2 ); + p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width , + Width ,brush); + + if (erase) + p.setPen(offcolor); + + pts.setPoints(3,pt.x()+1, pt.y()-1, + pt.x() + Width, pt.y() - Width, + pt.x() + Width, pt.y() - 1 ); + p.drawPolygon(pts); + + pts.setPoints(3, pt.x() + Segment_Length - 1, pt.y()-1, + pt.x() + Segment_Length - Width , pt.y() - Width, + pt.x() + Segment_Length - Width , pt.y() - 1 ); + + p.drawPolygon(pts); + + if (erase) + p.setPen(g.light()); + + + + break; + case 7 : + pt.rx() += (QCOORD)(Segment_Length/2); + pt.ry() += (QCOORD)(Segment_Length*2); + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + break; + case 8 : + pt.ry() += (QCOORD)(Segment_Length/2 + Width/2); + pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1); + + if (!show_off_colon && erase) { + p.setBrush(bgcolor); + brush.setColor(bgcolor); + } + + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + p.moveTo(pt); + + if (!show_off_colon && erase) { + p.setBrush(fgcolor); + brush.setColor(fgcolor); + } + + break; + case 9 : + pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2); + pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1); + + if (!show_off_colon && erase) { + p.setBrush(bgcolor); + brush.setColor(bgcolor); + } + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + + if (!show_off_colon && erase) { + p.setBrush(fgcolor); + brush.setColor(fgcolor); + } + break; + } + + } /* if (!smallLED) */ + + else{ + + pt.ry() += (QCOORD)Width/2; + + switch ( seg_number ) { + case 0 : + p.fillRect(pt.x(),pt.y()+ Width /2, Width , Segment_Length - Width -Width/2 ,brush); + break; + case 1 : + p.fillRect(pt.x()+Width,pt.y()- Width , Segment_Length -2* Width, Width ,brush); + break; + case 2 : + pt.rx() += (QCOORD)(Segment_Length); + p.fillRect(pt.x()-Width,pt.y()+ Width/2, Width , + Segment_Length - Width -Width/2 ,brush); + break; + case 3 : + pt.ry() += (QCOORD)Segment_Length; + p.fillRect(pt.x() + Width ,pt.y() - Width, Segment_Length- 2* Width, Width ,brush); + break; + case 4 : + pt.ry() += (QCOORD)(Segment_Length ); + p.fillRect(pt.x(), pt.y(), Width , Segment_Length - Width - Width/2 ,brush); + break; + case 5 : + pt.rx() += (QCOORD)(Segment_Length ); + pt.ry() += (QCOORD)(Segment_Length ); + p.fillRect(pt.x() - Width ,pt.y(), Width , + Segment_Length - Width - Width/2 ,brush); + break; + case 6 : + pt.ry() += (QCOORD)(Segment_Length*2); + p.fillRect(pt.x() + Width ,pt.y() -Width , Segment_Length -2* Width , + Width ,brush); + break; + case 7 : + pt.rx() += (QCOORD)(Segment_Length/2); + pt.ry() += (QCOORD)(Segment_Length*2); + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + break; + case 8 : + pt.ry() += (QCOORD)(Segment_Length/2 + Width/2); + pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1); + if (!show_off_colon && erase) { + p.setBrush(bgcolor); + brush.setColor(bgcolor); + } + + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + p.moveTo(pt); + if (!show_off_colon && erase) { + p.setBrush(fgcolor); + brush.setColor(fgcolor); + } + + break; + case 9 : + pt.ry() += (QCOORD)(3*Segment_Length/2 + Width/2); + pt.rx() += (QCOORD)(Segment_Length/2 - Width/2 + 1); + + if (!show_off_colon && erase) { + p.setBrush(bgcolor); + brush.setColor(bgcolor); + } + p.fillRect(pt.x() ,pt.y() - Width , Width , Width ,brush); + if (!show_off_colon && erase) { + p.setBrush(fgcolor); + brush.setColor(fgcolor); + } + + break; + } + + } /* end smallLED */ + + +} diff --git a/kscd/bwlednum.h b/kscd/bwlednum.h new file mode 100644 index 00000000..f3d66f8e --- /dev/null +++ b/kscd/bwlednum.h @@ -0,0 +1,93 @@ +/* + * + * BW_LED_Number a very very primitive LED + * + * Copyright: Bernd Johannes Wuebben, wuebben@math.cornell.edu + * + * + * $Id$ + * + */ + + +#ifndef BW_LED_NUM_H +#define BW_LED_NUM_H + +#include "qframe.h" +#include "qbitarray.h" + + +class BW_LED_Number : public QFrame +{ + Q_OBJECT + +public: + + BW_LED_Number( QWidget *parent=0, const char *name=0 ); + ~BW_LED_Number(); + + void setSmallLED(bool ); // if you LED is small it might look better + // if you call setSmallLED(TRUE) + + // this sets the fore and background color of the LED + // the forground defaults to yellow, the background defaults + // to black + + void setLEDColor( const QColor& foregroundColor, const QColor& backgroundColor ); + + + // this sets the color of the segments that are not iluminated + // the default is a rather dark red. + + void setLEDoffColor(QColor color); + + // calling showOffColon(TRUE) will show the colon if not illuminated + // this is rather ugly -- the default is that they are not shown. + + void showOffColon(bool off); + +signals: + void clicked(); + +public slots: + + // display one of the characters " 0 1 2 3 4 5 6 7 8 9 . : - " + void display( char c ); + + // display on e of the numbers " 0 1 2 3 4 5 6 7 8 9" + void display( int i ); + +protected: + + void resizeEvent( QResizeEvent * ); + void mouseReleaseEvent ( QMouseEvent * e ); + void drawContents( QPainter * ); + +private: + + bool seg_contained_in( char c, char* seg); + void drawSegment( const QPoint &, char, QPainter &, int, bool = FALSE ); + void drawSymbol( QPainter *p,char s ,bool repaint); + + char* old_segments; + char* current_segments; + + char current_symbol; + char old_symbol; + QColor offcolor; + QColor fgcolor; + QColor bgcolor; + + + bool smallLED; + bool show_off_colon; + +private: // Disabled copy constructor and operator= + + BW_LED_Number( const BW_LED_Number & ); + BW_LED_Number &operator=( const BW_LED_Number & ); + +}; + + +#endif // BW_LED_NUM_H diff --git a/kscd/cddaslave.c b/kscd/cddaslave.c new file mode 100644 index 00000000..6b65c640 --- /dev/null +++ b/kscd/cddaslave.c @@ -0,0 +1,532 @@ +/* + * @(#)cddaslave.c 1.11 13 Sep 1995 + * + * Digital audio manipulator for WorkMan. + * + * The CDDA architecture looks like this: + * + * WorkMan (or another UI!) + * ^^^ + * ||| (separate processes connected by pipe) + * vvv + * +------------- cddaslave -------------+ + * | | | + * command module CDDA reader audio output + * (portable) (per platform) (per platform) + * + * This source file has the command module and some of the scaffolding + * to hold cddaslave together, plus some non-system-dependent audio + * processing code. Look in plat_*_cdda.c for system-specific stuff. + */ + + +#include "libwm/include/wm_config.h" +#include "libwm/include/wm_cdda.h" + +#ifdef BUILD_CDDA /* { */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> + +#ifndef timerclear +#define timerclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0) +#endif + +int playing = 0; /* Should the CD be playing now? */ + +/* + * Loudness setting, plus the floating volume multiplier and decaying-average + * volume level. + */ +int loudness = 0; +unsigned int volume = 32768; +unsigned int level; + +/* + * Playback speed (0 = slow) + */ +int speed = 128; + +/* + * This is non-null if we're saving audio to a file. + */ +FILE *output = NULL; + +/* + * Audio file header format. + */ +typedef unsigned long u_32; +struct auheader { + u_32 magic; + u_32 hdr_size; + u_32 data_size; + u_32 encoding; + u_32 sample_rate; + u_32 channels; +}; + +#ifdef BIG_ENDIAN +# ifndef htonl +# define htonl(x) (x) +# endif +#else +extern unsigned long htonl(x); +#endif + +void *malloc(); +long cdda_transform(); + +/* + * Send status information upstream. + */ +void +send_status(struct cdda_block *blk) +{ + write(1, blk, sizeof(*blk)); +} + +/* + * Accept a command from our master. + * + * The protocol is byte-oriented: + * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end) + * xyz is the msf of the start of this chunk, i.e., the + * ending boundary for reverse play. + * S Stop. + * Q Quit. + * Vn Set volume level (0-255). + * Bn Set balance level (0-255). + * EnL Set an equalizer level (n = 0 for bass, 255 for treble) + * G Get current status. + * sn Set speed multiplier to n. + * dn Set direction to forward (n = 0) or reverse. + * Fllllx... Start saving to a file (length = l, followed by name) + * F0000 Stop saving. + * Ln Set loudness level (0-255). + */ +void +command(int cd_fd, struct cdda_block *blk) +{ + unsigned char inbuf[10]; + char *filename; + int namelen; + struct auheader hdr; + + if (read(0, inbuf, 1) <= 0) /* Parent died. */ + { + wmcdda_close(); + wmaudio_close(); + exit(0); + } + + switch (inbuf[0]) { + case 'P': + read(0, inbuf, 9); + playing = 1; + + wmaudio_stop(); + + wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2], + inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5], + inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]); + + wmaudio_ready(); + + level = 2500; + volume = 1 << 15; + + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'S': + playing = 0; + wmaudio_stop(); + blk->status = WMCDDA_ACK; + send_status(blk); + blk->status = WMCDDA_STOPPED; + send_status(blk); + break; + + case 'B': + read(0, inbuf, 1); + wmaudio_balance(inbuf[0]); + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'V': + read(0, inbuf, 1); + wmaudio_volume(inbuf[0]); + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'G': + blk->status = WMCDDA_ACK; + send_status(blk); + + if (playing) + blk->status = WMCDDA_PLAYED; + else + blk->status = WMCDDA_STOPPED; + wmaudio_state(blk); + send_status(blk); + break; + + case 'Q': + blk->status = WMCDDA_ACK; + send_status(blk); + wmcdda_close(); + wmaudio_close(); + exit(0); + + case 's': + read(0, inbuf, 1); + speed = inbuf[0]; + wmcdda_speed(speed); + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'd': + read(0, inbuf, 1); + wmcdda_direction(inbuf[0]); + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'L': + read(0, inbuf, 1); + loudness = inbuf[0]; + blk->status = WMCDDA_ACK; + send_status(blk); + break; + + case 'F': + read(0, &namelen, sizeof(namelen)); + if (output != NULL) + { + fclose(output); + output = NULL; + } + if (namelen) + { + filename = malloc(namelen + 1); + if (filename == NULL) + { + perror("cddaslave"); + wmcdda_close(); + wmaudio_close(); + exit(1); + } + + read(0, filename, namelen); + filename[namelen] = '\0'; + output = fopen(filename, "w"); + if (output == NULL) + perror(filename); + else + { + /* Write an .au file header. */ + hdr.magic = htonl(0x2e736e64); + hdr.hdr_size = htonl(sizeof(hdr) + 28); + hdr.data_size = htonl(~0); + hdr.encoding = htonl(3); /* linear-16 */ + hdr.sample_rate = htonl(44100); + hdr.channels = htonl(2); + + fwrite(&hdr, sizeof(hdr), 1, output); + fwrite("Recorded from CD by WorkMan", 28, 1, + output); + } + + free(filename); + } + + blk->status = WMCDDA_ACK; + send_status(blk); + } +} + +/* + * Transform some CDDA data. + */ +long +wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block) +{ + long i; + long *buf32 = (long *)rawbuf; + register short *buf16 = (short *)rawbuf; + register int aval; + + /* + * Loudness transformation. Basically this is a self-adjusting + * volume control; our goal is to keep the average output level + * around a certain value (2500 seems to be pleasing.) We do this + * by maintaining a decaying average of the recent output levels + * (where "recent" is some fraction of a second.) All output levels + * are multiplied by the inverse of the decaying average; this has + * the volume-leveling effect we desire, and isn't too CPU-intensive. + * + * This is done by modifying the digital data, rather than adjusting + * the system volume control, because (at least on some systems) + * tweaking the system volume can generate little pops and clicks. + * + * There's probably a more elegant way to achieve this effect, but + * what the heck, I never took a DSP class and am making this up as + * I go along, with a little help from some friends. + * + * This is all done with fixed-point math, oriented around powers + * of two, which with luck will keep the CPU usage to a minimum. + * More could probably be done, for example using lookup tables to + * replace multiplies and divides; whether the memory hit (128K + * for each table) is worthwhile is unclear. + */ + if (loudness) + { + /* We aren't really going backwards, but i > 0 is fast */ + for (i = buflen / 2; i > 0; i--) + { + /* + * Adjust this sample to the current level. + */ + aval = (*buf16 = (((long)*buf16) * volume) >> 15); + buf16++; + + /* + * Don't adjust the decaying average for each sample; + * that just spends CPU time for very little benefit. + */ + if (i & 127) + continue; + + /* + * We want to use absolute values to compute the + * decaying average; otherwise it'd sit around 0. + */ + if (aval < 0) + aval = -aval; + + /* + * Adjust more quickly when we start hitting peaks, + * or we'll get clipping when there's a sudden loud + * section after lots of quiet. + */ + if (aval & ~8191) + aval <<= 3; + + /* + * Adjust the decaying average. + */ + level = ((level << 11) - level + aval) >> 11; + + /* + * Let *really* quiet sounds play softly, or we'll + * amplify background hiss to full volume and blast + * the user's speakers when real sound starts up. + */ + if (! (level & ~511)) + level = 512; + + /* + * And adjust the volume setting using the inverse + * of the decaying average. + */ + volume = (2500 << 15) / level; + } + } + + if (speed == 128) + return (buflen); + + /* + * Half-speed play. Stretch things out. + */ + if (speed == 0) + { + buflen /= 2; /* Since we're moving 32 bits at a time */ + + for (i = buflen - 1; i > 0; i--) + { + buf32[i] = buf32[i / 2]; + } + + buflen *= 4; /* 2 for doubling the buffer, 2 from above */ + } + + /* + * Slow play; can't optimize it as well as half-speed. + */ + if (speed && speed < 128) + { + int multiplier = ((speed + 128) * 128) / 256; + int newlen; + int tally = 0, pos; + + buflen /= 4; /* Get the number of 32-bit values */ + + /* + * Buffer length doubles when speed is 0, stays the same + * when speed is 128. + */ + newlen = (buflen * 128) / multiplier; + + pos = buflen - 1; + for (i = newlen - 1; i > 0; i--) + { + buf32[i] = buf32[pos]; + tally += multiplier; + if (tally & 128) + { + pos--; + tally ^= 128; + } + } + + buflen = newlen * 4; + } + + return (buflen); +} + + +main(argc, argv) + char **argv; +{ + int cd_fd = 3; + fd_set readfd, dummyfd; + struct timeval timeout; + char *cddabuf; + long cddabuflen; + struct cdda_block blockinfo; + long result; + int nfds; + char *devname; + + /* + * Device name should be the command-line argument. + */ + if (argc < 2) + devname = ""; + else + devname = argv[1]; + + /* + * If we're running setuid root, bump up our priority then lose + * superuser access. + */ + nice(-14); + setgid(getgid()); + setuid(getuid()); + if (getuid() != geteuid()) + return 255; + + FD_ZERO(&dummyfd); + FD_ZERO(&readfd); + + timerclear(&timeout); + + cd_fd = wmcdda_init(&cddabuf, &cddabuflen, cd_fd, devname); + if (cd_fd < 0) + exit(1); + wmaudio_init(); + + blockinfo.status = WMCDDA_ACK; + send_status(&blockinfo); + blockinfo.status = WMCDDA_STOPPED; + +fprintf(stderr,"cddaslave: done init."); + /* + * Accept commands as they come in, and play some sound if we're + * supposed to be doing that. + */ + while (1) + { + FD_SET(0, &readfd); + + /* + * If we're playing, we don't want select to block. Otherwise, + * wait a little while for the next command. + */ + if (playing) + timeout.tv_usec = 0; + else + timeout.tv_usec = 500000; + + nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout); + + if (nfds < 0) /* Broken pipe; our GUI exited. */ + { + wmcdda_close(cd_fd); + wmaudio_close(); + fprintf(stderr,"cddaslave: Borken pipe; GUI must have exited."); + exit(0); + } + + if (FD_ISSET(0, &readfd)) + { + command(cd_fd, &blockinfo); + /* + * Process all commands in rapid succession, rather + * than possibly waiting for a CDDA read. + */ + continue; + } + + if (playing) + { + result = wmcdda_read(cd_fd, cddabuf, cddabuflen, + &blockinfo); + if (result <= 0) + { + /* Let the output queue drain. */ + if (blockinfo.status == WMCDDA_DONE) + { + wmaudio_mark_last(); + if (wmaudio_send_status()) + { + /* queue drained, stop polling*/ + playing = 0; + } + } + else + { + playing = 0; + send_status(&blockinfo); + } + } + else + { + result = wmcdda_normalize(cddabuf, result, + &blockinfo); + result = wmcdda_transform(cddabuf, result, + &blockinfo); + if (output) + fwrite(cddabuf, result, 1, output); + result = wmaudio_convert(cddabuf, result, + &blockinfo); + if (wmaudio_play(cddabuf, result, &blockinfo)) + { + playing = 0; + wmaudio_stop(); + send_status(&blockinfo); + } + } + } + else + send_status(&blockinfo); + } +} + +#else /* BUILD_CDDA } { */ + +#include <stdio.h> +int main() +{ + printf("cddaslave: will work only on Solaris 2.4 or newer."); + exit(0); +} + +#endif /* } */ diff --git a/kscd/cddbdlg.cpp b/kscd/cddbdlg.cpp new file mode 100644 index 00000000..cc0e9669 --- /dev/null +++ b/kscd/cddbdlg.cpp @@ -0,0 +1,198 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <qkeycode.h> +#include <qdatetime.h> +#include <qtextstream.h> +#include <qfile.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <klistview.h> +#include <klineedit.h> +#include <knuminput.h> + +#include <kglobal.h> +#include <kapplication.h> +#include <kdebug.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kcombobox.h> +#include <kmessagebox.h> + +#include <stdio.h> +#include <math.h> + +#include "version.h" +#include "kscd.h" +#include "cddbdlg.h" +#include "libkcddb/cdinfodialogbase.h" + +struct mytoc +{ + unsigned absframe; +}; + +CDDBDlg::CDDBDlg( QWidget* parent, const char* name ) + : KDialogBase( parent, name, false, i18n( "CD Editor" ), + Ok|Cancel|User1|User2, Ok, true ) +{ + KGlobal::locale()->insertCatalogue("libkcddb"); + + m_dlgBase = new CDInfoDialogBase( this, "m_dlgBase" ); + + setMainWidget( m_dlgBase ); + + setButtonText( User1, i18n( "Upload" ) ); + setButtonText( User2, i18n( "Fetch Info" ) ); + + connect( this, SIGNAL( okClicked() ), SLOT( save() ) ); + connect( this, SIGNAL( user1Clicked() ), SLOT( upload() ) ); + connect( this, SIGNAL( user2Clicked() ), SIGNAL( cddbQuery() ) ); + connect( m_dlgBase, SIGNAL( play( int ) ), SIGNAL( play( int ) ) ); + + cddbClient = new KCDDB::Client(); + cddbClient->setBlockingMode(false); + connect (cddbClient, SIGNAL(finished(CDDB::Result)), + SLOT(submitFinished(CDDB::Result))); +} + + +CDDBDlg::~CDDBDlg() +{ + delete cddbClient; +} + +void CDDBDlg::setData( + const KCDDB::CDInfo &_cddbInfo, + const KCDDB::TrackOffsetList &_trackStartFrames, + const QStringList &_playlist) +{ + // Let's make a deep copy of the cd struct info so that the data won't + // change the cd changes while we are playing with the dialog. + cddbInfo = _cddbInfo; + trackStartFrames = _trackStartFrames; + playlist = _playlist; + + // Write the complete record to the dialog. + m_dlgBase->setInfo(cddbInfo, trackStartFrames); + // FIXME: KDE4, move this logic into m_dlgBase->setInfo() once KCDDB:CDInfo is updated. + m_dlgBase->m_playOrder->setText( playlist.join( "," ) ); +} // setData + +void CDDBDlg::submitFinished(KCDDB::CDDB::Result r) +{ + if (r == KCDDB::CDDB::Success) + { + KMessageBox::information(this, i18n("Record submitted successfully."), + i18n("Record Submission")); + } + else + { + QString str = i18n("Error sending record.\n\n%1") + .arg(KCDDB::CDDB::resultToString(r)); + KMessageBox::error(this, str, i18n("Record Submission")); + } +} // submitFinished() + +void CDDBDlg::upload() +{ + if (!validInfo()) + return; + + updateFromDialog(); + + // Create a copy with a bumped revision number. + KCDDB::CDInfo copyInfo = cddbInfo; + copyInfo.revision++; + cddbClient->submit(copyInfo, trackStartFrames); +} // upload + +void CDDBDlg::save() +{ + updateFromDialog(); + + KCDDB::Cache::store(cddbInfo); + + emit newCDInfoStored(cddbInfo); +} // save + +bool CDDBDlg::validInfo() +{ + KCDDB::CDInfo copy = m_dlgBase->info(); + + if (copy.artist.isEmpty()) + { + KMessageBox::sorry(this, + i18n("The artist name of the disc has to be entered.\n" + "Please correct the entry and try again."), + i18n("Invalid Database Entry")); + return false; + } + + if (copy.title.isEmpty()) + { + KMessageBox::sorry(this, + i18n("The title of the disc has to be entered.\n" + "Please correct the entry and try again."), + i18n("Invalid Database Entry")); + return false; + } + + bool have_nonempty_title = false; + for (unsigned i = 0; i < copy.trackInfoList.count(); i++) + { + if (!copy.trackInfoList[i].title.isEmpty()) + { + have_nonempty_title = true; + break; + } + } + + if (!have_nonempty_title) + { + KMessageBox::sorry(this, + i18n("At least one track title must be entered.\n"\ + "Please correct the entry and try again."), + i18n("Invalid Database Entry")); + return false; + } + + return true; +} + +void CDDBDlg::updateFromDialog() +{ + KCDDB::CDInfo copy = m_dlgBase->info(); + + // Playorder... + QStringList strlist = QStringList::split( ',', m_dlgBase->m_playOrder->text() ); + + bool ret = true; + + QString teststr; + bool ok; + unsigned num; + + for ( QStringList::Iterator it = strlist.begin(); + it != strlist.end(); + ++it ) + { + teststr = *it; + num = teststr.toInt(&ok); + + if( !ok || num > cddbInfo.trackInfoList.count() ) + ret = false; + } + + if(!ret) + { + KMessageBox::sorry(this, + i18n("Invalid Playlist\nPlease use valid track numbers, " + "separated by commas.")); + } + + cddbInfo = copy; +} // updateFromDialog + +#include "cddbdlg.moc" diff --git a/kscd/cddbdlg.h b/kscd/cddbdlg.h new file mode 100644 index 00000000..bbef6e4e --- /dev/null +++ b/kscd/cddbdlg.h @@ -0,0 +1,46 @@ +#ifndef CDDBDLG_H +#define CDDBDLG_H + +#include <kdialogbase.h> + +#include "libkcddb/cdinfo.h" +#include "libkcddb/cddb.h" +#include "libkcddb/client.h" + +class CDInfoDialogBase; + +class CDDBDlg : public KDialogBase +{ + Q_OBJECT + + public: + CDDBDlg(QWidget* parent, const char* name = 0); + ~CDDBDlg(); + + void setData( + const KCDDB::CDInfo &_cddbInfo, + const KCDDB::TrackOffsetList &_trackStartFrames, + const QStringList &_playlist); + + private slots: + void save(); + void upload(); + void submitFinished(CDDB::Result); + + signals: + void cddbQuery(); + void newCDInfoStored(KCDDB::CDInfo); + void play(int i); + + private: + bool validInfo(); + void updateFromDialog(); + QString framesTime(unsigned frames); + + CDInfoDialogBase *m_dlgBase; + KCDDB::CDInfo cddbInfo; + KCDDB::TrackOffsetList trackStartFrames; + QStringList playlist; + KCDDB::Client *cddbClient; +}; +#endif // CDDBDLG_H diff --git a/kscd/configWidget.cpp b/kscd/configWidget.cpp new file mode 100644 index 00000000..84119c6b --- /dev/null +++ b/kscd/configWidget.cpp @@ -0,0 +1,143 @@ +/* + * configWidget - the config dialog page for KSCD settings + * + * $Id: + * + * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org> + * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <kdebug.h> +#include <klineedit.h> +#include <dcopref.h> +#include <kurlrequester.h> +#include <qcheckbox.h> +#include <kcombobox.h> +#include <qlayout.h> + +#include <config.h> +extern "C" { + // We don't have libWorkMan installed already, so get everything + // from within our own directory +#include "libwm/include/wm_config.h" +} + + +#include "configWidget.h" +#include "kscd.h" +#include "prefs.h" + +class SpecialComboBox : public KComboBox +{ +public: + SpecialComboBox(QWidget* parent, const char* name) + : KComboBox(parent, name) + {} + + // QComboBox::setCurrentText replaces the current text if + // the list doesn't contain text, while + // KComboBox::setCurrentItem doesn't + void setCurrentText(const QString& text) + { + setCurrentItem(text); + } +} ; + +/* + * Constructs a configWidget which is a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + * + * The dialog will by default be modeless, unless you set 'modal' to + * TRUE to construct a modal dialog. + */ +configWidget::configWidget(KSCD* player, QWidget* parent, const char* name) + : configWidgetUI(parent, name), + mPlayer(player) +{ + if (!name) + { + setName("configWidget"); + } + + kcfg_cdDevice->comboBox()->setEditable(true); + kcfg_cdDevice->comboBox()->insertItem(DEFAULT_CD_DEVICE); + getMediaDevices(); + + (new QVBoxLayout(audioSystemFrame))->setAutoAdd(true); + kcfg_AudioSystem = new SpecialComboBox(audioSystemFrame, "kcfg_AudioSystem"); + textLabel4->setBuddy(kcfg_AudioSystem); + +#if defined(BUILD_CDDA) + kcfg_DigitalPlayback_toggled(Prefs::digitalPlayback()); + + // fill ComboBox audioBackend + kcfg_AudioSystem->insertStringList(mPlayer->audioSystems()); +#else + kcfg_DigitalPlayback_toggled(false); + + kcfg_DigitalPlayback->setChecked(false); + kcfg_DigitalPlayback->hide(); +#endif + kcfg_SelectEncoding_toggled(Prefs::selectEncoding()); +} + +configWidget::~configWidget() +{ +} + +void configWidget::kcfg_DigitalPlayback_toggled(bool toggle) +{ + kcfg_AudioSystem->setEnabled(toggle); + textLabel4->setEnabled(toggle); + kcfg_AudioDevice->setEnabled(toggle); + textLabel5->setEnabled(toggle); +} + +void configWidget::getMediaDevices() +{ + DCOPRef ref("kded","mediamanager"); + DCOPReply rep = ref.call("fullList()"); + if (!rep.isValid()) { + return; + } + QStringList list = rep; + QStringList::const_iterator it = list.begin(); + QStringList::const_iterator itEnd = list.end(); + // it would be much better if libmediacommon was in kdelibs + while (it != itEnd) { + it++; + if (it == itEnd) break; + QString url="media:/"+(*it); // is it always right? ervin? + kdDebug() << "checking " << url << endl; + for (int i=0;i<9;i++) ++it; // go to mimetype (MIME_TYPE-NAME from medium.h) + kdDebug() << "Mime: " << *it << endl; + if (it!=itEnd && (*it)=="media/audiocd") { + kcfg_cdDevice->comboBox()->insertItem(url); + } + while (it !=itEnd && (*it)!="---") ++it; // go to end of current device's properties + ++it; + } +} + + +void configWidget::kcfg_SelectEncoding_toggled(bool toggle) +{ + kcfg_SelectedEncoding->setEnabled(toggle); +} + +#include "configWidget.moc" diff --git a/kscd/configWidget.h b/kscd/configWidget.h new file mode 100644 index 00000000..bd0ea70f --- /dev/null +++ b/kscd/configWidget.h @@ -0,0 +1,50 @@ +/* + * configWidget - the config dialog page for KSCD settings + * + * Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org> + * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef CONFIGWIDGET_H +#define CONFIGWIDGET_H + +#include "configWidgetUI.h" + +class KSCD; +class SpecialComboBox; + +class configWidget : public configWidgetUI +{ + Q_OBJECT + + public: + configWidget(KSCD* player, QWidget* parent = 0, const char* name = 0); + ~configWidget(); + + protected: + KSCD* mPlayer; + SpecialComboBox* kcfg_AudioSystem; + + public slots: + virtual void kcfg_DigitalPlayback_toggled(bool); + virtual void kcfg_SelectEncoding_toggled(bool); + private: + void getMediaDevices(); +}; + +#endif // CONFIGWIDGET_H diff --git a/kscd/configWidgetUI.ui b/kscd/configWidgetUI.ui new file mode 100644 index 00000000..83f9aca0 --- /dev/null +++ b/kscd/configWidgetUI.ui @@ -0,0 +1,462 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>configWidgetUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>configWidgetUI</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>539</width> + <height>605</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Interface</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>&Background color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_BackColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="2" column="1"> + <property name="name"> + <cstring>kcfg_BackColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>The background color that will be used for the LCD display.</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_Docking</cstring> + </property> + <property name="text"> + <string>Show icon in &system tray</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected an icon will appear in the system tray. Note that KsCD will <i>not</i> quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_TrackAnnouncement</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Show &track announcement</string> + </property> + </widget> + <spacer row="1" column="2" rowspan="2" colspan="1"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>292</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>&LCD color:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_ledColor</cstring> + </property> + </widget> + <widget class="KColorButton" row="1" column="1"> + <property name="name"> + <cstring>kcfg_ledColor</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>The foreground color that will be used in the LCD display.</string> + </property> + </widget> + <widget class="KFontRequester" row="0" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ledFont</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>LCD &font:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_ledFont</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="QButtonGroup"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="title"> + <string>Play Options</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KIntNumInput" row="0" column="1"> + <property name="name"> + <cstring>kcfg_SkipDelta</cstring> + </property> + <property name="minValue"> + <number>1</number> + </property> + <property name="maxValue"> + <number>1000</number> + </property> + <property name="suffix"> + <string> seconds</string> + </property> + <property name="specialValueText"> + <string>1 second</string> + </property> + <property name="whatsThis" stdset="0"> + <string>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>Skip &interval:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_SkipDelta</cstring> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_Autoplay</cstring> + </property> + <property name="text"> + <string>Auto&play when CD inserted</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_EjectOnFinish</cstring> + </property> + <property name="text"> + <string>&Eject CD when finished playing</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected the CD will automatically eject when it is finished.</string> + </property> + </widget> + <widget class="QCheckBox" row="3" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_StopExit</cstring> + </property> + <property name="text"> + <string>Stop playing CD on e&xit</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected the CD will automatically stop playing when quitting KsCD.</string> + </property> + </widget> + <spacer row="0" column="2" rowspan="4" colspan="1"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>171</width> + <height>81</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3</cstring> + </property> + <property name="title"> + <string>CD-ROM &Device</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KURLComboRequester" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_cdDevice</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Select audio de&vice:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_AudioDevice</cstring> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>kcfg_DigitalPlayback</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>&Use direct digital playback</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback consumes more system resources than the normal method of playback.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>Select &audio backend:</string> + </property> + <property name="indent"> + <number>0</number> + </property> + </widget> + <widget class="KURLRequester" row="3" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_AudioDevice</cstring> + </property> + </widget> + <widget class="QFrame" row="2" column="2"> + <property name="name"> + <cstring>audioSystemFrame</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox9</cstring> + </property> + <property name="title"> + <string> Music Information Services </string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_SelectEncoding</cstring> + </property> + <property name="text"> + <string>Allow en&coding selection:</string> + </property> + <property name="whatsThis" stdset="0"> + <string>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</string> + </property> + </widget> + <widget class="QComboBox"> + <item> + <property name="text"> + <string>AUTO</string> + </property> + </item> + <item> + <property name="text"> + <string>UTF-8</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1250</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1251</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1252</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1253</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1254</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1255</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1256</string> + </property> + </item> + <item> + <property name="text"> + <string>CP1257</string> + </property> + </item> + <property name="name"> + <cstring>kcfg_SelectedEncoding</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="duplicatesEnabled"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>45</height> + </size> + </property> + </spacer> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_DigitalPlayback</sender> + <signal>toggled(bool)</signal> + <receiver>configWidgetUI</receiver> + <slot>kcfg_DigitalPlayback_toggled(bool)</slot> + </connection> + <connection> + <sender>kcfg_Docking</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_TrackAnnouncement</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_SelectEncoding</sender> + <signal>toggled(bool)</signal> + <receiver>configWidgetUI</receiver> + <slot>kcfg_SelectEncoding_toggled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>kcfg_ledColor</tabstop> + <tabstop>kcfg_BackColor</tabstop> + <tabstop>kcfg_Docking</tabstop> + <tabstop>kcfg_TrackAnnouncement</tabstop> + <tabstop>kcfg_SkipDelta</tabstop> + <tabstop>kcfg_Autoplay</tabstop> + <tabstop>kcfg_EjectOnFinish</tabstop> + <tabstop>kcfg_StopExit</tabstop> + <tabstop>kcfg_cdDevice</tabstop> + <tabstop>kcfg_DigitalPlayback</tabstop> + <tabstop>kcfg_AudioDevice</tabstop> +</tabstops> +<slots> + <slot>kcfg_DigitalPlayback_toggled( bool )</slot> + <slot>kcfg_SelectEncoding_toggled(bool)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kcolorbutton.h</includehint> + <includehint>kfontrequester.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kurlrequester.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>klineedit.h</includehint> +</includehints> +</UI> diff --git a/kscd/configure.in.in b/kscd/configure.in.in new file mode 100644 index 00000000..9f92c1a7 --- /dev/null +++ b/kscd/configure.in.in @@ -0,0 +1,68 @@ +dnl +-------------------------+ +dnl | Checks for KSCD | +dnl +-------------------------+ +AC_CHECK_LIB(cdaudio, main, [LIBCDAUDIO="-lcdaudio -lmediad -lds"],[],[-lmedia -lds]) dnl for Irix +AC_SUBST(LIBCDAUDIO) +AC_CHECK_LIB(fpe, main, [LIBFPE="-lfpe"]) dnl for Irix CDDA +AC_SUBST(LIBFPE) dnl for Irix CDDA +AC_CHECK_LIB(cdrom, main, [LIBCDROM="-lcdrom"],[],[-lcdrom]) dnl for kscd FreeBSD +AC_SUBST(LIBCDROM) + +dnl Checks for libraries. +dnl added by wuebben Nov 27 1997 +AC_CHECK_LIB(Alib, main, [LIBALIB="-lAlib"]) dnl HP-UX network audio server +AC_SUBST(LIBALIB) +AC_CHECK_LIB(audio,main,[LIBAUDIO="-laudio"]) dnl SunOS audio driver +AC_SUBST(LIBAUDIO) +AC_CHECK_LIB(mme, main, [LIBMME="-lmme";EXTRAINCS="-I/usr/include/mme"]) dnl DEC MMS audio server +AC_SUBST(LIBMME) + +AC_SUBST(EXTRAINCS) +AC_SUBST(EXTRALIBS) + +AC_ARG_ENABLE(kscd-defaults,[ --enable-kscd-defaults use kscd default configs [default=yes]], +[ +if test $enableval = "no"; dnl + then use_kscd_defaults="no" + else use_kscd_defaults="yes" +fi +], use_kscd_defaults="yes" +) + +AC_DEFUN([KDE_COPY_KSCD_DEFAULTS], +[ +if echo $TOPSUBDIRS | grep "kscd" > /dev/null 2> /dev/null; then + if test "$use_kscd_defaults" = "yes"; then + test -d kscd || mkdir kscd + cp $srcdir/kscd/config.h.std kscd/config.h + else + cd kscd && $srcdir/kscd-script + cd $topdir + fi +fi +]) + +AC_OUTPUT_COMMANDS(KDE_COPY_KSCD_DEFAULTS) + +dnl +-------------------------+ +dnl | End KSCD checks | +dnl +-------------------------+ + +AC_MSG_CHECKING(if kscd can be compiled) +case "$host" in + *-*-linux*) kscd_compile=yes;; + *-*-*bsdi*) kscd_compile=yes;; + *-*-*freebsd*) kscd_compile=yes;; + *-*-*netbsd*) kscd_compile=yes;; + *-*-*openbsd*) kscd_compile=yes;; + *) kscd_compile=yes;; +esac +AC_MSG_RESULT($kscd_compile) +if test "$kscd_compile" = "no"; then + DO_NOT_COMPILE="$DO_NOT_COMPILE kscd" +fi + +if test "x$build_arts" != "xno"; then + AC_DEFINE_UNQUOTED(USE_ARTS, 1, [Define if aRts is available]) +fi + diff --git a/kscd/cr22-action-cdsmall.png b/kscd/cr22-action-cdsmall.png Binary files differnew file mode 100644 index 00000000..d9f8f05e --- /dev/null +++ b/kscd/cr22-action-cdsmall.png diff --git a/kscd/docking.cpp b/kscd/docking.cpp new file mode 100644 index 00000000..9792ba73 --- /dev/null +++ b/kscd/docking.cpp @@ -0,0 +1,158 @@ +/* + * KSCD -- a simpole cd player for the KDE project + * + * $Id$ + * + * Copyright (C) 1997 Bernd Johannes Wuebben + * wuebben@math.cornell.edu + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "docking.h" +#include "kscd.h" + +#include <qhbox.h> +#include <qtooltip.h> + +#include <kaboutdata.h> +#include <kactioncollection.h> +#include <kapplication.h> +#include <klocale.h> +#include <kglobal.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kpassivepopup.h> + +#include <kdebug.h> + +DockWidget::DockWidget( KSCD* parent, const char *name) + : KSystemTray( parent, name ) +{ + m_popup = 0; + setPixmap( loadIcon("cdsmall") ); + + KActionCollection* actionCollection = parent->actionCollection(); + m_backAction = actionCollection->action("Previous"); + m_forwardAction = actionCollection->action("Next"); + m_backPix = loadIcon("player_start"); + m_forwardPix = loadIcon("player_end"); + + // popup menu for right mouse button + QPopupMenu* popup = contextMenu(); + + popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_play", KIcon::Small), i18n("Play/Pause"), parent, SLOT(playClicked())); + popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_stop", KIcon::Small), i18n("Stop"), parent, SLOT(stopClicked())); + popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_end", KIcon::Small), i18n("Next"), parent, SLOT(nextClicked())); + popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_start", KIcon::Small), i18n("Previous"), parent, SLOT(prevClicked())); + popup->insertItem(KGlobal::iconLoader()->loadIconSet("player_eject", KIcon::Small), i18n("Eject"), parent, SLOT(ejectClicked())); + + QToolTip::add(this, kapp->aboutData()->programName()); +} + +DockWidget::~DockWidget() +{ +} + +void DockWidget::createPopup(const QString &songName, bool addButtons) +{ + if (!Prefs::trackAnnouncement()) + return; + + delete m_popup; + m_popup = new KPassivePopup(this); + + QHBox* box = new QHBox(m_popup); + + if (addButtons) + { + QPushButton* backButton = new QPushButton(m_backPix, 0, box, "popup_back"); + backButton->setFlat(true); + connect(backButton, SIGNAL(clicked()), m_backAction, SLOT(activate())); + } + + QLabel* l = new QLabel(songName, box); + l->setMargin(3); + + if (addButtons) + { + QPushButton* forwardButton = new QPushButton(m_forwardPix, 0, box, "popup_forward"); + forwardButton->setFlat(true); + connect(forwardButton, SIGNAL(clicked()), m_forwardAction, SLOT(activate())); + } + + m_popup->setView(box); + m_popup->setAutoDelete(false); + m_popup->show(); +} + +void DockWidget::setToolTip(const QString& text) +{ + if (tip == text) + { + return; + } + + tip = text; + QToolTip::remove(this); + + if (text.isEmpty()) + { + QToolTip::add(this, kapp->aboutData()->programName()); + } + else + { + QToolTip::add(this, text); + } +} + +void DockWidget::wheelEvent(QWheelEvent *e) +{ + if (e->orientation() == Horizontal) + return; + + KSCD* kscd = dynamic_cast<KSCD*>(parent()); + if (kscd == 0) + return; + + switch (e->state()) + { + case ShiftButton: + { + if (e->delta() > 0) + { + kscd->incVolume(); + } + else + { + kscd->decVolume(); + } + break; + } + default: + { + if (e->delta() > 0) + { + kscd->nextClicked(); + } + else + { + kscd->prevClicked(); + } + } + } +} + +#include "docking.moc" diff --git a/kscd/docking.h b/kscd/docking.h new file mode 100644 index 00000000..ff946c15 --- /dev/null +++ b/kscd/docking.h @@ -0,0 +1,75 @@ +/* + * kPPP: A pppd Front End for the KDE project + * + * $Id$ + * + * Copyright (C) 1997 Bernd Johannes Wuebben + * wuebben@math.cornell.edu + * + * This file was contributed by Harri Porten <porten@tu-harburg.de> + * Latest changes (dynamic tooltips) by Cristian Tibirna <tibirna@kde.org> + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + + +#ifndef _DOCKING_H_ +#define _DOCKING_H_ + +#include <stdio.h> +#include <qapplication.h> +#include <qpixmap.h> +#include <qtimer.h> +#include <qpopupmenu.h> +#include <qpoint.h> +#include <kdockwindow.h> + +class KSCD; + +class KAction; +class KToggleAction; +class KPassivePopup; + +class DockWidget : public KSystemTray +{ + Q_OBJECT + +public: + DockWidget( KSCD* parent, const char *name=0); + ~DockWidget(); + +public slots: + void setToolTip(const QString& text); + void createPopup(const QString& songName, bool addButtons = true); + +private: + virtual void wheelEvent( QWheelEvent *e); + + KPassivePopup* m_popup; + + KAction* m_forwardAction; + KAction* m_backAction; + + QPixmap m_backPix; + QPixmap m_forwardPix; + + QString tip; +}; + +#endif + + + diff --git a/kscd/hi128-app-kscd.png b/kscd/hi128-app-kscd.png Binary files differnew file mode 100644 index 00000000..25496a41 --- /dev/null +++ b/kscd/hi128-app-kscd.png diff --git a/kscd/hi16-app-kscd.png b/kscd/hi16-app-kscd.png Binary files differnew file mode 100644 index 00000000..e59a04eb --- /dev/null +++ b/kscd/hi16-app-kscd.png diff --git a/kscd/hi32-app-kscd.png b/kscd/hi32-app-kscd.png Binary files differnew file mode 100644 index 00000000..0586fee8 --- /dev/null +++ b/kscd/hi32-app-kscd.png diff --git a/kscd/hi48-app-kscd.png b/kscd/hi48-app-kscd.png Binary files differnew file mode 100644 index 00000000..4d001825 --- /dev/null +++ b/kscd/hi48-app-kscd.png diff --git a/kscd/hi64-app-kscd.png b/kscd/hi64-app-kscd.png Binary files differnew file mode 100644 index 00000000..1769ca22 --- /dev/null +++ b/kscd/hi64-app-kscd.png diff --git a/kscd/kcompactdisc.cpp b/kscd/kcompactdisc.cpp new file mode 100644 index 00000000..b69325a3 --- /dev/null +++ b/kscd/kcompactdisc.cpp @@ -0,0 +1,486 @@ +/* + * KCompactDisc - A CD drive interface for the KDE Project. + * + * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <dcopclient.h> +#include <dcopref.h> +#include <qfile.h> +#include <kdebug.h> +#include <klocale.h> +#include <kprotocolmanager.h> +#include <krun.h> +#include "kcompactdisc.h" +#include <netwm.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <signal.h> +#include <sys/utsname.h> +#include <unistd.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +/* this is for glibc 2.x which the ust structure in ustat.h not stat.h */ +#ifdef __GLIBC__ +#include <sys/ustat.h> +#endif + +#ifdef __FreeBSD__ +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#endif + +#ifdef __linux__ +#include <mntent.h> +#define KSCDMAGIC 0 +#endif + +#include <kprocess.h> +#include <config.h> + +extern "C" +{ +// We don't have libWorkMan installed already, so get everything +// from within our own directory +#include "libwm/include/wm_cddb.h" +#include "libwm/include/wm_cdrom.h" +#include "libwm/include/wm_cdtext.h" +#include "libwm/include/wm_config.h" +#include "libwm/include/wm_cdinfo.h" +#include "libwm/include/wm_helpers.h" + +// Sun, Ultrix etc. have a canonical CD device specified in the +// respective plat_xxx.c file. On those platforms you need not +// specify the CD device and DEFAULT_CD_DEVICE is not defined +// in config.h. +#ifndef DEFAULT_CD_DEVICE +#define DEFAULT_CD_DEVICE "/dev/cdrom" +#endif +} + +#include <qtextcodec.h> +#include <fixx11h.h> + +// Our internal definition of when we have no disc. Used to guard some +// internal arrays. +#define NO_DISC ((m_discId == missingDisc) && (m_previousDiscId == 0)) + +#define FRAMES_TO_MS(frames) \ +((frames) * 1000 / 75) + +#define TRACK_VALID(track) \ +((track) && (track <= m_tracks)) + +const QString KCompactDisc::defaultDevice = DEFAULT_CD_DEVICE; +const unsigned KCompactDisc::missingDisc = (unsigned)-1; + +KCompactDisc::KCompactDisc(InformationMode infoMode) : + m_device(QString::null), + m_status(0), + m_previousStatus(123456), + m_discId(missingDisc), + m_previousDiscId(0), + m_artist(QString::null), + m_title(QString::null), + m_track(0), + m_previousTrack(99999999), + m_infoMode(infoMode) +{ + // Debug. + // wm_cd_set_verbosity(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS_ALL ); + m_trackArtists.clear(); + m_trackTitles.clear(); + m_trackStartFrames.clear(); + connect(&timer, SIGNAL(timeout()), SLOT(timerExpired())); +} + +KCompactDisc::~KCompactDisc() +{ + // Ensure nothing else starts happening. + timer.stop(); + wm_cd_stop(); + wm_cd_set_verbosity(0x0); + wm_cd_destroy(); +} + +const QString &KCompactDisc::device() const +{ + return m_device; +} + +unsigned KCompactDisc::discLength() const +{ + if (NO_DISC || !m_tracks) + return 0; + return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]); +} + +unsigned KCompactDisc::discPosition() const +{ + return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]); +} + +QString KCompactDisc::discStatus(int status) +{ + QString message; + + switch (status) + { + case WM_CDM_TRACK_DONE: // == WM_CDM_BACK + message = i18n("Back/Track Done"); + break; + case WM_CDM_PLAYING: + message = i18n("Playing"); + break; + case WM_CDM_FORWARD: + message = i18n("Forward"); + break; + case WM_CDM_PAUSED: + message = i18n("Paused"); + break; + case WM_CDM_STOPPED: + message = i18n("Stopped"); + break; + case WM_CDM_EJECTED: + message = i18n("Ejected"); + break; + case WM_CDM_NO_DISC: + message = i18n("No Disc"); + break; + case WM_CDM_UNKNOWN: + message = i18n("Unknown"); + break; + case WM_CDM_CDDAERROR: + message = i18n("CDDA Error"); + break; + case WM_CDM_CDDAACK: + message = i18n("CDDA Ack"); + break; + default: + if (status <= 0) + message = strerror(-status); + else + message = QString::number(status); + break; + } + return message; +} + +/** + * Do everything needed if the user requested to eject the disc. + */ +void KCompactDisc::eject() +{ + if (m_status == WM_CDM_EJECTED) + { + emit trayClosing(); + wm_cd_closetray(); + } + else + { + wm_cd_stop(); + wm_cd_eject(); + } +} + +unsigned KCompactDisc::track() const +{ + return m_track; +} + +bool KCompactDisc::isPaused() const +{ + return (m_status == WM_CDM_PAUSED); +} + +bool KCompactDisc::isPlaying() const +{ + return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE); +} + +void KCompactDisc::pause() +{ + // wm_cd_pause "does the right thing" by flipping between pause and resume. + wm_cd_pause(); +} + +void KCompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack) +{ + wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK ); +} + +QString KCompactDisc::urlToDevice(const QString& device) +{ + KURL deviceUrl(device); + if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system") + { + kdDebug() << "Asking mediamanager for " << deviceUrl.fileName() << endl; + DCOPRef mediamanager("kded", "mediamanager"); + DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName()); + QStringList properties = reply; + if (!reply.isValid() || properties.count() < 6) + { + kdError() << "Invalid reply from mediamanager" << endl; + return defaultDevice; + } + else + { + kdDebug() << "Reply from mediamanager " << properties[5] << endl; + return properties[5]; + } + } + + return device; +} + +bool KCompactDisc::setDevice( + const QString &device_, + unsigned volume, + bool digitalPlayback, + const QString &audioSystem, + const QString &audioDevice) +{ + timer.stop(); + + QString device = urlToDevice(device_); + +#if !defined(BUILD_CDDA) + digitalPlayback = false; +#endif + int status = wm_cd_init( + digitalPlayback ? WM_CDDA : WM_CDIN, + QFile::encodeName(device), + digitalPlayback ? audioSystem.ascii() : 0, + digitalPlayback ? audioDevice.ascii() : 0, + 0); + m_device = wm_drive_device(); + kdDebug() << "Device change: " + << (digitalPlayback ? "WM_CDDA, " : "WM_CDIN, ") + << m_device << ", " + << (digitalPlayback ? audioSystem : QString::null) << ", " + << (digitalPlayback ? audioDevice : QString::null) << ", status: " + << discStatus(status) << endl; + + if (status < 0) + { + // Severe (OS-level) error. + m_device = QString::null; + } + else + { + // Init CD-ROM and display. + setVolume(volume); + } + + m_previousStatus = m_status = wm_cd_status(); + + if (m_infoMode == Asynchronous) + timerExpired(); + else + timer.start(1000, true); + return m_device != QString::null; +} + +void KCompactDisc::setVolume(unsigned volume) +{ + int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED); + kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl; +} + +void KCompactDisc::stop() +{ + wm_cd_stop(); +} + +const QString &KCompactDisc::trackArtist() const +{ + return trackArtist(m_track); +} + +const QString &KCompactDisc::trackArtist(unsigned track) const +{ + if (NO_DISC || !TRACK_VALID(track)) + return QString::null; + return m_trackArtists[track - 1]; +} + +unsigned KCompactDisc::trackLength() const +{ + return trackLength(m_track); +} + +unsigned KCompactDisc::trackLength(unsigned track) const +{ + if (NO_DISC || !TRACK_VALID(track)) + return 0; + return cd->trk[track - 1].length * 1000; +} + +unsigned KCompactDisc::trackPosition() const +{ + return cur_pos_rel * 1000; +} + +unsigned KCompactDisc::tracks() const +{ + return m_tracks; +} + +const QString &KCompactDisc::trackTitle() const +{ + return trackTitle(m_track); +} + +const QString &KCompactDisc::trackTitle(unsigned track) const +{ + if (NO_DISC || !TRACK_VALID(track)) + return QString::null; + return m_trackTitles[track - 1]; +} + +bool KCompactDisc::isAudio(unsigned track) const +{ + if (NO_DISC || !TRACK_VALID(track)) + return 0; + return !(cd->trk[track - 1].data); +} + +/* + * timerExpired + * + * - Data discs not recognized as data discs. + * + */ +void KCompactDisc::timerExpired() +{ + m_status = wm_cd_status(); + + if (WM_CDS_NO_DISC(m_status) || (m_device == QString::null)) + { + if (m_previousStatus != m_status) + { + m_previousStatus = m_status; + m_discId = missingDisc; + m_previousDiscId = 0; + m_trackArtists.clear(); + m_trackTitles.clear(); + m_trackStartFrames.clear(); + m_tracks = 0; + m_track = 0; + emit discChanged(m_discId); + } + } + else + { + m_discId = cddb_discid(); + if (m_previousDiscId != m_discId) + { + m_previousDiscId = m_discId; + kdDebug() << "New discId=" << m_discId << endl; + // Initialise the album and its signature from the CD. + struct cdtext_info *info = wm_cd_get_cdtext(); + if (info && info->valid) + { + m_artist = reinterpret_cast<char*>(info->blocks[0]->performer[0]); + m_title = reinterpret_cast<char*>(info->blocks[0]->name[0]); + } + else + { + m_artist = i18n("Unknown Artist"); + m_title = i18n("Unknown Title"); + } + + // Read or default CD data. + m_trackArtists.clear(); + m_trackTitles.clear(); + m_trackStartFrames.clear(); + m_tracks = wm_cd_getcountoftracks(); + for (unsigned i = 1; i <= m_tracks; i++) + { + if (info && info->valid) + { + m_trackArtists.append(reinterpret_cast<char*>(info->blocks[0]->performer[i])); + m_trackTitles.append(reinterpret_cast<char*>(info->blocks[0]->name[i])); + } + else + { + m_trackArtists.append(i18n("Unknown Artist")); + m_trackTitles.append(i18n("Track %1").arg(QString::number(i).rightJustify(2, '0'))); + } + // FIXME: KDE4 + // track.length = cd->trk[i - 1].length; + m_trackStartFrames.append(cd->trk[i - 1].start); + } + m_trackStartFrames.append(cd->trk[0].start); + m_trackStartFrames.append(cd->trk[m_tracks].start); + emit discChanged(m_discId); + } + + // Per-event processing. + m_track = wm_cd_getcurtrack(); + if (m_previousTrack != m_track) + { + m_previousTrack = m_track; + + // Update the current track and its length. + emit trackChanged(m_track, trackLength()); + } + if (isPlaying()) + { + m_previousStatus = m_status; + // Update the current playing position. + emit trackPlaying(m_track, trackPosition()); + } + else + if (m_previousStatus != m_status) + { + // If we are not playing, then we are either paused, or stopped. + switch (m_status) + { + case WM_CDM_PAUSED: + emit trackPaused(m_track, trackPosition()); + break; + case WM_CDM_EJECTED: + emit trayOpening(); + break; + default: + if (m_previousStatus == WM_CDM_PLAYING || m_previousStatus == WM_CDM_PAUSED + && m_status == WM_CDM_STOPPED) + { + emit discStopped(); + } + break; + } + + m_previousStatus = m_status; + } + } + + // Now that we have incurred any delays caused by the signals, we'll start the timer. + timer.start(1000, true); +} + +#include "kcompactdisc.moc" diff --git a/kscd/kcompactdisc.h b/kscd/kcompactdisc.h new file mode 100644 index 00000000..595567f4 --- /dev/null +++ b/kscd/kcompactdisc.h @@ -0,0 +1,303 @@ +/* + * KCompactDisc - A CD drive interface for the KDE Project. + * + * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef KCOMPACTDISC_H +#define KCOMPACTDISC_H + +#include <qobject.h> +#include <qtimer.h> +#include <qvaluelist.h> + +/** + * KCompactDisc - A CD drive interface for the KDE Project. + * + * The disc lifecycle is modelled by these signals: + * + * @see #trayClosing(): A disc is being inserted. + * @see #discChanged(): A disc was inserted or removed. + * @see #trayOpening(): A disc is being removed. + * + * The progress of playout is modelled by these signals: + * + * @see #trackPlaying(): A track started playing, or is still playing. + * @see #trackPaused(): A track was paused. + * @see #discStopped(): The disc stopped. + * + * All times in this interface are in milliseconds. Valid track numbers are + * positive numbers; zero is not a valid track number. + */ +class KCompactDisc : + public QObject +{ + Q_OBJECT +public: + enum InformationMode + { + Synchronous, // Return and emit signal when cdrom and cddb information arrives. + Asynchronous // Block until cdrom and cddb infromation has been obtained + }; + + KCompactDisc(InformationMode=Synchronous); + virtual ~KCompactDisc(); + + /** + * Open/close tray. + */ + void eject(); + + /** + * Start playout at given position of track. + */ + void play(unsigned startTrack = 0, unsigned startTrackPosition = 0, unsigned endTrack = 0); + + /** + * Pause/resume playout. + */ + void pause(); + + /** + * If the url is a media:/ or system:/ URL returns + * the device it represents, otherwise returns device + */ + static QString urlToDevice(const QString& device); + + /** + * @param device Name of CD device, e.g. /dev/cdrom. + * @param digitalPlayback Select digial or analogue playback. + * @param audioSystem For analogue playback, system to use, e.g. "arts". + * @param audioDevice For analogue playback, device to use. + * @return true if the device seemed usable. + */ + bool setDevice( + const QString &device = defaultDevice, + unsigned volume = 50, + bool digitalPlayback = true, + const QString &audioSystem = QString::null, + const QString &audioDevice = QString::null); + + void setVolume(unsigned volume); + + /** + * Stop playout. + */ + void stop(); + + /** + * The default CD for this system. + */ + static const QString defaultDevice; + + /** + * Current device. + * + * @return Null string if no usable device set. + */ + const QString &device() const; + + /** + * The discId for a missing disc. + */ + static const unsigned missingDisc; + + /** + * Current disc, missingDisc if no disc. + */ + unsigned discId() const { return m_discId; } + + /** + * CDDB signature of disc. + */ + const QValueList<unsigned> &discSignature() const { return m_trackStartFrames; } + + /** + * Artist for whole disc. + * + * @return Disc artist or null string. + */ + const QString &discArtist() const { return m_artist; } + + /** + * Title of disc. + * + * @return Disc title or null string. + */ + const QString &discTitle() const { return m_title; } + + /** + * Length of disc. + * + * @return Disc length in milliseconds. + */ + unsigned discLength() const; + + /** + * Position in disc. + * + * @return Position in milliseconds. + */ + unsigned discPosition() const; + /** + * Artist of current track. + * + * @return Track artist or null string. + */ + const QString &trackArtist() const; + + /** + * Artist of given track. + * + * @return Track artist or null string. + */ + const QString &trackArtist(unsigned track) const; + + /** + * Title of current track. + * + * @return Track title or null string. + */ + const QString &trackTitle() const; + + /** + * Title of given track. + * + * @return Track title or null string. + */ + const QString &trackTitle(unsigned track) const; + + /** + * Current track. + * + * @return Track number. + */ + unsigned track() const; + + /** + * Number of tracks. + */ + unsigned tracks() const; + + /** + * @return if the track is actually an audio track. + */ + bool isAudio(unsigned track) const; + + /** + * Length of current track. + * + * @return Track length in milliseconds. + */ + unsigned trackLength() const; + + /** + * Length of given track. + * + * @param track Track number. + * @return Track length in milliseconds. + */ + unsigned trackLength(unsigned track) const; + + /** + * Position in current track. + * + * @return Position in milliseconds. + */ + unsigned trackPosition() const; + + bool isPaused() const; + + bool isPlaying() const; + +signals: + + /** + * A disc is being inserted. + */ + void trayClosing(); + + /** + * A disc is being removed. + */ + void trayOpening(); + + /** + * A disc was inserted or removed. + * + * @param discId Current disc, missingDisc if no disc. + */ + void discChanged(unsigned discId); + + /** + * Disc stopped. See @see #trackPaused. + */ + void discStopped(); + + /** + * The current track changed. + * + * @param track Track number. + * @param trackLength Length within track in milliseconds. + */ + void trackChanged(unsigned track, unsigned trackLength); + + /** + * A track started playing, or is still playing. This signal is delivered at + * approximately 1 second intervals while a track is playing. At first sight, + * this might seem overzealous, but its likely that any CD player UI will use + * this to track the second-by-second position, so we may as well do it for + * them. + * + * @param track Track number. + * @param trackPosition Position within track in milliseconds. + */ + void trackPlaying(unsigned track, unsigned trackPosition); + + /** + * A track paused playing. + * + * @param track Track number. + * @param trackPosition Position within track in milliseconds. + */ + void trackPaused(unsigned track, unsigned trackPosition); + +private: + QTimer timer; + QString m_device; + int m_status; + int m_previousStatus; + unsigned m_discId; + unsigned m_previousDiscId; + QString m_artist; + QString m_title; + unsigned m_tracks; + QValueList<unsigned> m_trackStartFrames; + QStringList m_trackArtists; + QStringList m_trackTitles; + unsigned m_track; + unsigned m_previousTrack; + void checkDeviceStatus(); + QString discStatus(int status); + class KCompactDiscPrivate *d; + InformationMode m_infoMode; + +private slots: + void timerExpired(); +}; + +#endif diff --git a/kscd/kscd-script b/kscd/kscd-script new file mode 100755 index 00000000..8e54bc9e --- /dev/null +++ b/kscd/kscd-script @@ -0,0 +1,782 @@ +#!/bin/sh +# +# Kscd - A simple cd player for the KDE Project +# +# $Id$ +# +# Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. +# + +# the following is borrowed from configure +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + +config_file="config.h" +ORG_CONFIG="config.h.std" + +display_main_menu(){ + +clear +echo +echo " KSCD -- Configuration" +echo "===================================================================" +echo +echo " Linux ...................... L" +echo " FreeBSD/NetBSD ............. F" +echo " BSD386 ..................... B" +echo " Sun ........................ S" +echo " HPUX ....................... H" +echo " Irix ....................... I" +echo " Sony NEWS .................. N" +echo " OSF/1 ...................... O" +echo " Ultrix ..................... U" +echo " Generic SVR4 (not Sun) ..... V" +echo +echo " Quit ....................... Q" +echo +echo +echo " Bernd Johannes Wuebben <wuebben@kde.org>" +echo "==================================================================" +echo +echo $ac_n " Please chose acccording to your platform:$ac_c" + +read platform + +} + +give_instructions(){ + +echo "Please answer all questions. Simply hit [Enter] to select the default" +echo "values which are displayed in square brackets." +echo + +} + +write_config_header(){ + +echo "" >> $config_file +echo "/* This header file was automatically generated by the kscd" >> $config_file +echo " installation script. In case of trouble please edit" >> $config_file +echo " $ORG_CONFIG in the source directory of the kscd distribution" >> $config_file +echo " and rename it to $config_file .*/" >> $config_file +echo "" >> $config_file + +} + +linux_conf(){ + +clear +echo "KSCD configuration for Linux systems" +echo "------------------------------------" +echo + +give_instructions +write_config_header + +echo "#ifdef linux " >> $config_file +echo "" >> $config_file + +echo $ac_n "Please specify your cdrom device [/dev/cdrom]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="/dev/cdrom" +fi + +echo "Your choice was: $device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file + + + +echo +echo "I have made modifications to the workman engine which cause kscd to be" +echo "a very fast and responsive cd player for an average IDE CDROM drive." +echo "Answer NO to the next question unless you have problems getting kscd" +echo "to work." +echo +echo $ac_n "Revert to original Workman behavior? (y/n) [n]:$ac_c" + +read workman + +case "$workman" in + "yes" | "y" | "Yes" | "Y") echo "#define WORKMAN_ORIGINAL " >> $config_file ;; +esac + + +echo +echo "I have incorporated modifications from Dirk (milliByte@DeathsDoor.com)" +echo "which will make kscd perform better on any drive using the sbpcd or" +echo "mcdx drivers. If and only if your kernel uses one of these cd drivers you" +echo "should try answering YES to the next question. Please peruse the " +echo "documents in the spbcd directory of this distribution for more " +echo "information about theses modifications." +echo +echo $ac_n "Enable SBPCD_HACK? (y/n) [n]:$ac_c" + +read sbpcd + +case "$sbpcd" in + "yes" | "y" | "Yes" | "Y") echo "#define SBPCD_HACK " >> $config_file ;; +esac + + +echo +echo "If and only if you have an SCSI cdrom drive you should probably" +echo "answer YES to the next question" +echo +echo $ac_n "Enable LINUX_SCSI_PASSTHROUGH? (y/n) [n]:$ac_c" + +read scsi + +case "$scsi" in + "yes" | "y" | "Yes" | "Y") echo "#define LINUX_SCSI_PASSTHROUGH " >> $config_file ;; +esac + +echo +echo "Most cdrom devices have a volume range from 0 to 255." +echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME" +echo "until volume adjustment works to your satisfaction." +echo "Note: Some CDROM device only support a range from 128 to 255." +echo +echo $ac_n "MIN_VOLUME [0]:$ac_c" + +read MINVOLUME +if [ "$MINVOLUME" = "" ] +then + MINVOLUME="0" +fi + +echo "You entered $MINVOLUME " +echo "#define MIN_VOLUME $MINVOLUME " >> $config_file + +echo +echo $ac_n "MAX_VOLUME [255]:$ac_c" + +read MAXVOLUME +if [ "$MAXVOLUME" = "" ] +then + MAXVOLUME="255" +fi + +echo "You entered $MAXVOLUME " +echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file + +echo +echo "If you own one a cdrom player whose volume range is 128 to 255," +echo "you might want to try answering YES to the next question." +echo "Enabling 'Curved Volume' will give you a square root like" +echo "volume slider - volume curve." +echo +echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c" + +read curved + +case "$curved" in + "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;; +esac + +echo "" >> $config_file +echo "#endif /* linux */" >> $config_file + +} + +irix_conf(){ + +clear +echo "KSCD configuration for Irix" +echo "---------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(sgi)" >> $config_file +echo "" >> $config_file + +echo $ac_n "Please specify your cdrom device [/dev/scsi/sc0d6l0]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="/dev/scsi/sc0d6l0" +fi + +echo "Your choice was: $device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file + +echo $ac_n "Enable CDDA? (y/n) [y]:$ac_c" + +read curved + +case "$curved" in + "yes" | "y" | "Yes" | "Y" | "") echo "#define CDDA " >> $config_file ;; +esac +echo "" +echo "That's all for sgi ..." + +echo "#endif /* sgi */" >> $config_file + +} + +hp_conf(){ + +clear +echo "KSCD configuration for HPUX systems" +echo "-----------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(hpux) || defined (__hpux)" >> $config_file +echo "" >> $config_file + +echo $ac_n "Please specify your cdrom device [/dev/rscsi]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="/dev/rcsci" +fi + +echo "Your choice was: $device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file +echo "" +echo "That's all for HPUX ..." + +echo "#endif /* hpux */" >> $config_file + + +} + +bsd386_conf(){ + +clear +echo "KSCD configuration for BSD386 systems" +echo "-------------------------------------" +echo + +give_instructions +write_config_header + +echo "#ifdef __bsdi__ " >> $config_file + +echo +echo "Please answer YES to the next question if you have a Sound Blaster cdrom." +echo +echo $ac_n "Define SOUNDBALSTER? (y/n) [n]:$ac_c" + +read soundblaster + +case "$soundblaster" in + "yes" | "y" | "Yes" | "Y") echo "#define SOUNDBLASTER " >> $config_file ;; +esac + +echo "#endif /* __bsdi__ */" >> $config_file + +} + +freebsd_netbsd_conf(){ + +clear +echo "KSCD configuration for FreeBSD/NetBSD systems" +echo "---------------------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(__FreeBSD__) || defined(__NetBSD__)" >> $config_file +echo "" >> $config_file + +# get default device +if [ `/usr/bin/uname -s` = "NetBSD" ]; then + if [ `/usr/bin/uname -m` = "i386" ]; then + def_device=/dev/rcd0d + else + def_device=/dev/rcd0c + fi +else + # FreeBSD + def_device=/dev/acd0c +fi + +echo $ac_n "Please specify your cdrom device [$def_device]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="$def_device" +fi + +echo "Your choice was:$device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file + +echo +echo "Some experimental changes to the FreeBSD code were made." +echo "Please let me know whether answering yes or no to the next" +echo "question will give you a better functioning cd player." +echo "Users of NetBSD might want to experiment with this too." +echo +echo $ac_n "Define NEW_BSD_PLAYCLICKED? (y/n) [y]:$ac_c" + +read free_play_clicked + +case "$free_play_clicked" in + "yes" | "y" | "Yes" | "Y"| "") echo "#define NEW_BSD_PLAYCLICKED" >> $config_file ;; +esac + +echo "" >> $config_file +echo "#endif /* FreeBSD/NetBSD */" >> $config_file + +} + +sun_conf(){ + +clear +echo "KSCD configuration for Sun systems" +echo "----------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(sun) || defined(__sun__) " >> $config_file +echo "" >> $config_file + +echo $ac_n "Are you compiling on Solaris 2.x? (y/n) [y]:$ac_c" +read solaris + +case "$solaris" in + + n | N) + echo + ;; + *) + echo "#define SYSV" >> $config_file + solaris="y" + ;; +esac + +echo "" +echo "You need to enable the internal audio device if you wish" +echo "to get sound from your workstation, as opposed to only from" +echo "the head-phone jack of your CDROM." +echo "" +echo $ac_n "Activate internal audio device (CODEC)? (y/n) [y]:$ac_c" + +read codec + +case "$code" in + + n | N) + ;; + *) + echo "#define CODEC" >> $config_file + ;; +esac + +if [ "$solaris" = "y" ] +then + echo + echo "Do you wish to enable digital audio capablilites on drives that" + echo $ac_n "support it? (y/n) [y]:$ac_c" + read cdda + + case "$cdda" in + + n | N) + ;; + *) + echo "#define BUILD_CDDA" >> $config_file + ;; + esac +fi + + +echo +echo "Most cdrom devices have a volume range from 0 to 255." +echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME" +echo "until volume adjustment works to your satisfaction." +echo "Note: Some CDROM device only support a range from 128 to 255." +echo +echo $ac_n "MIN_VOLUME [128]:$ac_c" + +read MINVOLUME +if [ "$MINVOLUME" = "" ] +then + MINVOLUME="128" +fi + +echo "You entered $MINVOLUME " +echo "#define MIN_VOLUME $MINVOLUME " >> $config_file + +echo +echo $ac_n "MAX_VOLUME [255]:$ac_c" + +read MAXVOLUME +if [ "$MAXVOLUME" = "" ] +then + MAXVOLUME="255" +fi + +echo "You entered $MAXVOLUME " +echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file + +echo +echo "If you own one a cdrom player whose volume range is 128 to 255," +echo "you might want to try answering YES to the next question." +echo "Enabling 'Curved Volume' will give you a square root like" +echo "volume slider - volume curve." +echo +echo $ac_n "Enable CURVED_VOLUME? (y/n) [n]:$ac_c" + +read curved + +case "$curved" in + "yes" | "y" | "Yes" | "Y") echo "#define CURVED_VOLUME " >> $config_file ;; +esac + +echo "#endif /* Sun*/" >> $config_file + +} + +sony_conf(){ + +clear +echo "KSCD configuration for Sony NEWS systems" +echo "---------------------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(__sony_news) || defined(sony_news)" >> $config_file +echo "" >> $config_file + +echo "Unfortunately, I have conflicting reports:" +echo "You might need a,b or c below." +echo "Please let me know what is required on Sony NEWS." +echo +echo "a) #include <CD.h> " +echo +echo "b) #include <newsiodev/scu.h>" +echo " #include <newsiodev/scsireg.h>" +echo +echo "c) both of the above" +echo "" +echo $ac_n "Please choose a,b or c. [c]:$ac_c" + +read sony_include + +case "$sony_include" in + "a" | "A" ) + echo "Your choice was a" + echo "#include <CD.h>" >> $config_file + ;; + "b" | "B" ) + echo "Your choice was b" + echo "#include <newsiodev/scu.h>" >> $config_file + echo "#include <newsiodev/scsireg.h>" >> $config_file + ;; + * ) + echo "Your choice was c" + echo "#include <CD.h>" >> $config_file + echo "#include <newsiodev/scu.h>" >> $config_file + echo "#include <newsiodev/scsireg.h>" >> $config_file + ;; +esac + +echo +echo $ac_n "Please specify your cdrom device [/dev/rsd/b0i6u0p2\0]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="/dev/rsd/b0i6u0p2\0" +fi + +echo "Your choice was:$device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file + +echo +echo "While most CDROM devices have a volume range from 0 to 255," +echo "it appears that 128 to 255 is more appropriate for Sony NEWS." +echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME" +echo "until volume adjustment works to your satisfaction." +echo "Don't forget to let me know the result of your experiments." +echo +echo $ac_n "MIN_VOLUME [128]:$ac_c" + +read MINVOLUME +if [ "$MINVOLUME" = "" ] +then + MINVOLUME="128" +fi + +echo "You entered $MINVOLUME " +echo "#define MIN_VOLUME $MINVOLUME " >> $config_file + +echo +echo $ac_n "MAX_VOLUME [255]:$ac_c" + +read MAXVOLUME +if [ "$MAXVOLUME" = "" ] +then + MAXVOLUME="255" +fi + +echo "You entered $MAXVOLUME " +echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file + +echo "" >> $config_file +echo "#endif /* sony_news */" >> $config_file + +} + +ultrix_conf(){ + +clear +echo "KSCD configuration for Ultrix systems" +echo "---------------------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(ultrix) || defined(__ultrix)" >> $config_file +echo "" >> $config_file + +echo "Unfortunately, I have conflicting reports:" +echo "You might need a,b or c below." +echo "Please let me know what is required on Ultrix." +echo +echo "a) #include <sys/rzdisk.h>" +echo " #include <sys/cdrom.h>" +echo +echo "b) #include <sys/devio.h>" +echo " #include <io/cam/cam.h>" +echo " #include <io/cam/uagt.h>" +echo " #include <io/cam/dec_cam.h>" +echo " #include <io/cam/scsi_all.h>" +echo "" +echo "c) both of the above" +echo "" +echo $ac_n "Please choose a,b or c. [a]:$ac_c" + +read ultrix_include + +case "$ultrix_include" in + "c" | "C" ) + echo "Your choice was c)" + echo "" + echo "#include <sys/rzdisk.h>" >> $config_file + echo "#include <sys/cdrom.h>">> $config_file + echo "" + echo "#include <sys/devio.h>" >> $config_file + echo "#include <io/cam/cam.h>" >> $config_file + echo "#include <io/cam/uagt.h>" >> $config_file + echo "#include <io/cam/dec_cam.h>" >> $config_file + echo "#include <io/cam/scsi_all.h>" >> $config_file + echo "" >> $config_file + ;; + + "b" | "B" ) + echo "Your choice was b" + echo "#include <sys/devio.h>" >> $config_file + echo "#include <io/cam/cam.h>" >> $config_file + echo "#include <io/cam/uagt.h>" >> $config_file + echo "#include <io/cam/dec_cam.h>" >> $config_file + echo "#include <io/cam/scsi_all.h>" >> $config_file + ;; + * ) + echo "Your choice was a" + echo "#include <sys/rzdisk.h>" >> $config_file + echo "#include <sys/cdrom.h>">> $config_file + ;; +esac + +echo +echo "While most CDROM devices have a volume range from 0 to 255," +echo "it appears that 128 to 255 is more appropriate for Ultrix" +echo "Feel free to play with the parameters MINVOLUME and MAXVOLUME" +echo "until volume adjustment works to your satisfaction." +echo "Don't forget to let me know the result of your experiments." +echo +echo $ac_n "MIN_VOLUME [128]:$ac_c" + +read MINVOLUME +if [ "$MINVOLUME" = "" ] +then + MINVOLUME="128" +fi + +echo "You entered $MINVOLUME " +echo "#define MIN_VOLUME $MINVOLUME " >> $config_file + +echo +echo $ac_n "MAX_VOLUME [255]:$ac_c" + +read MAXVOLUME +if [ "$MAXVOLUME" = "" ] +then + MAXVOLUME="255" +fi + +echo "You entered $MAXVOLUME " +echo "#define MAX_VOLUME $MAXVOLUME " >> $config_file + +echo "" >> $config_file +echo "#endif /* ultrix */" >> $config_file + +} + +osf_conf(){ + +clear +echo "KSCD configuration for OSF systems" +echo "----------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> config_file +echo "#if defined(__osf__)" >> $config_file + +echo "Nothing to be done for OSF. The CD device should be automatically detected." +echo "If you have trouble look at plat_osf1.c and let me know what needs to be" +echo "changed." + +echo "" +echo $ac_n "Press [Enter] to continue.$ac_c" +read dummy + +echo "" >> $config_file +echo "#endif /* osf */" >> $config_file +echo "" >> $config_file + +#endif + + +} + +generic_svr_conf(){ + +clear +echo "KSCD configuration for a generic SVR4 system" +echo "--------------------------------------------" +echo + +give_instructions +write_config_header + +echo "" >> $config_file +echo "#if defined(SVR4) && !defined(sun) && !defined(__sun__)" >> $config_file + +echo $ac_n "Please specify your cdrom device [/dev/rcdrom/cd0]:$ac_c" + +read device +if [ "$device" = "" ] +then + device="/dev/rcdrom/cd0" +fi + +echo "Your choice was:$device" +echo "#define DEFAULT_CD_DEVICE \"$device\" " >> $config_file + +echo "" >> $config_file +echo "#endif /* SVR4 */" >> $config_file +echo "" >> $config_file + +} + +######################################################################## +# +# main +# +######################################################################## + +if [ -f $config_file ] +then + echo "Found old config file. Will clean up first ...." + rm $config_file + sleep 1 +fi + +display_main_menu + +if [ "$platform" = "q" ] +then + clear + echo "Good bye!" + exit 1 +fi + +case "$platform" in +l|L) linux_conf;; +f|F) freebsd_netbsd_conf;; +b|B) bsd386_conf;; +s|S) sun_conf;; +h|H) hp_conf;; +i|I) irix_conf;; +n|N) sony_conf;; +o|O) osf_conf;; +u|U) ultrix_conf;; +v|V) generic_svr_conf;; +*) linux_conf;; +esac + +sleep 1 +clear +echo "" +echo "I have written your configuration choices to $config_file." +echo "Should you have trouble with the configuration file this" +echo "script generates, you can rename $ORG_CONFIG to $config_file" +echo "and edit it manually to suit your configuration." +echo "" +echo "Have fun with kscd!" +echo "" +echo "Bernd Johannes Wuebben" +echo "wuebben@kde.org" +echo "wuebben@math.cornell.edu" +echo "" +echo "Press [Enter] to start the compilation." +read key +exit + + + + + + diff --git a/kscd/kscd.cpp b/kscd/kscd.cpp new file mode 100644 index 00000000..b462eb2a --- /dev/null +++ b/kscd/kscd.cpp @@ -0,0 +1,1677 @@ +/* + * Kscd - A simple cd player for the KDE Project + * + * Copyright (c) 1997 Bernd Johannes wuebben@math.cornell.edu + * Copyright (c) 2002-2003 Aaron J. Seigo <aseigo@kde.org> + * Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de> + * Copyright (c) 2003-2006 Richard Lärkäng <nouseforaname@home.se> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <qdir.h> +#include <qregexp.h> +#include <qtextstream.h> +#include <qlayout.h> +#include <qhbox.h> +#include <qvbox.h> +#include <qapplication.h> +#include <qgroupbox.h> +#include <qsqlpropertymap.h> + +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kaccel.h> +#include <kaction.h> +#include <dcopref.h> +#include <kcharsets.h> +#include <kcmdlineargs.h> +#include <kconfig.h> +#include <kdebug.h> +#include <kdialogbase.h> +#include <kemailsettings.h> +#include <kglobal.h> +#include <khelpmenu.h> +#include <kkeydialog.h> +#include <kiconloader.h> +#include <kinputdialog.h> +#include <klocale.h> +#include <kmainwindow.h> +#include <kmessagebox.h> +#include <kpopupmenu.h> +#include <kprotocolmanager.h> +#include <krun.h> +#include <kstandarddirs.h> +#include <kstdaction.h> +#include <kstringhandler.h> +#include <kurl.h> +#include <kuniqueapplication.h> +#include <kglobalsettings.h> +#include <kcmoduleloader.h> +#include <kconfigdialog.h> + +#include "docking.h" +#include "kscd.h" +#include "version.h" +#include "prefs.h" + +#include <kwin.h> +#include <netwm.h> +#include <stdlib.h> + +#include <config.h> + +#include "cddbdlg.h" +#include "configWidget.h" +#include <qtextcodec.h> +#include <kcompactdisc.h> +#include <fixx11h.h> + +static const char description[] = I18N_NOOP("KDE CD player"); + +bool stoppedByUser = false; + + +/**************************************************************************** + The GUI part +*****************************************************************************/ + +KSCD::KSCD( QWidget *parent, const char *name ) + : DCOPObject("CDPlayer"), + kscdPanelDlg( parent, name, Qt::WDestructiveClose ), + configDialog(0L), + cddialog(0L), //!!!! + jumpToTrack(0L), + updateTime(true), + m_dockWidget(0) +{ + m_cd = new KCompactDisc(); + cddbInfo.clear(); // The first freedb revision is "0" //!!!! + random_current = random_list.begin(); + + cddb = new KCDDB::Client(); + connect(cddb, SIGNAL(finished(CDDB::Result)), this, SLOT(lookupCDDBDone(CDDB::Result))); + +#if defined(BUILD_CDDA) + audio_systems_list + << "arts" +#if defined(HAVE_ARTS_LIBASOUND2) + << "alsa" +#endif +#ifdef USE_SUN_AUDIO + << "sun" +#endif + ; +#endif + + readSettings(); + initFont(); + drawPanel(); + setColors(); + + // the time slider + timeIcon->setPixmap(SmallIcon("player_time")); + connect(timeSlider, SIGNAL(sliderPressed()), SLOT(timeSliderPressed())); + connect(timeSlider, SIGNAL(sliderReleased()), SLOT(timeSliderReleased())); + connect(timeSlider, SIGNAL(sliderMoved(int)), SLOT(timeSliderMoved(int))); + connect(timeSlider, SIGNAL(valueChanged(int)), SLOT(jumpToTime(int))); + + // the volume slider + volumeIcon->setPixmap(SmallIcon("player_volume")); + volumeSlider->setValue(Prefs::volume()); + QString str; + str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) ); + volumelabel->setText(str); + connect(volumeSlider, SIGNAL(valueChanged(int)), SLOT(volChanged(int))); + + /* FIXME check for return value */ + setDevicePaths(/*Prefs::cdDevice(), Prefs::audioSystem(), Prefs::audioDevice()*/); + connect(m_cd, SIGNAL(trackPlaying(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned))); + connect(m_cd, SIGNAL(trackPaused(unsigned, unsigned)), this, SLOT(trackUpdate(unsigned, unsigned))); + connect(m_cd, SIGNAL(trackChanged(unsigned, unsigned)), this, SLOT(trackChanged(unsigned, unsigned))); + connect(m_cd, SIGNAL(discStopped()), this, SLOT(discStopped())); + connect(m_cd, SIGNAL(discChanged(unsigned)), this, SLOT(discChanged(unsigned))); + connect( &queryledtimer, SIGNAL(timeout()), SLOT(togglequeryled()) ); + connect( &titlelabeltimer, SIGNAL(timeout()), SLOT(titlelabeltimeout()) ); + connect( &cycletimer, SIGNAL(timeout()), SLOT(cycletimeout()) ); + connect( &jumpTrackTimer, SIGNAL(timeout()), SLOT(jumpTracks()) ); +/* + these are always connected in base class + connect( playPB, SIGNAL(clicked()), SLOT(playClicked()) ); + connect( nextPB, SIGNAL(clicked()), SLOT(nextClicked()) ); + connect( prevPB, SIGNAL(clicked()), SLOT(prevClicked()) ); + connect( stopPB, SIGNAL(clicked()), SLOT(stopClicked()) ); + connect( ejectPB, SIGNAL(clicked()), SLOT(ejectClicked()) ); +*/ + connect( repeatPB, SIGNAL(clicked()), SLOT(loopClicked()) ); + connect( songListCB, SIGNAL(activated(int)), SLOT(trackSelected(int))); + connect( shufflePB, SIGNAL(clicked()), SLOT(randomSelected())); + connect( cddbPB, SIGNAL(clicked()), SLOT(CDDialogSelected())); + connect(kapp, SIGNAL(kdisplayPaletteChanged()), this, SLOT(setColors())); + connect(kapp, SIGNAL(iconChanged(int)), this, SLOT(setIcons())); + QToolTip::remove(songListCB); + QToolTip::add(songListCB, i18n("Track list")); + + + // set up the actions and keyboard accels + m_actions = new KActionCollection(this); + + KAction* action; + action = new KAction(i18n("Play/Pause"), Key_P, this, SLOT(playClicked()), m_actions, "Play/Pause"); + action = new KAction(i18n("Stop"), Key_S, this, SLOT(stopClicked()), m_actions, "Stop"); + action = new KAction(i18n("Previous"), Key_B, this, SLOT(prevClicked()), m_actions, "Previous"); + action = new KAction(i18n("Next"), Key_N, this, SLOT(nextClicked()), m_actions, "Next"); + action = KStdAction::quit(this, SLOT(quitClicked()), m_actions); + action = KStdAction::keyBindings(this, SLOT(configureKeys()), m_actions, "options_configure_shortcuts"); + action = KStdAction::keyBindings(this, SLOT(configureGlobalKeys()), m_actions, "options_configure_globals"); + action = KStdAction::preferences(this, SLOT(showConfig()), m_actions); + action = new KAction(i18n("Loop"), Key_L, this, SLOT(loopClicked()), m_actions, "Loop"); + action = new KAction(i18n("Eject"), CTRL + Key_E, this, SLOT(ejectClicked()), m_actions, "Eject"); + action = new KAction(i18n("Increase Volume"), Key_Plus, this, SLOT(incVolume()), m_actions, "IncVolume"); + KShortcut increaseVolume = action->shortcut(); + increaseVolume.append( KKey( Key_Equal ) ); + action->setShortcut( increaseVolume ); + action = new KAction(i18n("Decrease Volume"), Key_Minus, this, SLOT(decVolume()), m_actions, "DecVolume"); + action = new KAction(i18n("Options"), CTRL + Key_T, this, SLOT(showConfig()), m_actions, "Options"); + action = new KAction(i18n("Shuffle"), Key_R, this, SLOT(randomSelected()), m_actions, "Shuffle"); + action = new KAction(i18n("CDDB"), CTRL + Key_D, this, SLOT(CDDialogSelected()), m_actions, "CDDB"); + + m_actions->readShortcutSettings("Shortcuts"); + + m_actions->action( "options_configure_globals" )->setText( i18n( "Configure &Global Shortcuts..." ) ); + + kapp->installKDEPropertyMap(); + QSqlPropertyMap *map = QSqlPropertyMap::defaultMap(); + map->insert("KComboBox", "currentText"); + + initGlobalShortcuts(); + + setupPopups(); + + if (Prefs::looping()) + { + loopled->on(); + loopled->show(); + repeatPB->setOn(true); + } + + setDocking(Prefs::docking()); + + setFocusPolicy(QWidget::NoFocus); + + songListCB->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + adjustSize(); + setFixedHeight(this->height()); +} // KSCD + + +KSCD::~KSCD() +{ + delete cddb; + delete m_cd; +} // ~KSCD + + +void KSCD::initGlobalShortcuts() { + + m_globalAccel = new KGlobalAccel( this ); + + //Definition of global shortcuts is based on 'local' shortcuts which follow + //the WIN key. + m_globalAccel->insert("Next", i18n("Next"), 0, KKey("WIN+N"), KKey("WIN+Right"), + this, SLOT(nextClicked())); + //NOTE: WIN+B collidates with amarok's default global shortcut. + m_globalAccel->insert("Previous", i18n("Previous"), 0, KKey("WIN+B"), KKey("WIN+Left"), + this, SLOT(prevClicked())); + m_globalAccel->insert("Play/Pause", i18n("Play/Pause"), 0, KKey("WIN+P"), 0, + this, SLOT(playClicked())); + m_globalAccel->insert("Stop", i18n("Stop"), 0, KKey("WIN+S"), 0, + this, SLOT(stopClicked())); + m_globalAccel->insert("IncVolume", i18n("Increase Volume"), 0, KKey("WIN+Plus"), KKey("WIN+Up"), + this, SLOT(incVolume())); + m_globalAccel->insert("DecVolume", i18n("Decrease Volume"), 0, KKey("WIN+Minus"), KKey("WIN+Down"), + this, SLOT(decVolume())); + m_globalAccel->insert("Shuffle", i18n("Shuffle"), 0, KKey("WIN+R"), 0, + this, SLOT(incVolume())); + + m_globalAccel->setConfigGroup( "GlobalShortcuts" ); + m_globalAccel->readSettings( kapp->config() ); + m_globalAccel->updateConnections(); +} + +bool KSCD::digitalPlayback() { +#if defined(BUILD_CDDA) + return !(Prefs::audioSystem().isEmpty()); +#else + return false; +#endif +} + +void KSCD::setVolume(int v) +{ + volChanged(v); + volumeSlider->setValue(v); +} + +void KSCD::setDevice(const QString& dev) +{ + Prefs::self()->setCdDevice(dev); + setDevicePaths(); +} + +/** + * Initialize smallfont which fits into the 13 and 14 pixel widgets. + */ +void KSCD::initFont() +{ +/* int theSmallPtSize = 10; + + // Find a font that fits the 13 and 14 pixel widgets + QFont fn( KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold ); + bool fits = false; + while (!fits && theSmallPtSize > 1) + { + QFontMetrics metrics(fn); + if(metrics.height() > 13) + { + --theSmallPtSize; + fn.setPointSize(theSmallPtSize); + } else { + fits = true; + } + } + smallfont = QFont(KGlobalSettings::generalFont().family(), theSmallPtSize, QFont::Bold); +*/ +} // initFont() + +/** + * drawPanel() constructs KSCD's little black LED area + * all settings are made via panel.ui + */ +void KSCD::drawPanel() +{ + setIcons(); + adjustSize(); + + const int D = 6; + for (int u = 0; u < 5; u++) { + trackTimeLED[u] = new BW_LED_Number(frameleds); + trackTimeLED[u]->setLEDoffColor(Prefs::backColor()); + trackTimeLED[u]->setLEDColor(Prefs::ledColor(), Prefs::backColor()); + trackTimeLED[u]->setGeometry(2 + u * 18, D, 23, 30); + connect(trackTimeLED[u], SIGNAL(clicked()), this, SLOT(cycleplaytimemode())); + } + + setLEDs(-1); + + queryled = new LedLamp(symbols); + queryled->move(+10, D + 1); + queryled->off(); + queryled->hide(); + + loopled = new LedLamp(symbols, LedLamp::Loop); + loopled->move(+10, D + 18); + loopled->off(); + + totaltimelabel->hide(); +} // drawPanel + +void KSCD::setIcons() +{ + playPB->setIconSet(SmallIconSet("player_play")); + stopPB->setIconSet(SmallIconSet("player_stop")); + ejectPB->setIconSet(SmallIconSet("player_eject")); + prevPB->setIconSet(SmallIconSet("player_start")); + nextPB->setIconSet(SmallIconSet("player_end")); + cddbPB->setIconSet(SmallIconSet("view_text")); + infoPB->setIconSet(SmallIconSet("run")); +} + +void KSCD::setupPopups() +{ + QPopupMenu* mainPopup = new QPopupMenu(this); + infoPB->setPopup(mainPopup); + infoPopup = new QPopupMenu (this); + + + infoPopup->insertItem("MusicMoz", 0); + infoPopup->insertItem("Ultimate Bandlist", 1); + infoPopup->insertItem("CD Universe", 2); + infoPopup->insertSeparator(); + infoPopup->insertItem("AlltheWeb", 3); + infoPopup->insertItem("Altavista", 4); + infoPopup->insertItem("Excite", 5); + infoPopup->insertItem("Google", 6); + infoPopup->insertItem("Google Groups", 7); + infoPopup->insertItem("HotBot", 8); + infoPopup->insertItem("Lycos", 9); + infoPopup->insertItem("Open Directory", 10); + infoPopup->insertItem("Yahoo!", 11); + + m_actions->action(KStdAction::name(KStdAction::Preferences))->plug(mainPopup); + //NEW add the shortcut dialogs + m_actions->action("options_configure_globals")->plug(mainPopup); + m_actions->action("options_configure_shortcuts")->plug(mainPopup); + mainPopup->insertSeparator(); + + mainPopup->insertItem(i18n("Artist Information"), infoPopup); + + connect( infoPopup, SIGNAL(activated(int)), SLOT(information(int)) ); + + KHelpMenu* helpMenu = new KHelpMenu(this, KGlobal::instance()->aboutData(), false); + mainPopup->insertItem(SmallIcon("help"),i18n("&Help"), helpMenu->menu()); + mainPopup->insertSeparator(); + m_actions->action(KStdAction::name(KStdAction::Quit))->plug(mainPopup); +} // setupPopups + +void KSCD::playClicked() +{ + if (m_cd->discId() == KCompactDisc::missingDisc) + return; + + kapp->processEvents(); + kapp->flushX(); + + if (!m_cd->isPlaying()) + { + kapp->processEvents(); + kapp->flushX(); + + if (m_cd->isPaused()) + { + // Unpause (!!). + m_cd->pause(); + } + else + { + setLEDs(0); + resetTimeSlider(true); + + if(Prefs::randomPlay()) + { + make_random_list(); + // next clicked handles updating the play button, etc. + nextClicked(); + } + else + { + m_cd->play(0, 0, playlist.isEmpty() ? 0 : 1); + } + } + + // Update UI to allow a subsequent pause. + statuslabel->setText(i18n("Play")); + playPB->setIconSet(SmallIconSet("player_pause")); + playPB->setText(i18n("Pause")); + } + else + { + m_cd->pause(); + + // Update UI to allow a subsequent play. + statuslabel->setText(i18n("Pause")); + playPB->setIconSet(SmallIconSet("player_play")); + playPB->setText(i18n("Play")); + } + + kapp->processEvents(); + kapp->flushX(); +} // playClicked() + +void KSCD::setShuffle(int shuffle) +{ + if (shuffle == 2) { + if(Prefs::randomPlay() && m_cd->tracks() > 0) { + shufflePB->blockSignals(true); + shufflePB->setOn(true); + shufflePB->blockSignals(false); + make_random_list(); /* koz: Build a unique, once, random list */ + if(m_cd->isPlaying()) + nextClicked(); + } + + return; + } + + Prefs::setRandomPlay(shuffle); + shufflePB->blockSignals(true); + shufflePB->setOn(shuffle); + shufflePB->blockSignals(false); + + if (Prefs::randomPlay() && m_cd->tracks() > 0) { + make_random_list(); /* koz: Build a unique, once, random list */ + if(m_cd->isPlaying()) + nextClicked(); + } +} + +void KSCD::stopClicked() +{ + stoppedByUser = true; + + kapp->processEvents(); + kapp->flushX(); + m_cd->stop(); +} // stopClicked() + +void KSCD::prevClicked() +{ + int track = m_cd->track(); + + if (Prefs::randomPlay()) { + track = prev_randomtrack(); + if (track == -1) { + return; + } + } else { + if (track <= 1) { + if (Prefs::looping()) { + track = m_cd->tracks(); + } else { + return; + } + } else { + track--; + } + } + + kapp->processEvents(); + kapp->flushX(); + m_cd->play(track, 0, playlist.isEmpty() ? 0 : track); +} // prevClicked() + +bool KSCD::nextClicked() +{ + unsigned track = m_cd->track(); + + if (Prefs::randomPlay()) { + track = next_randomtrack(); + if(track == 0) { + return false; + } + } else { + if(track < 1) { + track = 1; + } else if (track >= m_cd->tracks()) { + if (Prefs::looping()) { + track = 1; + } else { + return true; + } + } else { + track++; + } + } + + kapp->processEvents(); + kapp->flushX(); + m_cd->play(track, 0, Prefs::randomPlay() || !playlist.isEmpty() ? track + 1 : 0); + return true; +} // nextClicked() + +void KSCD::trackChanged(unsigned track, unsigned trackLength) +{ + QString tooltip = artistlabel->text(); + if (track < 1) + { + setLEDs(-1); + resetTimeSlider(true); + tracklabel->setText("--/--"); + titlelabel->clear(); + } + else + { +// if (!nextClicked()) +// { +// statuslabel->setText(i18n("Disc Finished")); +// m_cd->stop(); +// } +// break; + + if (songListCB->count()) + { + songListCB->setCurrentItem(track - 1); + // drop the number. + // for Mahlah, a picky though otherwise wonderful person - AJS + QString justTheName = songListCB->currentText(); + justTheName = justTheName.right(justTheName.length() - 4); + + QToolTip::remove(songListCB); + QToolTip::add(songListCB, i18n("Current track: %1").arg(justTheName)); + } + timeSlider->blockSignals(true); + timeSlider->setRange(0, trackLength ? trackLength - 1 : 0); + timeSlider->blockSignals(false); + QString str; + str.sprintf("%02d/%02d", track, m_cd->tracks()); + tracklabel->setText(str); + + QString title = cddbInfo.trackInfoList[track-1].title; + titlelabel->setText(title); + tooltip += "/"; + tooltip += KStringHandler::rsqueeze(title, 30); + } + emit trackChanged(tooltip); +} //trackChanged(int track) + + +void KSCD::jumpToTime(int ms, bool forcePlay) +{ + kapp->processEvents(); + kapp->flushX(); + + int track = m_cd->track(); + if ((m_cd->isPlaying() || forcePlay) && + ms < (int)m_cd->trackLength()) + { + if(Prefs::randomPlay() || !playlist.isEmpty()) + { + m_cd->play(track, ms, track + 1); + } + else + { + m_cd->play(track, ms); + } + } +} // jumpToTime(int ms) + +void KSCD::timeSliderPressed() +{ + updateTime = false; +} // timeSliderPressed() + +void KSCD::timeSliderMoved(int milliseconds) +{ + setLEDs(milliseconds); +} // timeSliderMoved(int seconds) + +void KSCD::timeSliderReleased() +{ + updateTime = true; +} // timeSliderReleased() + +void KSCD::quitClicked() +{ + // ensure nothing else starts happening + queryledtimer.stop(); + titlelabeltimer.stop(); + cycletimer.stop(); + jumpTrackTimer.stop(); + + writeSettings(); + //setShuffle(0); + statuslabel->clear(); + setLEDs(-1); + + // Good GOD this is evil + kapp->processEvents(); + kapp->flushX(); + + if(Prefs::stopExit()) + m_cd->stop(); + + delete m_cd; + + kapp->quit(); +} // quitClicked() + +bool KSCD::event( QEvent *e ) +{ + return QWidget::event(e); +} // event + + +void KSCD::loopOn() +{ + Prefs::setLooping(true); + loopled->on(); + loopled->show(); + kapp->processEvents(); + kapp->flushX(); +} // loopOn; + +void KSCD::loopOff() +{ + Prefs::setLooping(false); + loopled->off(); + loopled->show(); + kapp->processEvents(); + kapp->flushX(); +} // loopOff; + +void KSCD::loopClicked() +{ + if(Prefs::looping()) + { + loopOff(); + } + else + { + loopOn(); + } +} // loopClicked + +/** + * Do everything needed if the user requested to eject the disc. + * + */ +void KSCD::ejectClicked() +{ + m_cd->eject(); +} // ejectClicked + +void KSCD::closeEvent(QCloseEvent *e) +{ + if (Prefs::docking() && !kapp->sessionSaving()) + { + hide(); + e->ignore(); + return; + } + e->accept(); +} + +void KSCD::randomSelected() +{ + setShuffle(Prefs::randomPlay()?0:1); + + /* FIXME this helps us to display "Random" in Status line + should it maybe to be replaced with symbol "RAND" or something others */ + statuslabel->setText(Prefs::randomPlay()?i18n("Random"):i18n("Play")); +} // randomSelected + +/** + * A Track was selected for playback from the drop down box. + * + */ +void KSCD::trackSelected( int cb_index ) +{ + if (cb_index < 0) + return; + + unsigned int track = cb_index + 1; + setShuffle(0); + + m_cd->play(track, 0); +} // trackSelected + +void KSCD::updateConfigDialog(configWidget* widget) +{ + if(!widget) + return; + + static QString originalTitleOfGroupBox = widget->groupBox3->title(); + if(m_cd->isPlaying()) { + widget->groupBox3->setEnabled(false); + widget->groupBox3->setTitle( i18n( "CD Drive (you must stop playing to change this)" ) ); + } else { + widget->groupBox3->setEnabled(true); + widget->groupBox3->setTitle(originalTitleOfGroupBox); + } +} + +void KSCD::showConfig() +{ + static configWidget* confWidget = 0; + + if (KConfigDialog::showDialog("settings")) { + updateConfigDialog(confWidget); + return; + } + + configDialog = new KConfigDialog(this, "settings", Prefs::self()); + + configDialog->setHelp(QString::null); + + confWidget = new configWidget(this, 0, "Kscd"); + + // kscd config page + configDialog->addPage(confWidget, i18n("CD Player"), "kscd", i18n("Settings & Behavior")); + + // libkcddb page + KService::Ptr libkcddb = KService::serviceByDesktopName("libkcddb"); + if (libkcddb && libkcddb->isValid()) + { + KCModuleInfo info(libkcddb->desktopEntryPath()); + if (info.service()->isValid()) + { + KCModule *m = KCModuleLoader::loadModule(info, KCModuleLoader::Inline); + if (m) + { + m->load(); + KCDDB::Config* cfg = new KCDDB::Config(); + cfg->readConfig(); + configDialog -> addPage(m, cfg, QString("CDDB"), "cdtrack", i18n("Configure Fetching Items")); + + connect(configDialog, SIGNAL(okClicked()), m, SLOT(save())); + connect(configDialog, SIGNAL(applyClicked()), m, SLOT(save())); + connect(configDialog, SIGNAL(defaultClicked()), m, SLOT(defaults())); + } + } + } + + updateConfigDialog(confWidget); + + connect(configDialog, SIGNAL(settingsChanged()), this, SLOT(configDone())); + configDialog -> show(); +} // showConfig() + +void KSCD::configDone() +{ + setColors(); + setDocking(Prefs::docking()); + + setDevicePaths(); + + volumeIcon->setEnabled(!Prefs::digitalPlayback()); + volumeSlider->setEnabled(!Prefs::digitalPlayback()); + + // dialog deletes itself + configDialog = 0L; +} + +void KSCD::configureKeys() +{ + KKeyDialog::configure(m_actions, this); +} + +void KSCD::configureGlobalKeys() +{ + KKeyDialog::configure(m_globalAccel, true, this, true); +} + +void KSCD::setDevicePaths() +{ + if (!m_cd->setDevice(Prefs::cdDevice(), Prefs::volume(), Prefs::digitalPlayback(), + Prefs::audioSystem(), Prefs::audioDevice())) + { + // This device did not seem usable. + QString str = i18n("CD-ROM read or access error (or no audio disc in drive).\n"\ + "Please make sure you have access permissions to:\n%1").arg( + KCompactDisc::urlToDevice(Prefs::cdDevice())); + KMessageBox::error(this, str, i18n("Error")); + } +} // setDevicePath() + +void KSCD::setDocking(bool dock) +{ + Prefs::setDocking(dock); + if (Prefs::docking()) + { + if (!m_dockWidget) + { + m_dockWidget = new DockWidget(this, "dockw"); + connect(m_dockWidget, SIGNAL(quitSelected()), this, SLOT(quitClicked())); + } + + m_dockWidget->show(); + connect(this, SIGNAL(trackChanged(const QString&)), + m_dockWidget, SLOT(setToolTip(const QString&))); + connect(this, SIGNAL(trackChanged(const QString&)), + m_dockWidget, SLOT(createPopup(const QString&))); + } + else + { + show(); + delete m_dockWidget; + m_dockWidget = 0; + } +} + +void KSCD::incVolume() +{ + int v = Prefs::volume() + 5; + + if (v > 100) + { + v = 100; + } + + volChanged(v); + volumeSlider->setValue(v); +} // incVolume + +void KSCD::decVolume() +{ + int v = Prefs::volume() - 5; + + if (v < 0) + { + v = 0; + } + + volChanged(v); + volumeSlider->setValue(v); +} // decVolume + +void KSCD::volChanged( int vol ) +{ + QString str; + str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), vol) ); + volumelabel->setText(str); + m_cd->setVolume(vol); + Prefs::setVolume(vol); +} // volChanged + +void KSCD::make_random_list() +{ + /* koz: 15/01/00. I want a random list that does not repeat tracks. Ie, */ + /* a list is created in which each track is listed only once. The tracks */ + /* are picked off one by one until the end of the list */ + + int selected = 0; + bool rejected = false; + + //kdDebug(67000) << "Playlist has " << size << " entries\n" << endl; + random_list.clear(); + for(unsigned i = 0; i < m_cd->tracks(); i++) + { + do { + selected = 1 + (int) randSequence.getLong(m_cd->tracks()); + rejected = (random_list.find(selected) != random_list.end()); + } while(rejected == true); + random_list.append(selected); + } +/* + printf("debug: dump random list\n"); + RandomList::iterator it; + for(it = random_list.begin(); it != random_list.end(); it++) { + printf("%i ", *it); + } + printf("\n"); +*/ + random_current = random_list.end(); +} // make_random_list() + +int KSCD::next_randomtrack() +{ + /* Check to see if we are at invalid state */ + if(random_current == random_list.end()) + { + random_current = random_list.begin(); + } + else if(random_current == random_list.fromLast()) + { + if(!Prefs::looping()) + { + m_cd->stop(); + return 0; + } + else + { + // playing the same random list isn't very random, is it? + make_random_list(); + return next_randomtrack(); + } + } + else + { + ++random_current; + } + + return *random_current; +} // next_randomtrack + +int KSCD::prev_randomtrack() +{ + /* Check to see if we are at invalid state */ + if(random_current == random_list.end()) + { + random_current = random_list.fromLast(); + } + else if(random_current == random_list.begin()) + { + if(!Prefs::looping()) + { + return -1; + } + else + { + // playing the same random list isn't very random, is it? + make_random_list(); + return prev_randomtrack(); + } + } + else + { + --random_current; + } + + return *random_current; +} // prev_randomtrack + +void KSCD::discChanged(unsigned discId) +{ + cddbInfo.clear(); + if (discId == KCompactDisc::missingDisc) + { + statuslabel->setText(i18n("No disc")); + } + else + { + cddbInfo.id = QString::number(discId, 16).rightJustify(8,'0'); + cddbInfo.length = m_cd->discLength() / 1000; + cddbInfo.artist = m_cd->discArtist(); + cddbInfo.title = m_cd->discTitle(); + + // If it's a sampler, we'll do artist/title. + bool isSampler = (cddbInfo.title.compare("Various") == 0); + KCDDB::TrackInfo track; + for (unsigned i = 1; i <= m_cd->tracks(); i++) + { + if (isSampler) + { + track.title = m_cd->trackArtist(i); + track.title.append("/"); + track.title.append(m_cd->trackTitle(i)); + } + else + { + track.title = m_cd->trackTitle(i); + } + + // FIXME: KDE4 + // track.length = cd->trk[i - 1].length; + cddbInfo.trackInfoList.append(track); + } + } + + // Set the total time. + QTime dml; + dml = dml.addSecs(m_cd->discLength() / 1000); + + QString fmt; + if(dml.hour() > 0) + fmt.sprintf("%02d:%02d:%02d",dml.hour(),dml.minute(),dml.second()); + else + fmt.sprintf("%02d:%02d",dml.minute(),dml.second()); + totaltimelabel->setText(fmt); + + trackChanged(0, 0); + populateSongList(""); + //totaltimelabel->clear(); + totaltimelabel->lower(); + + if ((Prefs::autoplay() || KCmdLineArgs::parsedArgs()->isSet("start")) + && !m_cd->isPlaying()) + { + playClicked(); + } + + // We just populated the GUI with what we got from the CD. Now look for + // more from the Internet... + lookupCDDB(); +} + +void KSCD::discStopped() +{ + if (Prefs::ejectOnFinish() && !stoppedByUser && (m_cd->discId() != KCompactDisc::missingDisc)) + { + ejectClicked(); + } + + if (!stoppedByUser) + { + if (Prefs::randomPlay()) + { + // If nextClicked returns false, it was the last + // random track, and the player should be stopped + if (nextClicked()) + return; + } + else if (Prefs::looping()) + { + playClicked(); + return; + } + } + + statuslabel->setText(i18n("Stopped")); + playPB->setText(i18n("Play")); + playPB->setIconSet(SmallIconSet("player_play")); + + /* reset to initial value, only stopclicked() sets this to true */ + stoppedByUser = false; + + trackChanged(0, 0); + populateSongList(""); + totaltimelabel->clear(); + totaltimelabel->lower(); +} + +void KSCD::setLEDs(int milliseconds) +{ + QString symbols; + + if (milliseconds < 0) + { + symbols = "--:--"; + } + else + { + unsigned mymin; + unsigned mysec; + mymin = milliseconds / 60000; + mysec = (milliseconds % 60000) / 1000; + symbols.sprintf("%02d:%02d", mymin, mysec); + } + + for (int i = 0; i < 5; i++) + { + trackTimeLED[i]->display((char)symbols.local8Bit().at(i)); + } +} + +void KSCD::resetTimeSlider(bool enabled) +{ + timeSlider->setEnabled(enabled); + timeSlider->blockSignals(true); + timeSlider->setValue(0); + timeSlider->blockSignals(false); +} // resetTimeSlider(bool enabled); + +void KSCD::setColors() +{ + QColor led_color = Prefs::ledColor(); + QColor background_color = Prefs::backColor(); + + backdrop->setBackgroundColor(background_color); + + QColorGroup colgrp( led_color, background_color, led_color,led_color , led_color, + led_color, white ); + + titlelabel ->setPalette( QPalette(colgrp,colgrp,colgrp) ); + artistlabel->setPalette( QPalette(colgrp,colgrp,colgrp) ); + volumelabel->setPalette( QPalette(colgrp,colgrp,colgrp) ); + statuslabel->setPalette( QPalette(colgrp,colgrp,colgrp) ); + tracklabel ->setPalette( QPalette(colgrp,colgrp,colgrp) ); + totaltimelabel->setPalette( QPalette(colgrp,colgrp,colgrp) ); + + queryled->setPalette( QPalette(colgrp,colgrp,colgrp) ); + loopled->setPalette( QPalette(colgrp,colgrp,colgrp) ); + + for (int u = 0; u< 5;u++){ + trackTimeLED[u]->setLEDoffColor(background_color); + trackTimeLED[u]->setLEDColor(led_color,background_color); + } + + titlelabel ->setFont( Prefs::ledFont() ); + artistlabel->setFont( Prefs::ledFont() ); + volumelabel->setFont( Prefs::ledFont() ); + statuslabel->setFont( Prefs::ledFont() ); + tracklabel ->setFont( Prefs::ledFont() ); + totaltimelabel->setFont( Prefs::ledFont() ); +} + +void KSCD::readSettings() +{ +/* + time_display_mode = config->readNumEntry("TimeDisplay", TRACK_SEC); +*/ + +#ifndef DEFAULT_CD_DEVICE +#define DEFAULT_CD_DEVICE "/dev/cdrom" + // sun ultrix etc have a canonical cd rom device specified in the + // respective plat_xxx.c file. On those platforms you need nnot + // specify the cd rom device and DEFAULT_CD_DEVICE is not defined + // in config.h +#endif + + if (Prefs::cdDevice().isEmpty()) + { + Prefs::setCdDevice(DEFAULT_CD_DEVICE); + } + + volumeIcon->setEnabled(!Prefs::digitalPlayback()); + volumeSlider->setEnabled(!Prefs::digitalPlayback()); +} + +/** + * Write KSCD's Configuration into the kderc file. + * + */ +void KSCD::writeSettings() +{ + Prefs::writeConfig(); +} // writeSettings() + +void KSCD::CDDialogSelected() +{ + if (!cddialog) + { + cddialog = new CDDBDlg(this); + + cddialog->setData(cddbInfo, m_cd->discSignature(), playlist); + connect(cddialog,SIGNAL(cddbQuery()),SLOT(lookupCDDB())); + connect(cddialog,SIGNAL(newCDInfoStored(KCDDB::CDInfo)), + SLOT(setCDInfo(KCDDB::CDInfo))); + connect(cddialog,SIGNAL(finished()),SLOT(CDDialogDone())); + connect(cddialog,SIGNAL(play(int)),SLOT(trackSelected(int))); + } + + cddialog->show(); + cddialog->raise(); +} + +void KSCD::CDDialogDone() +{ + cddialog->delayedDestruct(); + cddialog = 0L; +} + +void KSCD::lookupCDDB() +{ + if (m_cd->discId() == KCompactDisc::missingDisc) + return; + kdDebug(67000) << "lookupCDDB() called" << endl; + + populateSongList(i18n("Start freedb lookup.")); + + setShuffle(2); + + led_on(); + + cddb->config().reparse(); + cddb->setBlockingMode(false); + cddb->lookup(m_cd->discSignature()); +} // lookupCDDB + +void KSCD::lookupCDDBDone(CDDB::Result result) +{ + led_off(); + if ((result != KCDDB::CDDB::Success) && + (result != KCDDB::CDDB::MultipleRecordFound)) + { + populateSongList(result == CDDB::NoRecordFound ? i18n("No matching freedb entry found.") : i18n("Error getting freedb entry.")); + return; + } + + // The intent of the original code here seems to have been to perform the + // lookup, and then to convert all the string data within the CDDB response + // using the use Prefs::selectedEncoding() and a QTextCodec. However, that + // seems to be irrelevant these days. + KCDDB::CDInfo info = cddb->bestLookupResponse(); + // TODO Why doesn't libcddb not return MultipleRecordFound? + //if( result == KCDDB::CDDB::MultipleRecordFound ) { + if( cddb->lookupResponse().count() > 1 ) { + CDInfoList cddb_info = cddb->lookupResponse(); + CDInfoList::iterator it; + QStringList list; + for ( it = cddb_info.begin(); it != cddb_info.end(); ++it ) { + list.append( QString("%1, %2, %3").arg((*it).artist).arg((*it).title) + .arg((*it).genre)); + } + + bool ok(false); + QString res = KInputDialog::getItem( + i18n("Select CDDB Entry"), + i18n("Select a CDDB entry:"), list, 0, false, &ok, + this ); + if ( ok ) { + // The user selected and item and pressed OK + uint c = 0; + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + if( *it == res) break; + c++; + } + if( c < cddb_info.size() ) + info = cddb_info[c]; + } else { + return; + // user pressed Cancel + } + } + + setCDInfo(info); + + // In case the cddb dialog is open, update it + if (cddialog) + cddialog->setData(cddbInfo, m_cd->discSignature(), playlist); +} // lookupCDDBDone + +void KSCD::setCDInfo(KCDDB::CDInfo info) +{ + // Some sanity provisions to ensure that the number of records matches what + // the CD actually contains. + while (info.trackInfoList.count() < cddbInfo.trackInfoList.count()) + { + info.trackInfoList.append(KCDDB::TrackInfo()); + } + while (info.trackInfoList.count() > cddbInfo.trackInfoList.count()) + { + info.trackInfoList.pop_back(); + } + cddbInfo = info; + populateSongList(""); +} + +void KSCD::led_off() +{ + queryledtimer.stop(); + queryled->off(); + queryled->hide(); + totaltimelabel->raise(); + totaltimelabel->show(); +} // led_off + +void KSCD::led_on() +{ + totaltimelabel->hide(); + totaltimelabel->lower(); + queryledtimer.start(800); + queryled->off(); + queryled->show(); + kapp->processEvents(); + kapp->flushX(); +} // led_on + +void KSCD::togglequeryled() +{ + queryled->show(); + queryled->toggle(); +} // togglequeryled + +void KSCD::titlelabeltimeout() +{ + // clear the cddb error message on the title label. + titlelabeltimer.stop(); + titlelabel->clear(); + +} // titlelabeltimeout + +void KSCD::trayOpening() +{ + statuslabel->setText(i18n("Ejected")); + trackChanged(0, 0); +} + +int KSCD::currentTrack() +{ + return m_cd->track(); +} + +int KSCD::currentTrackLength() +{ + return m_cd->trackLength(); +} + +int KSCD::currentPosition() +{ + return m_cd->trackPosition(); +} + +int KSCD::getStatus() +{ + if (m_cd->isPlaying()) + return 2; + else if (m_cd->isPaused()) + return 4; + else if (m_cd->discId() != KCompactDisc::missingDisc) + return 5; + else + return 6; +} + +bool KSCD::playing() +{ + return m_cd->isPlaying(); +} + +void KSCD::trackUpdate(unsigned /*track*/, unsigned trackPosition) +{ + unsigned tmp; + + switch (Prefs::timeDisplayMode()) + { + case TRACK_REM: + tmp = m_cd->trackLength() - trackPosition; + break; + + case TOTAL_SEC: + tmp = m_cd->discPosition(); + break; + + case TOTAL_REM: + tmp = m_cd->discLength() - m_cd->discPosition(); + break; + + case TRACK_SEC: + default: + tmp = trackPosition; + break; + } + if (updateTime) + { + setLEDs(tmp); + timeSlider->blockSignals(true); + timeSlider->setValue(trackPosition); + timeSlider->blockSignals(false); + } +} + +void KSCD::cycleplaytimemode() +{ + cycletimer.stop(); + + if (Prefs::timeDisplayMode() > 2) { + Prefs::setTimeDisplayMode(TRACK_SEC); + } else { + Prefs::setTimeDisplayMode(Prefs::timeDisplayMode() + 1); + } + + switch(Prefs::timeDisplayMode()) { + + case TRACK_REM: + volumelabel->setText(i18n("Tra Rem")); + break; + + case TOTAL_SEC: + volumelabel->setText(i18n("Tot Sec")); + break; + + case TOTAL_REM: + volumelabel->setText(i18n("Tot Rem")); + break; + + case TRACK_SEC: + default: + volumelabel->setText(i18n("Tra Sec")); + break; + } + + cycletimer.start(3000, true); +} // cycleplaymode + +void KSCD::cycletimeout() +{ + cycletimer.stop(); + QString str; + str = QString::fromUtf8( QCString().sprintf(i18n("Vol: %02d%%").utf8(), Prefs::volume()) ); + volumelabel->setText(str); +} // cycletimeout + + +void KSCD::information(int i) +{ + //kdDebug(67000) << "Information " << i << "\n" << endl; + + if(cddbInfo.artist.isEmpty()) + return; + + QString encodedArtist = KURL::encode_string_no_slash(cddbInfo.artist); + + QString str; + + switch(i) + { + case 0: + str = QString("http://musicmoz.org/cgi-bin/ext.cgi?artist=%1") + .arg(encodedArtist); + break; + + case 1: + str = QString("http://ubl.artistdirect.com/cgi-bin/gx.cgi/AppLogic+Search?select=MusicArtist&searchstr=%1&searchtype=NormalSearch") + .arg(encodedArtist); + break; + + case 2: + str = QString("http://www.cduniverse.com/cgi-bin/cdubin.exe/rlinka/ean=%1") + .arg(encodedArtist); + break; + + case 3: + str = QString("http://www.alltheweb.com/search?cat=web&q=%1") + .arg(encodedArtist); + break; + + case 4: + str = QString("http://altavista.com/web/results?q=%1&kgs=0&kls=1&avkw=xytx") + .arg(encodedArtist); + break; + + case 5: + str = QString("http://msxml.excite.com/_1_2UDOUB70SVHVHR__info.xcite/dog/results?otmpl=dog/webresults.htm&qkw=%1&qcat=web&qk=20&top=1&start=&ver=14060") + .arg(encodedArtist); + break; + + case 6: + str = QString("http://www.google.com/search?q=%1") + .arg(encodedArtist); + break; + + case 7: + str = QString("http://groups.google.com/groups?oi=djq&as_q=%1&num=20") + .arg(encodedArtist); + break; + + case 8: + str = QString("http://www.hotbot.com/default.asp?prov=Inktomi&query=%1&ps=&loc=searchbox&tab=web") + .arg(encodedArtist); + break; + + case 9: + str = QString("http://search.lycos.com/default.asp?lpv=1&loc=searchhp&tab=web&query=%1") + .arg(encodedArtist); + break; + + case 10: + str = QString("http://search.dmoz.org/cgi-bin/search?search=%1") + .arg(encodedArtist); + break; + + case 11: + str = QString("http://search.yahoo.com/bin/search?p=%1") + .arg(encodedArtist); + break; + + default: + return; + break; + } // switch() + + KRun::runURL(KURL( str ), "text/html"); +} // information + +/** + * Save state on session termination + */ +bool KSCD::saveState(QSessionManager& /*sm*/) +{ + writeSettings(); + KConfig* config = KApplication::kApplication()->sessionConfig(); + config->setGroup("General"); + config->writeEntry("Show", isVisible()); + return true; +} // saveState + + +/** + * Allow the user to type in the number of the track + */ +void KSCD::keyPressEvent(QKeyEvent* e) +{ + bool isNum; + int value = e->text().toInt(&isNum); + + if (e->key() == Qt::Key_F1) + { + kapp->invokeHelp(); + } + else if (isNum) + { + value = (jumpToTrack * 10) + value; + + if (value <= (int)cddbInfo.trackInfoList.count()) + { + jumpToTrack = value; + jumpTrackTimer.stop(); + jumpTrackTimer.start(333); + } + } + else + { + QWidget::keyPressEvent(e); + } +} //keyPressEvent + +void KSCD::jumpTracks() +{ + if (jumpToTrack > 0 && jumpToTrack <= (int)cddbInfo.trackInfoList.count()) + { + m_cd->play(jumpToTrack, 0, jumpToTrack + 1); + } + + jumpToTrack = 0; +} // jumpTracks + +QString KSCD::currentTrackTitle() +{ + int track = m_cd->track(); + return (track > -1) ? cddbInfo.trackInfoList[track-1].title : QString::null; +} + +QString KSCD::currentAlbum() +{ + return cddbInfo.title; +} + +QString KSCD::currentArtist() +{ + return cddbInfo.artist; +} + +QStringList KSCD::trackList() +{ + QStringList tracks; + for (TrackInfoList::const_iterator it = cddbInfo.trackInfoList.begin(); + it != cddbInfo.trackInfoList.end(); ++it) + tracks << (*it).title; + + return tracks; +} + +void KSCD::populateSongList(QString infoStatus) +{ + // set the artist and title labels as well as the dock tooltip. + if (!infoStatus.isEmpty()) + artistlabel->setText(infoStatus); + else + artistlabel->setText(QString("%1 - %2").arg(cddbInfo.artist, cddbInfo.title)); + + songListCB->clear(); + for (unsigned i = 0; i < cddbInfo.trackInfoList.count(); i++) + { + unsigned tmp = m_cd->trackLength(i + 1); + unsigned mymin; + unsigned mysec; + mymin = tmp / 60000; + mysec = (tmp % 60000) / 1000; + QString str1; + str1.sprintf("%02d: ", i + 1); + QString str2; + str2.sprintf(" (%02d:%02d) ", mymin, mysec); + str1.append(cddbInfo.trackInfoList[i].title); + str1.append(str2); + songListCB->insertItem(str1); + } + + emit trackChanged(m_cd->track(), m_cd->trackLength()); +} + +static const KCmdLineOptions options[] = +{ + {"s",0,0}, + {"start",I18N_NOOP("Start playing"),0}, + {"+[device]",I18N_NOOP("CD device, can be a path or a media:/ URL"),0}, + KCmdLineLastOption +}; + + +/** + * main() + */ +int main( int argc, char *argv[] ) +{ + KAboutData aboutData("kscd", I18N_NOOP("KsCD"), + KSCDVERSION, description, + KAboutData::License_GPL, + "(c) 2001, Dirk Försterling\n(c) 2003, Aaron J. Seigo"); + aboutData.addAuthor("Aaron J. Seigo", I18N_NOOP("Current maintainer"), "aseigo@kde.org"); + aboutData.addAuthor("Alexander Kern",I18N_NOOP("Workman library update, CDTEXT, CDDA"), "kernalex@kde.org"); + aboutData.addAuthor("Bernd Johannes Wuebben",0, "wuebben@kde.org"); + aboutData.addAuthor("Dirk Försterling", I18N_NOOP("Workman library, previous maintainer"), "milliByte@gmx.net"); + aboutData.addCredit("Wilfried Huss", I18N_NOOP("Patches galore")); + aboutData.addCredit("Steven Grimm", I18N_NOOP("Workman library")); + aboutData.addCredit("Sven Lueppken", I18N_NOOP("UI Work")); + aboutData.addCredit("freedb.org", I18N_NOOP("Special thanks to freedb.org for providing a free CDDB-like CD database"), 0, "http://freedb.org"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + KCmdLineArgs::addCmdLineOptions(options); + KUniqueApplication::addCmdLineOptions(); + + KCmdLineArgs* args = KCmdLineArgs::parsedArgs(); + if (!KUniqueApplication::start()) + { + fprintf(stderr, "kscd is already running\n"); + if (args->count()>0 || args->isSet("start")) + { + DCOPClient client; + if (client.attach()) + { + // Forward the command line args to the running instance. + DCOPRef ref("kscd", "CDPlayer"); + if (args->count() > 0) + { + ref.send("setDevice(QString)", QString(args->arg(0))); + } + if (args->isSet("start")) + { + ref.send("play()"); + } + } + } + exit(0); + } + + KUniqueApplication a; + + kapp->dcopClient()->setDefaultObject("CDPlayer"); + + KSCD *k = new KSCD(); + + a.setTopWidget( k ); + a.setMainWidget( k ); + + k->setCaption(a.caption()); + + if (kapp->isRestored()) + { + KConfig* config = KApplication::kApplication()->sessionConfig(); + config->setGroup("General"); + if (config->readBoolEntry("Show")) + k->show(); + } + else + { + k->show(); + } + + if (args->count()>0) Prefs::self()->setCdDevice(args->arg(0)); + + return a.exec(); +} + +#include "kscd.moc" diff --git a/kscd/kscd.desktop b/kscd/kscd.desktop new file mode 100644 index 00000000..cf0535e3 --- /dev/null +++ b/kscd/kscd.desktop @@ -0,0 +1,85 @@ +[Desktop Entry] +GenericName=CD Player +GenericName[af]=Cd Speler +GenericName[ar]=مشغل الأقراص المدمجة +GenericName[bg]=Плеър за аудио дискове +GenericName[bn]=সিডি প্লেয়ার +GenericName[br]=Ur c'hoarier CD +GenericName[ca]=Reproductor de CD +GenericName[cs]=CD přehrávač +GenericName[cy]=Chwaraewr CD +GenericName[da]=Cd-afspiller +GenericName[de]=CD-Wiedergabe +GenericName[el]=Αναπαραγωγέας CD +GenericName[eo]=Ludilo por muzikaj lumdiskoj +GenericName[es]=Reproductor de CDs +GenericName[et]=CD mängija +GenericName[eu]=CD erreproduzigailua +GenericName[fa]=پخشکنندۀ دیسک فشرده +GenericName[fi]=CD-soitin +GenericName[fr]=Lecteur de CD audio +GenericName[ga]=Seinnteoir Dlúthdhioscaí +GenericName[gl]=Reproductor de CD +GenericName[he]=נגן תקליטורים +GenericName[hi]=सीडी प्लेयर +GenericName[hu]=CD-lejátszó +GenericName[is]=CD spilari +GenericName[it]=Lettore CD +GenericName[ja]=CD プレーヤ +GenericName[kk]=CD ойнатқышы +GenericName[km]=កម្មវិធីចាក់ស៊ីឌី +GenericName[ko]=CD 재생기 +GenericName[lt]=CD grotuvas +GenericName[lv]=CD Atskaņotājs +GenericName[mk]=CD плеер +GenericName[ms]=Pemain CD +GenericName[nb]=CD-spiller +GenericName[nds]=CD-Afspeler +GenericName[ne]=सीडी प्लेयर +GenericName[nl]=CD-speler +GenericName[nn]=CD-spelar +GenericName[pa]=CD ਪਲੇਅਰ +GenericName[pl]=Odtwarzacz CD +GenericName[pt]=Leitor de CDs +GenericName[pt_BR]=Reprodutor de CDs +GenericName[ro]=Redare CD audio +GenericName[ru]=Проигрыватель аудиодисков +GenericName[se]=CD-čuojaheaddji +GenericName[sk]=CD prehrávač +GenericName[sl]=Predvajalnik CD-jev +GenericName[sr]=CD плејер +GenericName[sr@Latn]=CD plejer +GenericName[sv]=Cd-spelare +GenericName[ta]=குறுந்தகடு இயக்கி +GenericName[tg]=Бозингари Диски Фишурда +GenericName[th]=โปรแกรมเล่นซีดี +GenericName[tr]=CD Çalar +GenericName[uk]=Програвач КД +GenericName[uz]=Kompakt-disk pleyer +GenericName[uz@cyrillic]=Компакт-диск плейер +GenericName[ven]=Tshitambi tsha CD +GenericName[wa]=Djoweu di plakes lazer +GenericName[xh]=Umdlali We CD +GenericName[zh_CN]=CD 播放器 +GenericName[zh_HK]=CD 播放器 +GenericName[zh_TW]=CD 播放器 +GenericName[zu]=Umdlali we CD +Name=KsCD +Name[af]=Kscd +Name[bn]=কে-এস-সিডি +Name[eo]=Lumdiskludilo +Name[hi]=के-एससीडी +Name[sv]=Kscd +Name[ta]=Ksகுறுந்தகடு +Name[tg]=KsДиски Фишурда +Name[tr]=Kscd +Name[zh_TW]=KsCD 播放器 +Exec=kscd -caption "%c" %i %m +DocPath=kscd/index.html +Icon=kscd +Path= +Type=Application +Terminal=false +X-KDE-StartupNotify=true +X-DCOP-ServiceType=Multi +Categories=Qt;KDE;AudioVideo; diff --git a/kscd/kscd.h b/kscd/kscd.h new file mode 100644 index 00000000..6c94c09d --- /dev/null +++ b/kscd/kscd.h @@ -0,0 +1,258 @@ +/* + Kscd - A simple cd player for the KDE Project + + $Id$ + + Copyright (c) 1997 Bernd Johannes Wuebben math.cornell.edu + Copyright (c) 2002 Aaron J. Seigo <aseigo@kde.org> + Copyright (c) 2004 Alexander Kern <alex.kern@gmx.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __KSCD__ +#define __KSCD__ + +#include "bwlednum.h" + +// CD support. +class KCompactDisc; + +// CDDB support via libkcddb +#include <libkcddb/cddb.h> +#include <libkcddb/client.h> + + +#include <qpushbutton.h> +#include <qlabel.h> +#include <qdialog.h> +#include <qapplication.h> +#include <qtimer.h> +#include <qcombobox.h> +#include <qscrollbar.h> +#include <qslider.h> +#include <qtabdialog.h> +#include <qtooltip.h> +#include <qpopupmenu.h> +#include <qvaluelist.h> + +#include "ledlamp.h" +#include "panel.h" +#include "prefs.h" +#include "configWidget.h" +#include <kapplication.h> +#include <kconfigdialog.h> +#include <kprocess.h> +#include <krandomsequence.h> +#include <dcopobject.h> +#include <kglobalaccel.h> + +class CDDBDlg; +class DockWidget; +class QGridLayout; +class KActionCollection; +class KToggleAction; + +using namespace KCDDB; + +typedef QValueList<int> RandomList; + +class KSCD : public kscdPanelDlg, public KSessionManaged, virtual public DCOPObject { + + Q_OBJECT + K_DCOP + + // time display modes + enum time_display { TRACK_SEC = 0, TRACK_REM = 1, TOTAL_SEC = 2, TOTAL_REM = 3 }; + + +k_dcop: + bool playing(); + void play() { playClicked(); } + void stop() { stopClicked(); } + void previous() { prevClicked(); } + void next() { nextClicked(); } + void jumpTo(int seconds) { jumpToTime(seconds); } + void eject() { ejectClicked(); } + void quit() { quitClicked(); } + void toggleLoop() { loopClicked(); } + void toggleShuffle() { randomSelected(); } + void toggleTimeDisplay() { cycleplaytimemode(); } + void cddbDialog() { CDDialogSelected(); } + void optionDialog() { showConfig(); } + void setTrack(int t) { trackSelected(t > 0 ? t - 1 : 0); } + void volumeDown() { decVolume(); } + void volumeUp() { incVolume(); } + void setVolume(int v); + void setDevice(const QString& dev); + int getVolume() { return Prefs::volume(); } + int currentTrack(); + int currentTrackLength(); + int currentPosition(); + int getStatus(); + QString currentTrackTitle(); + QString currentAlbum(); + QString currentArtist(); + QStringList trackList(); + +public: + KSCD( QWidget *parent = 0, const char *name = 0 ); + ~KSCD(); + virtual bool saveState(QSessionManager& sm); + + void setDocking(bool dock); + bool digitalPlayback(); + void setDevicePaths(); + QStringList audioSystems() { return audio_systems_list; } + + KActionCollection* actionCollection() { return m_actions; } + +signals: + void trackChanged(const QString&); + +public slots: + void setColors(); + void togglequeryled(); + void randomSelected(); + void setShuffle(int shuffle); /* 0 -off, 1 - on, 2 - remake random list */ + void writeSettings(); + void playClicked(); + bool nextClicked(); + void prevClicked(); + void stopClicked(); + void ejectClicked(); + void jumpToTime(int seconds, bool forcePlay = false); + void quitClicked(); + void loopOn(); + void loopOff(); + void loopClicked(); + void trackSelected(int); + void showConfig(); + void incVolume(); + void decVolume(); + void volChanged(int); + void led_on(); + void led_off(); + void titlelabeltimeout(); + void cycleplaytimemode(); + void cycletimeout(); + + void information(int); + void jumpTracks(); + + void make_random_list(); /* koz: 15/01/00 */ + +protected: + // mostly start up stuff + void readSettings(); + void initFont(); + void drawPanel(); + void setupPopups(); + void setLEDs(int milliseconds); + void resetTimeSlider(bool enabled); + + void dragTime(int sec); + + void closeEvent(QCloseEvent *e); + void keyPressEvent( QKeyEvent* e ); + bool event( QEvent *e ); + // void focusOutEvent(QFocusEvent *e); + void playtime(); + void playtime(int seconds); + void calculateDisplayedTime(); + void calculateDisplayedTime(int sec); + void setSongListTo(int whichTrack); + void populateSongList(QString infoStatus); + void updatePlayPB(bool playing); + + void updateConfigDialog(configWidget* widget); + +private: + KConfigDialog *configDialog; + CDDBDlg *cddialog; + QPopupMenu *mainPopup; + QPopupMenu *infoPopup; + + BW_LED_Number *trackTimeLED[6]; + + KCompactDisc *m_cd; + QTimer titlelabeltimer; + QTimer queryledtimer; + QTimer cycletimer; + QTimer jumpTrackTimer; + + // random playlists + KRandomSequence randSequence; + RandomList random_list; + RandomList::iterator random_current; + + + int jumpToTrack; + LedLamp *queryled; + LedLamp *loopled; + bool randomplay_pending; + bool updateTime; + QStringList audio_systems_list; + + /** + * select a random track from the current disc. + * + */ + int next_randomtrack(); + int prev_randomtrack(); + int real_randomtrack(); + + void setTitle(int track); + + /** + * Info from CDDB, and exploded versions thereof. + */ + KCDDB::CDInfo cddbInfo; + QStringList playlist; + KCDDB::Client* cddb; + KActionCollection* m_actions; + KGlobalAccel* m_globalAccel; + KToggleAction* m_togglePopupsAction; + DockWidget* m_dockWidget; + void lookupDevice(); + void initGlobalShortcuts(); +public slots: + void lookupCDDB(); + +private slots: + void CDDialogSelected(); + void CDDialogDone(); + void setCDInfo(KCDDB::CDInfo); + void lookupCDDBDone(CDDB::Result); + void discStopped(); + void trackUpdate(unsigned track, unsigned trackPosition); + void trackChanged(unsigned track, unsigned trackLength); + void discChanged(unsigned discId); +// void trayClosing(); + void trayOpening(); + void configDone(); + void configureKeys(); + void configureGlobalKeys(); + void setIcons(); + + void timeSliderPressed(); + void timeSliderReleased(); + void timeSliderMoved(int milliseconds); +}; + + + +#endif + diff --git a/kscd/kscd.kcfg b/kscd/kscd.kcfg new file mode 100644 index 00000000..62a04984 --- /dev/null +++ b/kscd/kscd.kcfg @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="kscdrc"/> + <group name="GENERAL"> + <entry name="Volume" type="UInt"> + <label>Output Volume</label> + <default>40</default> + </entry> + <entry name="RandomPlay" type="Bool"> + <label>Play random tracks.</label> + <whatsthis>When this option is selected the playing order + of the CD tracks is chosen at random.</whatsthis> + <default>false</default> + </entry> + <entry name="Docking" type="Bool" key="DOCKING"> + <label>Show an icon in the system tray.</label> + <whatsthis>When this option is selected an icon will appear in the system tray. Note that KsCD will <i>not</i> quit when the window is closed if a system tray icon is displayed. You may quit KsCD by clicking the Quit button or right-clicking on the system tray icon and selecting the appropriate entry.</whatsthis> + <default>true</default> + </entry> + <entry name="TrackAnnouncement" type="Bool"> + <default>false</default> + </entry> + <entry name="Autoplay" type="Bool" key="AUTOPLAY"> + <label>Start playing when a CD is inserted.</label> + <whatsthis>When this option is selected the CD will start playing automatically upon being inserted into the CD-ROM.</whatsthis> + <default>false</default> + </entry> + <entry name="StopExit" type="Bool" key="STOPEXIT"> + <label>Stop playing the CD on program exit.</label> + <whatsthis>When this option is selected the CD will automatically stop playing when quitting KsCD.</whatsthis> + <default>true</default> + </entry> + <entry name="EjectOnFinish" type="Bool" key="EJECTONFINISH"> + <label>Eject CD when playing is finished.</label> + <whatsthis>When this option is selected the CD will automatically eject when it is finished.</whatsthis> + <default>false</default> + </entry> + <entry name="Looping" type="Bool"> + <label>Loop tracks.</label> + <default>false</default> + </entry> + <entry name="SkipDelta" type="UInt"> + <label>Skip interval.</label> + <whatsthis>This option controls the number of seconds KsCD will skip when the skip forwards or backwards buttons are pressed.</whatsthis> + <default>30</default> + </entry> + <entry name="TimeDisplayMode" type="Enum" key="TimeDisplay"> + <choices> + <choice name="TRACK_SEC"/> + <choice name="TRACK_REM"/> + <choice name="TOTAL_SEC"/> + <choice name="TOTAL_REM"/> + </choices> + <default>TRACK_SEC</default> + </entry> + <entry name="cdDevice" type="String" key="CDDevice"> + <label>Name of the CD-ROM device.</label> + <whatsthis>The CD-ROM device to use when playing CDs. This will typically look something like "/dev/cdrom". To have KsCD autodetect your CD-ROM, leave this field empty.</whatsthis> + </entry> + <entry name="AudioSystem" type="String"> + <label>The audio backend KsCD uses.</label> + </entry> + <entry name="AudioDevice" type="String"> + <label>The audio device KsCD uses.</label> + </entry> + <entry name="DigitalPlayback" type="Bool"> + <label>Use direct digital playback.</label> + <whatsthis>When this option is selected KsCD will attempt to play the CD using direct digital playback. This option is useful if the CD-ROM is not connected directly to the sound output on the computer. Note that digital playback is slower than the normal method of playback.</whatsthis> + <default>false</default> + </entry> + <entry name="ledColor" type="Color"> + <label>The foreground color that will be used in the LCD display.</label> + <default>#e2e0ff</default> + </entry> + <entry name="ledFont" type="Font"> + <label>The font that will be used for the LCD display.</label> + <default><code>QFont(KGlobalSettings::generalFont().family(), 10, QFont::Bold)</code></default> + </entry> + <entry name="BackColor" type="Color"> + <label>The background color that will be used for the LCD display.</label> + <default>black</default> + </entry> + <entry name="SelectEncoding" type="Bool"> + <label>Allow encoding selection.</label> + <whatsthis>When this option is selected, you have the ability to select encoding for the results of a CDDB request. The standard describes CDDB results as being strictly Latin1. This is not true, as non-English speaking users often use other 8-bit encodings.</whatsthis> + <default>false</default> + </entry> + <entry name="SelectedEncoding" type="Enum"> + <choices> + <choice name="AUTO"/> + <choice name="utf8"/> + <choice name="CP1250"/> + <choice name="CP1251"/> + <choice name="CP1252"/> + <choice name="CP1253"/> + <choice name="CP1254"/> + <choice name="CP1255"/> + <choice name="CP1256"/> + <choice name="CP1257"/> + </choices> + <default>utf8</default> + </entry> + </group> +</kcfg> diff --git a/kscd/kscd.lsm b/kscd/kscd.lsm new file mode 100644 index 00000000..27cdb0dc --- /dev/null +++ b/kscd/kscd.lsm @@ -0,0 +1,15 @@ +Begin3 +Title: Kscd +Version: 0.4 +Entered-date: 08/14/97 +Description: A simple CD Player for the KDE Project +Keywords: KDE, Qt, gui, X11, CD player, sound +Author: <wuebben@math.cornell.edu> Bernd Johannes Wuebben +Maintained-by: <wuebben@math.cornell.edu> Bernd Johannes Wuebben +Primary-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia +Home-Page: http://math.cornell.edu/~wuebben/kde.html +Alternate-site: ftp://math.cornell.edu/pub/wuebben/kscd +Original-site: ftp://fiwi02.wiwi.uni-tuebingen.de/pub/kde/apps/multimedia +Platform: Unix, Qt +Copying-policy: GPL +End diff --git a/kscd/kscd.profile.xml b/kscd/kscd.profile.xml new file mode 100644 index 00000000..542c5911 --- /dev/null +++ b/kscd/kscd.profile.xml @@ -0,0 +1,59 @@ +<?xml version="1.0" ?> +<!DOCTYPE profile SYSTEM "profile.dtd"> + +<profile id="kscd" servicename="KsCD"> + <name>KsCD</name> + <author>Aaron J. Seigo</author> + <action objid="CDPlayer" prototype="void play()" class="play" repeat="0" autostart="1"> + <name>Play/Pause</name> + <comment>Toggles between playing and pausing.</comment> + </action> + <action objid="CDPlayer" prototype="void stop()" class="stop" repeat="0" autostart="0"> + <name>Stop</name> + <comment>Stops the CD.</comment> + </action> + <action objid="CDPlayer" prototype="void eject()" class="eject" repeat="0" autostart="0"> + <name>Eject</name> + <comment>Ejects the CD.</comment> + </action> + <action objid="CDPlayer" prototype="void forward()" class="forward" repeat="1" autostart="0"> + <name>Fast Forward</name> + <comment>Skips forwards in the current track.</comment> + </action> + <action objid="CDPlayer" prototype="void backward()" class="rewind" repeat="1" autostart="0"> + <name>Rewind</name> + <comment>Skips backwards in the current track.</comment> + </action> + <action objid="CDPlayer" prototype="void previous()" class="previous" repeat="0" autostart="0"> + <name>Previous</name> + <comment>Skips to the previous track on the CD.</comment> + </action> + <action objid="CDPlayer" prototype="void next()" class="next" repeat="0" autostart="0"> + <name>Next</name> + <comment>Skips to the next track on the CD.</comment> + </action> + <action objid="CDPlayer" prototype="void setTrack(int)" class="number" repeat="0" autostart="1"> + <name>Set Track</name> + <comment>Skips to a specific track on the CD.</comment> + <argument type="int"> + <comment>The number of the track to skip to.</comment> + <range min="0" max="100"/> + </argument> + </action> + <action objid="CDPlayer" prototype="void setVolume(int)" repeat="0" autostart="0"> + <name>Set Volume</name> + <comment>Sets the play volume.</comment> + <argument type="int"> + <comment>The volume to set the CD player to.</comment> + <range min="0" max="100"/> + </argument> + </action> + <action objid="CDPlayer" prototype="void toggleLoop()" repeat="0" autostart="0"> + <name>Loop</name> + <comment>Toggles looping on and off.</comment> + </action> + <action objid="CDPlayer" prototype="void toggleShuffle()" repeat="0" autostart="0"> + <name>Shffule</name> + <comment>Toggles track shuffling on and off.</comment> + </action> +</profile> diff --git a/kscd/kscdmagic/Makefile.am b/kscd/kscdmagic/Makefile.am new file mode 100644 index 00000000..c5cc3a21 --- /dev/null +++ b/kscd/kscdmagic/Makefile.am @@ -0,0 +1,40 @@ + +# this 10 paths are KDE specific. Use them: +# kde_htmldir Where your docs should go to. (contains lang subdirs) +# kde_appsdir Where your application file (.kdelnk) should go to. +# kde_icondir Where your icon should go to. +# kde_minidir Where your mini icon should go to. +# kde_datadir Where you install application data. (Use a subdir) +# kde_locale Where translation files should go to.(contains lang subdirs) +# kde_cgidir Where cgi-bin executables should go to. +# kde_confdir Where config files should go to. +# kde_mimedir Where mimetypes should go to. +# kde_toolbardir Where general toolbar icons should go to. +# kde_wallpaperdir Where general wallpapers should go to. + +# just set the variable +APPSDIR = $(kde_appsdir)/Multimedia + +# set the include path for X, qt and KDE +INCLUDES= $(all_includes) + +####### This part is very kscdmagic specific +# you can add here more. This one gets installed +bin_PROGRAMS = kscdmagic + +# Which sources should be compiled for kscdmagic +kscdmagic_SOURCES = core.cpp main.cpp sound.cpp xlibwrap.cpp xlib.c + +# the library search path. +kscdmagic_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +# the libraries to link against. Be aware of the order. First the libraries, +# that depend on the following ones. +kscdmagic_LDADD = -lm $(LIB_KDEUI) + +# this option you can leave out. Just, if you use "make dist", you need it +noinst_HEADERS = symbol.h syna.h xlib.h magicconf.h + +messages: +# $(XGETTEXT) -kTRANS $(kscdmagic_SOURCES) + diff --git a/kscd/kscdmagic/README b/kscd/kscdmagic/README new file mode 100644 index 00000000..64af4f6f --- /dev/null +++ b/kscd/kscdmagic/README @@ -0,0 +1,146 @@ + +kscdmagic is based on synaesthesia +by Paul Harrison <pfh@yoyo.cc.monash.edu.au>. + + -dirk <milliByte@gmx.net> + + + + +SYNAESTHESIA v2.0 + +Introduction +============ + +This is a program for representing sounds visually from a CD or line +input or piped from another program. It goes beyond the usual oscilliscope +style program by combining an FFT and stereo positioning information to +give a two dimensional display. Some of the shapes I have observed are: + * Drums: clouds of color, fairly high + * Clean guitar: several horizontal lines, low down + * Rough guitar: a cloud, low down + * Trumpet: Lots of horizontal lines everywhere + * Flute: A single horizontal line, low down + * Voice: A vertical line with some internal structure + * Synthesizer: All kinds of weird shapes! + +Synaesthesia can run in a window in X or full screen using SVGAlib. + +The display represents frequency as vertical position on screen, +left-right position as left-right position on screen. It can also +understand surround sound encoded music, and shows ambient noise +in orange. + +X-Windows support was added in version 1.3, as well as a major redesign +of the interface. You can use Synaesthesia as a fully functional +CD player, suitable for use while working. + +There is command line support for play lists and piping from another +program (such as an mp3 player). + +Usage +===== + +Synaesthesia should work on Linux and BSD systems. (Note: I don't +have access to a BSD system myself, I have to rely on patches -- if it +doesn't work, please tell me!) LinuxPPC users may have to use the pipe +mode rather than taking sound input from the CD player, as I believe +sound recording is not yet implemented. + +Compile Synaesthesia by typing + + make + +then install it by typing + + make install + +This will create three versions of Synaesthesia: + + synaesthesia - full screen SVGAlib version (Linux only) + xsynaesthesia - Version that runs as a window in X + sdlsynaesthesia - Version that uses the SDL graphics library + +If you want to use the SDL version, you need to get SDL from +http://www.devolution.com/~slouken/SDL. + +You will need to run Synaesthesia as root to run it full screen +with SVGAlib. Other varieties can be run by any user providing you +provide permissions on /dev/dsp, /dev/cdrom, and /dev/mixer. + +Synaesthesia creates a configuration file, named ~/.synaesthesia, +to store settings such as brightness, color, and window size, as +well as which devices to use to control sound input. + +BSD users will have to edit this file to set the CD-ROM device name +before using Synaesthesia in order to control the CD. + +Run Synaesthesia with no parameters for further information on how to +use it. + +Notes for code rippers +====================== + +This program contains code that you may wish to use in your own projects. +If you want to, please do. (For example, you might want to add some +snazzy visual effects to your favorite MP3 or CD player) + +The actual code to do the mapping from sound to visual display is +all in core.cpp, it should be fairly easy to disentangle from other +parts of the program. It does make reference to some globals defined +in syna.h, namely the #defines m (log2 of the sample size for each +frame) and brightness, data (which stores the sound input), outputBmp, +lastOutputBmp and lastLastOutputBmp (which hold the output), outWidth +and outHeight (size of the bitmaps), and fadeMode, brightnessTwiddler, +starSize and pointsAreDiamonds (various parameters affecting the display). + +The normal way to use it would be: + + Call coreInit() to set up some look-up tables + Call setStarSize(starSize) to set up some more look-up tables + Loop + Put data into the data array + Call fade() to apply the fade/wave/heat effect to the output + Call coreGo() to add the next fragment of sound input to the output + Display contents of outputBmp to screen + +There is a simple anti-aliased polygon drawing engine in the file +polygon.h. sound.cpp contains code for driving the CD. xlib.c and +xlibwrap.cpp contain code for setting up a window under X (originally +ripped from the Xaos fractal viewer program :-) ). + +Authors +======= + +This program is free. If you like it, or have any suggestions, please +send me (Paul Harrison) an email (pfh@yoyo.cc.monash.edu.au). + +Thanks to Asger Alstrup Nielsen for many great suggestions, and for +writing optimized 32 bit loops for fading and drawing to screen. + +Thanks to Roger Knobbe for porting Synaesthesia to FreeBSD. + +Thanks to Ben Gertzfield and Martin Mitchell for some small fixes to the +CD controlling code. + +Thanks to Simon Budig for an improvement to the X code. + +Changes +======= + +1.1 - Added surround sound decoding. +1.2 - Fixed a bug in the ioctl calls to /dev/dsp. +1.3 - Asger Alstrup Nielsen's optimizations added. + Added X-Windows support. + More options, redesigned interface. +1.4 - Bug fixes, including a great reduction in + "Sound: Recording overrun" warnings. + New command line options: play lists and piping. + Support for SDL. +2.0 - Bug fixes: Fixed problem in xlib.c that caused occasional segfaults, + several endianness problems fixed. + New effects: Wave, heat, diamond shaped points. + Piping sound now longer requires the twiddle factor. + Yet another interface redesign. + Partial support for LinuxPPC (pipe mode only) + diff --git a/kscd/kscdmagic/core.cpp b/kscd/kscdmagic/core.cpp new file mode 100644 index 00000000..49fce661 --- /dev/null +++ b/kscd/kscdmagic/core.cpp @@ -0,0 +1,410 @@ +/* Synaesthesia - program to display sound graphically + Copyright (C) 1997 Paul Francis Harrison + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author may be contacted at: + pfh@yoyo.cc.monash.edu.au + or + 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "syna.h" + +double cosTable[n], negSinTable[n]; +int bitReverse[n]; +int scaleDown[256]; +int maxStarRadius; + +int bitReverser(int i) { + int sum=0,j; + for(j=0;j<m;j++) { + sum = (i&1)+sum*2; + i >>= 1; + } + return sum; +} + +void fft(double *x,double *y) { + int n2 = n, n1; + int twoToTheK; + for(twoToTheK=1;twoToTheK<n;twoToTheK*=2) { + n1 = n2; + n2 /= 2; + for(int j=0;j<n2;j++) { + double c = cosTable[j*twoToTheK&(n-1)], + s = negSinTable[j*twoToTheK&(n-1)]; + for(int i=j;i<n;i+=n1) { + int l = i+n2; + double xt = x[i] - x[l]; + x[i] = (x[i] + x[l]); + double yt = y[i] - y[l]; + y[i] = (y[i] + y[l]); + x[l] = xt*c - yt*s; + y[l] = xt*s + yt*c; + } + } + } +} + +void coreInit() { + int i; + + for(i=0;i<n;i++) { + negSinTable[i] = -sin(3.141592*2.0/n*i); + cosTable[i] = cos(3.141592*2.0/n*i); + bitReverse[i] = bitReverser(i); + } +} + +void setStarSize(double size) { + //int factor = (fadeMode == Flame ? 100 : + // (fadeMode == Wave ? 150 : 200)); + + double fadeModeFudge = (fadeMode == Wave ? 0.4 : + (fadeMode == Flame ? 0.6 : 0.78)); + + int factor; + if (size > 0.0) + factor = int(exp(log(fadeModeFudge) / (size*8.0))*255); + else + factor = 0; + + if (factor > 255) factor = 255; + + for(int i=0;i<256;i++) + scaleDown[i] = i*factor>>8; + + maxStarRadius = 1; + for(int i=255;i;i = scaleDown[i]) + maxStarRadius++; +} + +inline void addPixel(int x,int y,int br1,int br2) { + if (x < 0 || x >= outWidth || y < 0 || y >= outHeight) return; + + unsigned char *p = ucoutput+x*2+y*outWidth*2; + if (p[0] < 255-br1) p[0] += br1; else p[0] = 255; + if (p[1] < 255-br2) p[1] += br2; else p[1] = 255; + //p += lastOutput-output; + //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255; + //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255; +} + +inline void addPixelFast(unsigned char *p,int br1,int br2) { + if (p[0] < 255-br1) p[0] += br1; else p[0] = 255; + if (p[1] < 255-br2) p[1] += br2; else p[1] = 255; + //p += lastOutput-output; + //if (p[0] < 255-br1) p[0] += br1; else p[0] = 255; + //if (p[1] < 255-br2) p[1] += br2; else p[1] = 255; +} + +void fadeFade() { + register unsigned long *ptr = (unsigned long*)ucoutput; + int i = outWidth*outHeight*2/4; + do { + //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4; + if (*ptr) + //if (*ptr & 0xf0f0f0f0ul) + *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5); + //else { + // *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful; + //} + else + ptr++; + } while(--i > 0); +} + +inline unsigned char getPixel(int x,int y,int where) { + if (x < 0 || y < 0 || x >= outWidth || y >= outHeight) return 0; + return lastOutput[where]; +} + +inline void fadePixelWave(int x,int y,int where,int step) { + short j = + ( short(getPixel(x-1,y,where-2))+ + getPixel(x+1,y,where+2)+ + getPixel(x,y-1,where-step)+ + getPixel(x,y+1,where+step) + >> 2) + +lastOutput[where]; + if (!j) { ucoutput[where] = 0; return; } + j = j + -lastLastOutput[where] + -1; + if (j < 0) ucoutput[where] = 0; + else if (j & (255*256)) ucoutput[where] = 255; + else ucoutput[where] = j; +} + +void fadeWave() { + unsigned short *t = lastLastOutputBmp.data; + lastLastOutputBmp.data = lastOutputBmp.data; + lastOutputBmp.data = outputBmp.data; + outputBmp.data = t; + + int x,y,i,j,start,end; + int step = outWidth*2; + for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) { + fadePixelWave(x,0,i,step); + fadePixelWave(x,0,i+1,step); + fadePixelWave(x,outHeight-1,j,step); + fadePixelWave(x,outHeight-1,j+1,step); + } + + for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) { + fadePixelWave(0,y,i,step); + fadePixelWave(0,y,i+1,step); + fadePixelWave(outWidth-1,y,j,step); + fadePixelWave(outWidth-1,y,j+1,step); + } + + for(y=1, + start=outWidth*2+2, + end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) { + int i = start; + do { + short j = + ( short(lastOutput[i-2])+ + lastOutput[i+2]+ + lastOutput[i-step]+ + lastOutput[i+step] + >> 2) + +lastOutput[i]; + if (!j) { + ucoutput[i] = 0; + } else { + j = j + -lastLastOutput[i] + -1; + if (j < 0) ucoutput[i] = 0; + else if (j & (255*256)) ucoutput[i] = 255; + else ucoutput[i] = j; + } + } while(++i < end); + } +} + +inline void fadePixelHeat(int x,int y,int where,int step) { + short j = + ( short(getPixel(x-1,y,where-2))+ + getPixel(x+1,y,where+2)+ + getPixel(x,y-1,where-step)+ + getPixel(x,y+1,where+step) + >> 2) + +lastOutput[where]; + if (!j) { ucoutput[where] = 0; return; } + j = j + -lastLastOutput[where] + -1; + if (j < 0) ucoutput[where] = 0; + else if (j & (255*256)) ucoutput[where] = 255; + else ucoutput[where] = j; +} + +void fadeHeat() { + unsigned short *t = lastLastOutputBmp.data; + lastLastOutputBmp.data = lastOutputBmp.data; + lastOutputBmp.data = outputBmp.data; + outputBmp.data = t; + + int x,y,i,j,start,end; + int step = outWidth*2; + for(x=0,i=0,j=outWidth*(outHeight-1)*2;x<outWidth;x++,i+=2,j+=2) { + fadePixelHeat(x,0,i,step); + fadePixelHeat(x,0,i+1,step); + fadePixelHeat(x,outHeight-1,j,step); + fadePixelHeat(x,outHeight-1,j+1,step); + } + + for(y=1,i=outWidth*2,j=outWidth*4-2;y<outHeight;y++,i+=step,j+=step) { + fadePixelHeat(0,y,i,step); + fadePixelHeat(0,y,i+1,step); + fadePixelHeat(outWidth-1,y,j,step); + fadePixelHeat(outWidth-1,y,j+1,step); + } + + for(y=1, + start=outWidth*2+2, + end=outWidth*4-2; y<outHeight-1; y++,start+=step,end+=step) { + int i = start; + do { + short j = + ( short(lastOutput[i-2])+ + lastOutput[i+2]+ + +lastOutput[i-step] + +lastOutput[i+step] + >> 2) + +lastOutput[i]; + if (!j) { + ucoutput[i] = 0; + } else { + j = j + -lastLastOutput[i] + +(lastLastOutput[i] + -lastOutput[i]>>2) + -1; + if (j < 0) ucoutput[i] = 0; + else if (j & (255*256)) ucoutput[i] = 255; + else ucoutput[i] = j; + } + } while(++i < end); + } +} + +void fade() { + switch(fadeMode) { + case Stars : + fadeFade(); + break; + case Flame : + fadeHeat(); + break; + case Wave : + fadeWave(); + break; + default: + break; + } +} + +int coreGo() { + double x[n], y[n]; + double a[n], b[n]; + int clarity[n]; //Surround sound + int i,j,k; + + int brightFactor = int(brightness * brightnessTwiddler /(starSize+0.01)); + + if (-1 == getNextFragment()) + { + fprintf(stderr, "no frag\n" ); + return -1; + } + + for(i=0;i<n;i++) { + x[i] = data[i*2]; + y[i] = data[i*2+1]; + } + + fft(x,y); + + for(i=0 +1;i<n;i++) { + double x1 = x[bitReverse[i]], + y1 = y[bitReverse[i]], + x2 = x[bitReverse[n-i]], + y2 = y[bitReverse[n-i]], + aa,bb; + a[i] = sqrt(aa= (x1+x2)*(x1+x2) + (y1-y2)*(y1-y2) ); + b[i] = sqrt(bb= (x1-x2)*(x1-x2) + (y1+y2)*(y1+y2) ); + if (aa+bb != 0.0) + clarity[i] = (int)( + ( (x1+x2) * (x1-x2) + (y1+y2) * (y1-y2) )/(aa+bb) * 256 ); + else + clarity[i] = 0; + } + + // Asger Alstrupt's optimized 32 bit fade + // (alstrup@diku.dk) + /*register unsigned long *ptr = (unsigned long*)output; + i = outWidth*outHeight*2/4; + do { + //Bytewize version was: *(ptr++) -= *ptr+(*ptr>>1)>>4; + if (*ptr) + if (*ptr & 0xf0f0f0f0ul) + *(ptr++) -= ((*ptr & 0xf0f0f0f0ul) >> 4) + ((*ptr & 0xe0e0e0e0ul) >> 5); + else { + *(ptr++) = (*ptr * 14 >> 4) & 0x0f0f0f0ful; + //Should be 29/32 to be consistent. Who cares. This is totally + // hacked anyway. + //unsigned char *subptr = (unsigned char*)(ptr++); + //subptr[0] = (int)subptr[0] * 29 / 32; + //subptr[1] = (int)subptr[0] * 29 / 32; + //subptr[2] = (int)subptr[0] * 29 / 32; + //subptr[3] = (int)subptr[0] * 29 / 32; + } + else + ptr++; + } while(--i > 0); + */ + + int heightFactor = n/2 / outHeight + 1; + int actualHeight = n/2/heightFactor; + int heightAdd = outHeight + actualHeight >> 1; + + /* Correct for window size */ + double brightFactor2 = (brightFactor/65536.0/n)* + sqrt(actualHeight*outWidth/(320.0*200.0)); + + for(i=1;i<n/2;i++) { + //int h = (int)( b[i]*280 / (a[i]+b[i]+0.0001)+20 ); + if (a[i] > 0 || b[i] > 0) { + int h = (int)( b[i]*outWidth / (a[i]+b[i]) ); + int br1, br2, br = (int)( + (a[i]+b[i])*i*brightFactor2 ); + br1 = br*(clarity[i]+128)>>8; + br2 = br*(128-clarity[i])>>8; + if (br1 < 0) br1 = 0; else if (br1 > 255) br1 = 255; + if (br2 < 0) br2 = 0; else if (br2 > 255) br2 = 255; + //unsigned char *p = output+ h*2+(164-((i<<8)>>m))*(outWidth*2); + int px = h, + py = heightAdd - i / heightFactor; + + if (pointsAreDiamonds) { + addPixel(px,py,br1,br2); + br1=scaleDown[br1];br2=scaleDown[br2]; + + //TODO: Use addpixelfast + for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) { + for(k=0;k<j;k++) { + addPixel(px-j+k,py-k,br1,br2); + addPixel(px+k,py-j+k,br1,br2); + addPixel(px+j-k,py+k,br1,br2); + addPixel(px-k,py+j-k,br1,br2); + } + } + } else { + if (px < maxStarRadius || py < maxStarRadius || + px > outWidth-maxStarRadius || py > outHeight-maxStarRadius) { + addPixel(px,py,br1,br2); + for(j=1;br1>0||br2>0;j++,br1=scaleDown[br1],br2=scaleDown[br2]) { + addPixel(px+j,py,br1,br2); + addPixel(px,py+j,br1,br2); + addPixel(px-j,py,br1,br2); + addPixel(px,py-j,br1,br2); + } + } else { + unsigned char *p = ucoutput+px*2+py*outWidth*2, *p1=p, *p2=p, *p3=p, *p4=p; + addPixelFast(p,br1,br2); + for(;br1>0||br2>0;br1=scaleDown[br1],br2=scaleDown[br2]) { + p1 += 2; + addPixelFast(p1,br1,br2); + p2 -= 2; + addPixelFast(p2,br1,br2); + p3 += outWidth*2; + addPixelFast(p3,br1,br2); + p4 -= outWidth*2; + addPixelFast(p4,br1,br2); + } + } + } + } + } + return 0; +} diff --git a/kscd/kscdmagic/logo.h b/kscd/kscdmagic/logo.h new file mode 100644 index 00000000..478f8c70 --- /dev/null +++ b/kscd/kscdmagic/logo.h @@ -0,0 +1,50 @@ +int logo[48][48] = { +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,17,17,0,0,0,17,0,0,0,17,17,0,0,17,17,17}, +{0,0,0,0,0,0,0,0,17,17,17,17,17,0,0,17,0,0,17,0,17,0,0,17,0,17,17,0,17,17,0,17,0,0,17,0,17,0,17,0,17,0,0,17,0,0,17,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,0,17,0,0,17,0,17,0,17,0,17,0,17,0,0,17,0,17,0,17,0,17,0,0,0,0,0,17,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,17,0,17,17,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,17,0,0,0,17,17,0,0,17,0,0,0,17,0,17,0,0,0,0,17,0,17,0,0,17,17,0,0,0,17,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,17,17,17,17,17,17,17,0,0,0,17,0,0,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,17,17,17,17,17,17,17,17,0,0}, +{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,17,0}, +{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17}, +{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,17,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17,17,17,17,17,17,0,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,17,17,17,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,17,0,0,0,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,17,17,17,17,17,17,17,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,17}, +{17,0,0,0,0,17,17,17,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,17,17,0,0,0,0,17}, +{17,0,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,0,17}, +{0,17,0,0,0,0,0,0,0,0,0,0,17,0,0,17,0,0,0,17,0,0,17,0,0,0,0,0,0,0,0,0,17,0,0,0,0,17,0,0,0,0,0,0,0,0,17,0}, +{0,0,17,17,17,17,17,17,17,17,17,17,17,0,0,17,17,17,17,17,0,0,17,17,17,17,17,17,17,17,17,17,0,0,0,0,0,0,17,17,17,17,17,17,17,17,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{17,17,17,0,0,17,0,0,17,17,17,0,17,0,17,17,17,0,0,17,0,0,17,0,0,0,0,0,0,17,0,0,17,0,0,17,0,17,17,17,0,0,17,0,0,17,17,0}, +{17,0,0,17,0,17,0,17,0,0,0,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17}, +{17,0,0,17,0,17,0,17,0,17,17,0,17,0,0,17,0,0,17,0,17,0,17,0,0,0,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17}, +{17,0,0,17,0,17,0,17,0,0,17,0,17,0,0,17,0,0,17,17,17,0,17,0,0,0,0,0,17,17,17,0,17,0,0,17,0,17,0,0,17,0,17,0,17,0,0,17}, +{17,17,17,0,0,17,0,0,17,17,17,0,17,0,0,17,0,0,17,0,17,0,17,17,17,0,0,0,17,0,17,0,0,17,17,0,0,17,17,17,0,0,17,0,0,17,17,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} +}; diff --git a/kscd/kscdmagic/magicconf.h b/kscd/kscdmagic/magicconf.h new file mode 100644 index 00000000..86ed42de --- /dev/null +++ b/kscd/kscdmagic/magicconf.h @@ -0,0 +1,23 @@ + +#ifndef __MAGIC_CONF_H_ +#define __MAGIC_CONF_H_ + +/***************************************/ +/* For the incurably fiddle prone: */ + +/* log2 of sample size */ +#define m 8 + +/* overlap amount between samples. Set to 1 or 2 if you have a fast computer */ +#define overlap 0 + +/* Brightness */ +#define brightness 150 + +/* Sample frequency*/ +#define frequency 22050 + +#define MITSHM 1 /* use MIT X11 shared memory*/ + +/***************************************/ +#endif diff --git a/kscd/kscdmagic/main.cpp b/kscd/kscdmagic/main.cpp new file mode 100644 index 00000000..7196d3a2 --- /dev/null +++ b/kscd/kscdmagic/main.cpp @@ -0,0 +1,300 @@ +/* + + $Id$ + + kscdmagic 2.0 Dirk Frsterling <milliByte@gmx.de> + + based on: + + kscdmagic 1.0 Bernd Johannes Wuebben <wuebben@kde.org> + + based on: + + Synaesthesia - program to display sound graphically + Copyright (C) 1997 Paul Francis Harrison + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author may be contacted at: + pfh@yoyo.cc.monash.edu.au + 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia + +*/ + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#if defined(__linux__) || defined(__svr4__) || defined(__osf__) + +#include <signal.h> +#include <time.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <pwd.h> +#include <string.h> +#if defined(__linux__) +#include <getopt.h> +#endif + +#include <klocale.h> +#include <kglobal.h> +#include <kapplication.h> +#include <kaboutdata.h> +#include <dcopclient.h> +#include <kcmdlineargs.h> + +#include "logo.h" +#include "magicconf.h" +#include "syna.h" +#include "version.h" + +volatile short *data; + +int outWidth, outHeight; +double brightnessTwiddler; +SymbolID fadeMode = Stars; +double starSize = 0.125; +bool pointsAreDiamonds = true; + + +const int numRows = 4; +const int rowHeight = 50; +const int leftColWidth = 40; +const int rowMaxWidth = 310; +const int sliderBorder = 20; +const int sliderWidth = rowMaxWidth - leftColWidth - sliderBorder*2; +const int numberSpacing = 15; +const int uiWidth = 330; +const int uiHeight = 135; + + +static int isExpanded = 0; +static double bright = 1.0; + +Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp; +PolygonEngine<unsigned short,combiner,2> polygonEngine; + +void +allocOutput(int w,int h) +{ + outputBmp.size(w,h); + lastOutputBmp.size(w,h); + lastLastOutputBmp.size(w,h); + polygonEngine.size(w,h); + outWidth = w; + outHeight = h; +} // allocOutput() + +void +setBrightness(double bright) +{ + brightnessTwiddler = bright; +} // setBrightness() + + + +static void +cleanup( int sig ) +{ + (void) sig; + closeSound(); + exit(0); +} // cleanup() + + +// make sure the pid file is cleaned up when exiting unexpectedly. + +void +catchSignals() +{ + signal(SIGHUP, cleanup); /* Hangup */ + signal(SIGINT, cleanup); /* Interrupt */ + signal(SIGTERM, cleanup); /* Terminate */ + signal(SIGPIPE, cleanup); + signal(SIGQUIT, cleanup); +} // catchSignals() + +void +usage(char*) +{ + fprintf(stderr, "Valid command line options:\n"); + fprintf(stderr, " -b set brightness (1 - 10)\n"); + fprintf(stderr, " -w set width\n"); + fprintf(stderr, " -h set height\n"); + exit(1); +} // usage() + +void +error(const char *str, bool syscall) { + fprintf(stderr, PROGNAME ": Error %s\n",str); + if (syscall) + fprintf(stderr,"(reason for error: %s)\n",strerror(errno)); + exit(1); +} // error() + +void +warning(const char *str, bool syscall) { + fprintf(stderr, PROGNAME ": Possible error %s\n",str); + if (syscall) + fprintf(stderr,"(reason for error: %s)\n",strerror(errno)); +} // warning() + + + +int +processUserInput() +{ + + int mouseX, mouseY, mouseButtons; + char keyHit; + + inputUpdate(mouseX,mouseY,mouseButtons,keyHit); + + if( keyHit == 'q' ) + return -1; + + + if (sizeUpdate()) + { + isExpanded = 0; + } + + return 0; +} // processUserInput() + +int +main(int argc, char **argv) +{ + int windX=10; + int windY=30; + int windWidth=uiWidth; + int windHeight=uiHeight; + int c; + int xx, xy; + opterr = 0; + + /* + KAboutData aboutData( "kscdmagic", I18N_NOOP("kscdmagic"), + KSCDMAGICVERSION, I18N_NOOP("sound visualisation"), + KAboutData::License_GPL, + "(c) 2000, Dirk Frsterling"); + aboutData.addAuthor("Paul Harrison",0, "pfh@yoyo.cc.monash.edu.au"); + aboutData.addAuthor("Dirk Frsterling",0, "milliByte@gmx.net"); + + KCmdLineArgs::init( argc, argv, &aboutData ); + + KApplication magicApp; + */ + + + openSound(SourceCD, 44100, "/dev/dsp", NULL); + + catchSignals(); + + while ((c = getopt(argc, argv, "b:h:w:")) != -1){ + switch (c) + { + case '?': + fprintf(stderr, "%s: unknown option \"%s\"\n", + argv[0], argv[optind-1]); + usage(argv[0]); + exit(1); + case 'b': + bright = (double) atoi(optarg); + bright = bright/10; + break; + case 'w': + windWidth = atoi(optarg); + break; + case 'h': + windHeight = atoi(optarg); + break; + } + } + + if (bright > 1.0) + bright = 1.0; + else if (bright < 0.0) + bright = 0.0; + + if (windWidth < 1) + windWidth = uiWidth; + if (windHeight < 1) + windHeight = uiHeight; + + screenInit(windX,windY,windWidth,windHeight); + + allocOutput(outWidth,outHeight); + + coreInit(); + + + setStarSize(starSize); + setBrightness(bright); + + time_t timer = time(NULL); + + int frames = 0; + + for(;;) { + fade(); + if (-1 == coreGo()) + break; + + polygonEngine.clear(); + + for( xy = 0; xy < 48; xy++) + { + for( xx = 0; xx < 48; xx++) + { + if ( logo[xy][xx] != 0) + { + polygonEngine.add(32769, xx+10, xy+3); + } + } + } + polygonEngine.apply(outputBmp.data); + screenShow(); + + + + frames++; + if(processUserInput() == -1) + break; + } + + + timer = time(NULL) - timer; + delete ucoutput; + closeSound(); + + if (timer > 10) + fprintf(stderr,"Frames per second: %f\n", double(frames)/timer); + + return 0; +} // main() /* linux */ + +#else + +int main() +{ + fprintf(stderr,"KSCD Magic works currently only on Linux.\n"\ + "It should however be trivial to port it to other platforms ...\n"); +} // main() /* non-linux */ + +#endif + diff --git a/kscd/kscdmagic/polygon.h b/kscd/kscdmagic/polygon.h new file mode 100644 index 00000000..4337ab10 --- /dev/null +++ b/kscd/kscdmagic/polygon.h @@ -0,0 +1,106 @@ +#include <string.h> + +template<class Pixel,int extra=0> +struct Bitmap { + Pixel *data; + int width, height; + + Bitmap() : data(0) { }; + ~Bitmap() { delete[] data; }; + + void size(int w,int h) { + delete[] data; + width = w; + height = h; + data = new Pixel[w*h+extra]; + clear(); + } + + void clear() { + memset(data,0,sizeof(Pixel)*(width*height+extra)); + } +}; + +template<class Pixel, Pixel combine(Pixel a,Pixel b), int superSampleShift> +struct PolygonEngine : public Bitmap<Pixel,1> { +#define super (1<<superSampleShift) + void apply(Pixel *dest) { + /* Pixel sum=0; */ + int count = width*height; + Pixel *src = data; + /* + * I don't really want this. + * + while(count--) { + sum += *(src++); + if (sum) + *dest = combine(sum,*dest); + dest++; + } + */ + while(count--) + { + *dest = combine(*src, *dest); + src++; + dest++; + } + } + + void add(Pixel color,int x,int y) { + if (y < 0) return; + if (y >= height) return; + if (x < 0) x = 0; + if (x > width) x = width; + data[x+y*width] += color; + } + + /* Color is char[layers] */ + + // zwoosh, yknow, it goes... zwoosh an all these bars and lines and + // crap intersect. + Pixel colorTable[2][super+1]; + void pen(Pixel color) { + for(int i=0;i<super+1;i++) { + colorTable[0][i] = color*i; + colorTable[1][i] = -(color*i); + } + } + + void line(int x1,int y1,int x2,int y2) { + Pixel *colors; + if (y2 < y1) { + int temp; + temp = x2; x2 = x1; x1 = temp; + temp = y2; y2 = y1; y1 = temp; + colors = colorTable[1]; + } else { + if (y1 == y2) return; + + colors= colorTable[0]; + } + + int slope = (x1-x2 << 16)/(y1-y2); + int x = x1<<16, y = y1; + while(y < y2) { + add(colors[super-((x>>16)&(super-1))], + x>>(16+superSampleShift),y>>superSampleShift); + add(colors[(x>>16)&(super-1)], + 1+(x>>(16+superSampleShift)),y>>superSampleShift); + x += slope; + y++; + } + } + + void icon(double icon[][4],Pixel color,double x,double y, + double scaleX, double scaleY) { + pen(color); + x *= super; + y *= super; + scaleX *= super; + scaleY *= super; + for(int i=0;icon[i][1] != icon[i][3];i++) + line(int(icon[i][0]*scaleX+x),int(icon[i][1]*scaleY+y), + int(icon[i][2]*scaleX+x),int(icon[i][3]*scaleY+y)); + } +#undef super +}; diff --git a/kscd/kscdmagic/sound.cpp b/kscd/kscdmagic/sound.cpp new file mode 100644 index 00000000..0bcb8d8d --- /dev/null +++ b/kscd/kscdmagic/sound.cpp @@ -0,0 +1,252 @@ +/* Synaesthesia - program to display sound graphically + Copyright (C) 1997 Paul Francis Harrison + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author may be contacted at: + pfh@yoyo.cc.monash.edu.au + or + 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia +*/ + +#if defined(__linux__) || defined(__svr4__) + +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/sem.h> +#include <sys/shm.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> + +#if defined (__linux__) +#include <linux/soundcard.h> +#ifndef __GNUC__ +#define __GNUC__ 1 +#endif +#undef __STRICT_ANSI__ +#include <asm/types.h> +#include <linux/cdrom.h> +#endif + +#if defined (__svr4__) +#include <sys/soundcard.h> +#endif + + +// who knows when we'll need that... +#if defined (FreeBSD) +#include <sys/soundcard.h> +#include <sys/cdio.h> +#define CDROM_LEADOUT 0xAA +#define CD_FRAMES 75 /* frames per second */ +#define CDROM_DATA_TRACK 0x4 +#endif + +#include <time.h> + +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "syna.h" +#include "magicconf.h" + + +/* Sound Recording ================================================= */ + +#ifdef LITTLEENDIAN +#define SOUNDFORMAT AFMT_S16_LE +#else +#define SOUNDFORMAT AFMT_S16_BE +#endif + +//If kernel starts running out of sound memory playing mp3s, this could +//be the problem. OTOH if it is too small, it will start ticking on slow +//computers +#define MAXWINDOWSIZE 32 + +static SoundSource source; +static int inFrequency, downFactor, windowSize, pipeIn, device; +static short *dataIn; +static char *mixer; + +void +openSound(SoundSource source, int inFrequency, const char *dspName, + char *mixerName) +{ + ::source = source; + ::inFrequency = inFrequency; + ::windowSize = 1; + mixer = mixerName; + downFactor = inFrequency / frequency; + if (downFactor <= 0) + downFactor = 1; + + int format, stereo, fragment, fqc; + +#ifdef __FreeBSD__ + attempt(device = open(dspName,O_WRONLY),"opening dsp device",true); + format = SOUNDFORMAT; + attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true); + if (format != SOUNDFORMAT) error("setting format (2)"); + close(device); +#endif + if (source == SourcePipe) + attempt(device = open(dspName,O_WRONLY),"opening dsp device",true); + else + attempt(device = open(dspName,O_RDONLY),"opening dsp device",true); + + //Probably not needed + //attemptNoDie(ioctl(device,SNDCTL_DSP_RESET,0),"reseting dsp"); + format = SOUNDFORMAT; + fqc = (source == SourcePipe ? inFrequency : frequency); + stereo = 1; + + //int logWindowSize = -1, tmp = windowSize*downFactor; + //while(tmp) { + // tmp /= 2; + // logWindowSize++; + //} + + if (source == SourcePipe) + //fragment = 0x00020000 + (m-overlap+1)+logWindowSize; + fragment = 0x00010000*(MAXWINDOWSIZE+1) + (m-overlap+1);//+logWindowSize; + //Soundcard should read in windowSize + // blocks of sound before blocking + else + //fragment = 0x00020000 + (m-overlap+1); //2 fragments of size 2*(2^(m-overlap+1)) bytes + + //Added extra fragments to allow recording overrun (9/7/98) + fragment = 0x00080000 + (m-overlap+1); //8 fragments of size 2*(2^(m-overlap+1)) bytes + + + + + //Was 0x00010000 + m; + + attemptNoDie(ioctl(device,SNDCTL_DSP_SETFRAGMENT,&fragment),"setting fragment",true); +#ifndef __FreeBSD__ + attempt(ioctl(device,SNDCTL_DSP_SETFMT,&format),"setting format",true); + if (format != SOUNDFORMAT) error("setting format (2)"); +#endif + attempt(ioctl(device,SNDCTL_DSP_STEREO,&stereo),"setting stereo",true); + attemptNoDie(ioctl(device,SNDCTL_DSP_SPEED,&fqc),"setting frequency",true); + + data = new short[n*2]; + + if (source == SourcePipe) { + dataIn = new short[n*2*downFactor*MAXWINDOWSIZE]; + memset(dataIn,0,n*4*downFactor*MAXWINDOWSIZE); + pipeIn = dup(0); + close(0); + } +} + +void closeSound() { + delete data; + if (source == SourcePipe) { + delete dataIn; + close(pipeIn); + } + close(device); +} + +int readWholeBlock(int pipe,char *dest,int length) { + while(length > 0) { + int result = read(pipe,dest,length); + if (result < 1) + return -1; + dest += result; + length -= result; + } + return 0; +} + +int getNextFragment(void) { + if (source == SourcePipe) { + static int lastTime = 0; + int nowTime; + timeval timeVal1, timeVal2; + + gettimeofday(&timeVal1,0); + write(device, (char*)dataIn, n*4*downFactor*windowSize); + gettimeofday(&timeVal2,0); + + nowTime = timeVal1.tv_usec + timeVal1.tv_sec * 1000000; + if (nowTime > lastTime) { + int optimumFrags = + int(double(nowTime-lastTime)*inFrequency/1000000.0/(n*downFactor)) + +1; + if (optimumFrags > MAXWINDOWSIZE) + optimumFrags = MAXWINDOWSIZE; + + windowSize = optimumFrags; + } + + lastTime = timeVal2.tv_usec + timeVal2.tv_sec * 1000000; + + if (readWholeBlock(pipeIn, ((char*)dataIn), n*4*downFactor*windowSize) == -1) + return -1; + + int i,j; + for(i=0,j=0;i<n;i++,j+=downFactor) + ((long*)data)[i] = ((long*)dataIn)[j]; + } else { + int i; + count_info info; + if (-1 == ioctl(device,SNDCTL_DSP_GETIPTR,&info)) + info.blocks = 1; + if (info.blocks > 8 || info.blocks < 1) /* Sanity check */ + info.blocks = 1; + + for(i=0;i<info.blocks;i++) { + if (recSize != n) + memmove((char*)data,(char*)data+recSize*4,(n-recSize)*4); + + attemptNoDie( + readWholeBlock(device,(char*)data+n*4-recSize*4, recSize*4), + "reading from soundcard", true); + } + } + return 0; +} + +#else + +// generic dummy implementation + +#include "syna.h" + +int getNextFragment(void) { + return 0; +} + +void openSound(SoundSource source, int inFrequency, const char *dspName, + char *mixerName) +{ +} + +void closeSound() +{ +} + +#endif // linux || svr4 + diff --git a/kscd/kscdmagic/symbol.h b/kscd/kscdmagic/symbol.h new file mode 100644 index 00000000..aad04f47 --- /dev/null +++ b/kscd/kscdmagic/symbol.h @@ -0,0 +1,1028 @@ +#ifndef __SYNAES_SYMBOL_H__ +#define __SYNAES_SYMBOL_H__ +#if defined(__linux__) || defined(__svr4__) + +#define SYMBOLSWIDTH 586 +#define SYMBOLSHEIGHT 50 +unsigned char Symbols[586*50] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,241,72,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,13,30,37,31,24,5,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,132,254,237,72,0,0,0,0,0,0,0,0,0,0,0,1,7,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,46,0,0,0,0,0,0,0,0,0,0,0,0,0,10,87, + 176,225,244,246,244,238,206,135,39,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254, + 254,236,73,0,0,0,0,0,0,0,0,0,0,3,154,138,58,9,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,36,211,80,0,0,0,0,0,0,0,0,0,0,0,2,77,204,252,254,254,254, + 254,254,254,254,254,239,145,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,245, + 215,72,0,0,0,0,0,0,0,0,0,0,115,254,249,213,138,58,8,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,36,212,253,80,0,0,0,0,0,0,0,0,0,0,7,136,247,254,254,254,254, + 254,254,254,254,254,254,254,254,208,52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254, + 255,225,66,210,72,0,0,0,0,0,0,0,0,0,40,244,254,254,254,249,214,137,57,8, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,37,212,254,254,80,0,0,0,0,0,0,0,0,0,4,150,253,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,227,50,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,132,254,255,225,10,55,217,71,0,0,0,0,0,0,0,0,4,194,254,255,255, + 255,255,254,250,214,137,57,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,80,0,0, + 0,0,0,0,0,0,0,100,250,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,203,17,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,1,196,187,1, + 0,0,0,0,0,0,0,0,115,254,255,255,255,255,255,254,254,254,249,212,137,57, + 10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,36,211,254,254,254,254,80,0,0,0,0,0,0,0,0,29,230,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,130,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,41,244,255,255, + 255,255,255,255,255,255,254,254,254,249,212,137,58,8,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,212,254,254,254, + 254,254,80,0,0,0,0,0,0,0,0,134,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,255,255,255,232,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255, + 225,10,1,196,190,1,0,0,0,0,0,0,0,0,4,196,254,255,255,255,255,255,255,255, + 255,255,255,255,254,254,249,212,137,57,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,212,254,254,254,254,254,254,80,0,0, + 0,0,0,0,0,16,225,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,255,255,255,254,112,0,0,0,0,0,0,0,3,12,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16, + 0,0,0,0,0,0,7,9,0,0,0,0,0,0,0,0,0,0,0,0,111,155,155,111,0,0,0,16,148,155, + 155,58,0,0,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,132,254, + 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,115,253,254,255,255,255,255,255, + 255,255,255,255,255,255,255,254,254,254,250,194,15,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,38,179, + 38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,211,254,254,254,254,254, + 254,254,80,0,0,0,0,0,0,0,62,251,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,255,255,255,254,182,1,0,0,0,0,0,0,18,171,19,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,162,44,0,0,0,0,0,37,159,11,0,0,0,0,0,0,0,0,0,0,1,182,254, + 254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,0,23,136,1,0,0,0,0, + 0,68,92,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0, + 40,244,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,254, + 232,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,151,253,245,99,1,0,0, + 6,50,106,155,166,174,166,162,115,52,11,0,0,0,38,213,254,213,39,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,254,254,254,254,254,254,254,80, + 0,0,0,0,0,0,0,108,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,255,255,255,254,223,9,0,0,0,0,0,0,18,239,185,19,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,197,219,45,0,0,0,0,38,246,166,11,0,0,0,0,0,0,0,0,0,1,182,254,254, + 182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,0,23,192,196,1,0,0,0,0,68, + 233,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0, + 4,196,254,255,255,255,255,255,255,255,255,255,255,255,255,255,254,232,65, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,180,252,255,254,244,95,10,97, + 197,248,254,254,255,255,255,254,254,248,210,118,20,36,212,254,255,254,213, + 39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254,254,254,254,254,254,254, + 254,254,80,0,0,0,0,0,0,0,117,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,255,255,255,255,228,11,0,0,0,0,0,0,18,240,254,185, + 19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,0,0,0,0,0,0,0,1,197,254,218,44,0,0,0,38,247,253,165,11,0,0,0,0,0,0, + 0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,23,192, + 254,196,1,0,0,0,67,234,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190, + 1,0,0,0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,255, + 255,254,233,65,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,186,254,255, + 254,254,225,74,34,220,254,254,255,255,255,255,255,255,255,254,254,248,95, + 44,205,254,254,255,254,213,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,35,211,254, + 254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,122,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,228, + 11,0,0,0,0,0,0,18,240,254,254,186,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 17,187,197,197,197,197,197,195,46,0,0,0,0,0,0,0,142,197,197,197,197,197, + 197,107,0,0,0,0,0,0,0,81,196,197,197,197,197,197,197,197,197,197,197,197, + 197,197,197,197,197,197,197,197,197,194,37,0,0,0,0,0,0,1,197,254,254,219, + 44,0,0,38,247,254,253,165,11,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243, + 254,254,96,0,0,0,0,0,0,0,0,22,192,254,254,196,1,0,0,66,235,254,254,111, + 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,39,246, + 254,255,255,255,255,255,255,255,255,255,255,254,234,64,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,17,187,254,255,254,254,204,34,0,0,47,222,254,254, + 255,255,255,255,255,255,254,248,113,2,0,18,175,254,254,255,254,214,36,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,36,211,254,254,254,254,254,254,254,254,254,254, + 254,80,0,0,0,0,0,0,0,114,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,255,255,255,254,227,11,0,0,0,0,0,0,18,240,255,254,254, + 185,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60, + 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,251,47,0,0,0,0,0,0,1,197,254,254,254,220,43,0,38,247,254,254,253,166, + 11,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0, + 22,193,254,254,254,196,1,0,67,234,254,255,254,111,0,0,0,0,0,0,0,132,254, + 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,3,196,254,255,255,255,255,255, + 255,255,255,255,255,236,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,29, + 211,254,255,254,204,30,0,0,0,0,47,221,254,255,255,255,255,255,254,243,112, + 2,0,0,0,14,175,253,255,254,231,54,0,0,0,0,0,0,1,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,5,5,1, + 0,0,0,0,0,0,0,0,0,0,0,5,5,5,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,255, + 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,74,253,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,254, + 194,2,0,0,0,0,0,0,18,240,255,255,255,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254, + 254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254, + 254,254,219,44,38,247,254,254,254,253,166,11,0,0,0,0,0,1,182,254,254,182, + 1,0,0,27,243,254,254,96,0,0,0,0,0,0,23,192,254,254,254,254,196,1,68,233, + 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0, + 0,0,0,0,0,0,0,0,116,254,255,255,255,255,255,255,255,255,255,255,231,53, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,36,210,254,225,33,0,0,0,0,0, + 0,48,221,254,254,255,255,254,237,78,1,0,0,0,0,0,15,198,254,231,61,0,0,0, + 0,0,0,0,73,213,214,214,214,214,214,214,214,213,214,214,214,214,214,214, + 214,214,214,214,214,214,214,214,213,214,214,214,214,214,214,214,214,214, + 156,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,8,145,213,214,213,133,5,0,0,0,0,0,0,0,0,0,88,211,213, + 214,181,24,0,0,0,0,3,11,11,11,11,11,11,11,40,210,254,255,255,254,254,254, + 254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,26,235,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,255,255,255,254,131,0,0,0,0, + 0,0,0,18,240,255,255,255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23, + 242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254, + 138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254, + 254,218,83,247,254,254,254,254,253,165,11,0,0,0,0,1,182,254,254,182,1,0, + 0,27,243,254,254,96,0,0,0,0,0,23,191,254,254,254,254,254,197,69,233,254, + 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0, + 0,0,0,0,0,0,0,0,40,245,255,255,255,255,255,255,255,255,255,255,254,226, + 52,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,37,205,79,0,0,0,0,0,0,0, + 0,47,222,254,254,254,238,76,0,0,0,0,0,0,0,0,45,212,62,0,0,0,0,0,0,0,0,87, + 253,185,168,168,168,168,168,171,243,249,175,168,204,254,255,254,220,168, + 170,239,243,171,168,220,254,195,168,168,168,168,168,169,236,185,1,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,11,11,5, + 0,0,0,0,0,0,0,0,0,0,7,11,11,11,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,11,11,11,11,4,0,0,0,0,0,0,0,0,0,0,8,11,11,11,8,0,0,0,0,0, + 0,0,0,0,0,4,11,11,11,11,1,0,0,0,0,0,0,0,0,0,1,11,11,11,11,4,0,0,0,0,0,0, + 0,0,0,0,8,11,11,11,7,0,0,0,0,0,0,0,0,0,0,5,11,11,11,10,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,151,252,254,254,254,251,136,2,0, + 0,0,0,0,0,0,88,242,254,224,246,254,194,22,0,0,0,64,228,229,229,229,229, + 229,229,239,254,254,254,254,254,254,254,254,254,254,254,254,254,254,80, + 0,0,0,0,0,0,0,1,163,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,255,255,255,244,45,0,0,0,0,0,0,0,18,240,255,255,255,255,254, + 254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60, + 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,237,253,254,254,254, + 254,254,253,165,11,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0, + 0,0,22,191,254,254,254,254,254,254,233,235,254,254,254,255,255,254,111, + 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,4,196, + 255,255,255,255,255,255,255,255,255,255,255,254,225,52,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,21,4,0,0,0,0,0,0,0,0,0,48,222,254,239,77,0, + 0,0,0,0,0,0,0,0,1,24,0,0,0,0,0,0,0,0,0,87,251,50,0,0,0,0,0,10,223,251,67, + 0,53,246,255,253,99,0,31,241,222,9,0,153,253,80,0,0,0,0,0,2,200,185,1,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,224,229, + 229,229,179,18,0,0,0,0,0,0,0,0,37,203,229,229,229,214,55,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,226,229,229,229,170,14,0,0,0,0,0,0, + 0,0,45,208,229,229,229,209,47,0,0,0,0,0,0,0,0,13,167,229,229,229,227,101, + 1,0,0,0,0,0,0,0,1,109,227,229,229,229,161,11,0,0,0,0,0,0,0,0,52,213,229, + 229,229,205,39,0,0,0,0,0,0,0,0,16,177,229,229,229,225,90,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,255,255,255,255,238,15, + 0,0,0,0,0,0,11,225,254,207,34,104,247,253,80,0,0,0,71,253,255,255,255,255, + 254,254,254,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80, + 0,0,0,0,0,0,0,0,49,243,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,255,255,254,161,1,0,0,0,0,0,0,0,18,240,255,255,255,255,254, + 254,254,185,18,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60, + 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,253,165,11,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0, + 0,0,22,192,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254, + 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0, + 0,116,253,254,255,255,255,254,235,219,254,255,255,255,254,226,53,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,21,21,21,21,21,21,21,21,21, + 21,21,21,21,21,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,95,114,2,0,0,0,0,0, + 0,0,0,0,47,207,76,0,0,0,0,0,0,0,0,0,0,80,130,0,0,0,0,0,0,0,0,0,87,251,50, + 0,0,0,0,0,9,223,254,161,0,5,193,254,235,28,0,116,254,222,9,0,153,254,79, + 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,88,242,254,254,255,255,254,185,18,0,0,0,0,0,0,37,214,254,254, + 255,255,254,228,55,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,98,246,254, + 254,255,255,254,175,14,0,0,0,0,0,0,44,220,254,254,255,255,254,222,47,0, + 0,0,0,0,0,13,172,254,254,255,255,254,246,101,1,0,0,0,0,0,1,109,248,254, + 254,255,255,253,165,11,0,0,0,0,0,0,53,226,254,254,255,255,254,216,39,0, + 0,0,0,0,0,17,182,254,254,255,255,254,243,91,1,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,69,253,254,255,255,255,255,239,16,0,0,0,0,0,0,12, + 230,212,32,0,1,113,253,81,0,0,0,71,253,254,255,255,255,254,255,255,255, + 255,254,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0, + 2,141,253,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 229,36,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,254,254,184,19,0, + 0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183, + 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0, + 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,253, + 165,11,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,22,192,254,254,254, + 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132, + 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,41,244,254,255,255,254, + 233,66,37,212,254,255,255,255,254,226,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,108,240,241,241,241,241,241,241,241,241,241,241,241,241, + 241,224,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,199,249,115,2,0,0,0,0,0,0,0, + 29,99,119,113,55,4,0,0,0,0,0,0,0,80,240,230,22,0,0,0,0,0,0,0,0,87,251,50, + 0,0,0,0,0,9,223,255,228,18,0,97,253,165,1,4,196,254,222,9,0,153,254,79, + 0,0,0,0,0,1,200,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,8,53,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,6,176,254,255,255,255,255,254,242,110,18,0,0,0,0,0,106,250,255, + 255,255,255,254,252,154,44,0,0,0,0,10,51,0,0,0,0,0,0,0,3,56,2,0,0,0,1,64, + 193,254,255,255,255,255,254,237,57,0,0,0,0,0,39,143,251,255,255,255,255, + 254,251,123,0,0,0,0,0,0,54,236,254,255,255,255,254,254,196,66,1,0,0,0,2, + 69,202,254,255,255,255,255,254,234,98,11,0,0,0,0,43,151,252,255,255,255, + 255,254,251,137,35,0,0,0,0,17,108,241,254,255,255,255,254,254,187,61,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255, + 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255, + 255,254,255,255,254,255,254,255,254,254,254,254,254,254,254,254,254,254, + 80,0,0,0,0,0,0,0,0,0,13,192,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,244,84,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255, + 255,254,254,184,19,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60, + 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,253,165,11,1,182,254,254,182,1,0,0,27,243,254,254,96, + 0,23,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255, + 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0, + 0,0,0,4,196,254,255,254,233,66,0,0,37,211,254,255,255,255,254,225,34,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,53, + 248,254,248,115,2,0,0,0,0,3,112,186,152,158,153,173,163,23,0,0,0,0,0,80, + 240,254,253,85,0,0,0,0,0,0,0,0,87,251,50,0,5,36,37,37,45,228,255,252,79, + 0,20,226,73,0,40,244,254,222,9,0,153,253,105,37,15,0,1,32,38,208,185,1, + 0,0,0,0,0,0,0,0,0,0,0,0,8,150,240,86,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,186, + 253,253,253,253,242,144,228,183,19,0,0,0,0,3,126,249,253,253,253,251,170, + 192,227,55,0,0,10,161,237,76,0,0,0,0,0,3,123,243,112,2,0,1,98,241,163,201, + 253,253,253,253,238,78,0,0,0,0,0,45,219,201,162,250,253,253,253,250,140, + 6,0,0,0,0,0,0,0,75,237,253,253,253,253,203,161,242,102,1,0,2,109,243,157, + 207,253,253,253,253,234,143,236,164,11,0,0,52,225,194,169,251,253,253,253, + 250,158,206,214,40,0,0,17,181,229,143,241,253,253,253,253,196,167,240,91, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255, + 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255, + 255,254,254,255,254,255,254,254,254,254,254,254,254,254,254,254,254,254, + 80,0,0,0,0,0,0,0,0,0,0,24,188,253,254,254,254,254,254,254,254,254,254,254, + 254,254,237,97,1,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,254, + 254,254,184,18,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0, + 0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,253,164,12,182,254,254,182,1,0,0,27,243,254,254,96, + 22,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,255, + 255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,116,254,254,233,66,0,0,0,0,37,212,254,255,254,253,161,9,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109,253,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,234,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,109, + 254,254,254,248,115,2,0,0,0,113,161,193,249,253,252,224,149,171,14,0,0, + 0,80,239,254,255,254,154,0,0,0,0,0,0,0,0,87,251,50,0,38,240,247,247,247, + 253,255,254,165,1,0,115,12,0,121,254,255,222,9,0,153,254,249,247,102,0, + 8,214,247,253,185,1,0,0,0,0,0,0,0,0,0,0,0,2,145,252,254,241,58,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,17,67,68,68,68,93,228,254,254,171,2,0,0,0,0,3,59,68, + 68,68,73,190,254,254,226,27,3,156,253,254,237,48,0,0,0,0,115,250,254,247, + 86,0,87,245,254,251,135,67,68,68,68,46,0,0,0,0,0,34,219,254,254,192,67, + 68,68,68,62,6,0,0,0,0,0,0,0,0,0,45,68,68,68,68,149,251,254,245,74,0,99, + 247,254,250,126,67,68,68,68,105,237,254,253,146,0,41,225,254,254,182,66, + 68,68,68,78,205,254,254,211,15,7,179,254,254,229,78,68,68,68,68,158,252, + 254,243,63,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,69,253,254,254,254, + 254,254,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254, + 255,255,255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254, + 254,254,80,0,0,0,0,0,0,0,0,0,0,0,11,133,239,254,254,254,254,254,254,254, + 254,254,252,200,55,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255, + 255,255,255,254,254,185,17,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253, + 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,253,165,193,254,254,182,1,0,0,27,243,254, + 254,118,191,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0, + 0,0,0,0,0,0,0,0,0,0,41,244,234,65,0,0,0,0,0,0,36,213,254,253,161,10,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,219,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 159,254,255,255,254,248,115,2,0,31,186,194,254,255,255,254,254,238,162, + 105,0,0,80,239,254,255,255,255,201,3,0,0,0,0,0,0,0,87,251,50,0,16,102,116, + 246,254,255,255,255,236,26,0,10,0,7,208,254,255,222,9,0,153,255,255,254, + 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,254,255,254,88,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0, + 0,0,23,241,254,255,250,46,9,223,254,255,253,75,0,0,0,1,184,254,255,254, + 122,0,152,254,255,255,154,0,0,0,0,0,0,0,0,0,0,73,253,254,255,224,10,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,254,255,254,108,0,166,254,255,254, + 140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249,254, + 255,243,26,20,240,254,255,252,49,0,0,0,3,208,254,255,254,95,0,0,0,0,0,0, + 0,0,0,68,189,198,107,5,0,0,0,0,0,0,0,0,69,252,144,104,105,104,183,239,16, + 0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254, + 255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0, + 0,0,0,0,0,0,0,0,0,0,0,37,147,225,249,254,254,253,252,242,193,87,8,0,0,0, + 0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,254,254, + 184,18,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183, + 254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0, + 0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,252,254,254,182,1,0,0,27,243,254,254,242,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0, + 0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,4,176, + 65,0,0,0,0,0,0,0,0,36,211,160,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,109,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,233,29,0,0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,249,115, + 2,103,151,250,255,255,255,255,255,254,169,172,3,80,240,254,255,255,255, + 255,211,5,0,0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,253,94,0, + 0,0,53,248,255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0, + 0,0,0,0,0,0,0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,91,254,255,255,211,4,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,9, + 223,255,255,253,75,0,0,0,1,184,255,255,254,122,0,153,255,255,255,154,0, + 0,0,0,0,0,0,0,0,0,74,253,255,255,224,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,1,197,255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255, + 187,1,87,254,255,255,214,6,0,0,0,43,249,255,255,243,26,20,240,255,255,252, + 49,0,0,0,3,209,255,255,254,95,0,0,0,0,0,0,0,0,34,233,254,254,251,82,0,0, + 0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55, + 252,81,0,0,0,71,253,254,255,255,254,254,254,255,254,255,254,254,254,254, + 254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,52, + 83,84,84,73,32,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255, + 255,255,255,255,255,254,254,254,156,2,0,0,0,0,0,0,23,242,255,255,255,255, + 255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0, + 104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,182,1,0,0,27, + 243,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190, + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,0,0,0,0,0,0,0,0,0,0,26,10,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,17,218,254,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,254,135,0,0,0,0,0,0,0,0,0,0,0,0, + 1,178,255,255,255,255,255,255,249,112,123,158,254,255,255,255,255,255,254, + 187,177,81,240,254,255,255,255,255,255,211,5,0,0,0,0,0,0,0,87,251,50,0, + 0,0,19,240,255,255,255,255,254,161,0,0,0,116,254,255,255,222,9,0,153,255, + 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,213,255,255,254, + 88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,91,254,255,255,211,4,0,0,0,0,0, + 0,0,0,0,23,242,255,255,250,46,9,223,255,255,253,75,0,0,0,1,184,255,255, + 254,122,0,153,255,255,255,154,0,0,0,0,0,0,0,0,0,0,74,253,255,255,224,10, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108,0,166,255,255, + 254,140,0,0,0,0,118,254,255,255,187,1,87,254,255,255,214,6,0,0,0,43,249, + 255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0,0,0, + 0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252,121,73,73,73,168, + 239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,255,255,255, + 255,254,255,255,255,255,255,255,254,254,254,254,254,254,254,254,254,254, + 80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,18,240,255,255,255,255,255,255,255,255,255,255,255,254,218,43,0,0, + 0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254, + 254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,182,1,0,0,27,243,254,254,252,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132, + 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,108,251,253,253,253, + 253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,253,230,29, + 0,0,0,0,0,0,0,0,0,0,0,0,170,255,255,255,255,255,254,248,112,120,153,253, + 255,255,255,255,255,254,181,176,81,240,254,255,255,255,255,255,211,5,0, + 0,0,0,0,0,0,87,251,50,0,0,0,19,240,255,255,255,255,254,113,0,0,0,69,251, + 255,255,222,9,0,153,255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0, + 0,0,0,0,5,211,254,255,254,88,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,89,254, + 255,255,211,4,0,0,0,0,0,0,1,1,1,23,240,255,255,250,45,9,221,254,255,253, + 75,1,1,1,2,181,254,255,254,122,0,150,254,255,254,154,1,1,1,1,0,0,0,0,0, + 0,71,253,255,255,224,10,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,194,254,255, + 254,108,0,163,254,255,254,140,1,1,1,1,116,254,255,254,187,1,84,253,255, + 255,214,7,1,1,1,42,248,255,255,242,26,19,238,255,255,252,49,0,0,0,3,206, + 254,255,254,94,0,0,0,0,0,0,0,0,47,244,254,254,253,104,0,0,0,0,0,0,0,0,69, + 253,252,252,252,252,253,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0, + 0,0,71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254, + 254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,3,81,94,94,94,94,94, + 94,94,94,94,93,19,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255, + 255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253, + 60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,205,208,254,254,182,1,0,0,27,243,254, + 254,145,224,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,11,134,158,158,158,158,158,158,158,158,158,158,158,158, + 158,158,158,158,158,158,158,158,158,66,0,0,0,0,0,0,0,0,0,0,0,0,167,254, + 255,255,255,254,248,115,2,62,170,229,254,254,255,255,255,251,159,145,1, + 80,240,254,255,255,255,255,208,4,0,0,0,0,0,0,0,87,251,50,0,14,91,106,245, + 254,255,255,255,243,39,0,2,0,14,222,255,255,222,9,0,153,255,255,254,106, + 0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,90,243,254,214,32,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,54,184,190,190,190,155,195,254,250,124,1,0,0,0,0,17, + 161,190,190,190,176,157,250,254,187,14,1,101,246,254,210,142,190,190,190, + 188,140,235,254,229,52,0,46,222,254,240,136,187,190,190,190,127,4,0,0,0, + 0,14,174,254,252,162,169,190,190,190,167,24,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 75,239,254,224,43,0,54,227,254,236,135,187,190,190,190,148,208,254,247, + 100,0,17,184,254,251,156,173,190,190,190,169,168,253,253,167,7,2,123,250, + 254,190,15,0,0,0,0,85,242,254,218,35,0,0,0,0,0,0,0,0,1,115,233,237,163, + 14,0,0,0,0,0,0,0,0,69,252,114,63,63,63,164,239,16,0,0,0,0,0,0,12,230,146, + 0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255,255,254,255,254,255, + 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,217, + 250,250,250,250,250,250,250,250,250,248,51,0,0,0,0,0,0,0,0,0,0,0,0,18,240, + 255,255,255,255,255,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,23,242, + 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138, + 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,204,31,182,254,254, + 182,1,0,0,27,243,254,254,96,49,223,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10, + 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,252,250,250,250,250,250,250, + 250,250,250,250,250,250,250,250,250,250,250,250,250,254,127,0,0,0,0,0,0, + 0,0,0,0,0,0,121,254,255,255,254,248,115,2,0,5,170,149,242,254,254,254,252, + 181,184,44,0,0,80,239,254,254,255,254,166,0,0,0,0,0,0,0,0,87,251,50,0,38, + 243,250,250,250,254,254,254,187,4,0,82,6,0,144,254,254,222,9,0,153,255, + 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,90,204,37,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,55,227,254,254,255,255,253,168,191,126,3,0,0, + 0,0,17,184,254,254,255,255,254,207,157,184,19,0,0,1,101,203,155,250,254, + 254,255,254,238,141,211,57,0,0,0,47,207,142,233,254,254,255,255,252,137, + 5,0,0,0,0,14,173,164,199,254,254,255,255,254,195,24,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,76,208,49,0,0,0,55,210,141,237,254,254,255,254,250,157,202,104, + 1,0,0,17,182,159,205,254,254,255,255,254,194,168,166,12,0,0,3,123,186,21, + 0,0,0,0,0,0,86,205,41,0,0,0,0,0,0,0,0,0,0,0,15,16,1,0,0,0,0,0,0,0,0,0,69, + 252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71, + 253,254,255,254,254,254,254,254,254,255,254,254,254,254,254,254,254,254, + 254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,1,38,44,44,44,44,44,44,44,44, + 44,44,9,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255,255,255,255, + 254,218,44,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60,0,0,0, + 0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,251, + 47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,204,30,1,182,254,254,182,1,0,0,27,243,254,254,96,0,49,223, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254,111, + 0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31, + 245,137,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,44,62,243,127, + 0,0,0,0,0,0,0,0,0,0,0,0,65,251,254,254,248,115,2,0,0,0,27,177,159,174,192, + 185,159,183,80,0,0,0,0,80,239,254,254,254,99,0,0,0,0,0,0,0,0,87,251,50, + 0,6,43,44,44,52,229,254,253,98,0,11,209,53,0,56,249,254,222,9,0,153,255, + 255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,0,3,37,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,31,192,254,255,255,255,255,254,248,85,3,0,0,0,0,0, + 118,253,255,255,255,255,254,254,159,31,0,0,0,0,1,63,241,254,255,255,255, + 254,254,215,54,0,0,0,0,0,24,201,254,255,255,255,255,254,245,85,5,0,0,0, + 0,26,146,253,255,255,255,255,254,253,144,18,0,0,0,0,0,0,0,0,0,0,0,0,0,1, + 38,0,0,0,0,0,53,212,254,255,255,255,255,254,242,89,5,0,0,0,0,11,148,254, + 255,255,255,255,254,253,139,24,0,0,0,0,9,31,0,0,0,0,0,0,0,2,37,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,252,67,0,0,0,133,239,16,0,0, + 0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,71,253,254,255,255,255,254,255, + 255,255,255,255,255,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240, + 255,255,255,255,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,23,242,255, + 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0, + 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254, + 254,254,254,254,254,254,254,254,254,254,254,204,30,0,1,182,254,254,182, + 1,0,0,27,243,254,254,96,0,0,49,223,254,254,254,254,254,254,254,254,254, + 254,254,254,254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196, + 190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,245,112,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,21,241,127,0,0,0,0,0,0,0,0,0,0,0,0,14,216,254,248,115,2,0,0,0,0,0, + 18,115,175,181,180,148,47,0,0,0,0,0,0,80,239,254,239,36,0,0,0,0,0,0,0,0, + 87,251,50,0,0,0,0,0,9,223,255,238,28,0,75,252,146,0,8,212,254,222,9,0,153, + 255,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,0,2,111,220, + 52,0,0,0,0,0,0,0,0,0,0,0,0,54,220,151,217,254,254,254,254,250,127,3,0,0, + 0,0,0,0,11,165,253,254,254,254,254,194,168,201,29,0,0,0,0,1,102,245,254, + 254,254,254,230,143,225,75,0,0,0,0,0,47,221,254,254,254,254,248,153,210, + 137,5,0,0,23,191,177,186,254,254,254,254,254,188,175,194,24,0,0,0,0,0,0, + 0,0,0,0,0,1,96,223,66,0,0,0,72,224,144,228,254,254,254,254,246,149,215, + 126,3,0,0,0,0,19,184,254,254,254,254,253,181,181,185,20,0,0,6,144,203,32, + 0,0,0,0,0,2,106,221,57,0,0,0,0,0,0,0,0,0,0,2,34,41,5,0,0,0,0,0,0,0,0,0, + 69,252,67,0,0,0,133,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0, + 71,253,254,255,255,255,254,255,255,255,255,255,255,254,254,254,254,254, + 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,4,118,137,137,137,137,137, + 137,137,137,137,135,28,0,0,0,0,0,0,0,0,0,0,0,0,18,240,255,255,255,255,255, + 255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,255,255,255,253,60, + 0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0,0,0,0,0,104,254,254, + 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,204,31,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0, + 0,0,50,224,254,254,254,254,254,254,254,254,254,254,254,254,255,255,254, + 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,31,245,112,0,44,136,137,137,137,137,137,137,137,137,137,137,137,137,137, + 120,6,20,241,127,0,0,0,0,0,0,0,0,0,0,0,0,0,126,248,115,2,0,0,0,0,0,0,0, + 0,5,43,9,0,0,0,0,0,0,0,0,0,80,240,169,2,0,0,0,0,0,0,0,0,87,251,50,0,0,0, + 0,0,9,223,254,179,2,2,172,254,222,17,0,135,254,222,9,0,153,255,255,254, + 106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,1,109,247,254,225,41,0, + 0,0,0,0,0,0,0,0,0,51,226,254,247,138,115,116,116,116,90,3,0,0,0,0,0,0,0, + 0,11,105,116,116,116,116,167,252,254,202,18,0,0,0,0,1,78,116,116,116,116, + 134,241,254,237,64,0,0,0,0,0,43,116,116,116,116,118,214,254,251,129,0,20, + 191,254,253,172,111,116,116,116,116,174,253,254,193,14,0,0,0,0,0,0,0,0, + 0,0,93,244,254,233,54,0,69,236,254,242,127,116,116,116,116,120,219,254, + 250,117,0,0,0,0,0,19,110,116,116,116,116,181,254,254,184,10,4,143,252,254, + 205,20,0,0,0,0,104,246,254,228,45,0,0,0,0,0,0,0,0,3,136,242,246,185,20, + 0,0,0,0,0,0,0,0,69,252,168,137,137,137,198,239,16,0,0,0,0,0,0,12,230,146, + 0,0,0,55,252,81,0,0,0,66,236,237,237,237,237,237,237,247,254,255,254,254, + 254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,8,206, + 237,237,237,237,237,237,237,237,237,235,49,0,0,0,0,0,0,0,0,0,0,0,0,18,240, + 255,255,255,255,255,254,219,43,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255, + 255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0,0,0, + 0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254,254, + 251,254,254,254,254,254,254,254,205,31,0,0,0,1,182,254,254,182,1,0,0,27, + 243,254,254,96,0,0,0,0,50,224,254,254,254,254,254,254,245,249,254,254,254, + 255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,29,228,104,2,170,254,254,254,254,253,252,251,249,251,252, + 254,254,254,254,250,76,19,224,118,0,0,0,0,0,0,0,0,0,0,0,0,0,22,76,2,0,0, + 0,0,0,0,0,0,0,78,233,114,2,0,0,0,0,0,0,0,0,0,57,43,0,0,0,0,0,0,0,0,0,87, + 251,50,0,0,0,0,0,9,223,253,89,0,36,240,254,252,80,0,48,247,222,9,0,153, + 254,255,254,106,0,8,220,255,255,185,1,0,0,0,0,0,0,0,0,0,0,0,5,212,254,255, + 254,88,0,0,0,0,0,0,0,0,0,0,138,254,255,254,167,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,23,241,254,255,250,46,0,0,0,0,0,0,0,0,0,1,183,254,255,254, + 122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,200,1,73,253,254,255,224,10,0,0, + 0,32,245,254,255,247,36,0,0,0,0,0,0,0,0,0,1,196,254,255,254,108,0,165,254, + 255,254,140,0,0,0,0,117,254,255,254,187,1,0,0,0,0,0,0,0,0,0,42,249,254, + 255,243,26,19,239,254,255,252,49,0,0,0,3,207,254,255,254,94,0,0,0,0,0,0, + 0,0,53,248,255,254,254,112,0,0,0,0,0,0,0,0,69,253,241,237,237,237,246,239, + 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,4,14,14,14,14,14,14,14, + 72,235,254,254,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0, + 0,0,0,0,0,0,0,0,12,14,14,14,14,14,14,14,14,14,14,2,0,0,0,0,0,0,0,0,0,0, + 0,0,18,240,255,255,255,255,254,218,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242, + 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138, + 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,254, + 240,119,248,254,254,254,254,254,204,30,0,0,0,0,1,182,254,254,182,1,0,0, + 27,243,254,254,96,0,0,0,0,0,50,223,254,254,254,254,254,198,112,247,254, + 254,255,255,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,1,14,6,54,246,255,253,219,141,82,49,48,42,49,55,102,172, + 241,254,254,194,7,14,7,0,0,0,0,0,0,0,0,0,0,0,0,0,59,43,0,0,0,0,0,0,0,0, + 0,79,240,254,249,114,2,0,0,0,0,0,0,0,0,23,78,1,0,0,0,0,0,0,0,0,87,253,177, + 158,158,158,158,158,161,242,251,170,158,189,254,254,254,206,158,162,242, + 242,161,158,216,254,255,254,198,158,161,241,255,255,185,1,0,0,0,0,0,0,0, + 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0, + 0,0,1,184,255,255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74, + 253,255,255,224,10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197, + 255,255,254,108,0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0, + 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255, + 255,254,95,0,0,0,0,0,0,0,0,72,253,255,254,254,136,0,0,0,0,0,0,0,0,69,252, + 78,14,14,14,140,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0, + 0,0,0,0,0,0,0,70,235,254,255,254,254,254,254,254,254,254,254,254,254,80, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,18,240,255,255,255,254,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242,255, + 255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138,0, + 0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,254,240, + 81,38,247,254,254,254,254,205,30,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243, + 254,254,96,0,0,0,0,0,0,50,223,254,254,254,254,196,4,111,247,254,255,255, + 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,2,171,254,254,177,18,0,0,0,0,0,0,0,0,1,59,238,255,251,76,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,64,232,199,14,0,0,0,0,0,0,0,78,239,254,255,255, + 248,113,2,0,0,0,0,0,0,4,163,244,96,1,0,0,0,0,0,0,0,75,221,221,221,221,221, + 221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221,221, + 221,221,221,221,221,221,221,221,221,221,161,1,0,0,0,0,0,0,0,0,0,0,0,5,213, + 255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,23,242,255,255,250,46,0,0,0,0,0,0,0,0,0,1,184,255, + 255,254,122,0,0,0,0,0,0,0,0,0,0,105,254,255,255,201,1,74,253,255,255,224, + 10,0,0,0,33,246,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197,255,255,254,108, + 0,166,255,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0,0,0,0,0,0,0,0,43, + 249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,209,255,255,254,95,0,0, + 0,0,0,0,0,0,27,224,254,254,248,71,0,0,0,0,0,0,0,0,69,252,68,0,0,0,134,239, + 16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,70, + 235,254,254,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0, + 0,0,5,155,179,179,179,179,179,179,179,179,179,177,36,0,0,0,0,0,0,0,0,0, + 0,0,0,18,240,255,255,254,219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,242, + 255,255,255,255,255,253,60,0,0,0,0,0,0,1,183,254,254,254,254,254,254,138, + 0,0,0,0,0,0,0,104,254,254,254,254,254,254,254,254,254,254,254,254,254,254, + 254,254,254,254,254,254,254,251,47,0,0,0,0,0,0,1,197,254,254,254,241,81, + 0,38,247,254,254,254,204,31,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243, + 254,254,96,0,0,0,0,0,0,0,50,224,254,254,254,196,1,2,111,248,254,255,254, + 111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,54,246,254,254,129,1,0,0,0,0,0,0,0,0,0,15,223,255,254,193,6,0,0,0, + 0,0,0,0,0,0,0,0,0,64,232,254,253,165,11,0,0,0,0,0,78,239,254,255,255,255, + 254,248,113,2,0,0,0,0,4,129,251,254,244,96,1,0,0,0,0,0,0,2,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,0,0,0,0,0,0,0,0, + 0,0,0,0,5,213,255,255,254,88,0,0,0,0,0,0,0,0,0,0,139,254,255,255,167,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,23,241,255,255,250,46,0,0,0,0,0,0,0, + 0,0,1,183,254,255,254,122,0,0,0,0,0,0,0,0,0,0,104,254,255,255,201,1,73, + 253,255,255,224,10,0,0,0,33,245,255,255,247,36,0,0,0,0,0,0,0,0,0,1,197, + 255,255,254,108,0,166,254,255,254,140,0,0,0,0,118,254,255,255,187,1,0,0, + 0,0,0,0,0,0,0,43,249,255,255,243,26,20,240,255,255,252,49,0,0,0,3,208,255, + 255,254,95,0,0,0,0,0,0,0,0,0,51,162,167,84,2,0,0,0,0,0,0,0,0,69,253,199, + 179,179,179,218,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,254,80,0,0, + 0,0,0,0,0,0,0,0,0,0,6,179,206,206,206,206,206,206,206,206,206,204,42,0, + 0,0,0,0,0,0,0,0,0,0,0,18,240,255,254,220,43,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,18,196,206,206,206,206,206,204,48,0,0,0,0,0,0,0,148,206,206,206,206, + 206,206,112,0,0,0,0,0,0,0,84,206,206,206,206,206,206,206,206,206,206,206, + 206,206,206,206,206,206,206,206,206,206,203,38,0,0,0,0,0,0,1,197,254,254, + 241,82,0,0,38,247,254,254,205,31,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0, + 27,243,254,254,96,0,0,0,0,0,0,0,0,50,224,254,254,196,1,0,2,111,248,254, + 254,111,0,0,0,0,0,0,0,132,254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,2,170,254,255,255,242,137,48,10,1,1,1,1,3,19,76,187,253,255,255, + 250,75,0,0,0,0,0,0,0,0,0,0,0,39,231,254,254,254,253,164,11,0,0,0,78,238, + 254,255,255,255,255,255,254,248,113,2,0,0,4,129,250,255,255,254,244,72, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,127,251,254,234,49,0,0,0,0,0,0,0,0, + 0,0,63,236,254,250,143,136,142,142,141,91,1,0,0,0,0,0,0,0,0,6,113,142,142, + 142,131,178,253,254,215,23,0,0,0,0,0,0,0,0,0,0,98,246,254,243,75,0,0,0, + 0,0,34,138,142,142,141,126,224,254,253,145,0,26,206,254,254,182,123,142, + 142,142,130,185,254,254,207,17,0,0,0,0,0,0,0,0,0,0,109,249,254,240,64,0, + 84,242,254,247,130,139,142,142,141,128,229,254,252,132,0,0,0,0,0,0,0,0, + 0,0,13,185,254,254,198,12,5,161,253,254,220,114,141,142,142,137,152,250, + 254,237,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255, + 255,255,239,16,0,0,0,0,0,0,12,230,146,0,0,0,55,252,81,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,70,236,254,254,254,254,254,254,254,254,254,80,0,0,0,0,0,0,0, + 0,0,0,0,0,0,2,3,3,3,3,3,3,3,3,3,2,0,0,0,0,0,0,0,0,0,0,0,0,0,18,240,254, + 219,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,3,3,3,3,2,0,0,0,0,0,0, + 0,0,2,3,3,3,3,3,2,1,0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, + 3,3,3,2,0,0,0,0,0,0,0,1,197,254,240,82,0,0,0,38,247,254,205,30,0,0,0,0, + 0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0,0,0,0,0,50, + 224,254,196,1,0,0,1,111,248,254,111,0,0,0,0,0,0,0,132,254,255,225,10,1, + 196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,54,245,255,255,255,254,254,247,225, + 200,199,199,199,204,237,252,254,255,255,255,254,192,6,0,0,0,0,0,0,0,0,0, + 0,7,151,252,254,255,254,253,188,37,0,75,239,254,255,255,255,255,255,255, + 255,254,248,113,2,22,160,251,254,255,255,254,185,17,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,4,130,231,66,0,0,0,0,0,0,0,0,0,0,0,0,68,232,158,207,254, + 254,254,254,248,108,1,0,0,0,0,0,0,6,147,252,254,254,254,254,183,179,215, + 40,0,0,0,0,0,0,0,0,0,0,0,1,102,236,92,1,0,0,0,0,35,211,254,254,254,254, + 245,146,222,155,8,0,0,31,206,188,174,253,254,254,254,253,176,186,208,33, + 0,0,0,0,0,0,0,0,0,0,0,2,113,234,81,0,0,0,88,235,148,220,254,254,254,254, + 242,144,227,144,6,0,0,0,0,0,0,0,0,0,0,0,18,186,200,28,0,0,10,162,219,148, + 247,254,254,254,254,209,156,232,71,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,146,0,0, + 0,55,252,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,235,254,254,254,254,254, + 254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,6,6,6,6,6,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,18,240,218,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,197,240,82,0,0,0,0,38, + 247,205,30,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96, + 0,0,0,0,0,0,0,0,0,0,50,224,196,1,0,0,0,2,111,247,111,0,0,0,0,0,0,0,132, + 254,255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,117,193,193,193,193, + 193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,193,189,44, + 0,0,0,0,0,0,0,0,0,0,0,7,151,252,254,255,255,254,223,38,128,235,254,254, + 255,255,255,255,255,254,254,241,169,19,178,254,254,255,255,254,190,18,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 43,190,254,255,255,255,255,254,246,72,0,0,0,0,0,0,114,252,255,255,255,255, + 254,253,155,30,0,0,0,0,0,0,0,0,0,0,0,0,0,1,37,0,0,0,0,0,10,196,254,255, + 255,255,255,254,243,93,8,0,0,0,0,25,142,253,255,255,255,255,254,253,145, + 26,0,0,0,0,0,0,0,0,0,0,0,0,0,2,37,0,0,0,0,0,51,209,254,255,255,255,255, + 254,240,85,6,0,0,0,0,0,0,0,0,0,0,0,0,0,17,23,0,0,0,0,10,99,245,255,255, + 255,255,254,254,193,44,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12,230,147,0,0,0,55,252,81, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,254,254,254,254,80, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,205,216,216,216,215,147,7,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,18,204,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,183,82,0,0,0,0,0,38,198,31, + 0,0,0,0,0,0,0,0,0,0,1,182,254,254,182,1,0,0,27,243,254,254,96,0,0,0,0,0, + 0,0,0,0,0,0,50,167,1,0,0,0,0,2,112,104,0,0,0,0,0,0,0,132,254,255,225,10, + 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,26,209,217,217,217,217,217,217,217, + 217,217,217,217,217,217,217,217,217,217,217,217,217,217,108,0,0,0,0,0,0, + 0,0,0,0,0,0,8,151,252,254,254,245,99,1,1,28,104,164,209,218,218,218,214, + 173,106,40,2,0,38,212,254,254,254,213,37,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,234,254,255,255,255,253, + 166,11,0,0,0,0,0,0,26,198,254,255,255,255,254,215,41,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,79,239,254,255,255,255,253,155,9,0,0,0,0,0,0, + 32,206,254,255,255,255,254,208,34,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,89,243,254,255,255,255,252,144,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,10,162,253,255,255,255,254,236,73,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,69,253,255,255,255,255,255,239,16,0,0,0,0,0,0,12, + 230,222,45,0,3,131,253,81,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,236,254, + 254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,86,165,166,166,147, + 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,35,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,0,0,0,0, + 0,0,15,27,0,0,0,0,0,0,0,0,0,0,0,0,119,166,166,118,0,0,0,17,158,166,165, + 62,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,2,17,0,0,0,0,0,0,0,132,254,255, + 225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,159,166,166,166,166,166, + 166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,166,82,0,0, + 0,0,0,0,0,0,0,0,0,0,0,8,151,252,245,99,1,0,0,0,0,0,6,7,7,7,7,1,0,0,0,0, + 0,38,213,254,213,39,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,67,164,166,166,166,137,11,0,0,0,0,0,0,0,0,26, + 153,166,166,166,160,41,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 75,165,166,166,166,130,9,0,0,0,0,0,0,0,0,32,156,166,166,166,157,34,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,84,165,166,166,166,123,7,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,135,166,166,166,165,70,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,61,250,255,255,255,255, + 255,237,15,0,0,0,0,0,0,10,221,254,219,49,124,250,253,79,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,70,237,254,254,254,254,254,80,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225, + 10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,147,99,1,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,38,179,38,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,131,251, + 254,254,254,249,116,1,0,0,0,0,0,0,0,69,237,254,236,250,254,178,15,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,70,236,254,254,254,254,80,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254, + 255,225,10,1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,105,144, + 145,144,96,2,0,0,0,0,0,0,0,0,0,64,144,145,145,129,14,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,71,235,254,254,254,80,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10, + 1,196,190,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,72, + 236,254,253,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,132,254,255,225,10,12,209,141,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,237,254,80,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255, + 224,21,163,155,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,71,236,80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255,233,173,155,8,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,71,63,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,254,255, + 252,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,132,254,253,155,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132,253,156,9,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 132,160,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,}; + +#endif // linux || svr4 +#endif // __SYNAES_SYMBOL_H__ diff --git a/kscd/kscdmagic/syna.h b/kscd/kscdmagic/syna.h new file mode 100644 index 00000000..40128532 --- /dev/null +++ b/kscd/kscdmagic/syna.h @@ -0,0 +1,180 @@ +/* + $Id$ + + Synaesthesia - program to display sound graphically + Copyright (C) 1997 Paul Francis Harrison + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author may be contacted at: + phar6@student.monash.edu.au + or + 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia +*/ + +#ifndef __SYNA_H__ +#define __SYNA_H__ + + +#if defined(__linux__) || defined(__svr4__) || defined(__osf__) + +void error(const char *str, bool syscall=false); //Display error and exit +void warning(const char *str, bool syscall=false); //Display error + +//void error(char *str,bool syscall=false); +inline void attempt(int x, const char *y, bool syscall=false) { if (x == -1) error(y,syscall); } + +//void warning(char *str,bool syscall=false); +inline void attemptNoDie(int x, const char *y, bool syscall=false) { if (x == -1) warning(y,syscall); } + +#include "polygon.h" +#include "magicconf.h" + +/***************************************/ + + +#define PROGNAME "kscdmagic" + + +#ifdef __FreeBSD__ + + +typedef unsigned short sampleType; + +#else + +typedef short sampleType; + +#ifndef __linux__ + +#warning This target has not been tested! + +#endif +#endif + +#ifdef __osf__ +#include <machine/endian.h> +#else +#include <endian.h> +#endif +#if BYTE_ORDER == BIG_ENDIAN +#define BIGENDIAN +#else +#define LITTLEENDIAN +#endif + + +#define n (1<<m) +#define recSize (1<<m-overlap) + +/* core */ +extern volatile sampleType *data; +//extern unsigned char *output, *lastOutput, *lastLastOutput; +extern Bitmap<unsigned short> outputBmp, lastOutputBmp, lastLastOutputBmp; +#define ucoutput ((unsigned char*)outputBmp.data) +#define lastOutput ((unsigned char*)lastOutputBmp.data) +#define lastLastOutput ((unsigned char*)lastLastOutputBmp.data) + +inline unsigned short combiner(unsigned short a,unsigned short b) { + //Not that i want to give the compiler a hint or anything... + unsigned char ah = a>>8, al = a&255, bh = b>>8, bl = b&255; + if (ah < 64) ah *= 4; else ah = 255; + if (al < 64) al *= 4; else al = 255; + if (bh > ah) ah = bh; + if (bl > al) al = bl; + return ah*256+al; +} + +extern PolygonEngine<unsigned short,combiner,2> polygonEngine; + +extern int outWidth, outHeight; + +void allocOutput(int w,int h); + +void coreInit(); +void setStarSize(double size); +int coreGo(); +void fade(); + +/* *wrap */ +void screenInit(int xHint,int yHint,int widthHint,int heightHint); +void screenSetPalette(unsigned char *palette); +void screenEnd(void); +void screenShow(void); +int sizeUpdate(void); + +void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit); + +/* ui */ +void interfaceInit(); +void interfaceSyncToState(); +void interfaceEnd(); +bool interfaceGo(); + +enum SymbolID { + Speaker, Bulb, + Play, Pause, Stop, SkipFwd, SkipBack, + Handle, Pointer, Open, NoCD, Exit, + Zero, One, Two, Three, Four, + Five, Six, Seven, Eight, Nine, + Slider, Selector, Plug, Loop, Box, Bar, + Flame, Wave, Stars, Star, Diamond, Size, FgColor, BgColor, + Save, Reset, TrackSelect, + NotASymbol +}; + +/* State information */ + +extern SymbolID state; +extern int track, frames; +extern double trackProgress; +extern char **playList; +extern int playListLength, playListPosition; +extern SymbolID fadeMode; +extern bool pointsAreDiamonds; +extern double brightnessTwiddler; +extern double starSize; +extern double fgRedSlider, fgGreenSlider, bgRedSlider, bgGreenSlider; + +extern double volume; + +void setStateToDefaults(); +void saveConfig(); + +void putString(char *string,int x,int y,int red,int blue); + +/* sound */ +enum SoundSource { SourceLine, SourceCD, SourcePipe }; + +void cdOpen(char *cdromName); +void cdClose(void); +void cdGetStatus(int &track, int &frames, SymbolID &state); +void cdPlay(int trackFrame, int endFrame=-1); +void cdStop(void); +void cdPause(void); +void cdResume(void); +void cdEject(void); +void cdCloseTray(void); +int cdGetTrackCount(void); +int cdGetTrackFrame(int track); +void openSound(SoundSource sound, int downFactor, const char *dspName, char *mixerName); +void closeSound(); +void setupMixer(double &loudness); +void setVolume(double loudness); +int getNextFragment(void); + + +#endif // linux || svr4 +#endif // __SYNA_H__ diff --git a/kscd/kscdmagic/version.h b/kscd/kscdmagic/version.h new file mode 100644 index 00000000..aa5df67c --- /dev/null +++ b/kscd/kscdmagic/version.h @@ -0,0 +1 @@ +#define KSCDMAGICVERSION "2.0" diff --git a/kscd/kscdmagic/xlib.c b/kscd/kscdmagic/xlib.c new file mode 100644 index 00000000..2fb15c19 --- /dev/null +++ b/kscd/kscdmagic/xlib.c @@ -0,0 +1,781 @@ +/* + * XaoS, a fast portable realtime fractal zoomer + * Copyright (C) 1996,1997 by + * + * Jan Hubicka (hubicka@paru.cas.cz) + * Thomas Marsh (tmarsh@austin.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * + * Shamelessly ripped for use in xsynaesthesia + */ +/*#include "aconfig.h"*/ +#define X11_DRIVER +/*#define MITSHM*/ + +#ifdef X11_DRIVER +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/cursorfont.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef __FreeBSD__ +#include <malloc.h> +#else +#include <stdlib.h> +#endif +#include "xlib.h" +#ifdef AMIGA +#define XFlush(x) while(0) +#endif + +#undef PIXMAP + +#define chkalloc(n) if (!n) fprintf(stderr, "out of memory\n"), exit(-1) + +int xupdate_size(xdisplay * d) +{ + int tmp; + Window wtmp; + int width = d->width, height = d->height; + XGetGeometry(d->display, d->window, &wtmp, &tmp, &tmp, &d->width, &d->height, (unsigned int *) &tmp, (unsigned int *) &tmp); + if ((int)d->width != width || (int)d->height != height) + return 1; + return 0; +} + +void xflip_buffers(xdisplay * d) +{ + d->back = d->vbuffs[d->current]; + d->current ^= 1; + d->vbuff = d->vbuffs[d->current]; +} + +void draw_screen(xdisplay * d) +{ + switch (d->image[0]->bits_per_pixel) { + case 16:{ + unsigned short *de; + unsigned char *s; + unsigned char *e; + for (s = (unsigned char *) d->vbuffs[d->current], + e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height), + de = (unsigned short *) d->data[d->current]; s < e; s += 8, de += 8) + *de = d->pixels[*s], + *(de + 1) = d->pixels[*(s + 1)], + *(de + 2) = d->pixels[*(s + 2)], + *(de + 3) = d->pixels[*(s + 3)], + *(de + 4) = d->pixels[*(s + 4)], + *(de + 5) = d->pixels[*(s + 5)], + *(de + 6) = d->pixels[*(s + 6)], + *(de + 7) = d->pixels[*(s + 7)]; + s -= 8; + de -= 8; + for (; s < e; s++, de++) + *de = d->pixels[*s]; + break; + } + case 24:{ + unsigned char *de; + unsigned char *s; + unsigned char *e; + for (s = (unsigned char *) d->vbuffs[d->current], + e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height), + de = (unsigned char *) d->data[d->current]; s < e; s++, de+=3) + de[0] = d->pixels[*s], + de[1] = d->pixels[*s]>>8, + de[2] = d->pixels[*s]>>16; + + break; + } + case 32:{ + unsigned long *de; + unsigned char *s; + unsigned char *e; + for (s = (unsigned char *) d->vbuffs[d->current], + e = (unsigned char *) d->vbuffs[d->current] + (d->linewidth * d->height), + de = (unsigned long *) d->data[d->current]; s < e; s += 8, de += 8) + *de = d->pixels[*s], + *(de + 1) = d->pixels[*(s + 1)], + *(de + 2) = d->pixels[*(s + 2)], + *(de + 3) = d->pixels[*(s + 3)], + *(de + 4) = d->pixels[*(s + 4)], + *(de + 5) = d->pixels[*(s + 5)], + *(de + 6) = d->pixels[*(s + 6)], + *(de + 7) = d->pixels[*(s + 7)]; + s -= 8; + de -= 8; + for (; s < e; s++, de++) + *de = d->pixels[*s]; + break; + } + } +#ifdef MITSHM + if (d->SharedMemFlag) { + XShmPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0, + 0, d->width, d->height, True); + XFlush(d->display); + } else +#endif + { + XPutImage(d->display, d->window, d->gc, d->image[d->current], 0, 0, 0, 0, d->width, d->height); + XFlush(d->display); + } + d->screen_changed = 0; +} + +#ifdef MITSHM +int alloc_shm_image(xdisplay * new) +{ + register char *ptr; + int temp, size = 0, i; + ptr = DisplayString(new->display); + if (!ptr || (*ptr == ':') || !strncmp(ptr, "localhost:", 10) || + !strncmp(ptr, "unix:", 5) || !strncmp(ptr, "local:", 6)) { + new->SharedMemOption = XQueryExtension(new->display, "MIT-SHM", &temp, &temp, &temp); + } else { + new->SharedMemOption = False; + return 0; + } + new->SharedMemFlag = False; +#if 0 + new->SharedMemOption = True; + new->SharedMemFlag = False; +#endif + + if (new->SharedMemFlag) { + XShmDetach(new->display, &new->xshminfo[0]); + XShmDetach(new->display, &new->xshminfo[1]); + new->image[0]->data = (char *) NULL; + new->image[1]->data = (char *) NULL; + shmdt(new->xshminfo[0].shmaddr); + shmdt(new->xshminfo[1].shmaddr); + } + for (i = 0; i < 2; i++) { + if (new->SharedMemOption) { + int mul; + if (new->depth == 8) + mul = 1; + else if (new->depth <= 24) + mul = 2; + else + mul = 4; + new->SharedMemFlag = False; + new->image[i] = XShmCreateImage(new->display, new->visual, new->depth, ZPixmap, + NULL, &new->xshminfo[i], new->width, new->height * mul); + if (new->image[i]) { + temp = new->image[i]->bytes_per_line * new->image[i]->height; + new->linewidth = new->image[i]->bytes_per_line * 8 / new->image[i]->bits_per_pixel; + if (temp > size) + size = temp; + new->xshminfo[i].shmid = shmget(IPC_PRIVATE, size, IPC_CREAT | 0777); + if (new->xshminfo[i].shmid != -1) { + new->xshminfo[i].shmaddr = (char *) shmat(new->xshminfo[i].shmid, 0, 0); + if (new->xshminfo[i].shmaddr != (char *) -1) { + new->image[i]->data = new->xshminfo[i].shmaddr; + new->data[i] = new->vbuffs[i] = (char *) new->image[i]->data; + new->xshminfo[i].readOnly = True; + + new->SharedMemFlag = XShmAttach(new->display, &new->xshminfo[i]); + XSync(new->display, False); + if (!new->SharedMemFlag) { + XDestroyImage(new->image[i]); + new->image[i] = (XImage *) NULL; + new->SharedMemFlag = 0; + return 0; + } + } + /* Always Destroy Shared Memory Ident */ + shmctl(new->xshminfo[i].shmid, IPC_RMID, 0); + } + if (!new->SharedMemFlag) { + XDestroyImage(new->image[i]); + new->image[i] = (XImage *) NULL; + new->SharedMemFlag = 0; + return 0; + } + } else { + new->SharedMemFlag = 0; + return 0; + } + } else { + new->SharedMemFlag = 0; + return 0; + } + } + new->current = 0; + xflip_buffers(new); + return 1; +} + +void free_shm_image(xdisplay * d) +{ + if (d->SharedMemFlag) { + XDestroyImage(d->image[0]); + XDestroyImage(d->image[1]); + XShmDetach(d->display, &d->xshminfo[0]); + XShmDetach(d->display, &d->xshminfo[1]); + shmdt(d->xshminfo[0].shmaddr); + shmdt(d->xshminfo[1].shmaddr); + } +} + +#endif + +int alloc_image(xdisplay * d) +{ + int i; +#ifdef MITSHM + if (!d->params->nomitshm && alloc_shm_image(d)) { + if (d->depth != 8) { + for (i = 0; i < 2; i++) + d->vbuffs[i] = malloc(d->linewidth * d->height); + } + return 1; + } +#endif + for (i = 0; i < 2; i++) { + d->image[i] = XCreateImage(d->display, d->visual, d->depth, ZPixmap, 0, + NULL, d->width, d->height, 8, 0); + if (d->image[i] == NULL) { + printf("Out of memory for image..exiting\n"); + exit(3); + } + /*Add a little extra memory to catch overruns when dumping image to buffer in draw_screen*/ + d->image[i]->data = malloc(d->image[i]->bytes_per_line * d->height + 32); + memset(d->image[i]->data,0,d->image[i]->bytes_per_line * d->height); + + if (d->image[i]->data == NULL) { + printf("Out of memory for image buffers..exiting\n"); + exit(3); + } + d->data[i] = d->vbuffs[i] = (char *) d->image[i]->data; + d->linewidth = d->image[i]->bytes_per_line * 8 / d->image[i]->bits_per_pixel; + } + if (d->depth != 8) { + for (i = 0; i < 2; i++) { + /* Add a little extra memory to catch overruns */ + /* when dumping image to buffer in draw_screen */ + d->vbuffs[i] = malloc(d->linewidth * d->height + 32); + memset(d->vbuffs[i],0,d->linewidth * d->height); + + if (d->vbuffs[i] == NULL) { + printf("Out of memory for image buffers2..exiting\n"); + exit(3); + } + } + } + xflip_buffers(d); + return 1; +} + +void free_image(xdisplay * d) +{ + if (d->depth != 8) + free(d->vbuffs[0]), free(d->vbuffs[1]); +#ifdef MITSHM + if (d->SharedMemFlag) { + free_shm_image(d); + return; + } +#endif + XDestroyImage(d->image[0]); + XDestroyImage(d->image[1]); +} +#define MAX(x,y) ((x)>(y)?(x):(y)) + + +xdisplay *xalloc_display(const char *s, int xHint, int yHint, int x, int y, xlibparam * params) +{ + xdisplay *xd; + Visual *defaultvisual; + XVisualInfo vis; + + + xd = (xdisplay *) calloc(sizeof(xdisplay), 1); + chkalloc(xd); + xd->display = XOpenDisplay((char *) NULL); + if (!xd->display) { + free((void *) xd); + return NULL; + } + xd->screen = DefaultScreen(xd->display); + xd->attributes = (XSetWindowAttributes *) + malloc(sizeof(XSetWindowAttributes)); + chkalloc(xd->attributes); + xd->attributes->background_pixel = BlackPixel(xd->display, + xd->screen); + xd->attributes->border_pixel = BlackPixel(xd->display, xd->screen); + xd->attributes->event_mask = ButtonPressMask | StructureNotifyMask | ButtonReleaseMask | ButtonMotionMask | KeyPressMask | ExposureMask | KeyReleaseMask; + xd->attributes->override_redirect = False; + xd->attr_mask = CWBackPixel | CWBorderPixel | CWEventMask; + xd->classX = InputOutput; + xd->xcolor.n = 0; + xd->parent_window = RootWindow(xd->display, xd->screen); + defaultvisual = DefaultVisual(xd->display, xd->screen); + xd->params = params; + if (!params->usedefault) { + if (defaultvisual->class != PseudoColor || (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) && vis.colormap_size > 128)) { + xd->fixedcolormap = 1; + if (!XMatchVisualInfo(xd->display, xd->screen, 15, TrueColor, &vis)) { + if (!XMatchVisualInfo(xd->display, xd->screen, 16, TrueColor, &vis)) { + if (!XMatchVisualInfo(xd->display, xd->screen, 32, TrueColor, &vis) && + !XMatchVisualInfo(xd->display, xd->screen, 24, TrueColor, &vis)) { + if (!XMatchVisualInfo(xd->display, xd->screen, 8, PseudoColor, &vis) && + !XMatchVisualInfo(xd->display, xd->screen, 7, PseudoColor, &vis)) { + if (!XMatchVisualInfo(xd->display, xd->screen, 8, TrueColor, &vis) && + !XMatchVisualInfo(xd->display, xd->screen, 8, StaticColor, &vis) && + !XMatchVisualInfo(xd->display, xd->screen, 8, StaticGray, &vis)) { + printf("Display does not support PseudoColor depth 7,8,StaticColor depth 8, StaticGray depth 8, Truecolor depth 8,15,16,24 nor 32!\n"); + return NULL; + } else + xd->truecolor = 1; + } else + xd->fixedcolormap = 0, xd->truecolor = 0; + } else + xd->truecolor = 1; + } else + xd->truecolor = 1; + } else + xd->truecolor = 1; + } else { + xd->truecolor = 0; + } + xd->depth = vis.depth; + xd->visual = vis.visual; + } else { /*usedefault */ + vis.depth = xd->depth = DefaultDepth(xd->display, xd->screen); + xd->visual = defaultvisual; + switch (defaultvisual->class) { + case PseudoColor: + if (xd->depth <= 8) { + xd->depth = 8; + xd->truecolor = 0; + xd->fixedcolormap = 0; + } else { + printf("Pseudocolor visual on unsuported depth\n"); + return NULL; + } + break; + case TrueColor: + case StaticColor: + case StaticGray: + xd->truecolor = 1; + xd->fixedcolormap = 1; + if (xd->depth <= 8) + xd->depth = 8; + else if (xd->depth <= 16) + xd->depth = 16; + else if (xd->depth <= 32) + xd->depth = 32; + else { + printf("Truecolor visual on unsuported depth\n"); + return NULL; + } + break; + default: + printf("Unusuported visual\n"); + break; + } + } + /*xd->visual->map_entries = 256; */ + xd->colormap = xd->defaultcolormap = DefaultColormap(xd->display, xd->screen); + + xd->window_name = s; + xd->height = y; + xd->width = x; + xd->border_width = 2; + xd->lastx = 0; + xd->lasty = 0; + xd->font_struct = (XFontStruct *) NULL; + + xd->window = XCreateWindow(xd->display, xd->parent_window, xHint, yHint, + xd->width, xd->height, xd->border_width, + vis.depth, xd->classX, xd->visual, + xd->attr_mask, xd->attributes); + if (!xd->fixedcolormap && params->privatecolormap) { + unsigned long pixels[256]; + int i; + xd->colormap = XCreateColormap(xd->display, xd->window, xd->visual, AllocNone); + XAllocColorCells(xd->display, xd->colormap, 1, 0, 0, pixels, MAX(xd->visual->map_entries, 256)); + for (i = 0; i < 16; i++) { + xd->xcolor.c[i].pixel = pixels[i]; + } + XQueryColors(xd->display, xd->defaultcolormap, xd->xcolor.c, 16); + XStoreColors(xd->display, xd->colormap, xd->xcolor.c, 16); + xd->privatecolormap = 1; + } + if (!xd->fixedcolormap) + XSetWindowColormap(xd->display, xd->window, xd->colormap); + xd->gc = XCreateGC(xd->display, xd->window, 0L, &(xd->xgcvalues)); + XSetBackground(xd->display, xd->gc, + BlackPixel(xd->display, xd->screen)); + XSetForeground(xd->display, xd->gc, + WhitePixel(xd->display, xd->screen)); + XStoreName(xd->display, xd->window, xd->window_name); + XMapWindow(xd->display, xd->window); +#if 1 + XSelectInput(xd->display, xd->window, + /* ExposureMask | */ + KeyPress | + /* KeyRelease | */ + /* ConfigureRequest | */ + /* FocusChangeMask | */ + StructureNotifyMask | + ButtonPressMask | ButtonReleaseMask); +#endif +#ifdef PIXAMP + xd->pixmap = XCreatePixmap(xd->display, xd->window, xd->width, + xd->height, xd->depth); +#endif + + { + XColor c; + Pixmap p = XCreatePixmap(xd->display, xd->window, 1,1,1); + memset(&c,0,sizeof(c)); + xd->cursor = XCreatePixmapCursor(xd->display, p,p, + &c,&c, 0,0); + /* We don't need no fancy cursor + XDefineCursor(xd->display,xd->window,xd->cursor); + */ + XFreePixmap(xd->display, p); + } + + return (xd); +} + +void xsetcolor(xdisplay * d, int col) +{ + switch (col) { + case 0: + XSetForeground(d->display, d->gc, + BlackPixel(d->display, d->screen)); + break; + case 1: + XSetForeground(d->display, d->gc, + WhitePixel(d->display, d->screen)); + break; + default: + if ((col - 2) > d->xcolor.n) { + fprintf(stderr, "color error\n"); + exit(3); + } + XSetForeground(d->display, d->gc, + d->xcolor.c[col - 2].pixel); + break; + } +} +void xrotate_palette(xdisplay * d, int direction, unsigned char co[3][256], int ncolors) +{ + int i, p; + + if (d->privatecolormap) { + for (i = 0; i < d->xcolor.n; i++) { + p = d->xcolor.c[i].pixel; + d->xcolor.c[i].red = (int) co[0][p] * 256; + d->xcolor.c[i].green = (int) co[1][p] * 256; + d->xcolor.c[i].blue = (int) co[2][p] * 256; + } + XStoreColors(d->display, d->colormap, d->xcolor.c, d->xcolor.n); + } + if (d->truecolor) { + unsigned long oldpixels[256]; + memcpy(oldpixels, d->pixels, sizeof(oldpixels)); + p = (ncolors - 1 + direction) % (ncolors - 1) + 1; + for (i = 1; i < ncolors; i++) { /*this is ugly..I know */ + d->pixels[i] = oldpixels[p]; + p++; + if (p >= ncolors) + p = 1; + } + draw_screen(d); + } +} +int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite) +{ + d->xcolor.n++; + d->xcolor.c[d->xcolor.n - 1].flags = DoRed | DoGreen | DoBlue; + d->xcolor.c[d->xcolor.n - 1].red = r; + d->xcolor.c[d->xcolor.n - 1].green = g; + d->xcolor.c[d->xcolor.n - 1].blue = b; + d->xcolor.c[d->xcolor.n - 1].pixel = d->xcolor.n - 1; + if ((readwrite && !d->fixedcolormap) || d->privatecolormap) { + unsigned long cell; + if (d->privatecolormap) { + cell = d->xcolor.c[d->xcolor.n - 1].pixel += 16; + if (d->xcolor.c[d->xcolor.n - 1].pixel >= d->visual->map_entries) { + d->xcolor.n--; + return (-1); + } + } else { + if (!XAllocColorCells(d->display, d->colormap, 0, 0, 0, &cell, 1)) { + d->xcolor.n--; + if (d->xcolor.n <= 32) + printf("Colormap is too full! close some colorfull aplications or use -private\n"); + return (-1); + } + d->xcolor.c[d->xcolor.n - 1].pixel = cell; + } + XStoreColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1])); + return (cell); + } + if (!XAllocColor(d->display, d->colormap, &(d->xcolor.c[d->xcolor.n - 1]))) { + d->xcolor.n--; + if (d->xcolor.n <= 32) + printf("Colormap is too full! close some colorfull aplications or use -private\n"); + return (-1); + } + d->pixels[d->xcolor.n - 1] = d->xcolor.c[d->xcolor.n - 1].pixel; + return (d->depth != 8 ? d->xcolor.n - 1 : d->xcolor.c[d->xcolor.n - 1].pixel); +} + +void xfree_colors(xdisplay * d) +{ + unsigned long pixels[256]; + int i; + for (i = 0; i < d->xcolor.n; i++) + pixels[i] = d->xcolor.c[i].pixel; + if (!d->privatecolormap) + XFreeColors(d->display, d->colormap, pixels, d->xcolor.n, 0); + d->xcolor.n = 0; +} + +void xfree_display(xdisplay * d) +{ + XSync(d->display, 0); + if (d->font_struct != (XFontStruct *) NULL) { + XFreeFont(d->display, d->font_struct); + } + XUnmapWindow(d->display, d->window); +#ifdef PIXMAP + XFreePixmap(d->display, d->pixmap); +#endif + XDestroyWindow(d->display, d->window); + XFreeCursor(d->display, d->cursor); + XCloseDisplay(d->display); + free((void *) d->attributes); + free((void *) d); +} + +#ifdef PIXMAP +void xline(xdisplay * d, int x1, int y1, int x2, int y2) +{ + XDrawLine(d->display, d->pixmap, d->gc, x1, y1, x2, y2); + d->lastx = x2, d->lasty = y2; + d->screen_changed = 1; +} void xlineto(xdisplay * d, int x, int y) +{ + + XDrawLine(d->display, d->pixmap, d->gc, d->lastx, d->lasty, x, y); + d->lastx = x, d->lasty = y; + d->screen_changed = 1; +} void xrect(xdisplay * d, int x1, int y1, int x2, int y2) +{ + + XDrawRectangle(d->display, d->pixmap, d->gc, x1, y1, + (x2 - x1), (y2 - y1)); + d->lastx = x2, d->lasty = y2; + d->screen_changed = 1; +} void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2) +{ + + XFillRectangle(d->display, d->pixmap, d->gc, x1, y1, + (x2 - x1), (y2 - y1)); + d->lastx = x2, d->lasty = y2; + d->screen_changed = 1; +} void xpoint(xdisplay * d, int x, int y) +{ + + XDrawPoint(d->display, d->pixmap, d->gc, x, y); + d->lastx = x, d->lasty = y; + d->screen_changed = 1; +} void xflush(xdisplay * d) +{ + + draw_screen(d); + XFlush(d->display); +} + +void xclear_screen(xdisplay * d) +{ + xfillrect(d, 0, 0, d->width, d->height); + d->screen_changed = 1; +} + +#endif +void xmoveto(xdisplay * d, int x, int y) +{ + d->lastx = x, d->lasty = y; +} int xsetfont(xdisplay * d, char *font_name) +{ + + if (d->font_struct != (XFontStruct *) NULL) { + XFreeFont(d->display, d->font_struct); + } + d->font_struct = XLoadQueryFont(d->display, font_name); + if (!d->font_struct) { + fprintf(stderr, "could not load font: %s\n", font_name); + exit(3); + } + return (d->font_struct->max_bounds.ascent + d->font_struct->max_bounds.descent); +} + +void xouttext(xdisplay * d, char *string) +{ + int sz; + + sz = strlen(string); + XDrawImageString(d->display, d->window, d->gc, d->lastx, d->lasty, + string, sz); +#if 0 + d->lastx += XTextWidth(d->font_struct, string, sz); + d->screen_changed = 1; +#endif +} void xresize(xdisplay * d, XEvent * ev) +{ + +#ifdef PIXMAP + XFreePixmap(d->display, d->pixmap); +#endif + d->width = ev->xconfigure.width; + d->height = ev->xconfigure.height; +#ifdef PIXMAP + d->pixmap = XCreatePixmap(d->display, d->window, d->width, + d->height, d->depth); +#endif +} + +#ifdef PIXMAP +void xarc(xdisplay * d, int x, int y, unsigned int w, + unsigned int h, int a1, int a2) +{ + XDrawArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2); +} void xfillarc(xdisplay * d, int x, int y, unsigned int w, + unsigned int h, int a1, int a2) +{ + XFillArc(d->display, d->pixmap, d->gc, x, y, w, h, a1, a2); +} +#endif + +void xsize_set(xdisplay *d, int width, int height) +{ + XResizeWindow(d->display, d->window, width, height); +} + +int xmouse_x(xdisplay * d) +{ + + return d->mouse_x; +} + +int xmouse_y(xdisplay * d) +{ + return d->mouse_y; +} + +void xmouse_update(xdisplay * d) +{ + Window rootreturn, childreturn; + int rootx = 0, rooty = 0, buttons = 0; + + XEvent event; + + if (XCheckMaskEvent(d->display,ButtonPressMask | ButtonReleaseMask, &event)) { + if (event.type == ButtonPress) + d->mouse_buttons |= 1 << ((XButtonEvent*)(&event))->button; + else + d->mouse_buttons &= ~( 1 << ((XButtonEvent*)(&event))->button ); + } + + XQueryPointer(d->display, d->window, &rootreturn, &childreturn, + &rootx, &rooty, &(d->mouse_x), &(d->mouse_y), + &buttons); +} + +char xkeyboard_query(xdisplay * d) { + XEvent event; + + if (XCheckMaskEvent(d->display,KeyPressMask | KeyReleaseMask, &event)) { + char *str = + XKeysymToString(XLookupKeysym((XKeyPressedEvent*)(&event),0)); + + if ( ((XKeyPressedEvent*)(&event))->state & + (ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask) ) + return 0; + + if (str) { + char key; + + if (strlen(str) == 1) + key = str[0]; + else if (strcmp(str,"equal") == 0) + key = '='; + else if (strcmp(str,"minus") == 0) + key = '-'; + else if (strcmp(str,"bracketleft") == 0) + key = '['; + else if (strcmp(str,"bracketright") == 0) + key = ']'; + else if (strcmp(str,"comma") == 0) + key = ','; + else if (strcmp(str,"period") == 0) + key = '.'; + else if (strcmp(str,"slash") == 0) + key = '/'; + else return 0; + + if ( ((XKeyPressedEvent*)(&event))->state & ShiftMask ) + switch(key) { + case '=' : key = '+'; break; + case '[' : key = '{'; break; + case ']' : key = '}'; break; + case ',' : key = '<'; break; + case '/' : key = '?'; break; + default : + if (key >= 'a' && key <= 'z') + key = key+'A'-'a'; + break; + } + return key; + } + } + + return 0; +} + +int xsize_update(xdisplay *d,int *width,int *height) { + XEvent event; + + if (XCheckMaskEvent(d->display,StructureNotifyMask, &event)) { + if (event.type == ConfigureNotify) { + xupdate_size(d); + free_image(d); + alloc_image(d); + *width = d->linewidth; + *height = d->height; + return 1; + } + } + + return 0; +} + +unsigned int xmouse_buttons(xdisplay * d) +{ + return d->mouse_buttons; +} +#endif diff --git a/kscd/kscdmagic/xlib.h b/kscd/kscdmagic/xlib.h new file mode 100644 index 00000000..077a9052 --- /dev/null +++ b/kscd/kscdmagic/xlib.h @@ -0,0 +1,139 @@ +/* + * XaoS, a fast portable realtime fractal zoomer + * Copyright (C) 1996,1997 by + * + * Jan Hubicka (hubicka@paru.cas.cz) + * Thomas Marsh (tmarsh@austin.ibm.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#ifndef XAOS_X11_H +#define XAOS_X11_H + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/keysymdef.h> +/*#include "config.h"*/ +/*#define MITSHM*/ + +#ifdef MITSHM +#include <sys/types.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#endif /* MITSHM */ + +#ifdef n +#undef n +#endif + +typedef struct { + int n; + XColor c[256]; +} xcol_t; + +typedef struct { + int privatecolormap; + int usedefault; + int nomitshm; +} xlibparam; + +typedef struct { + Colormap colormap; + Colormap defaultcolormap; + int fixedcolormap; + int privatecolormap; + xlibparam *params; + Display *display; + Window parent_window; + Window window; + unsigned int width, height; + unsigned int border_width; + unsigned long background; + int depth; + unsigned int classX; + Visual *visual; + unsigned long valuemask; + XSetWindowAttributes *attributes; + unsigned long attr_mask; + XSizeHints sizehints; + int screen; + const char *window_name; + int status; + GC gc; + XGCValues xgcvalues; + xcol_t xcolor; + Pixmap pixmap; + XFontStruct *font_struct; + int screen_changed; + int lastx, lasty; + int mouse_x, mouse_y; + unsigned int mouse_buttons; + int current; + XImage *image[2]; +#ifdef MITSHM + XShmSegmentInfo xshminfo[2]; + int SharedMemOption; + int SharedMemFlag; +#endif /* MITSHM */ + unsigned long pixels[256]; + char *vbuffs[2]; + char *data[2]; + char *vbuff; + char *back; + int truecolor; + int linewidth; + + Cursor cursor; +} xdisplay; + +extern int alloc_shm_image(xdisplay * d); +extern void free_shm_image(xdisplay * d); +extern int alloc_image(xdisplay * d); +extern void free_image(xdisplay * d); +extern int xupdate_size(xdisplay * d); +extern void xflip_buffers(xdisplay * d); +extern xdisplay *xalloc_display(const char *n, int x, int y, int width,int height, xlibparam * p); +extern void xfree_display(xdisplay * d); +extern void xsetcolor(xdisplay * d, int col); +extern int xsetfont(xdisplay * d, char *font_name); +extern int xalloc_color(xdisplay * d, int r, int g, int b, int readwrite); +extern void xfree_colors(xdisplay * d); +extern void xline(xdisplay * d, int x1, int y1, int x2, int y2); +extern void xmoveto(xdisplay * d, int x, int y); +extern void xlineto(xdisplay * d, int x, int y); +extern void xrect(xdisplay * d, int x1, int y1, int x2, int y2); +extern void xfillrect(xdisplay * d, int x1, int y1, int x2, int y2); +extern void xarc(xdisplay * d, int x, int y, unsigned int w, + unsigned int h, int a1, int a2); +extern void xfillarc(xdisplay * d, int x, int y, unsigned int w, + unsigned int h, int a1, int a2); +extern void xpoint(xdisplay * d, int x, int y); +extern void xflush(xdisplay * d); +extern void xclear_screen(xdisplay * d); +extern void xrotate_palette(xdisplay * d, int direction, unsigned char c[3][256], int ncolors); +extern void draw_screen(xdisplay * d); +extern void xouttext(xdisplay * d, char *string); +extern void xresize(xdisplay * d, XEvent * ev); +extern void xsize_set(xdisplay *d, int width, int height); +extern int xsize_update(xdisplay *d,int *width,int *height); +extern int xmouse_x(xdisplay * d); +extern int xmouse_y(xdisplay * d); +extern void xmouse_update(xdisplay * d); +extern char xkeyboard_query(xdisplay * d); +extern unsigned int xmouse_buttons(xdisplay * d); + +#endif /* XAOS_X11_H */ diff --git a/kscd/kscdmagic/xlibwrap.cpp b/kscd/kscdmagic/xlibwrap.cpp new file mode 100644 index 00000000..b5241326 --- /dev/null +++ b/kscd/kscdmagic/xlibwrap.cpp @@ -0,0 +1,209 @@ +/* Synaesthesia - program to display sound graphically + Copyright (C) 1997 Paul Francis Harrison + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The author may be contacted at: + pfh@yoyo.cc.monash.edu.au + or + 27 Bond St., Mt. Waverley, 3149, Melbourne, Australia +*/ + +#if defined(__linux__) || defined (__svr4__) || defined(__osf__) + +#include <string.h> + +extern "C" { +#include "xlib.h" +} + +#include "magicconf.h" +#include "syna.h" + +static xlibparam xparams = { 0, 0, 0 }; +static xdisplay *d; + +static bool lowColor, paletteInstalled; +static unsigned char mapping[64]; + +int setPalette(int ,int r,int g,int b) { + return xalloc_color(d,r*257,g*257,b*257,0); +} + +void screenInit(int xHint,int yHint,int widthHint,int heightHint) +{ + + int i; + + d = xalloc_display("KSCD Magic",xHint,yHint,widthHint,heightHint,&xparams); + + if (d == 0) + error("setting up a window"); + + if (!alloc_image(d)) + error("allocating window buffer"); + outWidth = widthHint; + outHeight = heightHint; + + //XMoveWindow(d->display,d->window,xHint,yHint); + +#define BOUND(x) ((x) > 255 ? 255 : (x)) +#define PEAKIFY(x) BOUND((x) - (x)*(255-(x))/255/2) + + lowColor = (d->depth <= 8); + + /* Set up palette immediately. + * Original code has the following commented out. + */ + if (!lowColor) { + for(i=0;i<256;i++) + attempt(setPalette(i,PEAKIFY((i&15*16)), + PEAKIFY((i&15)*16+(i&15*16)/4), + PEAKIFY((i&15)*16)), + " in X: could not allocate sufficient palette entries"); + } else { + for(i=0;i<64;i++) + attempt(mapping[i] = setPalette(i,PEAKIFY((i&7*8)*4), + PEAKIFY((i&7)*32+(i&7*8)*2), + PEAKIFY((i&7)*32)), + " in X: could not allocate sufficient palette entries"); + } +} + +void screenSetPalette(unsigned char *palette) { + int i; + + if (paletteInstalled) + xfree_colors(d); + + if (!lowColor) { + for(i=0;i<256;i++) + attempt(setPalette(i,palette[i*3],palette[i*3+1],palette[i*3+2]), + " in X: could not allocate sufficient palette entries"); + } else { + const int array[8] = {0,2,5,7,9,11,13,15}; + for(i=0;i<64;i++) { + int p = (array[i&7]+array[i/8]*16) *3; + attempt(mapping[i] = setPalette(i,palette[p],palette[p+1],palette[p+2]), + " in X: could not allocate sufficient palette entries"); + } + } + + paletteInstalled = true; +} + +void screenEnd() { + if (paletteInstalled) + xfree_colors(d); + xfree_display(d); +} + +int sizeUpdate() +{ + int newWidth,newHeight; + + if (xsize_update(d,&newWidth,&newHeight)) + { + if (newWidth == outWidth && newHeight == outHeight) + return 0; + //delete[] output; + //outWidth = newWidth; + //outHeight = newHeight; + //output = new unsigned char [outWidth*outHeight*2]; + //memset(output,32,outWidth*outHeight*2); + allocOutput(newWidth,newHeight); + return 1; + } + return 0; +} + +void inputUpdate(int &mouseX,int &mouseY,int &mouseButtons,char &keyHit) { + xmouse_update(d); + mouseX = xmouse_x(d); + mouseY = xmouse_y(d); + mouseButtons = xmouse_buttons(d); + keyHit = xkeyboard_query(d); +} + +void screenShow(void) { + register unsigned long *ptr2 = (unsigned long*)ucoutput; + unsigned long *ptr1 = (unsigned long*)d->back; + int i = outWidth*outHeight/4; + if (lowColor) + do { + register unsigned int const r1 = *(ptr2++); + register unsigned int const r2 = *(ptr2++); + + //if (r1 || r2) { +#ifdef LITTLEENDIAN + register unsigned int const v = + mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)] + |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*256U; + *(ptr1++) = v | + mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)]*65536U + |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*16777216U; +#else + register unsigned int const v = + mapping[((r2&0xe0ul)>>5)|((r2&0xe000ul)>>10)] + |mapping[((r2&0xe00000ul)>>21)|((r2&0xe0000000ul)>>26)]*256U; + *(ptr1++) = v | + mapping[((r1&0xe0ul)>>5)|((r1&0xe000ul)>>10)]*65536U + |mapping[((r1&0xe00000ul)>>21)|((r1&0xe0000000ul)>>26)]*16777216U; +#endif + //} else ptr1++; + } while (--i); + else + do { + // Asger Alstrup Nielsen's (alstrup@diku.dk) + // optimized 32 bit screen loop + register unsigned int const r1 = *(ptr2++); + register unsigned int const r2 = *(ptr2++); + + //if (r1 || r2) { +#ifdef LITTLEENDIAN + register unsigned int const v = + ((r1 & 0x000000f0ul) >> 4) + | ((r1 & 0x0000f000ul) >> 8) + | ((r1 & 0x00f00000ul) >> 12) + | ((r1 & 0xf0000000ul) >> 16); + *(ptr1++) = v | + ((r2 & 0x000000f0ul) << 16 -4) + | ((r2 & 0x0000f000ul) << 16 -8) + | ((r2 & 0x00f00000ul) << 16 -12) + | ((r2 & 0xf0000000ul) << 16 -16); +#else + register unsigned int const v = + ((r2 & 0x000000f0ul) >> 4) + | ((r2 & 0x0000f000ul) >> 8) + | ((r2 & 0x00f00000ul) >> 12) + | ((r2 & 0xf0000000ul) >> 16); + *(ptr1++) = v | + ((r1 & 0x000000f0ul) << 16 -4) + | ((r1 & 0x0000f000ul) << 16 -8) + | ((r1 & 0x00f00000ul) << 16 -12) + | ((r1 & 0xf0000000ul) << 16 -16); +#endif + //} else ptr1++; + } while (--i); + + xflip_buffers(d); + draw_screen(d); + XFlush(d->display); +} + + + +#endif // linux || svr4 + diff --git a/kscd/ledlamp.cpp b/kscd/ledlamp.cpp new file mode 100644 index 00000000..706c16cf --- /dev/null +++ b/kscd/ledlamp.cpp @@ -0,0 +1,103 @@ +/* This file is part of the KDE libraries + Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +#include <stdio.h> +#include <qpainter.h> +#include <qbrush.h> +#include <qpen.h> +#include <qcolor.h> +#include "ledlamp.h" +#include "ledlamp.moc" + +LedLamp::LedLamp(QWidget *parent, Type t) : QFrame(parent), + w( 10 ), h( 7 ), dx( 4 ) +{ + // Make sure we're in a sane state + s = Off; + + // Set the frame style + // setFrameStyle(Sunken | Box); + setGeometry(0,0,h+1,w+1); + ledtype = t; +} // LedLamp + +void +LedLamp::drawContents(QPainter *painter) +{ + + + QBrush lightBrush(this->foregroundColor()); + QBrush darkBrush(this->backgroundColor()); + + QPen darkPen(this->backgroundColor(),1); + QPen lightPen(this->foregroundColor(), 1); + + switch(s) + { + case On: + painter->setBrush(lightBrush); + switch (ledtype) + { + case Rect: + painter->drawRect(1,1,w-3, h-2); + break; + case Loop: + painter->setBrush(lightBrush); + painter->setPen(lightPen); + + painter->drawLine(0, 2, 0, h-2); // | + painter->drawLine(1, 1, w-4, 1); // ~ + painter->drawLine(w-3, 2, w-3, h-2); // | + painter->drawLine(2, h-2, w-3, h-2); //_ + painter->drawLine(w-6,0,w-6,2); // ---+ + painter->drawLine(3,h-2,3,h); // +--- + break; + } + break; + + case Off: + painter->setBrush(darkBrush); + switch (ledtype) + { + case Rect: + painter->drawRect(1,1,w-3, h-2); + break; + case Loop: + painter->setBrush(darkBrush); + painter->setPen(darkPen); + + painter->fillRect(0,0,w,h, darkBrush); + /* + painter->drawLine(0, 2, 0, h-2); // | + painter->drawLine(1, 1, w-4, 1); // ~ + painter->drawLine(w-3, 2, w-3, h-2); // | + painter->drawLine(2, h-2, w-3, h-2); //_ + painter->drawLine(w-6,0,w-6,2); // ---+ + painter->drawLine(3,h-2,3,h); // +--- + */ + break; + } + break; + + default: + fprintf(stderr, "LedLamp: INVALID State (%d)\n", s); + } +} // drawContents + diff --git a/kscd/ledlamp.h b/kscd/ledlamp.h new file mode 100644 index 00000000..15d4b8b4 --- /dev/null +++ b/kscd/ledlamp.h @@ -0,0 +1,63 @@ + +/* + This file is part of the KDE libraries + Copyright (C) 1997 Richard Moore (moorer@cs.man.ac.uk) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + + +#ifndef LED_LAMP_H +#define LED_LAMP_H + +#include <qframe.h> + + +class LedLamp : public QFrame +{ + Q_OBJECT +public: + + enum Type { Rect, Loop }; + + LedLamp(QWidget *parent=0, Type t=Rect); + + enum State { On, Off }; + + + State state() const { return s; } + + + void setState(State state) { s= state; repaint(false); } + + + void toggleState() { if (s == On) s= Off; else if (s == Off) s= On; repaint(false); } +public slots: + void toggle() { toggleState(); }; + void on() { setState(On); }; + void off() { setState(Off); }; +protected: + void drawContents(QPainter *); +private: + const int w; + const int h; + const int dx; + State s; + int ledtype; +}; + + +#endif diff --git a/kscd/libwm/Makefile.am b/kscd/libwm/Makefile.am new file mode 100644 index 00000000..ae2a9144 --- /dev/null +++ b/kscd/libwm/Makefile.am @@ -0,0 +1,23 @@ +# +# Makefile.am for libworkman. Based on the example Makefile.am for a +# shared library. +# + +subdirs = include +SUBDIRS = audio +INCLUDES = $(all_includes) + +noinst_LTLIBRARIES = libworkman.la + +libworkman_la_LDFLAGS = $(ARTSC_LIBS) $(all_libraries) +libworkman_la_LIBADD = audio/libworkmanaudio.la $(ARTS_LIBASOUND) + +libworkman_la_SOURCES = cddb.c cdinfo.c cdrom.c wm_helpers.c cdtext.c\ +database.c index.c scsi.c cdda.c plat_linux_cdda.c plat_sun_cdda.c\ +plat_aix.c plat_bsd386.c plat_freebsd.c plat_hpux.c plat_irix.c \ +plat_linux.c plat_svr4.c plat_ultrix.c plat_news.c plat_openbsd.c \ +plat_osf1.c plat_sun.c plat_scor5.c \ +drv_sony.c drv_toshiba.c + +#libworkmanincludedir = $(includedir)/libwm +#libworkmaninclude_HEADERS = include/wm_cdrom.h include/wm_cdtext.h diff --git a/kscd/libwm/PLAT_IMPL_STATUS b/kscd/libwm/PLAT_IMPL_STATUS new file mode 100644 index 00000000..d1919728 --- /dev/null +++ b/kscd/libwm/PLAT_IMPL_STATUS @@ -0,0 +1,48 @@ +LIBRARY IMPLEMENTATION STATUS +----------------------------- + +This table should show up where the platform codes differ. + +- function not available +X function available +\ function dummy, null implementation or commented out +(X) function available in special configurations + +function |aix |bsd386|freebsd|hpux |irix |linux|news |openbsd|osf1 |scor5|sun |svr4 |ultrix| +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ +find_cdrom() | - | - | - | - | - | - | - | - | X | - | X | X | X | +gen_init() | \ | \ | \ | \ | (X) | \ | \ | \ | \ | \ | X | \ | \ | +wmcd_open() | X | X | X | X | X | X | X | X | X | X | X | X | X | +wmcd_close() | - | - | - | - | - | - | X | - | - | - | - | - | - | +wmcd_reopen() | X | X | X | X | X | X | X | X | X | X | X | X | X | +wm_scsi() | \ | \ | \ | X | \ | X | \ | \ | \ | X | X | X | \ | +keep_cd_open() | - | - | - | - | - | \ | - | - | - | \ | - | - | - | +gen_get_drive_status() | X | X | X | X | X | X | X | X | X | X | X | X | X | +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ +gen_get_trackcount() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_get_trackinfo() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_get_cdlen() | X | X | X | X | X | X | X | X | X | X | X | X | X | +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ +gen_play() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_pause() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_resume() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_stop() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_eject() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_closetray() | X | X | X | X | X | X | X | X | X | X | X | X | X | +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ +scale_volume() | X | X | X | - | - | X | - | X | X | - | - | - | X | +unscale_volume() | X | X | X | - | - | - | - | X | X | - | - | - | X | +gen_set_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X | +gen_get_volume() | X | X | X | X | X | X | X | X | X | X | X | X | X | +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ +cdda_init() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +get_ack() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +cdda_kill() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +gen_set_direction() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +gen_set_speed() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +gen_set_loudness() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +gen_save() | - | - | - | - | - | \ | - | - | - | - | X | - | - | +------------------------+-----+------+-------+-----+-----+-----+-----+-------+-----+-----+-----+-----+------+ + + + diff --git a/kscd/libwm/README b/kscd/libwm/README new file mode 100644 index 00000000..69b4ae65 --- /dev/null +++ b/kscd/libwm/README @@ -0,0 +1,20 @@ +$Id$ + +This directory contains the WorkMan library. + +libworkman is a multi-plaform CD-Player library for creating various +CD-Player-UIs. + +06.03.2003 Alex Kern <alex.kern@gmx.de> + I have reworked most part of libworkman, get rid of most of externals + add cdda support for linux and, I hope not breaked it for SUN ;-) + + Interface was cleaned, that means for a potentialy application developers + new rules: + 1. please include only wm_cdrom.h it's all defines and functions, what + you need. You have any exceptions. + 2. for cdtext, include wm_cdtext.h + 3. for cddb, include wm_cddb.h (think, it's not so powerfull and can be replaced with + any new external library. + 4. wm_cdinfo declares any externals now, I will change it in the future +
\ No newline at end of file diff --git a/kscd/libwm/audio/Makefile.am b/kscd/libwm/audio/Makefile.am new file mode 100644 index 00000000..318cb0a3 --- /dev/null +++ b/kscd/libwm/audio/Makefile.am @@ -0,0 +1,5 @@ +INCLUDES = $(all_includes) $(ARTSC_INCLUDE) + +noinst_LTLIBRARIES = libworkmanaudio.la + +libworkmanaudio_la_SOURCES = audio.c audio_alsa.c audio_arts.c audio_sun.c diff --git a/kscd/libwm/audio/audio.c b/kscd/libwm/audio/audio.c new file mode 100644 index 00000000..39c8330d --- /dev/null +++ b/kscd/libwm/audio/audio.c @@ -0,0 +1,29 @@ +#include "audio.h" + +#include <string.h> + +struct audio_oops* setup_arts(const char *dev, const char *ctl); +struct audio_oops* setup_alsa(const char *dev, const char *ctl); + +struct audio_oops* setup_soundsystem(const char* ss, const char* dev, const char* ctl) +{ + if(!ss) { + ERRORLOG("audio: Internal error, trying to setup a NULL soundsystem.\n"); + return NULL; + } + +#ifdef USE_ARTS + if(!strcmp(ss, "arts")) + return setup_arts(dev, ctl); +#endif +#if defined(HAVE_ARTS_LIBASOUND2) + if(!strcmp(ss, "alsa")) + return setup_alsa(dev, ctl); +#endif +#ifdef USE_SUN_AUDIO + if(!strcmp(ss, "sun")) + return setup_sun_audio(dev, ctl); +#endif + ERRORLOG("audio: unknown soundsystem '%s'\n", ss); + return NULL; +} diff --git a/kscd/libwm/audio/audio.h b/kscd/libwm/audio/audio.h new file mode 100644 index 00000000..089ea116 --- /dev/null +++ b/kscd/libwm/audio/audio.h @@ -0,0 +1,21 @@ +/* + * Audio 'LIB' defines + */ +#include "../include/wm_cdda.h" + +#ifndef NULL +#define NULL 0 +#endif + +struct audio_oops { + int (*wmaudio_open)(void); + int (*wmaudio_close)(void); + int (*wmaudio_play)(struct cdda_block*); + int (*wmaudio_stop)(void); + int (*wmaudio_state)(struct cdda_block*); + int (*wmaudio_balance)(int); + int (*wmaudio_volume)(int); +}; + +extern struct audio_oops* setup_soundsystem(const char*, const char*, const char*); + diff --git a/kscd/libwm/audio/audio_alsa.c b/kscd/libwm/audio/audio_alsa.c new file mode 100644 index 00000000..b1d4e938 --- /dev/null +++ b/kscd/libwm/audio/audio_alsa.c @@ -0,0 +1,334 @@ +/* + * Driver for Advanced Linux Sound Architecture, http://alsa.jcu.cz + * + * mpg123 comments: + * Code by Anders Semb Hermansen <ahermans@vf.telia.no> + * Cleanups by Jaroslav Kysela <perex@jcu.cz> + * Ville Syrjala <syrjala@sci.fi> + * + * adopted for libworkman cdda audio backend from Alexander Kern alex.kern@gmx.de + * + * Adapted to support both ALSA V0.x and V1.x APIs for PCM calls + * (Philip Nelson <teamdba@scotdb.com> 2004-03-15) + * + * This file comes under GPL license. + */ + + + +#include <config.h> + +#if defined(HAVE_ARTS_LIBASOUND2) + +#include <alsa/asoundlib.h> +#include "audio.h" + +char* device = NULL; +snd_pcm_t *handle; + +snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ +int rate = 44100; /* stream rate */ +int channels = 2; /* count of channels */ +int buffer_time = 2000000; /* ring buffer length in us */ +int period_time = 100000; /* period time in us */ + +snd_pcm_sframes_t buffer_size; +snd_pcm_sframes_t period_size; + +int alsa_open(void); +int alsa_close(void); +int alsa_stop(void); +int alsa_play(struct cdda_block *blk); +int alsa_state(struct cdda_block *blk); +struct audio_oops* setup_alsa(const char *dev, const char *ctl); + +static int set_hwparams(snd_pcm_hw_params_t *params, + snd_pcm_access_t accesspar) +{ + int err, dir, new_rate; + + /* choose all parameters */ + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + ERRORLOG("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err)); + return err; + } + /* set the interleaved read/write format */ + err = snd_pcm_hw_params_set_access(handle, params, accesspar); + if (err < 0) { + ERRORLOG("Access type not available for playback: %s\n", snd_strerror(err)); + return err; + } + /* set the sample format */ + err = snd_pcm_hw_params_set_format(handle, params, format); + if (err < 0) { + ERRORLOG("Sample format not available for playback: %s\n", snd_strerror(err)); + return err; + } + /* set the count of channels */ + err = snd_pcm_hw_params_set_channels(handle, params, channels); + if (err < 0) { + ERRORLOG("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); + return err; + } + /* set the stream rate */ +#if (SND_LIB_MAJOR < 1) + err = new_rate = snd_pcm_hw_params_set_rate_near(handle, params, rate, 0); +#else + new_rate = rate; + err = snd_pcm_hw_params_set_rate_near(handle, params, &new_rate, 0); +#endif + if (err < 0) { + ERRORLOG("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); + return err; + } + if (new_rate != rate) { + ERRORLOG("Rate doesn't match (requested %iHz, get %iHz)\n", rate, new_rate); + return -EINVAL; + } + /* set the buffer time */ +#if (SND_LIB_MAJOR < 1) + err = snd_pcm_hw_params_set_buffer_time_near(handle, params, buffer_time, &dir); +#else + err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); +#endif + if (err < 0) { + ERRORLOG("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); + return err; + } +#if (SND_LIB_MAJOR < 1) + buffer_size = snd_pcm_hw_params_get_buffer_size(params); +#else + err = snd_pcm_hw_params_get_buffer_size(params, &buffer_size); + if (err < 0) { + ERRORLOG("Unable to get buffer size : %s\n", snd_strerror(err)); + return err; + } +#endif + DEBUGLOG("buffersize %i\n", buffer_size); + + /* set the period time */ +#if (SND_LIB_MAJOR < 1) + err = snd_pcm_hw_params_set_period_time_near(handle, params, period_time, &dir); +#else + err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); +#endif + if (err < 0) { + ERRORLOG("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); + return err; + } + +#if (SND_LIB_MAJOR < 1) + period_size = snd_pcm_hw_params_get_period_size(params, &dir); +#else + err = snd_pcm_hw_params_get_period_size(params, &period_size, &dir); + if (err < 0) { + ERRORLOG("Unable to get hw period size: %s\n", snd_strerror(err)); + } +#endif + + DEBUGLOG("period_size %i\n", period_size); + + /* write the parameters to device */ + err = snd_pcm_hw_params(handle, params); + if (err < 0) { + ERRORLOG("Unable to set hw params for playback: %s\n", snd_strerror(err)); + return err; + } + return 0; +} + +static int set_swparams(snd_pcm_sw_params_t *swparams) +{ + int err; + + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle, swparams); + if (err < 0) { + ERRORLOG("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); + return err; + } + /* start the transfer when the buffer is full */ + err = snd_pcm_sw_params_set_start_threshold(handle, swparams, buffer_size); + if (err < 0) { + ERRORLOG("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); + return err; + } + /* allow the transfer when at least period_size samples can be processed */ + err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_size); + if (err < 0) { + ERRORLOG("Unable to set avail min for playback: %s\n", snd_strerror(err)); + return err; + } + /* align all transfers to 1 sample */ + err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); + if (err < 0) { + ERRORLOG("Unable to set transfer align for playback: %s\n", snd_strerror(err)); + return err; + } + /* write the parameters to the playback device */ + err = snd_pcm_sw_params(handle, swparams); + if (err < 0) { + ERRORLOG("Unable to set sw params for playback: %s\n", snd_strerror(err)); + return err; + } + return 0; +} + +int alsa_open( void ) +{ + int err; + + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + + DEBUGLOG("alsa_open\n"); + + snd_pcm_hw_params_alloca(&hwparams); + snd_pcm_sw_params_alloca(&swparams); + + if((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0/*SND_PCM_NONBLOCK*/)) < 0 ) { + ERRORLOG("open failed: %s\n", snd_strerror(err)); + return -1; + } + + if((err = set_hwparams(hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { + ERRORLOG("Setting of hwparams failed: %s\n", snd_strerror(err)); + return -1; + } + if((err = set_swparams(swparams)) < 0) { + ERRORLOG("Setting of swparams failed: %s\n", snd_strerror(err)); + return -1; + } + + return 0; +} + +int alsa_close( void ) +{ + int err; + + DEBUGLOG("alsa_close\n"); + + err = alsa_stop(); + +#if (SND_LIB_MAJOR < 1) + err = snd_pcm_close(handle); +#else + err = snd_pcm_close(handle); +#endif + + free(device); + + return err; +} + +/* + * Play some audio and pass a status message upstream, if applicable. + * Returns 0 on success. + */ +int +alsa_play(struct cdda_block *blk) +{ + signed short *ptr; + int err = 0, frames; + + ptr = (signed short *)blk->buf; + frames = blk->buflen / (channels * 2); + DEBUGLOG("play %i frames, %i bytes\n", frames, blk->buflen); + while (frames > 0) { + err = snd_pcm_writei(handle, ptr, frames); + + if (err == -EAGAIN) + continue; + if(err == -EPIPE) { + err = snd_pcm_prepare(handle); + continue; + } else if (err < 0) + break; + + ptr += err * channels; + frames -= err; + DEBUGLOG("played %i, rest %i\n", err / channels, frames); + } + + if (err < 0) { + ERRORLOG("alsa_write failed: %s\n", snd_strerror(err)); + err = snd_pcm_prepare(handle); + + if (err < 0) { + ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); + } + blk->status = WM_CDM_CDDAERROR; + return err; + } + + return 0; +} + +/* + * Stop the audio immediately. + */ +int +alsa_stop( void ) +{ + int err; + + DEBUGLOG("alsa_stop\n"); + + err = snd_pcm_drop(handle); + if (err < 0) { + ERRORLOG("Unable to drop pcm stream: %s\n", snd_strerror(err)); + } + + err = snd_pcm_prepare(handle); + if (err < 0) { + ERRORLOG("Unable to snd_pcm_prepare pcm stream: %s\n", snd_strerror(err)); + } + + return err; +} + +/* + * Get the current audio state. + */ +int +alsa_state(struct cdda_block *blk) +{ + return -1; /* not implemented yet for ALSA */ +} + +static struct audio_oops alsa_oops = { + .wmaudio_open = alsa_open, + .wmaudio_close = alsa_close, + .wmaudio_play = alsa_play, + .wmaudio_stop = alsa_stop, + .wmaudio_state = alsa_state, + .wmaudio_balance = NULL, + .wmaudio_volume = NULL +}; + +struct audio_oops* +setup_alsa(const char *dev, const char *ctl) +{ + static int init_complete = 0; + + if(dev && strlen(dev) > 0) { + device = strdup(dev); + } else { + device = strdup("plughw:0,0"); /* playback device */ + } + + if(init_complete) { + ERRORLOG("already initialized\n"); + return NULL; + } + if(!alsa_open()) + init_complete = 1; + else + return NULL; + + return &alsa_oops; +} + +#endif /* ALSA */ diff --git a/kscd/libwm/audio/audio_arts.c b/kscd/libwm/audio/audio_arts.c new file mode 100644 index 00000000..a7b033c9 --- /dev/null +++ b/kscd/libwm/audio/audio_arts.c @@ -0,0 +1,141 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Linux digital audio functions. + * + * + * Forget /dev/audio + * most modern soundcards accept 16LE with 44.1kHz + * Alexander Kern alex.kern@gmx.de + */ + +#include <config.h> + +#ifdef USE_ARTS + +#include <artsc.h> + +#include "audio.h" + +arts_stream_t arts_stream = NULL; + +int arts_open(void); +int arts_close(void); +int arts_stop(void); +int arts_play(struct cdda_block *blk); +int arts_state(struct cdda_block *blk); +struct audio_oops* setup_arts(const char *dev, const char *ctl); + +/* + * Initialize the audio device. + */ +int +arts_open(void) +{ + int err; + + DEBUGLOG("arts_open\n"); + + if(!(arts_stream = arts_play_stream(44100, 16, 2, "cddaslave"))) { + ERRORLOG("cannot open ARTS stream for playback\n"); + return -1; + } + /* 1000 ms because we read 75 frames = 1 sec */ + if((err = arts_stream_set(arts_stream, ARTS_P_BUFFER_TIME, 1000)) < 0) { + ERRORLOG("arts_stream_set failed (%s)\n", arts_error_text(err)); + return -1; + } + return 0; +} + +/* + * Close the audio device. + */ +int +arts_close(void) +{ + arts_stop(); + + DEBUGLOG("arts_close\n"); + arts_close_stream(arts_stream); + + arts_free(); + + return 0; +} + +/* + * Play some audio and pass a status message upstream, if applicable. + * Returns 0 on success. + */ +int +arts_play(struct cdda_block *blk) +{ + int err; + + if((err = arts_write(arts_stream, blk->buf, blk->buflen)) < 0) { + ERRORLOG("arts_write failed (%s)\n", arts_error_text(err)); + blk->status = WM_CDM_CDDAERROR; + return -1; + } + + return 0; +} + +/* + * Stop the audio immediately. + */ +int +arts_stop(void) +{ + DEBUGLOG("arts_stop\n"); + return 0; +} + +/* + * Get the current audio state. + */ +int +arts_state(struct cdda_block *blk) +{ + return -1; /* not implemented yet for ARTS */ +} + +static struct audio_oops arts_oops = { + .wmaudio_open = arts_open, + .wmaudio_close = arts_close, + .wmaudio_play = arts_play, + .wmaudio_stop = arts_stop, + .wmaudio_state = arts_state, + .wmaudio_balance = NULL, + .wmaudio_volume = NULL +}; + +struct audio_oops* +setup_arts(const char *dev, const char *ctl) +{ + int err; + + if((err = arts_init())) { + ERRORLOG("cannot initialize ARTS audio subsystem (%s)\n", arts_error_text(err)); + return NULL; + } + + arts_open(); + + return &arts_oops; +} +#endif diff --git a/kscd/libwm/audio/audio_sun.c b/kscd/libwm/audio/audio_sun.c new file mode 100644 index 00000000..78b1ab21 --- /dev/null +++ b/kscd/libwm/audio/audio_sun.c @@ -0,0 +1,525 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Sun (really Solaris) digital audio functions. + */ + +#include <config.h> + +#ifdef USE_SUN_AUDIO + +#include <stdio.h> +#include <malloc.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> +#include <sys/stropts.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +#include "audio.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * Since there's a lag time between writing audio to the audio device and + * hearing it, we need to make sure the status indicators correlate to what's + * playing out the speaker. Luckily, Solaris gives us some audio + * synchronization facilities that make this pretty easy. + * + * We maintain a circular queue of status information. When we write some + * sound to the audio device, we put its status info into the queue. We write + * a marker into the audio stream; when the audio device driver encounters the + * marker, it increments a field in a status structure. When we see that + * field go up, we grab the next status structure from the queue and send it + * to the parent process. + * + * The minimum size of the queue depends on the latency of the audio stream. + */ +#define QSIZE 500 + +struct cdda_block queue[QSIZE]; +int qtail; +int qstart; + +/* + * We only send WM_CDM_PLAYING status messages upstream when the CD is supposed + * to be playing; this is used to keep track. + */ +extern int playing; + +static int aufd, aucfd; +static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */ + +/* + * For fast linear-to-ulaw mapping, we use a lookup table that's generated + * at startup. + */ +unsigned char *ulawmap, linear_to_ulaw(); + +char *getenv(); + +/* + * Dummy signal handler so writes to /dev/audio will interrupt. + */ +static void +dummy( void ) +{ + signal(SIGALRM, dummy); +} + +/* + * Initialize the audio device. + */ +void +sun_audio_init( void ) +{ + audio_info_t info; + char *audiodev, *acdev; + int linval; + + audiodev = getenv("AUDIODEV"); + if (audiodev == NULL || + strncmp("/dev/", audiodev, 5) || + strstr(audiodev, "/../") ) + audiodev = "/dev/audio"; + + acdev = malloc(strlen(audiodev) + 4); + if (acdev == NULL) + { + perror("Can't allocate audio control filename"); + exit(1); + } + strcpy(acdev, audiodev); + strcat(acdev, "ctl"); + + aucfd = open(acdev, O_WRONLY, 0); + if (aucfd < 0) + { + perror(acdev); + exit(1); + } + free(acdev); + + aufd = open(audiodev, O_WRONLY, 0); + if (aufd < 0) + { + perror(audiodev); + exit(1); + } + + signal(SIGALRM, dummy); + + /* + * Try to set the device to CD-style audio; we can process it + * with the least CPU overhead. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 44100; + info.play.channels = 2; + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + if (errno == EINVAL) + { + /* + * Oh well, so much for that idea. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 8000; + info.play.channels = 1; + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_ULAW; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + { + perror("Can't set up audio device"); + exit(1); + } + + /* + * Initialize the linear-to-ulaw mapping table. + */ + if (ulawmap == NULL) + ulawmap = malloc(65536); + if (ulawmap == NULL) + { + perror("malloc"); + exit(1); + } + for (linval = 0; linval < 65536; linval++) + ulawmap[linval] = linear_to_ulaw(linval-32768); + ulawmap += 32768; + raw_audio = 0; + } + else + { + perror(audiodev); + exit(1); + } +} + +/* + * Get ready to play some sound. + */ +void +sun_audio_ready( void ) +{ + audio_info_t info; + + /* + * Start at the correct queue position. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + qtail = info.play.eof % QSIZE; + qstart = qtail; + + queue[qtail].status = WM_CDM_PLAYING; +} + +/* + * Stop the audio immediately. + */ +int +sun_audio_stop( void ) +{ + if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0) + perror("flush"); + return 0; +} + +/* + * Close the audio device. + */ +int +sun_audio_close( void ) +{ + wmaudio_stop(); + close(aufd); + close(aucfd); + return 0; +} + +/* + * Set the volume level. + */ +int +sun_audio_volume(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + info.play.gain = level; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) { + perror("AUDIO_SETINFO"); + return -1; + } + return 0; +} + +/* + * Set the balance level. + */ +int +sun_audio_balance(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + level *= AUDIO_RIGHT_BALANCE; + info.play.balance = level / 255; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) { + perror("AUDIO_SETINFO"); + return -1; + } + return 0; +} + +/* + * Mark the most recent audio block on the queue as the last one. + */ +void +sun_audio_mark_last( void ) +{ + queue[qtail].status = WM_CDM_TRACK_DONE; +} + +/* + * Figure out the most recent status information and send it upstream. + */ +int +sun_audio_send_status( void ) +{ + audio_info_t info; + int qhead; + + /* + * Now send the most current status information to our parent. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + qhead = info.play.eof % QSIZE; + + if (qhead != qstart && playing) + { + int balance; + + if (queue[qhead].status != WM_CDM_TRACK_DONE) + queue[qhead].status = WM_CDM_PLAYING; + queue[qhead].volume = info.play.gain; + queue[qhead].balance = (info.play.balance * 255) / + AUDIO_RIGHT_BALANCE; + + send_status(queue + qhead); + qstart = -1; + } + + return (queue[qhead].status == WM_CDM_TRACK_DONE); +} + +/* + * Play some audio and pass a status message upstream, if applicable. + * Returns 0 on success. + */ +int +sun_audio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + int i; + short *buf16; + int alarmcount = 0; + struct itimerval it; + long playablelen; + + alarm(1); + playablelen = dev_audio_convert(rawbuf, buflen, blk); + while (write(aufd, rawbuf, playablelen) <= 0) + if (errno == EINTR) + { + if (! raw_audio && alarmcount++ < 5) + { + /* + * 8KHz /dev/audio blocks for several seconds + * waiting for its queue to drop below a low + * water mark. + */ + wmaudio_send_status(); + timerclear(&it.it_interval); + timerclear(&it.it_value); + it.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &it, NULL); + continue; + } + +/* close(aufd); + close(aucfd); + wmaudio_init(); +*/ sun_audio_stop(); + alarm(2); + continue; + } + else + { + blk->status = WM_CDM_CDDAERROR; + return (-1); + } + alarm(0); + + /* + * Mark this spot in the audio stream. + * + * Marks don't always succeed (if the audio buffer is empty + * this call will block forever) so do it asynchronously. + */ + fcntl(aufd, F_SETFL, O_NONBLOCK); + if (write(aufd, rawbuf, 0) < 0) + { + if (errno != EAGAIN) + perror("audio mark"); + } + else + qtail = (qtail + 1) % QSIZE; + + fcntl(aufd, F_SETFL, 0); + + queue[qtail] = *blk; + + if (wmaudio_send_status() < 0) + return (-1); + else + return (0); +} + +/* + * Get the current audio state. + */ +int +sun_audio_state(struct cdda_block *blk) +{ + audio_info_t info; + int balance; + + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + blk->volume = info.play.gain; + blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE; + return 0; +} + +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ +#define ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + +unsigned char +linear_to_ulaw( sample ) +int sample; +{ + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if ( sign != 0 ) sample = -sample; /* get magnitude */ + if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F; + ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa ); +#ifdef ZEROTRAP + if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return ulawbyte; +} + +/* + * Downsample a block of CDDA data, if necessary, for playing out an old-style + * audio device. + */ +long +dev_audio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + short *buf16 = (short *)rawbuf; + int i, j, samples; + int mono_value; + unsigned char *rbend = rawbuf + buflen; + + /* Don't do anything if the audio device can take the raw values. */ + if (raw_audio) + return (buflen); + + for (i = 0; buf16 < (short *)(rbend); i++) + { + /* Downsampling to 8KHz is a little irregular. */ + samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12; + + /* And unfortunately, we don't always end on a nice boundary. */ + if (buf16 + samples > (short *)(rbend)) + samples = ((short *)rbend) - buf16; + + /* + * No need to average all the values; taking the first one + * is sufficient and less CPU-intensive. But we do need to + * do both channels. + */ + mono_value = (buf16[0] + buf16[1]) / 2; + buf16 += samples; + rawbuf[i] = ulawmap[mono_value]; + } + + return (i); +} + +static struct audio_oops sun_audio_oops = { + .wmaudio_open = sun_audio_open, + .wmaudio_close = sun_audio_close, + .wmaudio_play = sun_audio_play, + .wmaudio_stop = sun_audio_stop, + .wmaudio_state = sun_audio_state, + .wmaudio_balance = sun_audio_balance, + .wmaudio_volume = sun_audio_volume +}; + +struct audio_oops* +setup_sun_audio(const char *dev, const char *ctl) +{ + int err; + + if((err = sun_audio_init())) { + ERRORLOG("cannot initialize SUN /dev/audio subsystem \n"); + return NULL; + } + + sun_audio_open(); + + return &sun_audio_oops; +} + +#endif /* USE_SUN_AUDIO */ diff --git a/kscd/libwm/buildindex.c b/kscd/libwm/buildindex.c new file mode 100644 index 00000000..8edc3684 --- /dev/null +++ b/kscd/libwm/buildindex.c @@ -0,0 +1,221 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Build a WorkMan database index file from a flat text file. Requires + * 4.4BSD libdb library. + */ + +static char buildindex_id[] = "$Id$"; + +#include <stdio.h> +#include <db.h> +#include <fcntl.h> +#include <errno.h> +#include <netinet/in.h> /* for htonl() */ +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <unistd.h> + +char *strrchr(); + +main(argc, argv) + int argc; + char **argv; +{ + DB *db; + DBT key, data; + FILE *fp; + int lock = 1, i = 0, locked, frame; + char buf[1000], indname[MAXPATHLEN + 100], framebuf[8], *c; + unsigned long pos; + BTREEINFO bt; + struct stat st; + + if (argc > 2 && !strcmp(argv[1], "-n")) + { + lock = 0; + i++; + } + + if (argc < i + 2) + { + fprintf(stderr, "Usage: %s [-n] dbfile [dbfile ...]\n", + argv[0]); + exit(1); + } + + data.data = &pos; + data.size = sizeof(pos); + key.data = framebuf; + key.size = 7; /* %07d */ + + while (++i < argc) + { + fprintf(stderr, "Building index for %s\n", argv[i]); + + if ((fp = fopen(argv[i], "r")) == NULL) + { + perror(argv[i]); + continue; + } + + /* + * Figure out the file's mode, uid, gid, so we can set the + * permissions on the index file to the same thing. + */ + if (fstat(fileno(fp), &st)) + { + sprintf(indname, "%s: fstat", argv[i]); + perror(indname); + fclose(fp); + continue; + } + + if (lock && lockit(fileno(fp), F_WRLCK)) + { + sprintf(indname, "%s: Warning: Couldn't lock", argv[i]); + perror(indname); + locked = 0; + } + else + locked = 1; + + /* + * Create a database file. + */ + bt.flags = R_DUP; /* allow duplicate keys */ + bt.cachesize = 0; + bt.psize = 0; + bt.lorder = 4321; + bt.minkeypage = 0; + bt.compare = NULL; /* use lexical comparison */ + bt.prefix = NULL; /* no prefix comparisons */ + + /* Index files have ".ind" extensions */ + sprintf(indname, "%s.ind", argv[i]); + if ((db = dbopen(indname, O_CREAT | O_RDWR | O_TRUNC, + st.st_mode, DB_BTREE, &bt)) == NULL) + { + perror(indname); + if (locked) + lockit(fileno(fp), F_UNLCK); + fclose(fp); + continue; + } + + /* + * Now loop through the text file, inserting a record into + * the index file for each "tracks" line. + */ + while (! feof(fp)) + { + pos = ftell(fp); + buf[0] = '\0'; + if (fgets(buf, sizeof(buf), fp) == NULL || ! buf[0]) + { + /* End of file? */ + if (feof(fp)) + break; + + /* Nope. A read error. Unlink the database. */ + perror(argv[i]); + (void) unlink(indname); + break; + } + + if (strncmp(buf, "tracks ", 7)) + continue; + + /* + * Found the start of a record. Figure out the start + * time of the last track and put an entry in the + * index file with that as the key. + */ + c = strrchr(buf, ' '); /* this will always succeed */ + *c = '\0'; + c = strrchr(buf, ' '); /* this should too, but... */ + if (c == NULL) + { + fprintf(stderr, + "%s: Malformed tracks line at %lu\n", + argv[i], pos); + continue; + } + sscanf(c+1, "%d", &frame); + sprintf(framebuf, "%07d", frame); + pos = htonl(pos); + + if ((db->put)(db, &key, &data, 0)) + { + perror(indname); + unlink(indname); + break; + } + } + + /* + * Clean up. + */ + (void) (db->close)(db); + if (locked) + lockit(fileno(fp), F_UNLCK); + } +} + +/* + * Lock a file. Time out after a little while if we can't get a lock; + * this usually means the locking system is broken. + * + * Unfortunately, if there are lots of people contending for a lock, + * this can result in the file not getting locked when it probably should. + */ +int +lockit(fd, type) + int fd; + int type; +{ + struct flock fl; + int result, timer = 0; + + fl.l_type = type; + fl.l_whence = 0; + fl.l_start = 0; + fl.l_len = 0; + + while ((result = fcntl(fd, F_SETLK, &fl)) < 0) + { + if (errno != EACCES || errno != EAGAIN) + break; + if (timer++ == 30) + { + errno = ETIMEDOUT; + break; + } + + sleep(1); + } + + return (result); +} diff --git a/kscd/libwm/cdda.c b/kscd/libwm/cdda.c new file mode 100644 index 00000000..ca8d76ba --- /dev/null +++ b/kscd/libwm/cdda.c @@ -0,0 +1,458 @@ +/*************************************************************************** + cdda.c - description + ------------------- + begin : Mon Jan 27 2003 + copyright : (C) 2003 by Alex Kern + email : alex.kern@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +/*************************************************************************** + * * + * This is a common cddamaster piece of code * + * * + ***************************************************************************/ + +#include <string.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <stdio.h> +#include <unistd.h> +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdda.h" +#include "include/wm_cdrom.h" +#include "audio/audio.h" +#include <pthread.h> + +#if defined(BUILD_CDDA) + +static pthread_t thread_read; +static pthread_t thread_play; + +int get_next_block(int x); +void *cdda_fct_read(void* arg); +void *cdda_fct_play(void* arg); + +#define NUMBLOCKS 2 +#define NUMFRAMES 10 + +static struct cdda_block blks[NUMBLOCKS]; +static pthread_mutex_t blks_mutex[NUMBLOCKS]; + +static struct cdda_device dev; +static pthread_cond_t wakeup_audio; + +/* + * Loudness setting, plus the floating volume multiplier and decaying-average + * volume level. + */ +static unsigned int loudness = 0, volume = 32768, level; + +/* + * This is non-null if we're saving audio to a file. + */ +static FILE *output = NULL; + +/* + * These are driverdependent oops + * + */ +static struct audio_oops *oops = NULL; + +/* + * Audio file header format. + */ +typedef unsigned long u_32; +struct auheader { + u_32 magic; + u_32 hdr_size; + u_32 data_size; + u_32 encoding; + u_32 sample_rate; + u_32 channels; +}; + +/* had to change #ifdef to #if -> see wm_cdda.h */ +#ifdef __FreeBSD__ +/* Phungus not with htonl on FreeBSD */ +#include <sys/param.h> +#else +#if WM_BIG_ENDIAN +# ifndef htonl +# define htonl(x) (x) +# endif +#else +extern unsigned long htonl(unsigned long); +#endif +#endif + +/* + * Try to initialize the CDDA slave. Returns 0 on success. + */ +int +gen_cdda_init( struct wm_drive *d ) +{ + int ret = 0; + + if (d->cdda_slave > -1) + return 0; + + memset(&blks, 0, sizeof(blks)); + + dev.fd = -1; + dev.frames_at_once = NUMFRAMES; + dev.blocks = blks; + dev.numblocks = NUMBLOCKS; + dev.status = WM_CDM_UNKNOWN; + dev.devname = d->cd_device; + + if ((ret = wmcdda_init(&dev))) + return ret; + + oops = setup_soundsystem(d->soundsystem, d->sounddevice, d->ctldevice); + if (!oops) { + ERRORLOG("cdda: setup_soundsystem failed\n"); + wmcdda_close(&dev); + return -1; + } + + if(pthread_create(&thread_read, NULL, cdda_fct_read, &dev)) { + ERRORLOG("error by create pthread"); + oops->wmaudio_close(); + wmcdda_close(&dev); + return -1; + } + + if(pthread_create(&thread_play, NULL, cdda_fct_play, &dev)) { + ERRORLOG("error by create pthread"); + oops->wmaudio_close(); + wmcdda_close(&dev); + return -1; + } + d->cdda_slave = 0; + return 0; +} + +int +cdda_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *frame, int *track, int *ind ) +{ + if (d->cdda_slave > -1) { + if(dev.status) + *mode = dev.status; + else + *mode = oldmode; + + if (*mode == WM_CDM_PLAYING) { + *track = dev.track; + *ind = dev.index; + *frame = dev.frame; + } else if (*mode == WM_CDM_CDDAERROR) { + /* + * An error near the end of the CD probably + * just means we hit the end. + */ + *mode = WM_CDM_TRACK_DONE; + } + return 0; + } + + return -1; +} + +int +cdda_play( struct wm_drive *d, int start, int end, int realstart ) +{ + if (d->cdda_slave > -1) { + dev.command = WM_CDM_STOPPED; + + wmcdda_setup(start, end, realstart); + + level = 2500; + volume = 1 << 15; + + dev.track = -1; + dev.index = 0; + dev.frame = start; + dev.command = WM_CDM_PLAYING; + + return 0; + } + + return -1; +} + +int +cdda_pause( struct wm_drive *d ) +{ + if (d->cdda_slave > -1) { + if(WM_CDM_PLAYING == dev.command) { + dev.command = WM_CDM_PAUSED; + } else { + dev.command = WM_CDM_PLAYING; + } + + return 0; + } + + return -1; +} + +int +cdda_stop( struct wm_drive *d ) +{ + if (d->cdda_slave > -1) { + dev.command = WM_CDM_STOPPED; + oops->wmaudio_stop(); + return 0; + } + + return -1; +} + +int +cdda_eject( struct wm_drive *d ) +{ + if (d->cdda_slave > -1) { + dev.command = WM_CDM_EJECTED; + oops->wmaudio_stop(); + /*wmcdda_close(&dev);*/ + return 0; + } + + return -1; +} + +int +cdda_set_volume( struct wm_drive *d, int left, int right ) +{ + if (d->cdda_slave > -1) { + int bal, vol; + + bal = (right - left) + 100; + bal *= 255; + bal /= 200; + if (right > left) + vol = right; + else + vol = left; + vol *= 255; + vol /= 100; + + if(oops->wmaudio_balance) + oops->wmaudio_balance(bal); + if(oops->wmaudio_volume) + oops->wmaudio_volume(vol); + + return 0; + } + + return -1; +} + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +cdda_get_volume( struct wm_drive *d, int *left, int *right ) +{ + if (d->cdda_slave > -1) { + if(!oops->wmaudio_state) { + dev.volume = -1; + dev.balance = 128; + } + + *left = *right = (dev.volume * 100 + 254) / 255; + + if (dev.balance < 110) + *right = (((dev.volume * dev.balance + 127) / 128) * 100 + 254) / 255; + else if (dev.balance > 146) + *left = (((dev.volume * (255 - dev.balance) + 127) / 128) * 100 + 254) / 255; + + return 0; + } + + return -1; +} + +/* + * Turn off the CDDA slave. + */ +void +cdda_kill( struct wm_drive *d ) +{ + if (d->cdda_slave > -1) { + dev.command = WM_CDM_STOPPED; + oops->wmaudio_stop(); + sleep(1); + wmcdda_close(&dev); + oops->wmaudio_close(); + + dev.blocks = NULL; + wait(NULL); + d->cdda_slave = -1; + } +} + +/* + * Tell the CDDA slave to set the loudness level. + */ +void +cdda_set_loudness( struct wm_drive *d, int loud ) +{ + if (d->cdda_slave > -1) { + loudness = loud; + } +} + +/* + * Tell the CDDA slave to start (or stop) saving to a file. + */ +void +cdda_save( struct wm_drive *d, char *filename ) +{ + #if 0 + int len; + + if (filename == NULL || filename[0] == '\0') + len = 0; + else + len = strlen(filename); + write(d->cdda_slave, "F", 1); + write(d->cdda_slave, &len, sizeof(len)); + if (len) + write(d->cdda_slave, filename, len); + + + read(0, &namelen, sizeof(namelen)); + if (output != NULL) { + fclose(output); + output = NULL; + } + if (namelen) { + filename = malloc(namelen + 1); + if (filename == NULL) { + perror("cddas"); + wmcdda_close(dev); + oops->wmaudio_close(); + exit(1); + } + + read(0, filename, namelen); + filename[namelen] = '\0'; + output = fopen(filename, "w"); + if (output == NULL) { + perror(filename); + } else { + /* Write an .au file header. */ + hdr.magic = htonl(0x2e736e64); + hdr.hdr_size = htonl(sizeof(hdr) + 28); + hdr.data_size = htonl(~0); + hdr.encoding = htonl(3); /* linear-16 */ + hdr.sample_rate = htonl(44100); + hdr.channels = htonl(2); + + fwrite(&hdr, sizeof(hdr), 1, output); + fwrite("Recorded from CD by WorkMan", 28, 1, output); + } + free(filename); + +#endif +} + +int get_next_block(int x) +{ + int y = ++x; + return (y < NUMBLOCKS)?y:0; +} + +void *cdda_fct_read(void* arg) +{ + struct cdda_device *cddadev = (struct cdda_device*)arg; + int i, j, wakeup; + long result; + + while (cddadev->blocks) { + while(cddadev->command != WM_CDM_PLAYING) { + cddadev->status = cddadev->command; + sleep(1); + } + + i = 0; + pthread_mutex_lock(&blks_mutex[i]); + wakeup = 1; + + while(cddadev->command == WM_CDM_PLAYING) { + + result = wmcdda_read(cddadev, &blks[i]); + + if (result <= 0 && blks[i].status != WM_CDM_TRACK_DONE) { + ERRORLOG("cdda: wmcdda_read failed, stop playing\n"); + cddadev->command = WM_CDM_STOPPED; + break; + } else { + if (output) + fwrite(blks[i].buf, blks[i].buflen, 1, output); + } + + j = get_next_block(i); + pthread_mutex_lock(&blks_mutex[j]); + + if(wakeup) { + wakeup = 0; + pthread_cond_signal(&wakeup_audio); + } + + pthread_mutex_unlock(&blks_mutex[i]); + /* audio can start here */ + + i = j; + } + + pthread_mutex_unlock(&blks_mutex[i]); + } + + return 0; +} + +void *cdda_fct_play(void* arg) +{ + struct cdda_device *cddadev = (struct cdda_device*)arg; + int i = 0; + + while (cddadev->blocks) { + if(cddadev->command != WM_CDM_PLAYING) { + i = 0; + pthread_mutex_lock(&blks_mutex[i]); + pthread_cond_wait(&wakeup_audio, &blks_mutex[i]); + } else { + i = get_next_block(i); + pthread_mutex_lock(&blks_mutex[i]); + } + + if (oops->wmaudio_play(&blks[i])) { + oops->wmaudio_stop(); + ERRORLOG("cdda: wmaudio_play failed\n"); + cddadev->command = WM_CDM_STOPPED; + } + cddadev->frame = blks[i].frame; + cddadev->track = blks[i].track; + cddadev->index = blks[i].index; + cddadev->status = blks[i].status; + + pthread_mutex_unlock(&blks_mutex[i]); + } + + return 0; +} + +#endif diff --git a/kscd/libwm/cddaslave.c b/kscd/libwm/cddaslave.c new file mode 100644 index 00000000..9a008b84 --- /dev/null +++ b/kscd/libwm/cddaslave.c @@ -0,0 +1,572 @@ +/* + * $id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ****************************************************************** + * + * Digital audio manipulator for WorkMan. + * + * The CDDA architecture looks like this: + * + * WorkMan (or another UI!) + * ^^^ + * ||| (separate processes connected by pipe) + * vvv + * +------------- cddaslave -------------+ + * | | | + * command module CDDA reader audio output + * (portable) (per platform) (per platform) + * + * This source file has the command module and some of the scaffolding + * to hold cddaslave together, plus some non-system-dependent audio + * processing code. Look in plat_*_cdda.c for system-specific stuff. + * + */ + +#include "include/wm_cdda.h" + +#ifdef BUILD_CDDA + +static char cddaslave_id[] = "$Id$"; + +#include <stdio.h> +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include "include/wm_struct.h" +#include "include/wm_cdda.h" +#include "include/wm_platform.h" +#include "audio/audio.h" + +void send_status(struct cdda_block *); + +#define SEND_ACK(b); { (b)->status |= WMCDDA_ACK; send_status(b); } +#define SEND_STATUS(b); { (b)->status &= ~WMCDDA_ACK; send_status(b); } +#define SEND_STATUS_ACK(b, s); { (b)->status = ((s) | WMCDDA_ACK); send_status(b); } + +int receive_command(struct cdda_device *, struct cdda_block *); + +int playing = 0; /* Should the CD be playing now? */ +int pausing = 0; + +/* + * Loudness setting, plus the floating volume multiplier and decaying-average + * volume level. + */ +int loudness = 0; +unsigned int volume = 32768; +unsigned int level; + +/* + * Playback speed (0 = slow) + */ +int speed = 128; + +/* + * This is non-null if we're saving audio to a file. + */ +FILE *output = NULL; + +/* + * These are driverdependent oops + * + */ +struct audio_oops *oops = NULL; + +/* + * Audio file header format. + */ +typedef unsigned long u_32; +struct auheader { + u_32 magic; + u_32 hdr_size; + u_32 data_size; + u_32 encoding; + u_32 sample_rate; + u_32 channels; +}; + +/* had to change #ifdef to #if -> see wm_cdda.h */ +#ifdef __FreeBSD__ +/* Phungus not with htonl on FreeBSD */ +#include <sys/param.h> +#else +#if WM_BIG_ENDIAN +# ifndef htonl +# define htonl(x) (x) +# endif +#else +extern unsigned long htonl(x); +#endif +#endif + +void *malloc(); +long cdda_transform(); + +/* + * Send status information upstream. + */ +void +send_status(struct cdda_block *blk) +{ + DEBUGLOG("send_status, send %i(%s | %s)\n", blk->status, + gen_status(blk->status & WMCDDA_ACK), gen_status(blk->status & ~WMCDDA_ACK)); + write(1, blk, sizeof(*blk)); +} + +/* + * Accept a command from our master. + * + * The protocol is byte-oriented: + * PmsfMSFxyz Play from msf to MSF (MSF can be 0,0,0 to play to end) + * xyz is the msf of the start of this chunk, i.e., the + * ending boundary for reverse play. + * S Stop. + * E Eject. This means we just close the CD device and + * open it again later. + * Q Quit. + * Vn Set volume level (0-255). + * Bn Set balance level (0-255). + * EnL Set an equalizer level (n = 0 for bass, 255 for treble) + * G Get current status. + * sn Set speed multiplier to n. + * dn Set direction to forward (n = 0) or reverse. + * Fllllx... Start saving to a file (length = l, followed by name) + * F0000 Stop saving. + * Ln Set loudness level (0-255). + * A Pause/Resume + * I Get status, current frame + */ +int +receive_command(struct cdda_device *dev, struct cdda_block* blk) +{ + unsigned char inbuf[10]; + char *filename; + int namelen; + struct auheader hdr; + + if (read(0, inbuf, 1) <= 0) { + wmcdda_close(dev); + oops->wmaudio_close(); +/* ERRORLOG("cddaslave: parent died, exit\n");*/ + exit(0); + } + + DEBUGLOG("cddaslave: CMD %c\n", inbuf[0]); + + switch (inbuf[0]) { + case 'I': + if(dev->fd < 0) wmcdda_init(dev, blk); + SEND_ACK(blk); + break; + case 'A': /* pause/resume */ + if(WMCDDA_PLAYING & blk->status) { + oops->wmaudio_stop(); + SEND_STATUS_ACK(blk, WMCDDA_PAUSED); + } else if (WMCDDA_PAUSED & blk->status) { + SEND_STATUS_ACK(blk, WMCDDA_PLAYING); + } else { + SEND_ACK(blk); + } + break; + case 'E': + oops->wmaudio_stop(); + wmcdda_close(dev); + SEND_ACK(blk); + break; + case 'P': + read(0, inbuf, 9); + + wmcdda_setup(inbuf[0] * 60 * 75 + inbuf[1] * 75 + inbuf[2], + inbuf[3] * 60 * 75 + inbuf[4] * 75 + inbuf[5], + inbuf[6] * 60 * 75 + inbuf[7] * 75 + inbuf[8]); + + level = 2500; + volume = 1 << 15; + + blk->track = -1; + blk->index = 0; + blk->minute = inbuf[6]; + blk->second = inbuf[7]; + blk->frame = inbuf[8]; + SEND_STATUS_ACK(blk, WMCDDA_PLAYING); + break; + case 'S': + oops->wmaudio_stop(); + SEND_STATUS_ACK(blk, WMCDDA_STOPPED); + break; + case 'B': + read(0, inbuf, 1); + if(oops->wmaudio_balance) + oops->wmaudio_balance(inbuf[0]); + break; + case 'V': + read(0, inbuf, 1); + if(oops->wmaudio_balance) + oops->wmaudio_volume(inbuf[0]); + break; + case 'G': + SEND_ACK(blk); + if(!oops->wmaudio_state || oops->wmaudio_state(blk) == -1) { + blk->volume = -1; + blk->balance = 128; + } + send_status(blk); + break; + case 'Q': + SEND_ACK(blk); + wmcdda_close(dev); + oops->wmaudio_close(); + exit(0); +/* + case 's': + read(0, inbuf, 1); + speed = inbuf[0]; + wmcdda_speed(speed); + SEND_STATUS(blk, WMCDDA_ACK); + break; + + case 'd': + read(0, inbuf, 1); + wmcdda_direction(inbuf[0]); + SEND_STATUS(blk, WMCDDA_ACK); + break; +*/ + case 'L': + read(0, inbuf, 1); + loudness = inbuf[0]; + SEND_ACK(blk); + break; + case 'F': + read(0, &namelen, sizeof(namelen)); + if (output != NULL) { + fclose(output); + output = NULL; + } + if (namelen) { + filename = malloc(namelen + 1); + if (filename == NULL) { + perror("cddaslave"); + wmcdda_close(dev); + oops->wmaudio_close(); + exit(1); + } + + read(0, filename, namelen); + filename[namelen] = '\0'; + output = fopen(filename, "w"); + if (output == NULL) + perror(filename); + else { + /* Write an .au file header. */ + hdr.magic = htonl(0x2e736e64); + hdr.hdr_size = htonl(sizeof(hdr) + 28); + hdr.data_size = htonl(~0); + hdr.encoding = htonl(3); /* linear-16 */ + hdr.sample_rate = htonl(44100); + hdr.channels = htonl(2); + + fwrite(&hdr, sizeof(hdr), 1, output); + fwrite("Recorded from CD by WorkMan", 28, 1, output); + } + free(filename); + } + SEND_ACK(blk); + } + + return(dev->fd); +} + + +/* + * Transform some CDDA data. + */ +long +wmcdda_transform(unsigned char *rawbuf, long buflen, struct cdda_block *block) +{ + long i; + long *buf32 = (long *)rawbuf; + register short *buf16 = (short *)rawbuf; + register int aval; + + /* + * Loudness transformation. Basically this is a self-adjusting + * volume control; our goal is to keep the average output level + * around a certain value (2500 seems to be pleasing.) We do this + * by maintaining a decaying average of the recent output levels + * (where "recent" is some fraction of a second.) All output levels + * are multiplied by the inverse of the decaying average; this has + * the volume-leveling effect we desire, and isn't too CPU-intensive. + * + * This is done by modifying the digital data, rather than adjusting + * the system volume control, because (at least on some systems) + * tweaking the system volume can generate little pops and clicks. + * + * There's probably a more elegant way to achieve this effect, but + * what the heck, I never took a DSP class and am making this up as + * I go along, with a little help from some friends. + * + * This is all done with fixed-point math, oriented around powers + * of two, which with luck will keep the CPU usage to a minimum. + * More could probably be done, for example using lookup tables to + * replace multiplies and divides; whether the memory hit (128K + * for each table) is worthwhile is unclear. + */ + if (loudness) + { + /* We aren't really going backwards, but i > 0 is fast */ + for (i = buflen / 2; i > 0; i--) + { + /* + * Adjust this sample to the current level. + */ + aval = (*buf16 = (((long)*buf16) * volume) >> 15); + buf16++; + + /* + * Don't adjust the decaying average for each sample; + * that just spends CPU time for very little benefit. + */ + if (i & 127) + continue; + + /* + * We want to use absolute values to compute the + * decaying average; otherwise it'd sit around 0. + */ + if (aval < 0) + aval = -aval; + + /* + * Adjust more quickly when we start hitting peaks, + * or we'll get clipping when there's a sudden loud + * section after lots of quiet. + */ + if (aval & ~8191) + aval <<= 3; + + /* + * Adjust the decaying average. + */ + level = ((level << 11) - level + aval) >> 11; + + /* + * Let *really* quiet sounds play softly, or we'll + * amplify background hiss to full volume and blast + * the user's speakers when real sound starts up. + */ + if (! (level & ~511)) + level = 512; + + /* + * And adjust the volume setting using the inverse + * of the decaying average. + */ + volume = (2500 << 15) / level; + } + } + + if (speed == 128) + return (buflen); + + /* + * Half-speed play. Stretch things out. + */ + if (speed == 0) + { + buflen /= 2; /* Since we're moving 32 bits at a time */ + + for (i = buflen - 1; i > 0; i--) + { + buf32[i] = buf32[i / 2]; + } + + buflen *= 4; /* 2 for doubling the buffer, 2 from above */ + } + + /* + * Slow play; can't optimize it as well as half-speed. + */ + if (speed && speed < 128) + { + int multiplier = ((speed + 128) * 128) / 256; + int newlen; + int tally = 0, pos; + + buflen /= 4; /* Get the number of 32-bit values */ + + /* + * Buffer length doubles when speed is 0, stays the same + * when speed is 128. + */ + newlen = (buflen * 128) / multiplier; + + pos = buflen - 1; + for (i = newlen - 1; i > 0; i--) + { + buf32[i] = buf32[pos]; + tally += multiplier; + if (tally & 128) + { + pos--; + tally ^= 128; + } + } + + buflen = newlen * 4; + } + + return (buflen); +} + + +int main(int argc, char **argv) +{ + fd_set readfd, dummyfd; + struct timeval timeout; + struct cdda_block blockinfo; + long result; + int nfds; + struct cdda_device dev; + const char *sondsystem; + const char *sounddevice; + const char *sounddevicectl; + + memset(&blockinfo, 0, sizeof(struct cdda_block)); + + /* + * Device name should be the command-line argument. + */ + if (argc < 2) + dev.devname = NULL; + else + dev.devname = argv[1]; + + if (argc < 3) + sondsystem = "arts"; + else + sondsystem = argv[2]; + + if (argc < 4) + sounddevice = NULL; + else + sounddevice = argv[3]; + + if (argc < 5) + sounddevicectl = NULL; + else + sounddevicectl = argv[3]; + + DEBUGLOG("cddaslave: called with %s %s %s %s\n", + dev.devname?dev.devname:"", + sondsystem?sondsystem:"", + sounddevice?sounddevice:"", + sounddevicectl?sounddevicectl:""); + + /* + * If we're running setuid root, bump up our priority then lose + * superuser access. + */ + nice(-14); + setgid(getgid()); + setuid(getuid()); + if (geteuid() != getuid()) + return 255; + + FD_ZERO(&dummyfd); + FD_ZERO(&readfd); + + timerclear(&timeout); + + dev.fd = -1; + wmcdda_init(&dev, &blockinfo); + + oops = setup_soundsystem(sondsystem, sounddevice, sounddevicectl); + if (!oops) { + ERRORLOG("cddaslave: setup_soundsystem failed\n"); + exit(1); + } + + DEBUGLOG("cddaslave: sent first ACK\n"); + SEND_ACK(&blockinfo); + + /* + * Accept commands as they come in, and play some sound if we're + * supposed to be doing that. + */ + while (1) { + FD_SET(0, &readfd); + + /* + * If we're playing, we don't want select to block. Otherwise, + * wait a little while for the next command. + */ + if (playing) + timeout.tv_usec = 0; + else + timeout.tv_usec = 500000; + + nfds = select(1, &readfd, &dummyfd, &dummyfd, &timeout); + + if (nfds < 0) { /* Broken pipe; our GUI exited. */ + wmcdda_close(&dev); + oops->wmaudio_close(); + exit(0); + } + + if (FD_ISSET(0, &readfd)) { + receive_command(&dev, &blockinfo); + /* + * Process all commands in rapid succession, rather + * than possibly waiting for a CDDA read. + */ + continue; + } + + if ((blockinfo.status & ~WMCDDA_ACK) == WMCDDA_PLAYING) { + result = wmcdda_read(&dev, &blockinfo); + if (result <= 0 && blockinfo.status != WMCDDA_DONE) { + ERRORLOG("cddaslave: wmcdda_read failed\n"); + blockinfo.status = WMCDDA_STOPPED; + send_status(&blockinfo); + } else { + result = wmcdda_normalize(&dev, &blockinfo); + if (output) + fwrite(dev.buf, result, 1, output); + + if (oops->wmaudio_play(dev.buf, dev.buflen, &blockinfo)) { + oops->wmaudio_stop(); + ERRORLOG("cddaslave: wmaudio_play failed\n"); + blockinfo.status = WMCDDA_STOPPED; + send_status(&blockinfo); + } + } + } else + send_status(&blockinfo); + } + + return 0; +} + +#endif /* BUILD_CDDA */ diff --git a/kscd/libwm/cddb.c b/kscd/libwm/cddb.c new file mode 100644 index 00000000..657ab6a4 --- /dev/null +++ b/kscd/libwm/cddb.c @@ -0,0 +1,588 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * establish connection to cddb server and get data + * socket stuff gotten from gnu port of the finger command + * + * provided by Sven Oliver Moll + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/signal.h> +#include <ctype.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdinfo.h" +#include "include/wm_helpers.h" +#include "include/wm_cddb.h" + +struct wm_cddb cddb; + +extern struct wm_cdinfo thiscd; +static int cur_cddb_protocol; +static char *cur_cddb_server; +static char *cur_cddb_mail_adress; +static char *cur_cddb_path_to_cgi; +static char *cur_cddb_proxy_server; + +/* local prototypes */ +int cddb_sum(int); +char *string_split(char *line, char delim); +void string_makehello(char *line, char delim); +int connect_open(void); +void connect_close(void); +void connect_getline(char *line); +void connect_read_entry(void); +void cddbp_send(const char *line); +void cddbp_read(char *category, unsigned int id); +void http_send(char* line); +void http_read(char *category, unsigned int id); +void cddb_request(void); +/* local prototypes END */ + +static int Socket; +static FILE *Connection; + +/* + * + */ +void +cddb_cur2struct(void) +{ + cddb.protocol = cur_cddb_protocol; + strcpy(cddb.cddb_server, cur_cddb_server); + strcpy(cddb.mail_adress, cur_cddb_mail_adress); + strcpy(cddb.path_to_cgi, cur_cddb_path_to_cgi); + strcpy(cddb.proxy_server, cur_cddb_proxy_server); +} /* cddb_cur2struct() */ + +/* + * + */ +void +cddb_struct2cur(void) +{ + cur_cddb_protocol = cddb.protocol; + cur_cddb_server = (cddb.cddb_server); + cur_cddb_mail_adress = (cddb.mail_adress); + cur_cddb_path_to_cgi = (cddb.path_to_cgi); + cur_cddb_proxy_server = (cddb.proxy_server); +} /* cddb_struct2cur() */ + + +/* + * Subroutine from cddb_discid + */ +int +cddb_sum(int n) +{ + char buf[12], + *p; + int ret = 0; + + /* For backward compatibility this algorithm must not change */ + sprintf(buf, "%lu", (unsigned long)n); + for (p = buf; *p != '\0'; p++) + ret += (*p - '0'); + + return (ret); +} /* cddb_sum() */ + + +/* + * Calculate the discid of a CD according to cddb + */ +unsigned long +cddb_discid(void) +{ + int i, + t, + n = 0; + + /* For backward compatibility this algorithm must not change */ + for (i = 0; i < thiscd.ntracks; i++) { + + n += cddb_sum(thiscd.trk[i].start / 75); + /* + * Just for demonstration (See below) + * + * t += (thiscd.trk[i+1].start / 75) - + * (thiscd.trk[i ].start / 75); + */ + } + + /* + * Mathematics can be fun. Example: How to reduce a full loop to + * a simple statement. The discid algorhythm is so half-hearted + * developed that it doesn't even use the full 32bit range. + * But it seems to be always this way: The bad standards will be + * accepted, the good ones turned down. + * Boy, you pulled out the /75. This is not correct here, because + * this calculation needs the integer division for both .start + * fields. + */ + + t = (thiscd.trk[thiscd.ntracks].start / 75) + - (thiscd.trk[0].start / 75); + return ((n % 0xff) << 24 | t << 8 | thiscd.ntracks); +} /* cddb_discid() */ + +/* + * Split a string into two components according to the first occurrence of + * the delimiter. + */ +char * +string_split(char *line, char delim) +{ + char *p1; + + for (p1=line;*p1;p1++) + { + if(*p1 == delim) + { + *p1 = 0; + return ++p1; + } + } + return (NULL); +} /* string_split() */ + +/* + * Generate the hello string according to the cddb protocol + * delimiter is either ' ' (cddbp) or '+' (http) + */ +void +string_makehello(char *line,char delim) +{ + char mail[84],*host; + + strcpy(mail,cddb.mail_adress); + host=string_split(mail,'@'); + + sprintf(line,"%shello%c%s%c%s%c%s%c%s", + delim == ' ' ? "cddb " : "&", + delim == ' ' ? ' ' : '=', + mail,delim, + host,delim, + WORKMAN_NAME,delim, + WORKMAN_VERSION); +} /* string_makehello() */ + +/* + * Open the TCP connection to the cddb/proxy server + */ +int +connect_open(void) +{ + char *host; + struct hostent *hp; + struct sockaddr_in soc_in; + int port; + + if(cddb.protocol == 3) /* http proxy */ + host = wm_strdup(cddb.proxy_server); + else + host = wm_strdup(cddb.cddb_server); + /* + * t=string_split(host,':'); + */ + port=atoi(string_split(host,':')); + if(!port) + port=8880; + +#ifndef NDEBUG + printf("%s:%d\n",host,port); +#endif + hp =gethostbyname(host); + + if (hp == NULL) + { + static struct hostent def; + static struct in_addr defaddr; + static char *alist[1]; + static char namebuf[128]; + int inet_addr(); + + defaddr.s_addr = inet_addr(host); + if ((int) defaddr.s_addr == -1) + { +#ifndef NDEBUG + printf("unknown host: %s\n", host); +#endif + return (-1); + } + strcpy(namebuf, host); + def.h_name = namebuf; + def.h_addr_list = alist, def.h_addr = (char *)&defaddr; + def.h_length = sizeof (struct in_addr); + def.h_addrtype = AF_INET; + def.h_aliases = 0; + hp = &def; + } + soc_in.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (char *)&soc_in.sin_addr, hp->h_length); + soc_in.sin_port = htons(port); + Socket = socket(hp->h_addrtype, SOCK_STREAM, 0); + if (Socket < 0) + { + perror("socket"); + return (-1); + } + fflush(stdout); + if (connect(Socket, (struct sockaddr *)&soc_in, sizeof (soc_in)) < 0) + { + perror("connect"); + close(Socket); + return (-1); + } + + Connection = fdopen(Socket, "r"); + return (0); +} /* connect_open() */ + + +/* + * Close the connection + */ +void +connect_close(void) +{ + (void)fclose(Connection); + close(Socket); +} /* connect_close() */ + +/* + * Get a line from the connection with CR and LF stripped + */ +void +connect_getline(char *line) +{ + char c; + + while ((c = getc(Connection)) != '\n') + { + *line = c; + if ((c != '\r') && (c != (char)0xff)) + line++; + } + *line=0; +} /* connect_getline() */ + +/* + * Read the CD data from the server and place them into the cd struct + */ +void +connect_read_entry(void) +{ + char type; + int trknr; + + char *t,*t2,tempbuf[2000]; + + while(strcmp(tempbuf,".")) + { + connect_getline(tempbuf); + + t=string_split(tempbuf,'='); + if(t != NULL) + { + type=tempbuf[0]; + + if(strncmp("TITLE",tempbuf+1,5)) + continue; + + if('D' == type) + { + /* + * Annahme: "Interpret / Titel" ist falsch. + * Daher: NULL-String erwarten. + */ + t2=string_split(t,'/'); + if(t2 == NULL) + t2 = t; + if(*t2 == ' ') + t2++; + strncpy(cd->cdname,t2,sizeof(cd->cdname)-1); + cd->cdname[sizeof(cd->cdname)-1]='\0'; + + for(t2=t;*t2;t2++) + { + if((*t2 == ' ') && (*(t2+1) == 0)) + *t2=0; + } + strncpy(cd->artist,t,sizeof(cd->artist)-1); + cd->artist[sizeof(cd->artist)-1]='\0'; + } + + if('T' == type) + { + trknr=atoi(tempbuf+6); + /* + * printf("Track %d:%s\n",trknr,t); + */ + wm_strmcpy(&cd->trk[trknr].songname,t); + } + /* + * fprintf(stderr, "%s %s\n",tempbuf,t); + */ + } + } +} /* connect_read_entry() */ + +/* + * Send a command to the server using cddbp + */ +void +cddbp_send(const char *line) +{ + write(Socket, line, strlen(line)); + write(Socket, "\n", 1); +} /* cddbp_send() */ + +/* + * Send the "read from cddb" command to the server using cddbp + */ +void +cddbp_read(char *category, unsigned int id) +{ + char tempbuf[84]; + sprintf(tempbuf, "cddb read %s %08x", category, id); + cddbp_send(tempbuf); +} /* cddbp_read() */ + +/* + * Send a command to the server using http + */ +void +http_send(char* line) +{ + char tempbuf[2000]; + + write(Socket, "GET ", 4); +#ifndef NDEBUG + printf("GET "); +#endif + if(cddb.protocol == 3) + { + write(Socket, "http://", 7); + write(Socket, cddb.cddb_server, strlen(cddb.cddb_server)); +#ifndef NDEBUG + printf("http://%s",cddb.cddb_server); +#endif + } + write(Socket, cddb.path_to_cgi, strlen(cddb.path_to_cgi)); + write(Socket, "?cmd=" ,5); + write(Socket, line, strlen(line)); +#ifndef NDEBUG + printf("%s?cmd=%s",cddb.path_to_cgi,line); +#endif + string_makehello(tempbuf,'+'); + write(Socket, tempbuf, strlen(tempbuf)); +#ifndef NDEBUG + printf("%s",tempbuf); +#endif + write(Socket, "&proto=1 HTTP/1.0\n\n", 19); +#ifndef NDEBUG + printf("&proto=1 HTTP/1.0\n"); +#endif + do + connect_getline(tempbuf); + while(strcmp(tempbuf,"")); +} /* http_send() */ + +/* + * Send the "read from cddb" command to the server using http + */ +void +http_read(char *category, unsigned int id) +{ + char tempbuf[84]; + sprintf(tempbuf, "cddb+read+%s+%08x", category, id); + http_send(tempbuf); +} /* http_read() */ + +/* + * The main routine called from the ui + */ +void +cddb_request(void) +{ + int i; + char tempbuf[2000]; + extern int cur_ntracks; + + int status; + char category[21]; + unsigned int id; + + strcpy(cddb.cddb_server,"localhost:888"); + strcpy(cddb.mail_adress,"svolli@bigfoot.com"); + /* + * cddb.protocol = 1; + */ + wipe_cdinfo(); + + switch(cddb.protocol) + { + case 1: /* cddbp */ +#ifndef NDEBUG + printf("USING CDDBP\n"); + printf("open\n"); +#endif + connect_open(); + connect_getline(tempbuf); +#ifndef NDEBUG + printf("[%s]\n",tempbuf); +#endif + /* + * if(atoi(tempbuf) == 201) return; + */ + + /* + * strcpy(tempbuf,"cddb hello svolli bigfoot.com Eierkratzer eins"); + */ + string_makehello(tempbuf,' '); +#ifndef NDEBUG + fprintf(stderr, "%s\n", tempbuf); +#endif + cddbp_send(tempbuf); + connect_getline(tempbuf); +#ifndef NDEBUG + printf("[%s]\n",tempbuf); + printf("query\n"); +#endif + sprintf(tempbuf, "cddb query %08x %d",thiscd.cddbid,thiscd.ntracks); + for (i = 0; i < cur_ntracks; i++) + if (thiscd.trk[i].section < 2) + sprintf(tempbuf + strlen(tempbuf), " %d", + thiscd.trk[i].start); + sprintf(tempbuf + strlen(tempbuf), " %d\n", thiscd.length); +#ifndef NDEBUG + printf(">%s<\n",tempbuf); +#endif + cddbp_send(tempbuf); + connect_getline(tempbuf); +#ifndef NDEBUG + printf("[%s]\n",tempbuf); +#endif + status=atoi(tempbuf); + /* + * fprintf(stderr, "status:%d\n",status); + * fprintf(stderr,"category:%s\n",category); + * fprintf(stderr,"id:%s\n",id); + */ + if(status == 200) /* Exact match */ + { + sscanf(tempbuf,"%d %20s %08x",&status,category,&id); + cddbp_read(category,id); + connect_read_entry(); + } + + if(status == 211) /* Unexact match, multiple possible + * Hack: always use first. */ + { + connect_getline(tempbuf); + sscanf(tempbuf,"%20s %08x",category,&id); + while(strcmp(tempbuf,".")) + connect_getline(tempbuf); + cddbp_read(category,id); + connect_read_entry(); + } + + cddbp_send("quit"); + connect_close(); +#ifndef NDEBUG + printf("close\n"); +#endif + break; + case 2: /* http */ + case 3: /* http proxy */ +#ifndef NDEBUG + printf("USING HTTP%s\n", + (cddb.protocol == 3) ? " WITH PROXY" : ""); + printf("query\n"); +#endif + sprintf(tempbuf, "cddb+query+%08x+%d",thiscd.cddbid,thiscd.ntracks); + for (i = 0; i < cur_ntracks; i++) + if (thiscd.trk[i].section < 2) + sprintf(tempbuf + strlen(tempbuf), "+%d", + thiscd.trk[i].start); + sprintf(tempbuf + strlen(tempbuf), "+%d", thiscd.length); +#ifndef NDEBUG + printf(">%s<\n",tempbuf); +#endif + connect_open(); + http_send(tempbuf); + connect_getline(tempbuf); +#ifndef NDEBUG + printf("[%s]\n",tempbuf); +#endif + status=atoi(tempbuf); + /* + * fprintf(stderr, "status:%d\n",status); + * fprintf(stderr, "category:%s\n",category); + * fprintf(stderr, "id:%s\n",id); + */ + + if(status == 200) /* Exact match */ + { + connect_close(); + connect_open(); + sscanf(tempbuf,"%d %20s %08x",&status,category,&id); + http_read(category,id); + connect_read_entry(); + } + + if(status == 211) /* Unexact match, multiple possible + * Hack: always use first. */ + { + connect_getline(tempbuf); + sscanf(tempbuf,"%20s %08x",category,&id); + while(strcmp(tempbuf,".")) + connect_getline(tempbuf); + connect_close(); + connect_open(); + http_read(category,id); + connect_read_entry(); + } + /* moved close above break */ + connect_close(); + break; + default: /* off */ + break; + } +} /* cddb_request() */ + diff --git a/kscd/libwm/cdinfo.c b/kscd/libwm/cdinfo.c new file mode 100644 index 00000000..b04dec69 --- /dev/null +++ b/kscd/libwm/cdinfo.c @@ -0,0 +1,890 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Get information about a CD. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#include "include/wm_config.h" + +#include "include/wm_struct.h" +#include "include/wm_cdrom.h" +#include "include/wm_cdinfo.h" +#include "include/wm_database.h" +#include "include/wm_helpers.h" + +struct wm_play *playlist = NULL; +struct wm_cdinfo thiscd, *cd = &thiscd; + +int cur_track = -1; /* Current track number, starting at 1 */ +int cur_index = 0; /* Current index mark */ +int cur_lasttrack = 999; /* Last track to play in current chunk */ +int cur_firsttrack = 0; /* First track of current chunk */ +int cur_pos_abs; /* Current absolute position in seconds */ +int cur_frame; /* Current frame number */ +int cur_pos_rel; /* Current track-relative position in seconds */ +int cur_tracklen; /* Length in seconds of current track */ +int cur_cdlen; /* Length in seconds of entire CD */ +int cur_ntracks; /* Number of tracks on CD (= tracks + sections) */ +int cur_nsections; /* Number of sections currently defined */ + +int cur_listno; /* Current index into the play list, if playing */ +char * cur_artist; /* Name of current CD's artist */ +char * cur_cdname; /* Album name */ +char * cur_trackname; /* Take a guess */ +char cur_contd; /* Continued flag */ +char cur_avoid; /* Avoid flag */ + +int exit_on_eject = 0; + +int cur_stopmode = -1; +int info_modified; + +/* + * insert_trackinfo() + * + * Add a new track to the CD info structure. Pass the position of the new + * entry in the track list -- 0 will make this the first track, 1 the second, + * etc. The new entry will be zeroed out. + */ +static void +insert_trackinfo(int num) +{ + struct wm_trackinfo *newtrk; + + /* Easy case: the list is empty */ + if (cd->trk == NULL) { + if ((cd->trk = (struct wm_trackinfo *) calloc(1, + sizeof(*newtrk))) == NULL) + { + perror("insert_trackinfo"); + exit(1); + } else { + return; + } /* if() else */ + } /* if() */ + /* Stick the new entry in cd->trk[]. */ + if ((newtrk = (struct wm_trackinfo *) malloc(sizeof(*newtrk) * + (cur_ntracks + 1))) == NULL) + { + perror("insert_trackinfo"); + exit(1); + } + + if (num) + memcpy(newtrk, cd->trk, sizeof(*newtrk) * num); + memset(&newtrk[num], 0, sizeof(*newtrk)); + if (num < cur_ntracks) + memcpy(&newtrk[num + 1], &cd->trk[num], sizeof(*newtrk) * + (cur_ntracks - num)); + + free(cd->trk); + cd->trk = newtrk; +} + +/* + * split_trackinfo() + * + * Split a track in two at a particular position (absolute, in frames). All + * internal data structures and variables will be adjusted to the new + * numbering scheme. Pass in the track number (>=1) to split, which is also + * the index into cd->trk[] of the new entry. + * + * If pos is within 1 second of the start of another track, the split fails. + * + * Returns 1 on success, 0 if the track couldn't be inserted. + * + * Note: updating user interface elements is up to the caller. + */ +int +split_trackinfo( int pos ) +{ + int i, l, num; + + if (pos < cd->trk[0].start) + return (0); + + /* First find the appropriate track. */ + for (num = 0; num < cur_ntracks; num++) + if (cd->trk[num].start - 75 < pos && + cd->trk[num].start + 75 > pos) + return (0); + else if (cd->trk[num].start > pos) + break; + if (num == 0) + return (0); + + /* Insert the new entry into the track array. */ + insert_trackinfo(num); + + /* Update the easy variables. */ + if (cur_track > num) + cur_track++; + if (cur_firsttrack > num) + cur_firsttrack++; + if (cur_lasttrack > num) + cur_lasttrack++; + + /* Update the user-defined playlists. */ + if (cd->lists != NULL) + for (l = 0; cd->lists[l].name != NULL; l++) + if (cd->lists[l].list != NULL) + for (i = 0; cd->lists[l].list[i]; i++) + if (cd->lists[l].list[i] > num) + cd->lists[l].list[i]++; + + /* Update the internal playlist. */ + if (playlist != NULL) + for (i = 0; playlist[i].start; i++) + { + if (playlist[i].start > num) + playlist[i].start++; + if (playlist[i].end > num) + playlist[i].end++; + } + + /* Now adjust the information in cd->trk[]. */ + cd->trk[num].start = pos; + if (num == cur_ntracks) + cd->trk[num].length = cur_cdlen - pos / 75; + else + cd->trk[num].length = (cd->trk[num + 1].start - pos) / 75; + cd->trk[num - 1].length -= cd->trk[num].length; + if (cur_track == num) + cur_tracklen -= cd->trk[num].length; + cd->trk[num].track = cd->trk[num - 1].track; + cd->trk[num].data = cd->trk[num - 1].data; + cd->trk[num].contd = 1; + cd->trk[num].volume = cd->trk[num - 1].volume; + + if (cd->trk[num - 1].section == 0) + cd->trk[num - 1].section = 1; + cd->trk[num].section = cd->trk[num - 1].section + 1; + + cur_ntracks++; + cur_nsections++; + + for (i = num + 1; i < cur_ntracks; i++) + if (cd->trk[i].track == cd->trk[num].track) + cd->trk[i].section++; + + return (1); +} + +/* + * remove_trackinfo() + * + * Remove a track's internal data. This is similar to split_trackinfo() + * above, but simpler. A track's initial section can't be removed. Track + * numbers start at 0. + * + * Returns 1 on success, 0 on failure. + */ +int +remove_trackinfo( int num ) +{ + int i, l; + + if (num < 1 || num >= cur_ntracks || cd->trk[num].section < 2) + return (0); + + cd->trk[num - 1].length += cd->trk[num].length; + + for (i = num; i < cur_ntracks - 1; i++) + memcpy(&cd->trk[i], &cd->trk[i + 1], sizeof(cd->trk[0])); + + if (cur_track > num) + cur_track--; + if (cur_firsttrack > num) + cur_firsttrack--; + if (cur_lasttrack > num) + cur_lasttrack--; + + /* Update the user-defined playlists. */ + if (cd->lists != NULL) + for (l = 0; cd->lists[l].name != NULL; l++) + if (cd->lists[l].list != NULL) + for (i = 0; cd->lists[l].list[i]; i++) + if (cd->lists[l].list[i] > num) + cd->lists[l].list[i]--; + + /* Update the internal playlist. */ + if (playlist != NULL) + for (i = 0; playlist[i].start; i++) + { + if (playlist[i].start > num) + playlist[i].start--; + if (playlist[i].end > num) + playlist[i].end--; + } + + cur_ntracks--; + cur_nsections--; + + /* + * Update the section numbers for this track. If this is the only + * user-created section in a track, get rid of the section number + * in the track's entry. + */ + if (num == cur_ntracks || cd->trk[num - 1].track != cd->trk[num].track) + { + if (cd->trk[num - 1].section == 1) + cd->trk[num - 1].section = 0; + } + else + for (i = num; i < cur_ntracks; i++) + if (cd->trk[i].track == cd->trk[num - 1].track) + cd->trk[i].section--; + + return (1); +} + +/* + * listentry() + * + * Return a scrolling list entry. + */ +char * +listentry( int num ) +{ + static char buf[600]; + const char *name; + char tracknum[20]; + int digits; + int sdigits; + + if (num >= 0 && num < cur_ntracks) + { + +/* + if (big_spaces) + { + digits = 2; + sdigits = cur_nsections < 9 ? -1 : -2; + } + else + { + digits = cd->trk[num].track < 10 ? 3 : 2; + sdigits = cur_nsections < 9 ? -1 : -3; + } +*/ + + digits = 2; + sdigits = cur_nsections < 9 ? -1 : -2; + + name = cd->trk[num].songname ? cd->trk[num].songname : ""; + + if (cur_nsections) + { + if (cd->trk[num].section > 9) + { + sprintf(tracknum, "%*d.%d", digits, + cd->trk[num].track, + cd->trk[num].section); + } else { + if (cd->trk[num].section) + { + sprintf(tracknum, "%*d.%*d", digits, + cd->trk[num].track, sdigits, + cd->trk[num].section); + } else { + sprintf(tracknum, "%*d%*s", digits, + cd->trk[num].track, + 2 - sdigits, " "); +/* 2 - sdigits - big_spaces, " ");*/ + } + } + } else { + sprintf(tracknum, "%*d", digits, cd->trk[num].track); + } + + if (cd->trk[num].data) + { + sprintf(buf, "%s) %3dMB %s", tracknum, + cd->trk[num].length / 1024, name); + } else { + sprintf(buf, "%s) %02d:%02d %s", tracknum, + cd->trk[num].length / 60, + cd->trk[num].length % 60, name); + } + + return (buf); + } else { + return (NULL); + } +} /* listentry() */ + +/* + * trackname() + * + * Return a track's name. + */ +const char * +trackname( int num ) +{ + if (num >= 0 && num < cur_ntracks) + { + if (cd->trk[num].songname) + { + return (cd->trk[num].songname); + } else { + return (""); + } + } else { + return (NULL); + } +} /* trackname() */ + +/* + * tracklen() + * + * Return a track's length in seconds. + */ +int +tracklen( int num ) +{ + if (cd != NULL && num >= 0 && num < cur_ntracks) + return (cd->trk[num].length); + else + return (0); +} + +/* + * get_default_volume() + * + * Return the default volume (0-32, 0=none) for the CD or a track. + */ +int +get_default_volume( int track ) +{ + if (! track) + return (cd->volume); + else if (track <= cur_ntracks) + return (cd->trk[track - 1].volume); + else + return (0); +} + +/* + * get_contd() + * + * Return the contd value for a track. + */ +int +get_contd( int num ) +{ + if (num >= 0 && num < cur_ntracks) + return (cd->trk[num].contd); + else + return (0); +} + +/* + * get_avoid() + * + * Return the avoid value for a track. + */ +int +get_avoid( int num ) +{ + if (num >= 0 && num < cur_ntracks) + return (cd->trk[num].avoid); + else + return (0); +} + +/* + * get_autoplay() + * + * Is autoplay set on this disc? + */ +int +get_autoplay( void ) +{ + return ( cd->autoplay ); +} + +/* + * get_playmode() + * + * Return the default playmode for the CD. + */ +int +get_playmode( void ) +{ + return ( cd->playmode ); +} + +/* + * get_runtime() + * + * Return the total running time for the current playlist in seconds. + */ +int +get_runtime( void ) +{ + int i; + + if (playlist == NULL || playlist[0].start == 0 || cur_firsttrack == -1) + return (cd == NULL ? 0 : cd->length); + + for (i = 0; playlist[i].start; i++) + ; + + return (playlist[i].starttime); +} + +/* + * default_volume() + * + * Set the default volume for the CD or a track. + */ +void +default_volume( int track, int vol ) +{ + if (track == 0) + cd->volume = vol; + else if (track <= cur_ntracks) + cd->trk[track - 1].volume = vol; +} + +/* + * Play the next thing on the playlist, if any. + */ +void +play_next_entry( int forward ) +{ + if (cd == NULL) + return; + if (playlist != NULL && playlist[cur_listno].start) + { + wm_cd_play(playlist[cur_listno].start, 0, + playlist[cur_listno].end); + cur_listno++; + } + else + wm_cd_stop(); +} + +/* + * Play the next track, following playlists as necessary. + */ +void +play_next_track( int forward ) +{ + if (cd == NULL) + return; + + /* Is the current playlist entry done? Move on, if so. */ + if (playlist == NULL || cur_track + 1 == playlist[cur_listno - 1].end) + play_next_entry( forward ); + else + wm_cd_play(cur_track + 1, 0, playlist[cur_listno - 1].end); +} + +/* + * Play the previous track, hopping around the playlist as necessary. + */ +void +play_prev_track( int forward ) +{ + if (cd == NULL) + return; + + if (playlist == NULL) + return; + + /* If we're in the middle of this playlist entry, go back one track */ + if (cur_track > playlist[cur_listno - 1].start) + wm_cd_play(cur_track - 1, 0, playlist[cur_listno - 1].end); + else + if (cur_listno > 1) + { + cur_listno--; + wm_cd_play(playlist[cur_listno - 1].end - 1, 0, + playlist[cur_listno - 1].end); + } + else + wm_cd_play(playlist[0].start, 0, playlist[0].end); +} + +/* + * stash_cdinfo(artist, cdname) + */ +void +stash_cdinfo(char *artist, char *cdname, int autoplay, int playmode ) +{ + if (cd != NULL) + { + if (strcmp(cd->artist, artist)) + info_modified = 1; + strncpy(cd->artist, artist,sizeof(cd->artist)-1); + cd->artist[sizeof(cd->artist)-1]='\0'; + + if (strcmp(cd->cdname, cdname)) + info_modified = 1; + strncpy(cd->cdname, cdname,sizeof(cd->cdname)-1); + cd->cdname[sizeof(cd->cdname)-1]='\0'; + + if (!!cd->autoplay != !!autoplay) + info_modified = 1; + cd->autoplay = autoplay; + + if (!!cd->playmode != !!playmode) + info_modified = 1; + cd->playmode = playmode; + } +} /* stash_cdinfo() */ + +/* + * wipe_cdinfo() + * + * Clear out all a CD's soft information (presumably in preparation for + * reloading from the database.) + */ +void +wipe_cdinfo( void ) +{ + struct wm_playlist *l; + int i; + + if (cd != NULL) + { + cd->artist[0] = cd->cdname[0] = '\0'; + cd->autoplay = cd->playmode = cd->volume = 0; + cd->whichdb = NULL; + freeup(&cd->otherrc); + freeup(&cd->otherdb); + + if (thiscd.lists != NULL) + { + for (l = thiscd.lists; l->name != NULL; l++) + { + free(l->name); + free(l->list); + } + free(thiscd.lists); + thiscd.lists = NULL; + } + + for (i = 0; i < cur_ntracks; i++) + { + freeup(&cd->trk[i].songname); + freeup(&cd->trk[i].otherrc); + freeup(&cd->trk[i].otherdb); + cd->trk[i].avoid = cd->trk[i].contd = 0; + cd->trk[i].volume = 0; + if (cd->trk[i].section > 1) + remove_trackinfo(i--); + } + } +} + +/* + * stash_trkinfo(track, songname, contd, avoid) + * + * Update information about a track on the current CD. + */ +void +stash_trkinfo( int track, char *songname, int contd, int avoid ) +{ + if (cd != NULL) + { + track--; + if (!!cd->trk[track].contd != !!contd) + info_modified = 1; + cd->trk[track].contd = track ? contd : 0; + + if (!!cd->trk[track].avoid != !!avoid) + info_modified = 1; + cd->trk[track].avoid = avoid; + + if ((cd->trk[track].songname == NULL && songname[0]) || + (cd->trk[track].songname != NULL && + strcmp(cd->trk[track].songname, songname))) + { + info_modified = 1; + wm_strmcpy(&cd->trk[track].songname, songname); + } + } +} + +/* + * new_playlist() + * + * Add a playlist to a CD. + */ +struct wm_playlist * +new_playlist(struct wm_cdinfo* cdinfo, char* listname) +{ + int nlists = 0; + struct wm_playlist *l; + + if (cdinfo->lists != NULL) + { + for (nlists = 0; cdinfo->lists[nlists].name != NULL; nlists++) + ; + l = (struct wm_playlist *)realloc(cdinfo->lists, (nlists + 2) * + sizeof (struct wm_playlist)); + } + else + l = (struct wm_playlist *)malloc(2 * sizeof (struct wm_playlist)); + + if (l == NULL) + return (NULL); + + l[nlists + 1].name = NULL; + l[nlists].name = NULL; /* so wm_strmcpy doesn't free() it */ + wm_strmcpy(&l[nlists].name, listname); + l[nlists].list = NULL; + cdinfo->lists = l; + + return (&l[nlists]); +} + +/* + * make_playlist() + * + * Construct a playlist for the current CD. If we're in shuffle mode, play + * each non-avoided track once, keeping continued tracks in the right order. + * + * If playmode is 2, use playlist number (playmode-2). XXX should do + * bounds checking on this, probably. + * + * If consecutive tracks are being played, only make one playlist entry for + * them, so the CD player won't pause between tracks while we wake up. + */ +void +make_playlist( int playmode, int starttrack ) +{ + int i, avoiding = 1, entry = 0, count, track, + *thislist; + + cur_listno = 0; + if (playlist != NULL) + free(playlist); + playlist = malloc(sizeof (*playlist) * (cur_ntracks + 1)); + if (playlist == NULL) + { + perror("playlist"); + exit(1); + } + + /* If this is a data-only CD, we can't play it. */ + if ((starttrack && cd->trk[starttrack - 1].data) || + (cur_ntracks == 1 && cd->trk[0].data)) + { + playlist[0].start = 0; + playlist[0].end = 0; + playlist[1].start = 0; + return; + } + + if (playmode == 1) + { + char *done = malloc(cur_ntracks); + + if (done == NULL) + { + perror("randomizer"); + exit(1); + } + + count = cur_ntracks; + if (starttrack && cd->trk[starttrack - 1].avoid) + count++; + for (i = 0; i < cur_ntracks; i++) + if (cd->trk[i].contd || cd->trk[i].avoid || + cd->trk[i].data) + { + done[i] = 1; + count--; + } + else + done[i] = 0; + + for (i = 0; i < count; i++) + { + int end; /* for readability */ + if (starttrack) + { + track = starttrack - 1; + starttrack = 0; + } + else + while (done[track = rand() % cur_ntracks]) + ; + + playlist[i].start = track + 1; + + /* play all subsequent continuation tracks too */ + for (end = track + 1; end < cur_ntracks + 1; end++) + if (! cd->trk[end].contd || + cd->trk[end].avoid || + cd->trk[end].data) + break; + playlist[i].end = end + 1; + + done[track]++; + } + playlist[i].start = 0; + + free(done); + } + else if (playmode >= 2 && cd->lists && cd->lists[playmode - 2].name) + { + count = 2; /* one terminating entry, and one for start */ + thislist = cd->lists[playmode - 2].list; + + for (i = 0; thislist[i]; i++) + if (thislist[i + 1] != thislist[i] + 1) + count++; + + if (playlist != NULL) + free(playlist); + playlist = malloc(sizeof (*playlist) * count); + if (playlist == NULL) + { + perror("playlist"); + exit(1); + } + + count = 0; + if (starttrack) + { + playlist[0].start = starttrack; + for (track = 0; thislist[track]; track++) + if (starttrack == thislist[track]) + break; + if (! thislist[track]) + { + playlist[0].end = starttrack + 1; + playlist[1].start = thislist[0]; + count = 1; + track = 0; + } + } + else + { + playlist[0].start = thislist[0]; + track = 0; + } + + for (i = track; thislist[i]; i++) + if (thislist[i + 1] != thislist[i] + 1) + { + playlist[count].end = thislist[i] + 1; + count++; + playlist[count].start = thislist[i + 1]; + } + } + else + { + for (i = starttrack ? starttrack - 1 : 0; i < cur_ntracks; i++) + if (avoiding && ! (cd->trk[i].avoid || cd->trk[i].data)) + { + playlist[entry].start = i + 1; + avoiding = 0; + } + else if (! avoiding && (cd->trk[i].avoid || + cd->trk[i].data)) + { + playlist[entry].end = i + 1; + avoiding = 1; + entry++; + } + if (! avoiding) + playlist[entry].end = i + 1; + playlist[entry + !avoiding].start = 0; + } + + /* + * Now go through the list, whatever its source, and figure out + * cumulative starting times for each entry. + */ + entry = count = 0; + do { + playlist[entry].starttime = count; + + if (playlist[entry].start) + for (i = playlist[entry].start; i < + playlist[entry].end; i++) + count += cd->trk[i - 1].length; + } while (playlist[entry++].start); +} + +/* + * Find a particular track's location in the current playlist. Sets the + * appropriate variables (cur_listno, cur_firsttrack, cur_lasttrack). + */ +void +pl_find_track( int track ) +{ + int i; + + if (playlist == NULL) + { +#ifndef NDEBUG + fprintf(stderr, "Null playlist! Huh?\n"); +#endif + return; + } + + for (i = 0; playlist[i].start; i++) + if (track >= playlist[i].start && track < playlist[i].end) + { + cur_listno = i + 1; + cur_firsttrack = playlist[i].start; + cur_lasttrack = playlist[i].end - 1; + return; + } + + /* + * Couldn't find the track in question. Make a special entry with + * just that track. + */ + if (! playlist[i].start) + { + playlist = realloc(playlist, (i + 2) * sizeof(*playlist)); + if (playlist == NULL) + { + perror("playlist realloc"); + exit(1); + } + + playlist[i + 1].start = playlist[i + 1].end = 0; + playlist[i + 1].starttime = playlist[i].starttime + + cd->trk[track - 1].length; + playlist[i].start = track; + playlist[i].end = track + 1; + cur_listno = i + 1; + cur_firsttrack = track; + cur_lasttrack = track; + } +} diff --git a/kscd/libwm/cdrom.c b/kscd/libwm/cdrom.c new file mode 100644 index 00000000..980ce3ce --- /dev/null +++ b/kscd/libwm/cdrom.c @@ -0,0 +1,899 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Interface between most of WorkMan and the low-level CD-ROM library + * routines defined in plat_*.c and drv_*.c. The goal is to have no + * platform- or drive-dependent code here. + */ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +/* #include <sys/time.h> */ + +#include "config.h" + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cddb.h" +#include "include/wm_cdrom.h" +#include "include/wm_database.h" +#include "include/wm_platform.h" +#include "include/wm_helpers.h" +#include "include/wm_cdinfo.h" +#include "include/wm_cdtext.h" + +#ifdef CAN_CLOSE +#include <fcntl.h> +#endif + +/* local prototypes */ +int read_toc(void); + +#define WM_MSG_CLASS WM_MSG_CLASS_CDROM + +/* extern struct wm_drive generic_proto, toshiba_proto, sony_proto; */ +/* toshiba33_proto; <=== Somehow, this got lost */ + +/* + * The supported drive types are listed here. NULL means match anything. + * The first match in the list is used, and substring matches are done (so + * put long names before their shorter prefixes.) + */ +struct drivelist { + const char *ven; + const char *mod; + const char *rev; + struct wm_drive_proto *proto; +} drives[] = { +{ "TOSHIBA", "XM-3501", NULL, &toshiba_proto }, +{ "TOSHIBA", "XM-3401", NULL, &toshiba_proto }, +{ "TOSHIBA", "XM-3301", NULL, &toshiba_proto }, +{ "SONY", "CDU-8012", NULL, &sony_proto }, +{ "SONY", "CDU 561", NULL, &sony_proto }, +{ "SONY", "CDU-76S", NULL, &sony_proto }, +{ WM_STR_GENVENDOR, WM_STR_GENMODEL, WM_STR_GENREV, &generic_proto }, +{ NULL, NULL, NULL, &generic_proto } +}; + +/* + * Solaris 2.2 will remove the device out from under us. Getting an ENOENT + * is therefore sometimes not a problem. + */ +int intermittent_dev = 0; + +static int wm_cd_cur_balance = 10; +static int wm_cur_cdmode = WM_CDM_UNKNOWN; +static char *wm_cd_device = NULL; /* do not use this extern */ + +static struct wm_drive drive = { + 0, + NULL, + NULL, + NULL, + NULL, + -1, + -1, + NULL, + NULL, + NULL, + NULL, + NULL, + + NULL +}; + +extern struct wm_cdinfo thiscd; + +/* + * Macro magic + * + */ +#define FREE(x); if(x) free(x); x = NULL; + +#define STRDUP(old, neu); \ + if(old) free(old); old = NULL;\ + if(neu) old = strdup(neu); + +#define CARRAY(id) ((id)-1) + +#define DATATRACK 1 +/* + * init the workmanlib + */ +int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem, + const char *sounddevice, const char *ctldevice ) +{ + drive.cdda = (WM_CDDA == cdin); +#if !defined(BUILD_CDDA) + if(drive.cdda) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "Libworkman library was compiled without cdda support\n"); + return -1; + } +#endif + wm_cd_destroy(); + + STRDUP(wm_cd_device, cd_device); + drive.cd_device = wm_cd_device; + STRDUP(drive.soundsystem, soundsystem); + STRDUP(drive.sounddevice, sounddevice); + STRDUP(drive.ctldevice, ctldevice); + + return wm_cd_status(); +} + +int wm_cd_destroy( void ) +{ + free_cdtext(); + + if(drive.fd != -1) { + /* first free one old */ + if(drive.proto && drive.proto->gen_close) + drive.proto->gen_close(&drive); + else + close(drive.fd); + } + drive.fd = -1; + FREE(wm_cd_device); + FREE(drive.soundsystem); + FREE(drive.sounddevice); + FREE(drive.ctldevice); + FREE(drive.vendor); + FREE(drive.model); + FREE(drive.revision); + drive.proto = NULL; + + return 0; +} +/* + * Give information about the drive we found during wmcd_open() + */ +const char *wm_drive_vendor( void ) +{ + return drive.vendor?drive.vendor:""; +} + +const char *wm_drive_model( void ) +{ + return drive.model?drive.model:""; +} + +const char *wm_drive_revision( void ) +{ + return drive.revision?drive.revision:""; +} + +const char *wm_drive_device( void ) +{ + return drive.cd_device ? drive.cd_device : ""; +} + +/* + * Figure out which prototype drive structure we should be using based + * on the vendor, model, and revision of the current drive. + */ +int +find_drive_struct(const char *vendor, const char *model, const char *rev) +{ + struct drivelist *d; + + for (d = drives; d; d++) { + if(((d->ven != NULL) && strncmp(d->ven, vendor, strlen(d->ven))) || + ((d->mod != NULL) && strncmp(d->mod, model, strlen(d->mod))) || + ((d->rev != NULL) && strncmp(d->rev, rev, strlen(d->rev)))) + continue; + + if(!(d->proto)) + goto fail; + + STRDUP(drive.vendor, vendor); + STRDUP(drive.model, model); + STRDUP(drive.revision, rev); + + drive.proto = d->proto; + return 0; + } + +fail: + return -1; +} /* find_drive_struct() */ + +/* + * read_toc() + * + * Read the table of contents from the CD. Return a pointer to a wm_cdinfo + * struct containing the relevant information (minus artist/cdname/etc.) + * This is a static struct. Returns NULL if there was an error. + * + * XXX allocates one trackinfo too many. + */ +int +read_toc( void ) +{ + struct wm_playlist *l; + int i; + int pos; + + if(!drive.proto) + return -1; + + if(drive.proto && drive.proto->gen_get_trackcount && + (drive.proto->gen_get_trackcount)(&drive, &thiscd.ntracks) < 0) { + return -1 ; + } + + thiscd.artist[0] = thiscd.cdname[0] = '\0'; + thiscd.whichdb = thiscd.otherrc = thiscd.otherdb = thiscd.user = NULL; + thiscd.length = 0; + thiscd.autoplay = thiscd.playmode = thiscd.volume = 0; + + /* Free up any left-over playlists. */ + if (thiscd.lists != NULL) { + for (l = thiscd.lists; l->name != NULL; l++) { + free(l->name); + free(l->list); + } + free(thiscd.lists); + thiscd.lists = NULL; + } + + if (thiscd.trk != NULL) + free(thiscd.trk); + + thiscd.trk = malloc((thiscd.ntracks + 1) * sizeof(struct wm_trackinfo)); + if (thiscd.trk == NULL) { + perror("malloc"); + return -1; + } + + for (i = 0; i < thiscd.ntracks; i++) { + if(drive.proto && drive.proto->gen_get_trackinfo && + (drive.proto->gen_get_trackinfo)(&drive, i + 1, &thiscd.trk[i].data, + &thiscd.trk[i].start) < 0) { + return -1; + } + + thiscd.trk[i].avoid = thiscd.trk[i].data; + thiscd.trk[i].length = thiscd.trk[i].start / 75; + + thiscd.trk[i].songname = thiscd.trk[i].otherrc = + thiscd.trk[i].otherdb = NULL; + thiscd.trk[i].contd = 0; + thiscd.trk[i].volume = 0; + thiscd.trk[i].track = i + 1; + thiscd.trk[i].section = 0; + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "track %i, start frame %i\n", + thiscd.trk[i].track, thiscd.trk[i].start); + } + + if(drive.proto && drive.proto->gen_get_cdlen && + (drive.proto->gen_get_cdlen)(&drive, &thiscd.trk[i].start) < 0) { + return -1; + } + thiscd.trk[i].length = thiscd.trk[i].start / 75; + +/* Now compute actual track lengths. */ + pos = thiscd.trk[0].length; + for (i = 0; i < thiscd.ntracks; i++) { + thiscd.trk[i].length = thiscd.trk[i+1].length - pos; + pos = thiscd.trk[i+1].length; + if (thiscd.trk[i].data) + thiscd.trk[i].length = (thiscd.trk[i + 1].start - thiscd.trk[i].start) * 2; + if (thiscd.trk[i].avoid) + wm_strmcpy(&thiscd.trk[i].songname, "DATA TRACK"); + } + + thiscd.length = thiscd.trk[thiscd.ntracks].length; + thiscd.cddbid = cddb_discid(); + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "read_toc() successful\n"); + return 0; +} /* read_toc() */ + +/* + * wm_cd_status() + * + * Return values: + * see wm_cdrom.h + * + * Updates variables. + */ +int +wm_cd_status( void ) +{ + static int oldmode = WM_CDM_UNKNOWN; + int mode, err, tmp; + + if(!drive.proto) { + oldmode = WM_CDM_UNKNOWN; + err = wmcd_open( &drive ); + if (err < 0) { + wm_cur_cdmode = WM_CDM_UNKNOWN; + return err; + } + } + + if(drive.proto && drive.proto->gen_get_drive_status && + (drive.proto->gen_get_drive_status)(&drive, oldmode, &mode, &cur_frame, + &(thiscd.curtrack), &cur_index) < 0) { + perror("WM gen_get_drive_status"); + return -1; + } else { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, + "gen_get_drive_status returns status %s, track %i, frame %i\n", + gen_status(mode), thiscd.curtrack, cur_frame); + } + + if(WM_CDS_NO_DISC(oldmode) && WM_CDS_DISC_READY(mode)) { + /* device changed */ + thiscd.ntracks = 0; + if(read_toc() || 0 == thiscd.ntracks) + { + close(drive.fd); + drive.fd = -1; + mode = WM_CDM_NO_DISC; + } + else /* refresh cdtext info */ + get_glob_cdtext(&drive, 1); + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "device status changed() from %s to %s\n", + gen_status(oldmode), gen_status(mode)); + } + oldmode = mode; + + /* + * it seems all driver have'nt state for stop + */ + if(WM_CDM_PAUSED == mode && 0 == cur_frame) { + mode = WM_CDM_STOPPED; + thiscd.curtrack = 0; + } + + switch (mode) { + case WM_CDM_PLAYING: + case WM_CDM_PAUSED: + cur_pos_abs = cur_frame / 75; + /* search for right track */ + for(tmp = thiscd.ntracks; + tmp > 1 && cur_frame < thiscd.trk[CARRAY(tmp)].start; + tmp--) + ; + thiscd.curtrack = tmp; + /* Fall through */ + + + case WM_CDM_UNKNOWN: + if (mode == WM_CDM_UNKNOWN) + { + mode = WM_CDM_NO_DISC; + cur_lasttrack = cur_firsttrack = -1; + } + /* Fall through */ + + case WM_CDM_STOPPED: + if(thiscd.curtrack >= 1 && thiscd.curtrack <= thiscd.ntracks && thiscd.trk != NULL) { + cur_trackname = thiscd.trk[CARRAY(thiscd.curtrack)].songname; + cur_avoid = thiscd.trk[CARRAY(thiscd.curtrack)].avoid; + cur_contd = thiscd.trk[CARRAY(thiscd.curtrack)].contd; + cur_pos_rel = (cur_frame - thiscd.trk[CARRAY(thiscd.curtrack)].start) / 75; + if (cur_pos_rel < 0) + cur_pos_rel = -cur_pos_rel; + } + if((playlist != NULL) && playlist[0].start & (cur_listno > 0)) { + cur_pos_abs -= thiscd.trk[playlist[CARRAY(cur_listno)].start - 1].start / 75; + cur_pos_abs += playlist[CARRAY(cur_listno)].starttime; + } + if (cur_pos_abs < 0) + cur_pos_abs = cur_frame = 0; + + if (thiscd.curtrack < 1) + thiscd.curtracklen = thiscd.length; + else + thiscd.curtracklen = thiscd.trk[CARRAY(thiscd.curtrack)].length; + /* Fall through */ + + case WM_CDM_TRACK_DONE: + wm_cur_cdmode = mode; + break; + case WM_CDM_FORWARD: + case WM_CDM_EJECTED: + wm_cur_cdmode = mode; + break; + } + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, + "wm_cd_status returns %s\n", gen_status(wm_cur_cdmode)); + return wm_cur_cdmode; +} + +int +wm_cd_getcurtrack( void ) +{ + if(WM_CDS_NO_DISC(wm_cur_cdmode)) + return 0; + return thiscd.curtrack; +} + +int +wm_cd_getcurtracklen( void ) +{ + if(WM_CDS_NO_DISC(wm_cur_cdmode)) + return 0; + + return thiscd.curtracklen; +} + +int +wm_cd_getcountoftracks( void ) +{ + if(WM_CDS_NO_DISC(wm_cur_cdmode)) + return 0; + + return thiscd.ntracks; +} + +int wm_cd_gettracklen( int track ) +{ + if (track < 1 || + track > thiscd.ntracks || + thiscd.trk == NULL) + return 0; + + return thiscd.trk[CARRAY(track)].length; +} + +/* + * wm_cd_play(starttrack, pos, endtrack) + * + * Start playing the CD or jump to a new position. "pos" is in seconds, + * relative to start of track. + */ +int +wm_cd_play( int start, int pos, int end ) +{ + int real_start, real_end, status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status) || thiscd.ntracks < 1) + return -1; + + /* + * check ranges + */ + for(real_end = thiscd.ntracks; (thiscd.trk[CARRAY(real_end)].data == DATATRACK); real_end--) + ; + for(real_start = 1; (thiscd.trk[CARRAY(real_start)].data == DATATRACK); real_start++) + ; + + if(end == WM_ENDTRACK) end = real_end; + if(end > real_end) end = real_end; + + /* + * handle as overrun + */ + if(start < real_start) start = real_start; + if(start > real_end) start = real_end; + + /* + * Try to avoid mixed mode and CD-EXTRA data tracks + */ + if(start > end || thiscd.trk[CARRAY(start)].data == DATATRACK) { + wm_cd_stop(); + return -1; + } + + cur_firsttrack = start; + cur_lasttrack = end; + + wm_cd_play_chunk(thiscd.trk[CARRAY(start)].start + pos * 75, end == thiscd.ntracks ? + thiscd.length * 75 : thiscd.trk[CARRAY(end)].start - 1, thiscd.trk[CARRAY(start)].start); + /* So we don't update the display with the old frame number */ + wm_cd_status(); + + return thiscd.curtrack; +} + +/* + * wm_cd_play_chunk(start, end) + * + * Play the CD from one position to another (both in frames.) + */ +int +wm_cd_play_chunk( int start, int end, int realstart ) +{ + int status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status)) + return -1; + + end--; + if (start >= end) + start = end-1; + + if(!(drive.proto) || !(drive.proto->gen_play)) { + perror("WM gen_play: function pointer NULL"); + return -1; + } + + return (drive.proto->gen_play)(&drive, start, end, realstart); +} + +/* + * Set the offset into the current track and play. -1 means end of track + * (i.e., go to next track.) + */ +int +wm_cd_play_from_pos( int pos ) +{ + int status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status)) + return -1; + + if (pos == -1) + pos = thiscd.trk[thiscd.curtrack - 1].length - 1; + + if (wm_cur_cdmode == WM_CDM_PLAYING) + return wm_cd_play(thiscd.curtrack, pos, playlist[cur_listno-1].end); + else + return -1; +} /* wm_cd_play_from_pos() */ + +/* + * wm_cd_pause() + * + * Pause the CD, if it's in play mode. If it's already paused, go back to + * play mode. + */ +int +wm_cd_pause( void ) +{ + static int paused_pos; + int status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status)) + return -1; + + if(WM_CDM_PLAYING == wm_cur_cdmode) { + if(drive.proto && drive.proto->gen_pause) + (drive.proto->gen_pause)(&drive); + + paused_pos = cur_pos_rel; + } else if(WM_CDM_PAUSED == status) { + if(!(drive.proto->gen_resume) || (drive.proto->gen_resume(&drive) > 0)) { + wm_cd_play(thiscd.curtrack, paused_pos, playlist[cur_listno-1].end); + } + } else { + return -1; + } + wm_cd_status(); + return 0; +} /* wm_cd_pause() */ + +/* + * wm_cd_stop() + * + * Stop the CD if it's not already stopped. + */ +int +wm_cd_stop( void ) +{ + int status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status)) + return -1; + + if (status != WM_CDM_STOPPED) { + + if(drive.proto && drive.proto->gen_stop) + (drive.proto->gen_stop)(&drive); + + status = wm_cd_status(); + } + + return (status != WM_CDM_STOPPED); +} /* wm_cd_stop() */ + +/* + * Eject the current CD, if there is one, and set the mode to 5. + * + * Returns 0 on success, 1 if the CD couldn't be ejected, or 2 if the + * CD contains a mounted filesystem. + */ +int +wm_cd_eject( void ) +{ + int err = -1; + + wm_cd_stop(); + + if(drive.proto && drive.proto->gen_eject) + err = (drive.proto->gen_eject)(&drive); + + if (err < 0) { + if (err == -3) { + return 2; + } else { + return 1; + } + } + + wm_cd_status(); + + return 0; +} + +int wm_cd_closetray(void) +{ + int status, err = -1; + + status = wm_cd_status(); + if (status == WM_CDM_UNKNOWN || status == WM_CDM_NO_DISC) + return -1; + + if(drive.proto->gen_closetray) + err = (drive.proto->gen_closetray)(&drive); + + return(err ? 0 : wm_cd_status()==2 ? 1 : 0); +} /* wm_cd_closetray() */ + +struct cdtext_info* +wm_cd_get_cdtext( void ) +{ + int status; + + status = wm_cd_status(); + + if(WM_CDS_NO_DISC(status)) + return NULL; + + return get_glob_cdtext(&drive, 0); +} + +/* + * find_trkind(track, index, start) + * + * Start playing at a particular track and index, optionally using a particular + * frame as a starting position. Returns a frame number near the start of the + * index mark if successful, 0 if the track/index didn't exist. + * + * This is made significantly more tedious (though probably easier to port) + * by the fact that CDROMPLAYTRKIND doesn't work as advertised. The routine + * does a binary search of the track, terminating when the interval gets to + * around 10 frames or when the next track is encountered, at which point + * it's a fair bet the index in question doesn't exist. + */ +int +wm_find_trkind( int track, int ind, int start ) +{ + int top = 0, bottom, current, interval, ret = 0, i, status; + + status = wm_cd_status(); + if(WM_CDS_NO_DISC(status)) + return 0; + + for (i = 0; i < thiscd.ntracks; i++) { + if (thiscd.trk[i].track == track) + break; + } + + bottom = thiscd.trk[i].start; + + for (; i < thiscd.ntracks; i++) { + if (thiscd.trk[i].track > track) + break; + } + + top = i == thiscd.ntracks ? (thiscd.length - 1) * 75 : thiscd.trk[i].start; + if (start > bottom && start < top) + bottom = start; + + current = (top + bottom) / 2; + interval = (top - bottom) / 4; + + do { + wm_cd_play_chunk(current, current + 75, current); + + if (wm_cd_status() != 1) + return 0; + while (cur_frame < current) { + if(wm_cd_status() != 1 || wm_cur_cdmode != WM_CDM_PLAYING) + return 0; + else + wm_susleep(1); + } + + if (thiscd.trk[thiscd.curtrack - 1].track > track) + break; + + if(cur_index >= ind) { + ret = current; + current -= interval; + } else { + current += interval; + } + + interval /= 2; + + } while (interval > 2); + + return ret; +} /* find_trkind() */ + +int +wm_cd_set_verbosity( int level ) +{ + wm_lib_set_verbosity(level); + return wm_lib_get_verbosity(); +} + +/* + * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL, + * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS + */ + +int +wm_cd_volume( int vol, int bal ) +{ + int left, right; + const int bal1 = (vol - WM_VOLUME_MUTE)/(WM_BALANCE_ALL_RIGHTS - WM_BALANCE_SYMMETRED); + +/* + * Set "left" and "right" to volume-slider values accounting for the + * balance setting. + * + */ + if(vol < WM_VOLUME_MUTE) vol = WM_VOLUME_MUTE; + if(vol > WM_VOLUME_MAXIMAL) vol = WM_VOLUME_MAXIMAL; + if(bal < WM_BALANCE_ALL_LEFTS) bal = WM_BALANCE_ALL_LEFTS; + if(bal > WM_BALANCE_ALL_RIGHTS) bal = WM_BALANCE_ALL_RIGHTS; + + left = vol - (bal * bal1); + right = vol + (bal * bal1); + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calculate volume left %i, right %i\n", left, right); + + if (left > WM_VOLUME_MAXIMAL) + left = WM_VOLUME_MAXIMAL; + if (right > WM_VOLUME_MAXIMAL) + right = WM_VOLUME_MAXIMAL; + + if(!(drive.proto) || !(drive.proto->gen_set_volume)) + return -1; + else + return (drive.proto->gen_set_volume)(&drive, left, right); +} /* cd_volume() */ + +int +wm_cd_getvolume( void ) +{ + int left, right; + + if(!(drive.proto) || !(drive.proto->gen_get_volume) || + (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1) + return -1; + + if (left < right) { + wm_cd_cur_balance = (right - left) / 2; + if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS) + wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS; + return right; + } else if (left == right) { + wm_cd_cur_balance = WM_BALANCE_SYMMETRED; + return left; + } else { + wm_cd_cur_balance = (right - left) / 2; + if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS) + wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS; + return left; + } +} + +int +wm_cd_getbalance( void ) +{ + int left, right; + + if(!(drive.proto) || !(drive.proto->gen_get_volume) || + (drive.proto->gen_get_volume)(&drive, &left, &right) < 0 || left == -1) + return WM_BALANCE_SYMMETRED; + + if (left < right) { + wm_cd_cur_balance = (right - left) / 2; + if (wm_cd_cur_balance > WM_BALANCE_ALL_RIGHTS) + wm_cd_cur_balance = WM_BALANCE_ALL_RIGHTS; + } else if (left == right) { + wm_cd_cur_balance = WM_BALANCE_SYMMETRED; + } else { + wm_cd_cur_balance = (right - left) / 2; + if (wm_cd_cur_balance < WM_BALANCE_ALL_LEFTS) + wm_cd_cur_balance = WM_BALANCE_ALL_LEFTS; + } + return wm_cd_cur_balance; +} + +/* + * Prototype wm_drive structure, with generic functions. The generic functions + * will be replaced with drive-specific functions as appropriate once the drive + * type has been sensed. + */ +struct wm_drive_proto generic_proto = { + gen_init, /* functions... */ + gen_close, + gen_get_trackcount, + gen_get_cdlen, + gen_get_trackinfo, + gen_get_drive_status, + gen_get_volume, + gen_set_volume, + gen_pause, + gen_resume, + gen_stop, + gen_play, + gen_eject, + gen_closetray, + gen_get_cdtext +}; + +const char* +gen_status(int status) +{ + static char tmp[250]; + switch(status) { + case WM_CDM_TRACK_DONE: + return "WM_CDM_TRACK_DONE"; + case WM_CDM_PLAYING: + return "WM_CDM_PLAYING"; + case WM_CDM_FORWARD: + return "WM_CDM_FORWARD"; + case WM_CDM_PAUSED: + return "WM_CDM_PAUSED"; + case WM_CDM_STOPPED: + return "WM_CDM_STOPPED"; + case WM_CDM_EJECTED: + return "WM_CDM_EJECTED"; + case WM_CDM_DEVICECHANGED: + return "WM_CDM_DEVICECHANGED"; + case WM_CDM_NO_DISC: + return "WM_CDM_NO_DISC"; + case WM_CDM_UNKNOWN: + return "WM_CDM_UNKNOWN"; + case WM_CDM_CDDAERROR: + return "WM_CDM_CDDAERROR"; + case WM_CDM_CDDAACK: + return "WM_CDM_CDDAACK"; + default: + { + sprintf(tmp, "unexpected status %i", status); + return tmp; + } + } +} diff --git a/kscd/libwm/cdtext.c b/kscd/libwm/cdtext.c new file mode 100644 index 00000000..bb4e19b8 --- /dev/null +++ b/kscd/libwm/cdtext.c @@ -0,0 +1,428 @@ +/*************************************************************************** + cdtext.c - description + ------------------- + begin : Mon Feb 12 2001 + copyright : (C) 2001,2003 by Alex Kern + email : alex.kern@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifdef libunicode + #include <unicode.h> +#endif + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdrom.h" +#include "include/wm_database.h" +#include "include/wm_platform.h" +#include "include/wm_helpers.h" +#include "include/wm_cdinfo.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_MISC + +/* local prototypes */ +int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock); +int free_cdtext_info(struct cdtext_info* cdtextinfo); +struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks); +void get_data_from_cdtext_pack( + const struct cdtext_pack_data_header *pack, + const struct cdtext_pack_data_header *pack_previous, + cdtext_string *p_componente); + +struct cdtext_info wm_cdtext_info = { 0, 0, 0, 0 }; + +int free_cdtext_info_block(struct cdtext_info_block* cdtextinfoblock) +{ + if(cdtextinfoblock) + { + if(cdtextinfoblock->name) + free(cdtextinfoblock->name); + if(cdtextinfoblock->performer) + free(cdtextinfoblock->performer); + if(cdtextinfoblock->songwriter) + free(cdtextinfoblock->songwriter); + if(cdtextinfoblock->composer) + free(cdtextinfoblock->composer); + if(cdtextinfoblock->arranger) + free(cdtextinfoblock->arranger); + if(cdtextinfoblock->message) + free(cdtextinfoblock->message); + if(cdtextinfoblock->UPC_EAN_ISRC_code) + free(cdtextinfoblock->UPC_EAN_ISRC_code); + + if(cdtextinfoblock->block_encoding_text) + free(cdtextinfoblock->block_encoding_text); + } + + return 0; +} + +int free_cdtext_info(struct cdtext_info* cdtextinfo) +{ + int i; + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: free_cdtext_info() called\n"); + + if(cdtextinfo) + { + for(i = 0; i < 8; i++) + { + if(cdtextinfo->blocks[i]) + { + free_cdtext_info_block(cdtextinfo->blocks[i]); + } + } + memset(cdtextinfo, 0, sizeof(struct cdtext_info)); + } + + return 0; +} + +struct cdtext_info_block* malloc_cdtext_info_block(int count_of_tracks) +{ + int memamount; + struct cdtext_info_block* lp_block; + + lp_block = malloc(sizeof(struct cdtext_info_block)); + if(!lp_block) + { + return (struct cdtext_info_block*)0; + } + memset(lp_block, 0, sizeof(struct cdtext_info_block)); + + memamount = count_of_tracks * sizeof(cdtext_string); + + lp_block->name = malloc(memamount); + if(!lp_block->name) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->name, 0, memamount); + + lp_block->performer = malloc(memamount); + if(!lp_block->performer) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->performer, 0, memamount); + + lp_block->songwriter = malloc(memamount); + if(!lp_block->songwriter) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->songwriter, 0, memamount); + + lp_block->composer = malloc(memamount); + if(!lp_block->composer) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->composer, 0, memamount); + + lp_block->arranger = malloc(memamount); + if(!lp_block->arranger) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->arranger, 0, memamount); + + lp_block->message = malloc(memamount); + if(!lp_block->message) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->message, 0, memamount); + + lp_block->UPC_EAN_ISRC_code = malloc(memamount); + if(!lp_block->UPC_EAN_ISRC_code) + return (struct cdtext_info_block*)free_cdtext_info_block(lp_block); + memset(lp_block->UPC_EAN_ISRC_code, 0, memamount); + + return lp_block; +} + +void get_data_from_cdtext_pack( + const struct cdtext_pack_data_header *pack, + const struct cdtext_pack_data_header *pack_previous, + cdtext_string *p_componente) +{ + + int arr = pack->header_field_id2_tracknumber; + int i; + int language_block; + int unicode; + + language_block = (pack->header_field_id4_block_no >> 4) & 0x07; + unicode = pack->header_field_id4_block_no & 0x80; + + if(!unicode) + { + for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i++) + { + if(pack->text_data_field[i] == 0x00) /* end marker */ + { + arr++; + } + else if(pack->text_data_field[i] == 0x09) /* repeat last marker */ + { + /* ASSERT(arr > 0) */ + strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1])); + arr++; + } + else + { + strncat((char*)(p_componente[arr]), (char*)(&(pack->text_data_field[i])), 1); + } + } + } +#ifdef libunicode + else /* doublebytes ;-) */ + { + for(i = 0; i < DATAFIELD_LENGHT_IN_PACK; i += 2) + { + if((Uchar)(pack->text_data_field[i]) == 0x0000) /* end marker */ + { + arr++; + } + else if((Uchar)(pack->text_data_field[i]) == 0x0909) /* repeat last marker */ + { + /* ASSERT(arr > 0) */ + strcat((char*)(p_componente[arr]), (char*)(p_componente[arr-1])); + arr++; + } + else + { + strncat((char*)(p_componente[arr]), u_uc_to_utf8((Uchar*)(&(pack->text_data_field[i]))), 1); + } + } + } +#else + else { + wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, "can't handle unicode"); + } +#endif +} + +struct cdtext_info* +get_glob_cdtext(struct wm_drive *d, int redo) +{ + /* alloc cdtext_info */ + + unsigned char *buffer; + int buffer_length; + int ret; + int i; + struct cdtext_pack_data_header *pack, *pack_previous; + cdtext_string *p_componente; + struct cdtext_info_block *lp_block; + if(!d->proto || d->proto->gen_get_cdtext == NULL || d->proto->gen_get_trackcount == NULL) { + return NULL; + } + + if(!redo && wm_cdtext_info.valid) { + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, "CDTEXT DEBUG: recycle cdtext\n"); + return &wm_cdtext_info; + } else + free_cdtext_info(&wm_cdtext_info); + + lp_block = 0; + p_componente = 0; + buffer = 0; + buffer_length = 0; + + ret = (d->proto->gen_get_cdtext)(d, &buffer, &buffer_length); + if(!ret) + { + (d->proto->gen_get_trackcount)(d, &(wm_cdtext_info.count_of_entries)); + if( wm_cdtext_info.count_of_entries < 0 ) + wm_cdtext_info.count_of_entries = 1; + else + wm_cdtext_info.count_of_entries++; + + i = 0; + + pack = 0; /* NULL pointer*/ + while(i < buffer_length) + { + pack_previous = pack; + pack = (struct cdtext_pack_data_header*)(buffer+i); + /* to implement: check_crc(pack); */ + + /* only for valid packs */ + if(pack->header_field_id1_typ_of_pack >= 0x80 && pack->header_field_id1_typ_of_pack < 0x90) + { + int code, j; + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT DEBUG: valid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + i, + pack->header_field_id1_typ_of_pack, + pack->header_field_id2_tracknumber, + pack->header_field_id3_sequence, + pack->header_field_id4_block_no, + pack->text_data_field[0], + pack->text_data_field[1], + pack->text_data_field[2], + pack->text_data_field[3], + pack->text_data_field[4], + pack->text_data_field[5], + pack->text_data_field[6], + pack->text_data_field[7], + pack->text_data_field[8], + pack->text_data_field[9], + pack->text_data_field[10], + pack->text_data_field[11], + pack->crc_byte1, + pack->crc_byte2); + wm_cdtext_info.count_of_valid_packs++; + + code = (pack->header_field_id4_block_no >> 4) & 0x07; + if(0 == lp_block || lp_block->block_code != code) /* find or create one new block */ + { + lp_block = 0; + for(j = 0; j < MAX_LANGUAGE_BLOCKS && wm_cdtext_info.blocks[j] != 0 && 0 == lp_block; j++) + { + if(wm_cdtext_info.blocks[j]->block_code == code) + { + lp_block = wm_cdtext_info.blocks[j]; + } + } + + if(MAX_LANGUAGE_BLOCKS <= j) + { + free_cdtext_info(&wm_cdtext_info); + wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, + "CDTEXT ERROR: more as 8 languageblocks defined\n"); + return NULL; + } + + if(0 == lp_block) + { + /* make next new block */ + lp_block = malloc_cdtext_info_block(wm_cdtext_info.count_of_entries); + if(0 == lp_block) + { + wm_lib_message(WM_MSG_LEVEL_ERROR | WM_MSG_CLASS, + "CDTEXT ERROR: out of memory, can't create a new language block\n"); + free_cdtext_info(&wm_cdtext_info); + return NULL /*ENOMEM*/; + } + else + { + wm_cdtext_info.blocks[j] = lp_block; + wm_cdtext_info.blocks[j]->block_code = code; + wm_cdtext_info.blocks[j]->block_unicode = pack->header_field_id4_block_no & 0x80; + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: created a new language block; code %i, %s characters\n", code, lp_block->block_unicode?"doublebyte":"singlebyte"); +/* + unsigned char block_encoding; not jet! + cdtext_string* block_encoding_text; +*/ + } + } + } + } + + switch(pack->header_field_id1_typ_of_pack) + { + case 0x80: + p_componente = (lp_block->name); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x81: + p_componente = (lp_block->performer); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x82: + p_componente = (lp_block->songwriter); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x83: + p_componente = (lp_block->composer); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x84: + p_componente = (lp_block->arranger); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x85: + p_componente = (lp_block->message); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x86: + memcpy((char*)(lp_block->binary_disc_identification_info), + (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK); + break; + case 0x87: + memcpy((char*)(lp_block->binary_genreidentification_info), + (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK); + break; + case 0x88: + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: PACK with code 0x88 (TOC)\n"); + break; + case 0x89: + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: PACK with code 0x89 (second TOC)\n"); + break; + case 0x8A: + case 0x8B: + case 0x8C: + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: PACK with code 0x%02X (reserved)\n", pack->header_field_id1_typ_of_pack); + break; + case 0x8D: + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT INFO: PACK with code 0x8D (for content provider only)\n"); + break; + case 0x8E: + p_componente = (lp_block->UPC_EAN_ISRC_code); + get_data_from_cdtext_pack(pack, pack_previous, p_componente); + break; + case 0x8F: + memcpy((char*)(lp_block->binary_size_information), + (char*)(pack->text_data_field), DATAFIELD_LENGHT_IN_PACK); + break; + default: + wm_lib_message(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS, + "CDTEXT ERROR: invalid packet at 0x%08X: 0x %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", + i, + pack->header_field_id1_typ_of_pack, + pack->header_field_id2_tracknumber, + pack->header_field_id3_sequence, + pack->header_field_id4_block_no, + pack->text_data_field[0], + pack->text_data_field[1], + pack->text_data_field[2], + pack->text_data_field[3], + pack->text_data_field[4], + pack->text_data_field[5], + pack->text_data_field[6], + pack->text_data_field[7], + pack->text_data_field[8], + pack->text_data_field[9], + pack->text_data_field[10], + pack->text_data_field[11], + pack->crc_byte1, + pack->crc_byte2); + wm_cdtext_info.count_of_invalid_packs++; + } + i += sizeof(struct cdtext_pack_data_header); + } /* while */ + } + + if(0 == ret && wm_cdtext_info.count_of_valid_packs > 0) + wm_cdtext_info.valid = 1; + + return &wm_cdtext_info; +} + +void free_cdtext(void) +{ + if (wm_cdtext_info.valid) + free_cdtext_info(&wm_cdtext_info); +} diff --git a/kscd/libwm/configure.in.in b/kscd/libwm/configure.in.in new file mode 100644 index 00000000..a0ce3ab7 --- /dev/null +++ b/kscd/libwm/configure.in.in @@ -0,0 +1,72 @@ +dnl +-------------------------------+ +dnl | Checks for LIBWORKMAN | +dnl +-------------------------------+ +AC_MSG_CHECKING(for CDDA) + +AC_ARG_WITH(kscd-cdda, [ --with-kscd-cdda build CDDA support in kscd [default=yes]], +[ + if test $withval = yes; then + libwm_with_cdda=yes + else + libwm_with_cdda=no + fi +],libwm_with_cdda=yes) + +if test "$libwm_with_cdda" = "yes"; then +case $host in + *-*-linux*) + AC_CHECK_HEADERS(pthread.h) + AC_TRY_COMPILE( + [ +#ifndef __GNUC__ +#define __GNUC__ 1 +#endif +/* needed for vanilla kernel headers, which do provide __u64 only + for ansi */ +#undef __STRICT_ANSI__ +/* needed for non-ansi kernel headers */ +#define asm __asm__ +#define inline __inline__ +#include <linux/types.h> +#include <linux/cdrom.h> +#undef asm +#undef inline + ],[ +#if defined(__linux__) +ioctl(1, CDROMREADAUDIO, 0); +#else + #error platform? +#endif + ],, libwm_with_cdda=no) + ;; + *-*-sunos*) + AC_CHECK_HEADERS(pthread.h) + AC_TRY_COMPILE( + [ +#include <sys/types.h> +#include <sys/cdio.h> + ],[ +#if defined(__sun) || defined(sun) +ioctl(1, CDROMCDDA, 0); +#else + #error platform? +#endif + ],, libwm_with_cdda=no) + ;; + *) + libwm_with_cdda=no + ;; +esac +fi + +if test "$libwm_with_cdda" = "yes"; then + AC_DEFINE(BUILD_CDDA, 1, [Define if you will CDDA support in kscd]) +fi +AM_CONDITIONAL(libwm_with_cdda, test "$libwm_with_cdda" = "yes") + +AC_MSG_RESULT($libwm_with_cdda) + + +dnl +-------------------------------+ +dnl | End LIBWORKMAN checks | +dnl +-------------------------------+ diff --git a/kscd/libwm/database.c b/kscd/libwm/database.c new file mode 100644 index 00000000..f7f4e7cc --- /dev/null +++ b/kscd/libwm/database.c @@ -0,0 +1,1543 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Manage the CD database. All these routines assume that the "cd" global + * structure contains current information (as far as the outside world knows; + * obviously it won't contain track titles just after a CD is inserted.) + */ + +#define RCFILE "/.workmanrc" +#define DBFILE "/.workmandb" +#define FUZZFRAMES 75 + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <time.h> +#include "include/wm_config.h" +#include "include/wm_helpers.h" +#include "include/wm_struct.h" +#include "include/wm_cdinfo.h" +#include "include/wm_cddb.h" +#include "include/wm_index.h" +#include "include/wm_database.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_DB + +#define SWALLOW_LINE(fp) { int _c; while ((_c = getc(fp)) != '\n' && _c != EOF); } + +/* local prototypes */ +char *print_cdinfo(struct wm_cdinfo *pcd, int prefs); +FILE *open_rcfile(const char *name, const char *mode); +int *reset_tracks(void); +int search_db( FILE *fp, int prefs, int scan, int holesize_wanted ); +void spinwheels(int secs); +int lockit(int fd, int type); +void save_globals(FILE *fp); +int save_entry(char *filename, int pref); +/* local prototypes END */ + +static int suppress_locking = 0; /* Turn off locking of datafile (dangerous) */ + +static char *rcfile = NULL; /* Personal rcfile */ +static char *dbfiles = NULL; /* Colon-separated list of databases */ +static char **databases = NULL; /* NULL-terminated list of databases */ + +static char *otherrc = NULL; /* Unrecognized cruft from start of rcfile */ + +static long rcpos, rclen; /* XXX */ + +static int found_in_db, found_in_rc; +static long holepos, firstpos; + +static int fuzz_frames = FUZZFRAMES; + +static int wm_db_save_disabled = FALSE; + +static int cur_playnew = -1; + +extern int cur_ntracks, cur_nsections; + +int mark_a = 0; +int mark_b = 0; + + +/* + * + */ +int wm_db_get_playnew( void ) +{ + return 0; +} + +/* + * split_workmandb() + * + * Split the WORKMANDB environment variable, if any, into a list of database + * files in the global "databases". If WORKMANDB is not available, make + * a single entry with $HOME/DBFILE. + * + * Also, fill the "rcfile" global with the personal preferences filename. + * + * The environment variables should have already been read and placed in the + * "rcfile" and "dbfiles" globals, respectively. + */ +void +split_workmandb( void ) +{ + int ndbs, i; + char *home, *wmdb; + int no_rc = 0, no_db = 0; + + if (rcfile == NULL) + { + if ((home = getenv("HOME")) != NULL) + { + rcfile = malloc(strlen(home) + sizeof(RCFILE)); + if (rcfile == NULL) + { + +nomem: + perror("split_workmandb()"); + exit(1); + } + + strcpy(rcfile, home); + strcat(rcfile, RCFILE); + } + else + no_rc = 1; + + } + + if ((wmdb = dbfiles) == NULL) + { + if ((home = getenv("HOME")) != NULL) + { + wmdb = malloc(strlen(home) + sizeof(DBFILE)); + if (wmdb == NULL) + goto nomem; + + databases = malloc(2 * sizeof (databases[0])); + if (databases == NULL) + goto nomem; + + strcpy(wmdb, home); + strcat(wmdb, DBFILE); + databases[0] = wmdb; + databases[1] = NULL; + } + else + { + static char *emptydb = NULL; + + databases = &emptydb; + no_db = 1; + } + } + else + { + ndbs = 1; + for (home = wmdb; *home; home++) + if (*home == ':') + { + *home = '\0'; + ndbs++; + } + + databases = malloc((ndbs + 1) * sizeof(databases[0])); + if (databases == NULL) + goto nomem; + + for (i = 0; i < ndbs; i++) + { + databases[i] = wmdb; + wmdb += strlen(wmdb) + 1; + } + + databases[i] = NULL; + } + + if (no_db || no_rc) + { +#ifndef NDEBUG + fprintf(stderr, +"WorkMan was run without a home directory, probably by a system daemon.\n"); + fprintf(stderr, "It doesn't know where to find "); + if (no_rc) + { + fprintf(stderr, "your personal preferences file "); + if (no_db) + fprintf(stderr, "or the\ndatabase of CD descriptions"); + } + else + fprintf(stderr, "the database of CD descriptions"); + + fprintf(stderr, +".\nYou can use the X resources \"workman.db.shared\" and \"workman.db.personal\"\nto tell WorkMan where to look.\n"); +#endif + wm_db_save_disabled = TRUE; + } +} + +/* + * print_cdinfo(cd, prefs) + * + * cd A pointer to a cdinfo struct. + * prefs Flag: write personal preferences? + * + * Print a CD's information (in more or less readable form) to a buffer. + * Returns a pointer to the buffer. + * + * XXX - could be more efficient about calling wm_strmcat() and strlen(). + */ +char * +print_cdinfo(struct wm_cdinfo *cd, int prefs) +{ + int i; + char tempbuf[2000]; /* XXX - is this always big enough? */ + static char *cdibuf = NULL; + struct wm_playlist *l; + + sprintf(tempbuf, "\ntracks %d", cd->ntracks); + for (i = 0; i < cur_ntracks; i++) + if (cd->trk[i].section < 2) + sprintf(tempbuf + strlen(tempbuf), " %d", + cd->trk[i].start); + sprintf(tempbuf + strlen(tempbuf), " %d\n", cd->length); + + wm_strmcpy(&cdibuf, tempbuf); + + if (cur_nsections) + { + sprintf(tempbuf, "sections %d", cur_nsections); + /* fixed a bug here */ + for (i = 0; i < cur_ntracks; i++) + if (cd->trk[i].section > 1) + sprintf(tempbuf + strlen(tempbuf), " %d", + cd->trk[i].start); + sprintf(tempbuf + strlen(tempbuf), "\n"); + + wm_strmcat(&cdibuf, tempbuf); + } + + if (prefs) + { + if (cd->autoplay) + wm_strmcat(&cdibuf, "autoplay\n"); + for (l = cd->lists; l != NULL && l->name != NULL; l++) + { + wm_strmcat(&cdibuf, "playlist "); + + i = strlen(cdibuf) - 1; + wm_strmcat(&cdibuf, l->name); + while (cdibuf[++i]) + if (cdibuf[i] == ' ' || cdibuf[i] == '\t') + cdibuf[i] = '_'; + + if (l->list != NULL) + { + for (i = 0; l->list[i]; i++) + ; + sprintf(tempbuf, " %d", i); + wm_strmcat(&cdibuf, tempbuf); + for (i = 0; l->list[i]; i++) + { + sprintf(tempbuf, " %d", l->list[i]); + wm_strmcat(&cdibuf, tempbuf); + } + wm_strmcat(&cdibuf, "\n"); + } + else + wm_strmcat(&cdibuf, " 0\n"); + } + + if (cd->volume) + { + /* + * Have to maintain compatibility with old versions, + * where volume was 0-32. + */ + sprintf(tempbuf, "cdvolume %d\n", (cd->volume * 32) / 100); + wm_strmcat(&cdibuf, tempbuf); + } + + if (cd->playmode) + { + sprintf(tempbuf, "playmode %d\n", cd->playmode); + wm_strmcat(&cdibuf, tempbuf); + } + + if (mark_a) + { + sprintf(tempbuf, "mark %d START\n", mark_a); + wm_strmcat(&cdibuf, tempbuf); + } + if (mark_b) + { + sprintf(tempbuf, "mark %d END\n", mark_b); + wm_strmcat(&cdibuf, tempbuf); + } + + if (cd->otherrc) + wm_strmcat(&cdibuf, cd->otherrc); + + for (i = 0; i < cur_ntracks; i++) + { + if (cd->trk[i].avoid) + { + sprintf(tempbuf, "dontplay %d\n", i + 1); + wm_strmcat(&cdibuf, tempbuf); + } + if (cd->trk[i].volume) + { + sprintf(tempbuf, "volume %d %d\n", i + 1, + (cd->trk[i].volume * 32) / 100); + wm_strmcat(&cdibuf, tempbuf); + } + if (cd->trk[i].otherrc) + wm_strmcat(&cdibuf, cd->trk[i].otherrc); + } + } + else + { + if (cd->cdname[0]) + { + wm_strmcat(&cdibuf, "cdname "); + wm_strmcat(&cdibuf, cd->cdname); + wm_strmcat(&cdibuf, "\n"); + } + + if (cd->artist[0]) + { + wm_strmcat(&cdibuf, "artist "); + wm_strmcat(&cdibuf, cd->artist); + wm_strmcat(&cdibuf, "\n"); + } + + if (cd->otherdb) + wm_strmcat(&cdibuf, cd->otherdb); + + for (i = 0; i < cur_ntracks; i++) + { + if (cd->trk[i].section > 1) + wm_strmcat(&cdibuf, "s-"); + wm_strmcat(&cdibuf, "track "); + if (cd->trk[i].songname != NULL) + wm_strmcat(&cdibuf, cd->trk[i].songname); + wm_strmcat(&cdibuf, "\n"); + if (cd->trk[i].contd) + { + if (cd->trk[i].section > 1) + wm_strmcat(&cdibuf, "s-"); + wm_strmcat(&cdibuf, "continue\n"); + } + if (cd->trk[i].otherdb) + wm_strmcat(&cdibuf, cd->trk[i].otherdb); + } + } + + return (cdibuf); +} /* print_cdinfo() */ + +/* + * Open the rcfile for reading or writing. + * + * name Filename + * mode "r" or "w" + */ +FILE * +open_rcfile(const char *name, const char *mode) +{ + FILE *fp; + struct stat st; + + fp = fopen(name, mode); + if (fp == NULL) + { + if (errno != ENOENT || mode[0] == 'w') + perror(name); + } + else + { + /* Don't let people open directories or devices */ + if (fstat(fileno(fp), &st) < 0) + { + perror(name); + fclose(fp); + return (NULL); + } + +#ifdef S_ISREG + if (! S_ISREG(st.st_mode)) +#else + if ((st.st_mode & S_IFMT) != S_IFREG) +#endif + { + errno = EISDIR; + perror(name); + fclose(fp); + return (NULL); + } + + if (mode[0] == 'w') /* create -- put data in so locks work */ + { + fputs("# WorkMan database file\n", fp); + fclose(fp); + fp = fopen(name, "r+"); + if (fp == NULL) + if (errno != ENOENT) + perror(name); + } + } + + return (fp); +} + +/* + * + * allocate and clear "trackmap". + * + */ +int *reset_tracks(void) +{ + int i, j; + int *trackmap; + /* + * Since we access track numbers indirectly (to handle sections + * with at least a little elegance), the track mapping needs to be + * set up before we read anything. Initially it must assume that + * no sections will appear in this datafile. + */ + trackmap = malloc(sizeof(int) * cur_ntracks); + if (trackmap == NULL) + { + perror("trackmap"); + exit(1); + } + j = 0; + for (i = 0; i < cd->ntracks; i++) + { + trackmap[i] = j; + while (cd->trk[++j].section > 1) + ; + } + return trackmap; +} /* reset_tracks() */ + +/* + * Load a new-format database file, searching for a match with the currently + * inserted CD. Modify the in-core copy of the CD info based on what's found + * in the database. + * + * Returns 1 if there was a match or 0 if not. + * + * fp FILE* of database or rcfile. + * prefs 1 if we're searching .workmanrc, 0 for .workmandb, + * 2 if just reading settings + * scan Scan for "tracks" location and entry size only + * holesize_wanted How big a hole we're looking for, if any. + * + * If a hole was found along the way, update the global "holepos" with its + * starting offset in the file. A hole is defined as a bunch of blank lines + * preceding a "tracks" line. Holepos will contain the best match. + * + * In addition, "firstpos" will be filled with the position of the first + * "tracks" keyword, so we know how much room is available for global + * settings at the rcfile's start. + */ +int +search_db( FILE *fp, int prefs, int scan, int holesize_wanted ) + +{ + char keyword[64], listname[64], *c; + int b, i, j, track = 0, listsize, ntracks, scratch, searching = 1; + int *trackmap = 0, gotsections = 0; + int fudge, maxfudge, sizediff, bestfudge = 0; + long pos = 0, thisholepos = -1, holesize = 99991239; + struct wm_playlist *l; + + rclen = 0; + + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: Reached search_db()\n" ); + + /* We may not find any holes at all! */ + if (holesize_wanted) + holepos = -1; + + if( prefs != 2 ) + trackmap = reset_tracks(); + + if (prefs) + freeup(&otherrc); + firstpos = -1; + while (! feof(fp)) + { + pos = ftell(fp); + keyword[0] = '\0'; + do + b = getc(fp); + while (b != EOF && b != '\n' && isspace(b)); + + if (b == EOF || feof(fp)) + break; + + if (b != '\n') + { + keyword[0] = b; + fscanf(fp, "%62s", &keyword[1]); + } + if (keyword[0] == '\0') /* Blank line. */ + { + if (thisholepos < 0) + thisholepos = pos; + continue; + } + + /* Strip off "s-" if we've seen a "sections" keyword */ + if (gotsections && keyword[0] == 's' && keyword[1] == '-') + { + for (c = &keyword[2]; (c[-2] = *c) != '\0'; c++) + ; + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG , "db: stripped off the 's-'. Result is %s\n", keyword); + } + + /* If this is the start of a CD entry, see if it matches. */ + if (! strcmp(keyword, "tracks")) + { + if (prefs == 2) + break; + + /* Is this the end of a hole? */ + if (holesize_wanted && (thisholepos >= 0)) + { + /* Yep. Is it better than the last one? */ + if (pos - thisholepos < holesize && pos - + thisholepos >= holesize_wanted) + { + holepos = thisholepos; + holesize = pos - thisholepos; + } + thisholepos = -1; + } + + /* Is it the start of the CD entries? */ + if (firstpos == -1) + firstpos = pos; + + /* Is this the end of the entry we really wanted? */ + if (! searching) + { + rclen = pos - rcpos; + break; + } + + /* If we have a near match, indicate that we + should stop reading tracks, etc now */ + if (searching == 2) + { + searching = 3; + continue; + } + + fscanf(fp, "%d", &ntracks); + + if (ntracks != cd->ntracks) + { +chomp: + SWALLOW_LINE(fp); + continue; + } + + fudge = 0; + maxfudge = (ntracks * fuzz_frames) >> 1; + track = 0; + for (i = 0; i < ntracks; i++) + { + fscanf(fp, "%d", &scratch); + if (scratch != cd->trk[track].start) + { + sizediff = abs(scratch - cd->trk[track].start); + if (sizediff > fuzz_frames || + (sizediff && scan)) + break; + fudge += sizediff; + } + while (cd->trk[++track].section > 1) + ; + } + if (i != ntracks) + goto chomp; + + if (fudge > 0) /* best near match? */ + { + if (fudge > maxfudge) + goto chomp; + if (bestfudge == 0 || fudge < bestfudge) + bestfudge = fudge; + else + goto chomp; + rcpos = pos; + track = 0; + searching = 2; + } + else /* probably exact match */ + { + fscanf(fp, "%d", &scratch); + + if (scratch != -1 && scratch != cd->length) + goto chomp; + + /* Found it! */ + rcpos = pos; + track = 0; + searching = 0; + } + + SWALLOW_LINE(fp); /* Get rid of newline */ + } + + /* Global mode stuff goes here */ + else if (! strcmp(keyword, "cddbprotocol")) + { + getc(fp); + i = getc(fp); /* only first letter is used */ + cddb.protocol = i == 'c' ? 1 : + i == 'h' ? 2 : 3 ; + do + i = getc(fp); + while (i != '\n' && i != EOF); + } + + else if (! strcmp(keyword, "cddbserver")) + { + getc(fp); /* lose the space */ + if (cddb.cddb_server[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.cddb_server, + sizeof(cddb.cddb_server), fp); + if ((i = strlen(cddb.cddb_server))) + cddb.cddb_server[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbmailadress")) + { + getc(fp); /* lose the space */ + if (cddb.mail_adress[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.mail_adress, + sizeof(cddb.mail_adress), fp); + if ((i = strlen(cddb.mail_adress))) + cddb.mail_adress[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbpathtocgi")) + { + getc(fp); /* lose the space */ + if (cddb.path_to_cgi[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.path_to_cgi, + sizeof(cddb.path_to_cgi), fp); + if ((i = strlen(cddb.path_to_cgi))) + cddb.path_to_cgi[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cddbproxy")) + { + getc(fp); /* lose the space */ + if (cddb.proxy_server[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cddb.proxy_server, + sizeof(cddb.proxy_server), fp); + if ((i = strlen(cddb.proxy_server))) + cddb.proxy_server[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "whendone")) + { + getc(fp); + i = getc(fp); /* only first letter is used */ + if (cur_stopmode == -1) /* user's setting preferred */ + cur_stopmode = i == 's' ? 0 : i == 'r' ? 1 : 2; + do + i = getc(fp); + while (i != '\n' && i != EOF); + } + + else if (! strcmp(keyword, "playnew")) + { + if (cur_playnew == -1) + cur_playnew = 1; + SWALLOW_LINE(fp); + } + + /* If we're searching, skip to the next "tracks" line. */ + else if (((searching & 1)|| scan) + && !(prefs && firstpos == -1)) + SWALLOW_LINE(fp) + + else if (! strcmp(keyword, "sections")) + { + gotsections = 1; + fscanf(fp, "%d", &ntracks); + + free(trackmap); + trackmap = (int *) malloc(sizeof(int) * + (cur_ntracks + ntracks)); + if (trackmap == NULL) + { + perror("section mapping"); + exit(1); + } + + /* + * If sections are already defined, use these as a + * reference, mapping this CD entry's section numbers + * to the ones in core. + * + * Otherwise, split the CD up according to the sections + * listed here. + */ + if (cur_nsections) + { + track = 0; + i = 0; + while (ntracks) + { + ntracks--; + fscanf(fp, "%d", &scratch); + while (scratch > cd->trk[track].start) + { + if (cd->trk[track].section < 2) + trackmap[i++] = track; + ++track; + + if (track == cur_ntracks) + break; + } + + /* rc has later sections than db... */ + if (track == cur_ntracks) + break; + + /* Matches can be approximate */ + if (scratch+75 > cd->trk[track].start && + scratch-75 < cd->trk[track].start) + trackmap[i++] = track++; + else + trackmap[i++] = -1; + + if (track == cur_ntracks) + break; + } + + /* This only happens if track == cur_ntracks */ + while (ntracks--) + trackmap[i++] = -1; + + while (track < cur_ntracks) + { + if (cd->trk[track].section < 2) + trackmap[i++] = track; + track++; + } + + track = 0; + SWALLOW_LINE(fp); + } + else + { + while (ntracks--) + { + fscanf(fp, "%d", &scratch); + split_trackinfo(scratch); + } + + for (i = 0; i < cur_ntracks; i++) + { + trackmap[i] = i; + /* split_trackinfo() sets this */ + cd->trk[i].contd = 0; + } + + SWALLOW_LINE(fp); + } + } + + else if (! strcmp(keyword, "track")) + { + char buf[502]; + + getc(fp); /* lose the space */ + + /* don't overwrite existing track names. */ + /* However, overwrite them if there was a "bad" fuzzy match before */ + if ((trackmap[track] == -1 || track > (cd->ntracks + cur_nsections)) && (searching == 2)) + SWALLOW_LINE(fp) + else if (cd->trk[trackmap[track]].songname && + cd->trk[trackmap[track]].songname[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(buf, sizeof(buf), fp); + wm_lib_message(WM_MSG_CLASS_DB|WM_MSG_LEVEL_DEBUG, "found track %s\n", buf); + if( (i = strlen(buf)) ) + buf[i - 1] = '\0'; + wm_strmcpy(&cd->trk[trackmap[track]].songname, buf); + } + track++; + } + + else if (! strcmp(keyword, "playmode")) + fscanf(fp, "%d", &cd->playmode); + + else if (! strcmp(keyword, "autoplay")) + cd->autoplay = 1; + + else if (! strcmp(keyword, "cdname")) + { + /* because of fuzzy matching that may change + the disk contents, we reset everything when + we find the name, in hopes that we will recover + most, if not all, of the information from the + file. */ +/* + * nasty bug was here. Was it? BUGBUGBUG + * + * wipe_cdinfo(); + * trackmap = reset_tracks(); + */ + + getc(fp); /* lose the space */ + /* don't overwrite existing cd name. */ + if (cd->cdname[0] && (searching == 2)) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + if (searching > 1) + { + strcpy(cd->cdname, "Probably://"); + fgets(cd->cdname + strlen(cd->cdname), sizeof(cd->cdname), fp); + } + else + { + fgets(cd->cdname, sizeof(cd->cdname), fp); + } + if ( (i = strlen(cd->cdname)) ) + cd->cdname[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "artist")) + { + getc(fp); /* lose the space */ + /* don't overwrite existing artist names. */ + if (cd->artist[0]) + do + i = getc(fp); + while (i != '\n' && i != EOF); + else + { + fgets(cd->artist, sizeof(cd->artist), fp); + if( (i = strlen(cd->artist)) ) + cd->artist[i - 1] = '\0'; + } + } + + else if (! strcmp(keyword, "cdvolume")) + { + fscanf(fp, "%d", &cd->volume); + cd->volume = (cd->volume * 100) / 32; + } + + else if (! strcmp(keyword, "dontplay")) + { + fscanf(fp, "%d", &i); + if (trackmap[i - 1] != -1) + cd->trk[trackmap[i - 1]].avoid = 1; + } + + else if (! strcmp(keyword, "continue")) + { + if (trackmap[track - 1] != -1) + cd->trk[trackmap[track - 1]].contd = 1; + } + + else if (! strcmp(keyword, "volume")) + { + fscanf(fp, "%d", &i); + if (trackmap[i - 1] == -1) + SWALLOW_LINE(fp) + else + { + i = trackmap[i - 1]; + fscanf(fp, "%d", &cd->trk[i].volume); + cd->trk[i].volume = (cd->trk[i].volume*100)/32; + if (cd->trk[i].volume > 32) + cd->trk[i].volume = 0; + } + } + + else if (! strcmp(keyword, "playlist")) + { + getc(fp); + fscanf(fp, "%63s", listname); + +/* XXX take this out at some point */ + if (! strcmp(listname, "Default")) + strcpy(listname, "List A"); + + for (i = 0; listname[i]; i++) + if (listname[i] == '_') + listname[i] = ' '; + + l = new_playlist(cd, listname); + if (l == NULL) + { +plnomem: + perror("playlist read"); + exit(1); + } + + fscanf(fp, "%d", &listsize); + + l->list = malloc(sizeof(int) * (listsize + 1)); + if (l->list == NULL) + goto plnomem; + + /* Leave out tracks that weren't in .workmandb. */ + j = 0; + for (i = 0; i < listsize; i++) + { + fscanf(fp, "%d", &scratch); + scratch = trackmap[scratch - 1]; + if (scratch != -1) + l->list[j++] = scratch + 1; + } + + l->list[j] = 0; + } + + else if (! strcmp(keyword, "mark")) + { + int mark_val = -1, mark_namelen; + char mark_name[32]; + + fscanf(fp, "%d", &mark_val); + if (mark_val == -1) + goto chomp; + + if (getc(fp) != ' ') + continue; + + fgets(mark_name, sizeof(mark_name), fp); + if( ( mark_namelen = strlen(mark_name)) ) + mark_name[mark_namelen - 1] = '\0'; + +/* + if (! strcmp(mark_name, "START")) + set_abtimer(0, mark_val); + else if (! strcmp(mark_name, "END")) + set_abtimer(1, mark_val); + +*/ + } + + /* Unrecognized keyword. Put it in the right place. */ + else + { + char **buf, input[BUFSIZ]; + + if (track && trackmap[track - 1] == -1) + { + SWALLOW_LINE(fp); + continue; + } + + i = track ? trackmap[track - 1] : 0; + buf = prefs ? i ? &cd->trk[i].otherrc : &cd->otherrc : + i ? &cd->trk[i].otherdb : &cd->otherdb; + if (firstpos == -1) { + if (prefs) { + buf = &otherrc; + } else { + goto chomp; + } /* if() else */ + } /* if() */ + wm_strmcat(buf, keyword); + do { + input[sizeof(input) - 1] = 'x'; + fgets(input, sizeof(input), fp); + wm_strmcat(buf, input); + } while (input[sizeof(input) - 1] != 'x'); + } + } + + if (rclen == 0 && !searching) + rclen = pos - rcpos; + + if (searching > 1) /* A near match has been found. Good enough. */ + searching = 0; + + cddb_struct2cur(); + return (! searching); + +} /* search_db() */ + +/* + * Delay some amount of time without using interval timers. + */ +void +spinwheels(int secs) { + struct timeval tv; + + tv.tv_usec = 0; + tv.tv_sec = secs; + select(0, NULL, NULL, NULL, &tv); +} /* spinwheels() */ + +/* + * lockit(fd, type) + * + * fd file descriptor + * type lock type + * + * Lock a file. Time out after a little while if we can't get a lock; + * this usually means the locking system is broken. + * + * Unfortunately, if there are lots of people contending for a lock, + * this can result in the file not getting locked when it probably should. + */ +int +lockit(int fd, int type) +{ + struct flock fl; + int result, timer = 0; + + if (suppress_locking) + return (0); + + fl.l_type = type; + fl.l_whence = 0; + fl.l_start = 0; + fl.l_len = 0; + + while ((result = fcntl(fd, F_SETLK, &fl)) < 0) + { + if (errno != EACCES || errno != EAGAIN) + break; + if (timer++ == 30) + { + errno = ETIMEDOUT; + break; + } + + spinwheels(1); + } + + return (result); +} /* lockit() */ + +/* + * Search all the database files and our personal preference file for + * more information about the current CD. + */ +void +load( void ) +{ + FILE *fp; + char **dbfile; + int locked = 0; + int dbfound = 0, *trklist, i; + unsigned long dbpos; + +/* This is some kind of profiling code. I don't change it + to wm_lib_message() for now... */ +#ifndef NDEBUG + long t1, t2; + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t1); + printf("%s (%d): search start = %ld\n", __FILE__, __LINE__, t1); + fflush(stdout); + } +#endif + + dbfile = databases; + + found_in_db = 0; + + /* Turn the cd->trk array into a simple array of ints. */ + trklist = (int *)malloc(sizeof(int) * cd->ntracks); + for (i = 0; i < cd->ntracks; i++) + trklist[i] = cd->trk[i].start; + + do { + if (*dbfile && idx_find_entry(*dbfile, cd->ntracks, trklist, + cd->length * 75, 0, &dbpos) == 0) + dbfound = 1; + + fp = *dbfile ? open_rcfile(*dbfile, "r") : NULL; + if (fp != NULL) + { + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (db) lock"); + else + locked = 1; + + if (dbfound) + fseek(fp, dbpos, 0); + + if (search_db(fp, 0, 0, 0)) + { + found_in_db = 1; + cd->whichdb = *dbfile; + } + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (db) lock"); + + fclose(fp); + } + } while (*++dbfile != NULL && cd->whichdb == NULL); + +#ifndef NDEBUG + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t2); + printf("%s (%d): db search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1); + fflush(stdout); + } +#endif + + fp = rcfile ? open_rcfile(rcfile, "r") : NULL; + if (fp != NULL) + { + locked = 0; + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (rc) lock"); + else + locked = 1; + + rcpos = 0; + found_in_rc = search_db(fp, 1, 0, 0); + if (! found_in_rc) + cd->autoplay = wm_db_get_playnew(); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (rc) lock"); + + fclose(fp); + } + + free(trklist); + + if (cur_playnew == -1) + cur_playnew = 0; + +#ifndef NDEBUG + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + { + time(&t2); + printf("%s (%d): search end = %ld, elapsed = %ld\n", __FILE__, __LINE__, t2, t2 - t1); + fflush(stdout); + } +#endif +} /* load() */ + +/* + * Load program settings from the rcfile. + */ +void +load_settings( void ) +{ + FILE *fp; + int locked; + + fp = rcfile ? open_rcfile(rcfile, "r") : NULL; + if (fp != NULL) + { + locked = 0; + if (lockit(fileno(fp), F_RDLCK)) + perror("Couldn't get read (rc) lock"); + else + locked = 1; + + rcpos = 0; + found_in_rc = search_db(fp, 2, 0, 0); + if (! found_in_rc) + cd->autoplay = wm_db_get_playnew(); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Couldn't relinquish (rc) lock"); + + fclose(fp); + } +} /* load_settings() */ + +/* + * save_globals() + * + * Save the global preferences, scooting CD entries to the end if needed. + * The assumption here is that the rcfile is locked, and that firstpos has + * been set by a previous scan. + */ +void +save_globals(FILE *fp) +{ + char *globes = NULL, *cdentry = NULL, temp[100]; + long curpos; + int globesize, hit_cdent = 0, c = 0; + + if (otherrc) + wm_strmcpy(&globes, otherrc); + + if (cddb.protocol) + { + sprintf(temp, "cddbprotocol "); + switch(cddb.protocol) + { + case 1: /* cddbp */ + sprintf(temp + strlen(temp), "cddbp\n"); + break; + case 2: /* http */ + sprintf(temp + strlen(temp), "http\n"); + break; + case 3: /* proxy */ + sprintf(temp + strlen(temp), "proxy\n"); + break; + default: + break; + } + wm_strmcat(&globes, temp); + + if(cddb.mail_adress[0]) + { + sprintf(temp,"cddbmailadress %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + + if(cddb.cddb_server[0]) + { + sprintf(temp,"cddbserver %s\n", + cddb.cddb_server); + wm_strmcat(&globes, temp); + } + + if(cddb.path_to_cgi[0]) + { + sprintf(temp,"cddbpathtocgi %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + + if(cddb.proxy_server[0]) + { + sprintf(temp,"cddbproxy %s\n", + cddb.mail_adress); + wm_strmcat(&globes, temp); + } + } + + if (cur_stopmode == 1 || cur_stopmode == 2) + { + sprintf(temp, "whendone %s\n", cur_stopmode == 1 ? "repeat" : + "eject"); + wm_strmcat(&globes, temp); + } + + if (cur_playnew == 1) + wm_strmcat(&globes, "playnew\n"); + + curpos = firstpos; + if (curpos < 0) + curpos = 0; + + fseek(fp, curpos, SEEK_SET); + + if (firstpos < (globesize = globes != NULL ? strlen(globes) : 0)) + { + while (1) + { + temp[sizeof(temp)-1] = 'x'; + + if (fgets(temp, sizeof(temp), fp) == NULL) + { + fseek(fp, 0, SEEK_SET); + if (globes != NULL) + { + fwrite(globes, globesize, 1, fp); + free(globes); + } + if (cdentry != NULL) + { + fwrite(cdentry, strlen(cdentry), 1, fp); + free(cdentry); + } + return; + } + + if (! strncmp(temp, "tracks ", 7)) + { + hit_cdent = 1; + if (curpos >= globesize) + break; + } + + if (! hit_cdent) + { + curpos += strlen(temp); + if (temp[sizeof(temp)-1] == '\0') + while ((c = getc(fp)) != '\n' && + c != EOF) + curpos++; + if (c == '\n') + curpos++; + + continue; + } + + wm_strmcat(&cdentry, temp); + curpos += strlen(temp); + while (temp[sizeof(temp)-1] == '\0') + { + temp[sizeof(temp)-1] = 'x'; + if (fgets(temp, sizeof(temp), fp) == NULL) + break; + wm_strmcat(&cdentry, temp); + curpos += strlen(temp); + } + } + + if (cdentry != NULL) + { + fseek(fp, 0, SEEK_END); + fwrite(cdentry, strlen(cdentry), 1, fp); + free(cdentry); + } + } + + if (globes != NULL) + { + fseek(fp, 0, SEEK_SET); + fwrite(globes, globesize, 1, fp); + free(globes); + } + + while (globesize++ < curpos) + putc('\n', fp); +} /* save_globals() */ + +/* + * save_entry() + * + * Save the CD information to one database. + * + * filename Database to save to. + * pref 0 for hard data, 1 for preferences. + * + * If an entry for this CD exists already, overwrite it with the new entry + * if the new entry is the same size or smaller, or with newlines if the new + * entry is larger (in which case the new entry is appended to the file.) + * + * Also, if the preference information is being updated, save it to the + * file while we've got it locked. Scoot stuff from the beginning of + * the file to the end as needed to facilitate this. + * + * XXX Preference-saving should probably be done elsewhere, like in an + * Apply button on the Goodies popup, and in any case needs to go to a + * different file (.Xdefaults?) + * + * Returns 0 on success. + */ +int +save_entry(char *filename, int pref) +{ + FILE *fp; + char *buf; + int len, i, locked = 0; + + + if( filename == NULL ) + return (-1); + + fp = open_rcfile(filename, "r+"); + if (fp == NULL) + { + if (errno == ENOENT) /* doesn't exist already */ + fp = open_rcfile(filename, "w"); + if (fp == NULL) + return (-1); + } + + if (lockit(fileno(fp), F_WRLCK)) + perror("Warning: Couldn't get write lock"); + else + locked = 1; + + buf = print_cdinfo(cd, pref); + len = strlen(buf); /* doesn't return if there's an error */ + + rcpos = -1; + search_db(fp, pref, 1, len); + if (rcpos != -1) /* XXX */ + { + /* + * Jump to the entry's position in the database file, if + * it was found. + */ + fseek(fp, rcpos, SEEK_SET); + + if (rclen >= len && holepos == -1) + { + /* + * If the new entry will fit in the space occupied by + * the old one, overwrite the old one and make a hole + * of the appropriate size at its end. + * + * No need to update the index file in this case, as + * the entry's position hasn't changed. + */ + fputs(buf, fp); + for (i = len; i < rclen; i++) + fputc('\n', fp); + } + else + { + /* + * Overwrite the old entry with a hole and delete + * its pointer in the index file. + */ + for (i = 0; i < rclen; i++) + fputc('\n', fp); + idx_delete_entry(filename, cd->trk[cd->ntracks-1].start, + 0, rcpos); + + rcpos = -1; + } + } + + /* + * Old entry wasn't found, or its new version wouldn't fit where + * the old one was. + */ + if (rcpos == -1) + { + /* + * Write the new entry in a hole, if there is one, + * or at the end of the file. + */ + if (holepos >= 0) + { + fseek(fp, holepos, SEEK_SET); + if (holepos < firstpos) + firstpos = holepos; + } + else + { + fseek(fp, 0, SEEK_END); + holepos = ftell(fp); + } + fputs(buf, fp); + + /* + * Write a new index entry for this CD. + */ + idx_write_entry(filename, cd->trk[cd->ntracks - 1].start, + holepos); + } + + if (pref) + save_globals(fp); + + fflush(fp); + + if (locked && lockit(fileno(fp), F_UNLCK)) + perror("Warning: Couldn't relinquish write lock"); + + fclose(fp); + + return (0); +} /* save_entry() */ + +/* + * save() + * + * Save CD information to the appropriate datafile (the first file in the + * list, unless the entry came from another database file) and to the + * personal prefs file. + */ +int +save( void ) +{ + + if( wm_db_save_disabled == FALSE ) + { + if (save_entry(rcfile, 1)) + return (0); + + if (cd->whichdb == NULL || access(cd->whichdb, W_OK)) + cd->whichdb = databases[0]; + + if (save_entry(cd->whichdb, 0)) + return (0); + + return( WM_DB_SAVE_ERROR ); + } else { + return( WM_DB_SAVE_DISABLED ); + } +} /* save() */ diff --git a/kscd/libwm/drv_sony.c b/kscd/libwm/drv_sony.c new file mode 100644 index 00000000..a9db5d73 --- /dev/null +++ b/kscd/libwm/drv_sony.c @@ -0,0 +1,166 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Vendor-specific drive control routines for Sony CDU-8012 series. + */ + +#include <stdio.h> +#include <errno.h> +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_scsi.h" + +#define PAGE_AUDIO 0x0e + +/* local prototypes */ +static int sony_init( struct wm_drive *d ); +static int sony_set_volume(struct wm_drive *d, int left, int right ); +static int sony_get_volume( struct wm_drive *d, int *left, int *right ); + + +extern int min_volume, max_volume; + +struct wm_drive_proto sony_proto = { + sony_init, /* functions... */ + gen_close, + gen_get_trackcount, + gen_get_cdlen, + gen_get_trackinfo, + gen_get_drive_status, + sony_get_volume, + sony_set_volume, + gen_pause, + gen_resume, + gen_stop, + gen_play, + gen_eject, + gen_closetray, + NULL /* gen_get_cdtext */ +}; + +/* + * Initialize the driver. + */ +static int sony_init( struct wm_drive *d ) { + +/* Sun use Sony drives as well */ +#if defined(SYSV) && defined(CODEC) + codec_init(); +#endif + min_volume = 128; + max_volume = 255; + return( 0 ); +} /* sony_init() */ + +/* + * On the Sony CDU-8012 drive, the amount of sound coming out the jack + * increases much faster toward the top end of the volume scale than it + * does at the bottom. To make up for this, we make the volume scale look + * sort of logarithmic (actually an upside-down inverse square curve) so + * that the volume value passed to the drive changes less and less as you + * approach the maximum slider setting. Additionally, only the top half + * of the volume scale is valid; the bottom half is all silent. The actual + * formula looks like + * + * max^2 - (max - vol)^2 max + * v = --------------------- + --- + * max * 2 2 + * + * Where "max" is the maximum value of the volume scale, usually 100. + */ +static int +scale_volume(vol, max) + int vol, max; +{ + vol = (max*max - (max - vol) * (max - vol)) / max; + return ((vol + max) / 2); +} + +/* + * Given a value between min_volume and max_volume, return the standard-scale + * volume value needed to achieve that hardware value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + */ +static int +unscale_volume(cd_vol, max) + int cd_vol, max; +{ + int vol = 0, top = max, bot = 0, scaled = 0; + + cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol <= scaled) + top = vol - 1; + else + bot = vol + 1; + } + + /* Might have looked down too far for repeated scaled values */ + if (cd_vol < scaled) + vol++; + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} + +/* + * Get the volume. Sun's CD-ROM driver doesn't support this operation, even + * though their drive does. Dumb. + */ +static int +sony_get_volume( struct wm_drive *d, int *left, int *right ) +{ + unsigned char mode[16]; + + /* Get the current audio parameters first. */ + if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode)) + return (-1); + + *left = unscale_volume(mode[9], 100); + *right = unscale_volume(mode[11], 100); + + return (0); +} + +/* + * Set the volume using the wacky scale outlined above. The Sony drive + * responds to the standard set-volume command. + */ +static int +sony_set_volume(struct wm_drive *d, int left, int right ) +{ + left = scale_volume(left, 100); + right = scale_volume(right, 100); + return (gen_set_volume(d, left, right)); +} diff --git a/kscd/libwm/drv_toshiba.c b/kscd/libwm/drv_toshiba.c new file mode 100644 index 00000000..ef009867 --- /dev/null +++ b/kscd/libwm/drv_toshiba.c @@ -0,0 +1,149 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Vendor-specific drive control routines for Toshiba XM-3401 series. + */ + +static char drv_toshiba_id[] = "$Id$"; + +#include <stdio.h> +#include <errno.h> +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_scsi.h" + +#define SCMD_TOSH_EJECT 0xc4 + +/* local prototypes */ +static int tosh_init( struct wm_drive *d ); +static int tosh_eject( struct wm_drive *d ); +static int tosh_set_volume( struct wm_drive *d, int left, int right ); +static int tosh_get_volume( struct wm_drive *d, int *left, int *right ); + +struct wm_drive_proto toshiba_proto = { + tosh_init, /* functions... */ + gen_close, + gen_get_trackcount, + gen_get_cdlen, + gen_get_trackinfo, + gen_get_drive_status, + tosh_get_volume, + tosh_set_volume, + gen_pause, + gen_resume, + gen_stop, + gen_play, + tosh_eject, + gen_closetray, + NULL /* gen_get_cdtext */ +}; + +/* + * Initialize the driver. + */ +static int +tosh_init( struct wm_drive *d ) +{ + extern int min_volume; + +/* Sun use Toshiba drives as well */ +#if defined(SYSV) && defined(CODEC) + codec_init(); +#endif + min_volume = 0; + return ( 0 ); +} + +/* + * Send the Toshiba code to eject the CD. + */ +static int +tosh_eject( struct wm_drive *d ) +{ + return (sendscsi(d, NULL, 0, 0, SCMD_TOSH_EJECT, 1, 0,0,0,0,0,0,0,0,0,0)); +} + +/* + * Set the volume. The low end of the scale is more sensitive than the high + * end, so make up for that by transforming the volume parameters to a square + * curve. + */ +static int +tosh_set_volume( struct wm_drive *d, int left, int right ) +{ + left = (left * left * left) / 10000; + right = (right * right * right) / 10000; + return (gen_set_volume(d, left, right)); +} + +/* + * Undo the transformation above using a binary search (so no floating-point + * math is required.) + */ +static int +unscale_volume(cd_vol, max) + int cd_vol, max; +{ + int vol = 0, top = max, bot = 0, scaled = 0; + + /*cd_vol = (cd_vol * 100 + (max_volume - 1)) / max_volume;*/ + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = (vol * vol) / max; + if (cd_vol <= scaled) + top = vol - 1; + else + bot = vol + 1; + } + + /* Might have looked down too far for repeated scaled values */ + if (cd_vol < scaled) + vol++; + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} + +/* + * Get the volume. + */ +static int +tosh_get_volume( struct wm_drive *d, int *left, int *right ) +{ + int status; + + status = gen_get_volume(d, left, right); + if (status < 0) + return (status); + *left = unscale_volume(*left, 100); + *right = unscale_volume(*right, 100); + + return (0); +} diff --git a/kscd/libwm/include/wm_cdda.h b/kscd/libwm/include/wm_cdda.h new file mode 100644 index 00000000..11c8eb3e --- /dev/null +++ b/kscd/libwm/include/wm_cdda.h @@ -0,0 +1,203 @@ +#ifndef WM_CDDA_H +#define WM_CDDA_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Information about a particular block of CDDA data. + */ +struct cdda_block { + unsigned char status; + unsigned char track; + unsigned char index; + unsigned char reserved; + + int frame; + char *buf; + long buflen; +}; + +struct cdda_device { + int fd; + const char *devname; + + unsigned char status; + unsigned char track; + unsigned char index; + unsigned char command; + + int frame; + int frames_at_once; + + /* Average volume levels, for level meters */ + unsigned char lev_chan0; + unsigned char lev_chan1; + + /* Current volume setting (0-255) */ + unsigned char volume; + + /* Current balance setting (0-255, 128 = balanced) */ + unsigned char balance; + + struct cdda_block *blocks; + int numblocks; +}; + +#include "wm_cdrom.h" +#include "wm_config.h" +#include "wm_struct.h" +/* + * cdda_block status codes. + */ + +/* + * Enable or disable CDDA building depending on platform capabilities, and + * determine endianness based on architecture. (Gross!) + * + * For header-comfort, the macros LITTLE_ENDIAN and BIG_ENDIAN had to be + * renamed. At least Linux does have bytesex.h and endian.h for easy + * byte-order examination. + */ + +#ifdef HAVE_MACHINE_ENDIAN_H + #include <machine/endian.h> + #if BYTE_ORDER == LITTLE_ENDIAN + #define WM_LITTLE_ENDIAN 1 + #define WM_BIG_ENDIAN 0 + #else + #define WM_LITTLE_ENDIAN 0 + #define WM_BIG_ENDIAN 1 + #endif +#elif defined(__sun) || defined(sun) +# ifdef SYSV +# include <sys/types.h> +# include <sys/cdio.h> +# ifndef CDROMCDDA +# undef BUILD_CDDA +# endif +# ifdef i386 +# define WM_LITTLE_ENDIAN 1 +# define WM_BIG_ENDIAN 0 +# else +# define WM_BIG_ENDIAN 1 +# define WM_LITTLE_ENDIAN 0 +# endif +# else +# undef BUILD_CDDA +# endif + +/* Linux only allows definition of endianness, because there's no + * standard interface for CDROM CDDA functions that aren't available + * if there is no support. + */ +#elif defined(__linux__) +/*# include <bytesex.h>*/ +# include <endian.h> +/* + * XXX could this be a problem? The results are only 0 and 1 because + * of the ! operator. How about other linux compilers than gcc ? + */ +# define WM_LITTLE_ENDIAN !(__BYTE_ORDER - __LITTLE_ENDIAN) +# define WM_BIG_ENDIAN !(__BYTE_ORDER - __BIG_ENDIAN) +#elif defined WORDS_BIGENDIAN + #define WM_LITTLE_ENDIAN 0 + #define WM_BIG_ENDIAN 1 +#else + #define WM_LITTLE_ENDIAN 1 + #define WM_BIG_ENDIAN 0 +#endif + +/* + * The following code shouldn't take effect now. + * In 1998, the WorkMan platforms don't support __PDP_ENDIAN + * architectures. + * + */ + +#if !defined(WM_LITTLE_ENDIAN) +# if !defined(WM_BIG_ENDIAN) +# error yet unsupported architecture + foo bar this is to stop the compiler. +# endif +#endif + +#if defined(BUILD_CDDA) +/* + * The following code support us by optimize cdda operations + */ +#define CDDARETURN(x) if(x && x->cdda == 1) return +#define IFCDDA(x) if(x && x->cdda == 1) +int cdda_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *ind); +int cdda_play(struct wm_drive *d, int start, int end, int realstart); +int cdda_pause(struct wm_drive *d); +int cdda_stop(struct wm_drive *d); +int cdda_eject(struct wm_drive *d); +int cdda_set_volume(struct wm_drive *d, int left, int right); +int cdda_get_volume(struct wm_drive *d, int *left, int *right); +void cdda_kill(struct wm_drive *d); +void cdda_save(struct wm_drive *d, char *filename); +int cdda_get_ack(int); +int gen_cdda_init(struct wm_drive *d ); + +void cdda_set_direction(struct wm_drive *d, int newdir); +void cdda_set_speed(struct wm_drive *d, int speed); +void cdda_set_loudness(struct wm_drive *d, int loud); + + +int wmcdda_init(struct cdda_device*); +int wmcdda_open(const char*); +int wmcdda_close(struct cdda_device*); +int wmcdda_setup(int start, int end, int realstart); +long wmcdda_read(struct cdda_device*, struct cdda_block *block); +void wmcdda_speed(int speed); +void wmcdda_direction(int newdir); + +#else + #define CDDARETURN(x) + #define IFCDDA(x) + #define cdda_get_drive_status + #define cdda_play + #define cdda_pause + #define cdda_resume + #define cdda_stop + #define cdda_eject + #define cdda_set_volume + #define cdda_get_volume + #define cdda_kill + #define cdda_save + #define cdda_get_ack +#endif /* defined(BUILD_CDDA) */ + +#include <stdio.h> + +#ifdef DEBUG + #define DEBUGLOG(fmt, args...) fprintf(stderr, fmt, ##args) +#else + #define DEBUGLOG(fmt, args...) +#endif +#define ERRORLOG(fmt, args...) fprintf(stderr, fmt, ##args) + +#endif /* WM_CDDA_H */ diff --git a/kscd/libwm/include/wm_cddb.h b/kscd/libwm/include/wm_cddb.h new file mode 100644 index 00000000..26a0c2d9 --- /dev/null +++ b/kscd/libwm/include/wm_cddb.h @@ -0,0 +1,36 @@ +#ifndef WM_CDDB_H +#define WM_CDDB_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +unsigned long cddb_discid(void); +void cddb_struct2cur(void); +void cddb_cur2struct(void); +void cddb_select(void); +void connect_cddb(void); +void update_cddbserver(void); +void cddb_request(void); + +#endif /* WM_CDDB_H */ diff --git a/kscd/libwm/include/wm_cdinfo.h b/kscd/libwm/include/wm_cdinfo.h new file mode 100644 index 00000000..dbb8e949 --- /dev/null +++ b/kscd/libwm/include/wm_cdinfo.h @@ -0,0 +1,76 @@ +#ifndef WM_CDINFO_H +#define WM_CDINFO_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Prototypes from cdinfo.c + * + * This is just one more step to a more modular and understandable code. + */ + +#include "wm_struct.h" +extern char *cur_trackname; /* Take a guess */ +extern int cur_index; /* Current index mark */ +extern int cur_frame; /* Current frame number */ +extern struct wm_play *playlist; /* = NULL */ + +/*extern int cur_track;*/ /* Current track number, starting at 1 */ +extern char *cur_artist; /* Name of current CD's artist */ +extern char cur_avoid; /* Avoid flag */ +extern char cur_contd; /* Continued flag */ +extern char *cur_cdname; /* Album name */ +extern int cur_nsections; /* Number of sections currently defined */ +extern int exit_on_eject; +extern int cur_pos_abs; +extern int cur_pos_rel; +extern int cur_cdlen; + +extern int cur_ntracks; +extern int cur_lasttrack; +extern int cur_firsttrack; +extern int cur_listno; +extern int cur_stopmode; + +void wipe_cdinfo( void ); +void play_next_entry( int forward ); +void make_playlist( int playmode, int starttrack ); +int get_autoplay( void ); +int get_playmode( void ); +void pl_find_track( int track ); +void play_prev_track( int forward ); +void play_next_track( int forward ); +int tracklen( int num ); +int get_default_volume( int track ); +int split_trackinfo( int pos ); +int remove_trackinfo( int num ); +void freeup( char **x ); +int get_runtime( void ); +const char *trackname( int num ); +void stash_cdinfo( char *artist, char *cdname, int autoplay, int playmode ); +void stash_trkinfo( int track, char *songname, int contd, int avoid ); +int get_avoid( int num ); +int get_contd( int num ); +void default_volume( int track, int vol ); +char *listentry( int num ); + +#endif /* WM_CDINFO_H */ diff --git a/kscd/libwm/include/wm_cdrom.h b/kscd/libwm/include/wm_cdrom.h new file mode 100644 index 00000000..d38f8b68 --- /dev/null +++ b/kscd/libwm/include/wm_cdrom.h @@ -0,0 +1,114 @@ +#ifndef WM_CDROM_H +#define WM_CDROM_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Prototypes from cdrom.c + * + * This is just one more step to a more modular and understandable code. + */ +#define WM_CDS_NO_DISC(status) (status == WM_CDM_UNKNOWN ||\ + status == WM_CDM_EJECTED ||\ + status == WM_CDM_NO_DISC) + +#define WM_CDS_DISC_READY(status)(status == WM_CDM_TRACK_DONE ||\ + status == WM_CDM_PLAYING ||\ + status == WM_CDM_FORWARD ||\ + status == WM_CDM_PAUSED ||\ + status == WM_CDM_STOPPED) + +#define WM_CDS_DISC_PLAYING(status)(status == WM_CDM_TRACK_DONE ||\ + status == WM_CDM_PLAYING ||\ + status == WM_CDM_FORWARD ||\ + status == WM_CDM_PAUSED) +#define WM_CDM_BACK 1 +#define WM_CDM_TRACK_DONE 1 +#define WM_CDM_PLAYING 2 +#define WM_CDM_FORWARD 3 +#define WM_CDM_PAUSED 4 +#define WM_CDM_STOPPED 5 +#define WM_CDM_EJECTED 6 +#define WM_CDM_DEVICECHANGED 9 /* deprecated */ +#define WM_CDM_NO_DISC 10 +#define WM_CDM_UNKNOWN 11 +#define WM_CDM_CDDAERROR 12 +#define WM_CDM_CDDAACK 0xF0 + +#define WM_CDIN 0 +#define WM_CDDA 1 + +#define WM_ENDTRACK 0 + +#define WM_BALANCE_SYMMETRED 0 +#define WM_BALANCE_ALL_LEFTS -10 +#define WM_BALANCE_ALL_RIGHTS 10 + +#define WM_VOLUME_MUTE 0 +#define WM_VOLUME_MAXIMAL 100 + +int wm_cd_init( int cdin, const char *cd_device, const char *soundsystem, + const char *sounddevice, const char *ctldevice ); +int wm_cd_destroy( void ); + +int wm_cd_status( void ); + +int wm_cd_getcurtrack( void ); +int wm_cd_getcurtracklen( void ); +int wm_cd_getcountoftracks( void ); + +int wm_cd_gettracklen( int track ); + +int wm_cd_play( int start, int pos, int end ); +int wm_cd_play_chunk( int start, int end, int realstart ); +int wm_cd_play_from_pos( int pos ); +int wm_cd_pause( void ); +int wm_cd_stop( void ); +int wm_cd_eject( void ); +int wm_cd_closetray( void ); + +int wm_find_trkind( int, int, int ); + +/* + * for vaild values see wm_helpers.h + */ +int wm_cd_set_verbosity( int ); + +const char * wm_drive_vendor( void ); +const char * wm_drive_model( void ); +const char * wm_drive_revision( void ); +const char * wm_drive_device( void ); + +/* + * volume is valid WM_VOLUME_MUTE <= vol <= WM_VOLUME_MAXIMAL, + * balance is valid WM_BALANCE_ALL_LEFTS <= balance <= WM_BALANCE_ALL_RIGHTS + */ +int wm_cd_volume( int volume, int balance); + +/* + * please notice, that more OSs don't allow to read balance and volume + * in this case you get -1 for volume and WM_BALANCE_SYMMETRED for balance + */ +int wm_cd_getvolume( void ); +int wm_cd_getbalance( void ); + +#endif /* WM_CDROM_H */ diff --git a/kscd/libwm/include/wm_cdtext.h b/kscd/libwm/include/wm_cdtext.h new file mode 100644 index 00000000..e843ee95 --- /dev/null +++ b/kscd/libwm/include/wm_cdtext.h @@ -0,0 +1,105 @@ +#ifndef WM_CDTEXT_H +#define WM_CDTEXT_H + +/*************************************************************************** + wm_cdtext.h - description + ------------------- + begin : Mon Feb 12 2001 + copyright : (C) 2001 by Alex Kern + email : alex.kern@gmx.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* + * cdtext base structure and defines + */ + +#define MAX_LENGHT_OF_CDTEXT_STRING 162 /* max 160 bytes + 2 * 0x00 by UNICODES */ +#define DATAFIELD_LENGHT_IN_PACK 12 +#define MAX_LANGUAGE_BLOCKS 8 + +struct cdtext_pack_data_header { + unsigned char header_field_id1_typ_of_pack; + unsigned char header_field_id2_tracknumber; + unsigned char header_field_id3_sequence; + unsigned char header_field_id4_block_no; + unsigned char text_data_field[DATAFIELD_LENGHT_IN_PACK]; + unsigned char crc_byte1; + unsigned char crc_byte2; +}; + +typedef unsigned char cdtext_string[MAX_LENGHT_OF_CDTEXT_STRING]; + +/* meke it more generic + it can be up to 8 blocks with different encoding */ + +struct cdtext_info_block { + /* management */ + unsigned char block_code; + unsigned char block_unicode; /* 0 - single chars, 1 - doublebytes */ + unsigned char block_encoding; /* orange book -? */ + cdtext_string* block_encoding_text; + + /* variable part of cdtext */ + cdtext_string* name; + cdtext_string* performer; + cdtext_string* songwriter; + cdtext_string* composer; + cdtext_string* arranger; + cdtext_string* message; + cdtext_string* UPC_EAN_ISRC_code; + + /* fix part of cdtext */ + unsigned char binary_disc_identification_info[DATAFIELD_LENGHT_IN_PACK]; + unsigned char binary_genreidentification_info[DATAFIELD_LENGHT_IN_PACK]; + unsigned char binary_size_information[DATAFIELD_LENGHT_IN_PACK]; +}; + +struct cdtext_info { + /* somethimes i get hunderts of bytes, without anyone valid pack + my CDU-561 for example */ + int count_of_entries; /* one more because album need one too */ + int count_of_valid_packs; + int count_of_invalid_packs; + int valid; + + struct cdtext_info_block *blocks[MAX_LANGUAGE_BLOCKS]; +}; + +#ifndef IGNORE_FEATURE_LIST + +struct feature_list_header { + unsigned char lenght_msb; + unsigned char lenght_1sb; + unsigned char lenght_2sb; + unsigned char lenght_lsb; + unsigned char reserved1; + unsigned char reserved2; + unsigned char profile_msb; + unsigned char profile_lsb; +}; + +struct feature_descriptor_cdread { + unsigned char feature_code_msb; + unsigned char feature_code_lsb; + unsigned char settings; + unsigned char add_lenght; + unsigned char add_settings; + unsigned char reserved1; + unsigned char reserved2; + unsigned char reserved3; +}; + +#endif /* IGNORE_FEATURE_LIST */ + +struct cdtext_info* wm_cd_get_cdtext( void ); + +#endif /* WM_CDTEXT_H */ diff --git a/kscd/libwm/include/wm_config.h b/kscd/libwm/include/wm_config.h new file mode 100644 index 00000000..b04027f4 --- /dev/null +++ b/kscd/libwm/include/wm_config.h @@ -0,0 +1,377 @@ +#ifndef WM_CONFIG_H +#define WM_CONFIG_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ********************************************************************** + * + * This file consists of several parts. First, there's a generic, + * platform independent part. Set needed options there. + * The following parts are platform dependent. You may search for the + * names listed below and then set your OS specific options there. + * Don't be surprised, if there are no options for your OS. They aren't + * needed in any case. + * + * The default values should produce a functional WorkMan on every + * platform. + * + ********************* + * Current platforms: + ********************* + * BSD386 + * FreeBSD + * HP-UX + * Irix (SGI) + * Linux + * News (Sony NewsOS) + * OpenBSD + * OSF1 + * Sun (SunOS/Solaris, Sparc or x86) + * SVR4 + * Ultrix + * AIX + * + * The above order corresponds with the order of the platform specific + * options below. + */ + +#include <config.h> +/****************************************************************** + * generic options + ****************************************************************** + ** ** *** **** ** **** ** * ** ** ** * ** + * * * *** **** * * *** * ** ** *** * * * * * * ** + * * *** **** ** *** ** ** ** * * ** * * ** + * * * *** **** **** *** * ** ** *** * * * * *** ** + * * * * ** **** * * ** ** **** ** * * *** ** + ******************************************************************/ + +/* + * This option is obvious. But please do not forget the original + * WorkMan version string if you need support. + */ + +#define WORKMAN_NAME "LibWorkMan" +#define WORKMAN_VERSION "1.4.0" + +/* + * If your CD-ROM drive closes its tray if the device is opened, then + * the next define can make WorkMans "Eject" button an "open/close" + * button. If it disturbs you, just comment it out. + * + * ### this is preliminary. It may have no effect for you ### + */ +#define CAN_CLOSE 1 + +/* + * Define the following if you want the balance slider to + * decrease one channel's volume while increasing the other's + */ +/* #define SYMETRIC_BALANCE 1 */ + + +/* + * Define this if you want CDDA support. Supported systems are: + * + * - Solaris (2.4 or newer) + * --> Linux is on the way. Don't define it now. It would lead to errors only. + */ +/*#define BUILD_CDDA 1*/ + + + +/****************************************************************** + * BSD386 + ****************************************************************** + *** **** *** ******* ** **** **** ************ + *** ** ** ****** * ***** ****** ** ** ** *************** + *** ****** **** ** *** ***** ***** *** ************ + *** ** ****** ** * *** ******** ** ** ** ** *********** + *** **** *** *** ****** **** **** ************ + ******************************************************************/ +#if defined(__bsdi__) || defined(__bsdi) + +/* + * This lets you use the SoundBlaster Mixer on BSD/386 + */ +#define SOUNDBLASTER 1 + +#define DEFAULT_CD_DEVICE "/dev/rsr2c" + +#endif /* __bsdi__ (BSD/386) */ + +/****************************************************************** + * FreeBSD + ****************************************************************** + *** ** *** ** ** **** *** *********** + *** ****** ** ** ****** ****** ** ** ****** * ********** + *** **** *** **** **** ****** **** ** ********* + *** ****** ** ** ****** ****** ** ****** ** * ********** + *** ****** ** ** ** ** **** *** *********** + ******************************************************************/ +#if defined(__FreeBSD__) || defined(__FreeBSD) + +#if __FreeBSD_version >= 500100 +#define DEFAULT_CD_DEVICE "/dev/acd0" +#else +#define DEFAULT_CD_DEVICE "/dev/acd0c" +#endif + +#endif /* freebsd */ + +/* DragonFly */ +#if defined(__DragonFly__) +#define DEFAULT_CD_DEVICE "/dev/acd0c" +#endif + +/****************************************************************** + * NetBSD + ****************************************************************** + *** *** ** ** ** **** *** ***************** + *** ** ** ******** **** ** ** ****** * **************** + *** * * ** ****** **** ****** **** ** *************** + *** ** ** ******** **** ** ****** ** * **************** + *** *** ** **** **** **** *** ***************** + ******************************************************************/ +#if defined(__NetBSD__) || defined(__NetBSD) + +#if defined(__i386__) + #define DEFAULT_CD_DEVICE "/dev/rcd0d" +#else + #define DEFAULT_CD_DEVICE "/dev/rcd0c" +#endif + +#endif /* netbsd */ + +/****************************************************************** + * HP-UX + ****************************************************************** + *** ** ** ********* ** ** ** *************************** + *** ** ** ** ******** ** *** **************************** + *** ** *** ** ** **** ***************************** + *** ** ** ************ ** *** **************************** + *** ** ** ************* *** ** *************************** + ******************************************************************/ +#if defined(hpux) || defined (__hpux) + +#define DEFAULT_CD_DEVICE "/dev/rscsi" + +#endif /* hpux */ + +/****************************************************************** + * Irix + ****************************************************************** + *** ** *** ** ** ********************************* + ***** **** ** **** ***** ********************************** + ***** **** ***** ****** *********************************** + ***** **** ** **** ***** ********************************** + *** ** ** ** ** ** ********************************* + ******************************************************************/ +#if defined(sgi) || defined(__sgi) + +#define DEFAULT_CD_DEVICE "/dev/scsi/sc0d6l0" + +#endif /* sgi IRIX */ + +/****************************************************************** + * Linux + ****************************************************************** + *** ****** ** *** ** ** ** ** *********************** + *** ******** **** ** ** ** *** ************************ + *** ******** **** * * ** ** **** ************************* + *** ******** **** ** ** ** *** ************************ + *** ** ** *** *** *** ** *********************** + ******************************************************************/ +#if defined(__linux__) + +/* + * Uncomment the following line to have WorkMan send SCSI commands + * directly to the CD-ROM drive. If you have a SCSI drive you + * probably want this, but it will cause WorkMan to not work on IDE + * drives. + */ +/*#define LINUX_SCSI_PASSTHROUGH 1*/ + +/* + * Which device should be opened by WorkMan at default? + */ +#define DEFAULT_CD_DEVICE "/dev/cdrom" + +/* + * Uncomment the following if you use the sbpcd or mcdx device driver. + * It shouldn't hurt if you use it on other devices. It'll be nice to + * hear from non-sbpcd (or mcdx) users if this is right. + */ +/*#define SBPCD_HACK 1*/ + +/* + * Linux Soundcard support + * Disabled by default, because some people rely on it + */ +/* #define OSS_SUPPORT 1 */ + +/* + * This has nothing to do with the above. + */ + +/* #define CURVED_VOLUME */ + +/* + * Uncomment the following if you want to try out a better responding + * WorkMan, especially with IDE drives. This may work with non-IDE + * drives as well. But it may be possible, that it doesn't work at all. + * If your drive/driver combination cannot handle the faster access, + * the driver will usually hang and you have to reboot your machine. + */ +/* #define FAST_IDE 1 */ + +/* + * There are two alternative ways of checking a device containing a + * mounted filesystem. Define BSD_MOUNTTEST for the test using + * getmntent(). Undefine it for using the SVR4 ustat(). + * I built in the choice, because it's not clear which method should + * be used in Linux. The ustat manpage tells us since 1995, that + * fstat() should be used, but I'm too dumb to do so. + */ + +#define BSD_MOUNTTEST + +#endif /* __linux */ + +/****************************************************************** + * Sony NewsOS + ****************************************************************** + *** *** ** ** ***** *** ***************************** + *** ** ** ****** ***** ** ******************************** + *** * * ** **** ** ** **** ****************************** + *** ** ** ****** * * ****** **************************** + *** *** ** ** * *** ***************************** + ******************************************************************/ +#if defined(__sony_news) || defined(sony_news) + +#define DEFAULT_CD_DEVICE "/dev/rsd/b0i6u0p2\0" + +#endif + +/****************************************************************** + * OSF1 + ****************************************************************** + **** **** *** *** *** ******************************* + *** ** ** ****** ****** ** ******************************* + *** ** **** **** *** ***** ******************************* + *** ** ****** ** **** ****** ******************************* + **** **** *** *** ***** ***************************** + ******************************************************************/ +#if defined(__osf__) || defined(__osf) + +/* not needed here, it's figured out by plat_osf1.c */ +/* #define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0" */ + +#endif + +/****************************************************************** + * SunOS/Solaris + ****************************************************************** + **** *** ** ** *** *************************************** + *** ****** ** ** ** *************************************** + ***** **** ** ** * * *************************************** + ******* ** ** ** ** *************************************** + **** **** *** *** *************************************** + ******************************************************************/ +#if defined(sun) || defined(__sun) + +/* + * Define the following for Solaris 2.x + * If you don't want WorkMan to try to activate the SPARCstation 5 + * internal audio input so you get sound from the workstation, comment + * out the CODEC define. + */ + +#define SYSV 1 +#define CODEC 1 +#define DEFAULT_CD_DEVICE "/vol/dev/aliases/cdrom0" + +/* + * set the following to "SUNW,CS4231" for Sun and to "SUNW,sb16" + * for PC (with SoundBlaster 16) running Solaris x86 + * (only important if you define CODEC above) + */ +#define SUN_AUD_DEV "SUNW,CS4231" +/*#define SUN_AUD_DEV "SUNW,sbpro"*/ + + +#endif + +/****************************************************************** + * SVR4 + ****************************************************************** + **** *** **** ** *** * ******************************** + *** ****** **** ** ** ** * ******************************** + ***** ***** ** *** *** ******************************* + ******* *** ** *** ** ***** ******************************** + **** ***** **** ** ***** ******************************** + ******************************************************************/ +#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news) + +#define DEFAULT_CD_DEVICE "/dev/rcdrom/cd0" + +#endif + +/****************************************************************** + * Ultrix + ****************************************************************** + *** ** ** ***** ** *** ** ** ****************** + *** ** ** ******* **** ** **** ***** ******************* + *** ** ** ******* **** ***** ****** ******************** + *** ** ** ******* **** ** **** ***** ******************* + **** *** *** **** ** ** ** ** ****************** + ******************************************************************/ +#if defined(ultrix) || defined(__ultrix) + +#endif + +/****************************************************************** + * IBM AIX + ****************************************************************** + **** *** ** ** ***************************************** + *** ** **** ***** ****************************************** + *** **** ****** ******************************************* + *** ** **** ***** ****************************************** + *** ** ** ** ** ***************************************** + ******************************************************************/ +#if defined(AIXV3) || defined(__AIXV3) + +#define DEFAULT_CD_DEVICE "/dev/cd0" + +#endif /* IBM AIX */ + +/******************************************************************/ +#endif /* WM_CONFIG_H */ + + + + + + + + diff --git a/kscd/libwm/include/wm_database.h b/kscd/libwm/include/wm_database.h new file mode 100644 index 00000000..401f12bc --- /dev/null +++ b/kscd/libwm/include/wm_database.h @@ -0,0 +1,42 @@ +#ifndef WM_DATABASE_H +#define WM_DATABASE_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Prototypes for WorkMan database + * + * This is just one more step to a more modular and understandable code. + */ + + +#define WM_DB_SAVE_ERROR 1 +#define WM_DB_SAVE_DISABLED 2 + +int wm_db_get_playnew( void ); +void split_workmandb( void ); +int save( void ); +void load( void ); +void load_settings( void ); + + +#endif /* WM_DATABASE_H */ diff --git a/kscd/libwm/include/wm_helpers.h b/kscd/libwm/include/wm_helpers.h new file mode 100644 index 00000000..ad98a365 --- /dev/null +++ b/kscd/libwm/include/wm_helpers.h @@ -0,0 +1,109 @@ +#ifndef WM_HELPERS_H +#define WM_HELPERS_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Here to be found: Prototypes. Including variable names to be easier + * to read. + * This is just one more step to a more modular and understandable code. + * + */ + +/* + * LibWorkMan message levels. I'm not sure how to call them all and which + * use they should fulfill. This is not very urgent now, because there + * aren't many messages in LibWorkMan now. + */ +#define WM_MSG_LEVEL_NONE 0 /**/ +#define WM_MSG_LEVEL_ERROR 1 /**/ +#define WM_MSG_LEVEL_TWO 2 +#define WM_MSG_LEVEL_THREE 3 +#define WM_MSG_LEVEL_FOUR 4 +#define WM_MSG_LEVEL_INFO 5 /**/ +#define WM_MSG_LEVEL_SIX 6 +#define WM_MSG_LEVEL_VERB 7 /**/ +#define WM_MSG_LEVEL_EIGHT 8 +#define WM_MSG_LEVEL_DEBUG 9 /**/ + +/* + * Message classes. This is somehow a definition of + * the message's source. + */ + +#define WM_MSG_CLASS_PLATFORM 0x010 +#define WM_MSG_CLASS_SCSI 0x020 +#define WM_MSG_CLASS_CDROM 0x040 +#define WM_MSG_CLASS_DB 0x080 +#define WM_MSG_CLASS_MISC 0x100 + +#define WM_MSG_CLASS_ALL 0xff0 + +extern int wm_lib_verbosity; + +/* + * I did not know any better place... + */ +#ifdef DEBUG +#define CHECKPOINT(t) fprintf(stderr, "%s (%d): %s\n", __FILE__, __LINE__, t ); +#else +#define CHECKPOINT(t) +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifdef __linux__ +#include <signal.h> +/* Linux doesn't have a SIGEMT */ +#if !defined( SIGEMT ) +# define SIGEMT SIGUNUSED +#endif +#endif /* linux */ + +void freeup( char **x ); +void wm_strmcat( char **t, const char *s); +void wm_strmcpy( char **t, const char *s ); +char * wm_strdup( char *s ); +/* Somebody's version query unsatisfied? */ +int wm_libver_major( void ); /* return major internal version number */ +int wm_libver_minor( void ); /* return minor internal version number */ +int wm_libver_pl( void ); /* return internal patchlevel number */ +char * wm_libver_name( void ); /* return internal name (LibWorkMan) */ +char * wm_libver_number( void ); /* returns string: "<major>.<minor>.<pl>" */ +char * wm_libver_string( void ); /* returns string: "<name> <number>" */ +char * wm_libver_date( void ); /* returns string: date of compilation */ +void wm_lib_set_verbosity( int level ); /* set verbosity level */ +int wm_lib_get_verbosity( void ); /* get verbosity level */ +void wm_lib_message( unsigned int level, const char *format, ... ) +#ifdef __GNUC__ + __attribute__ ((format(printf,2,3))) +#endif + ; /* put out a message on stderr */ +int wm_susleep( int usec ); + +#endif /* WM_HELPERS_H */ diff --git a/kscd/libwm/include/wm_index.h b/kscd/libwm/include/wm_index.h new file mode 100644 index 00000000..7c348b75 --- /dev/null +++ b/kscd/libwm/include/wm_index.h @@ -0,0 +1,36 @@ +#ifndef WM_INDEX_H +#define WM_INDEX_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Prototypes for wm_index.c + * + * This is just one more step to a more modular and understandable code. + */ + +int idx_find_entry( char *file, int ntracks, int *tracks, + int len, int fuzz, unsigned long *pos ); +int idx_delete_entry(char *file, int track, int fuzz, unsigned long pos ); +int idx_write_entry( char *file, int track, unsigned long pos ); + +#endif /* WM_INDEX_H */ diff --git a/kscd/libwm/include/wm_platform.h b/kscd/libwm/include/wm_platform.h new file mode 100644 index 00000000..edce8119 --- /dev/null +++ b/kscd/libwm/include/wm_platform.h @@ -0,0 +1,51 @@ +#ifndef WM_PLATFORM_H +#define WM_PLATFORM_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * The platform interface + * + * This is just one more step to a more modular and understandable code. + */ + +#include "wm_struct.h" + +int wmcd_open( struct wm_drive *d ); +int wmcd_reopen( struct wm_drive *d ); + +/* + * void keep_cd_open( void ); + */ +int wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply ); + +/**************************************** + * + * The drive prototypes. + * + */ +extern struct wm_drive_proto generic_proto; +extern struct wm_drive_proto sony_proto; +extern struct wm_drive_proto toshiba_proto; + +#endif /* WM_PLATFORM_H */ diff --git a/kscd/libwm/include/wm_scsi.h b/kscd/libwm/include/wm_scsi.h new file mode 100644 index 00000000..0acc62ef --- /dev/null +++ b/kscd/libwm/include/wm_scsi.h @@ -0,0 +1,50 @@ +#ifndef WM_SCSI_H +#define WM_SCSI_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * SCSI prototypes (scsi.c) + * + * This is just one more step to a more modular and understandable code. + */ + +#include "wm_struct.h" + +#define WM_ERR_SCSI_INQUIRY_FAILED -1 + +int wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, + unsigned char *buf ); +int sendscsi( struct wm_drive *d, void *buf, + unsigned int len, int dir, + unsigned char a0, unsigned char a1, + unsigned char a2, unsigned char a3, + unsigned char a4, unsigned char a5, + unsigned char a6, unsigned char a7, + unsigned char a8, unsigned char a9, + unsigned char a10, unsigned char a11 ); + int wm_scsi_get_drive_type( struct wm_drive *d, char *vendor, + char *model, char *rev ); +int wm_scsi_get_cdtext( struct wm_drive *d, + unsigned char **pp_buffer, int *p_buffer_length ); + +#endif /* WM_SCSI_H */ diff --git a/kscd/libwm/include/wm_struct.h b/kscd/libwm/include/wm_struct.h new file mode 100644 index 00000000..790de3b0 --- /dev/null +++ b/kscd/libwm/include/wm_struct.h @@ -0,0 +1,198 @@ +#ifndef WM_STRUCT_H +#define WM_STRUCT_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + + +/* + * Structure for a single track. This is pretty much self-explanatory -- + * one of these exists for each track on the current CD. + */ +struct wm_trackinfo +{ + char *songname; /* Name of song, dynamically allocated */ + char *otherdb; /* Unrecognized info for this track */ + char *otherrc; + int length; /* Length of track in seconds or Kbytes */ + int start; /* Starting position (f+s*75+m*60*75) */ + int volume; /* Per-track volume (1-32, 0 to disable) */ + int track; /* Physical track number */ + int section; /* Section number (0 if track not split) */ + int contd; /* Flag: continuation of previous track */ + int avoid; /* Flag: don't play this track. */ + int data; /* Flag: data track */ +}; + +/* + * Structure for internal playlist management. The internal playlist is + * simply the list of track ranges that are being played currently. This + * is built whenever the CD starts playing; it's used in normal and shuffle + * modes as well as playlist mode. + * + * The "starttime" element represents how much time has elapsed by the time + * we get to this entry. For instance, if the list begins with a 5-minute + * track and a 3-minute track, the third entry would have a starttime of 8 + * minutes. This is used so that the elapsed play time can be displayed + * even in shuffle or playlist modes. + * + * The last member of the list has a start track of 0, and its starttime is + * the total playing time of the playlist (which will usually be overestimated, + * since we don't play leadouts in some cases.) + */ +struct wm_play +{ + int start; /* Start track, or 0 if end of list */ + int end; /* last track plus 1 */ + int starttime; /* Number of seconds elapsed previously */ +}; + +/* + * Structure for playlists (as seen by the user.) This is simply a name + * followed by a zero-terminated list of track numbers to play. The list + * is terminated by a NULL name. + */ +struct wm_playlist +{ + char *name; /* Name of this playlist */ + int *list; /* List of tracks */ +}; + +struct wm_cdinfo +{ + char artist[84]; /* Artist's name */ + char cdname[84]; /* Disc's name */ + int ntracks; /* Number of tracks on the disc */ + int curtrack; + int curtracklen; + int length; /* Total running time in seconds */ + int autoplay; /* Start playing CD immediately */ + int playmode; /* How to play the CD */ + int volume; /* Default volume (1-32, 0 for none) */ + struct wm_trackinfo *trk; /* struct wm_trackinfo[ntracks] */ + struct wm_playlist *lists; /* User-specified playlists */ + char *whichdb; /* Which database is this entry from? */ + char *otherdb; /* Unrecognized lines from this entry */ + char *otherrc; + char *user; /* Name of originating user */ + unsigned int cddbid; /* CDDB-ID of the current disc */ + struct cdinfo *next; /* For browsers, etc. */ +}; + +/* The global variable "cd" points to the struct for the CD that's playing. */ +extern struct wm_cdinfo *cd; + +extern struct wm_playlist *new_playlist(); + +#define WM_STR_GENVENDOR "Generic" +#define WM_STR_GENMODEL "drive" +#define WM_STR_GENREV "type" + + +/* + * Drive descriptor structure. Used for access to low-level routines. + */ +struct wm_drive_proto +{ + int (*gen_init)(); + int (*gen_close)(); + int (*gen_get_trackcount)(); + int (*gen_get_cdlen)(); + int (*gen_get_trackinfo)(); + int (*gen_get_drive_status)(); + int (*gen_get_volume)(); + int (*gen_set_volume)(); + int (*gen_pause)(); + int (*gen_resume)(); + int (*gen_stop)(); + int (*gen_play)(); + int (*gen_eject)(); + int (*gen_closetray)(); + int (*gen_get_cdtext)(); +}; + +struct wm_drive +{ + int cdda; /* cdda 1, cdin 0 */ + const char *cd_device; + char *soundsystem; + char *sounddevice; + char *ctldevice; + int fd; /* File descriptor, if used by platform */ + int cdda_slave; /* File descriptor for CDDA */ + + char *vendor; /* Vendor name */ + char *model; /* Drive model */ + char *revision; /* Revision of the drive */ + void *aux; /* Pointer to optional platform-specific info */ + void *daux; /* Pointer to optional drive-specific info */ + + struct wm_drive_proto *proto; +}; + +/* + * Structure for information of the usage of cddb. + */ +struct wm_cddb { + int protocol; /* 0-off, 1-cddbp, 2-http, 3-htproxy */ + char cddb_server[84]; /* host.domain.name:port */ + char mail_adress[84]; /* user@domain.name */ + char path_to_cgi[84]; /* (/)path/to/cddb.cgi */ + char proxy_server[84]; /* host.domain.name:port */ +}; +extern struct wm_cddb cddb; + + +/* + * Each platform has to define generic functions, so may as well declare + * them all here to save space. + * These functions should never be seen outside libworkman. So I don't care + * about the wm_ naming convention here. + */ +int gen_init( struct wm_drive *d ); +int gen_close( struct wm_drive *d ); +int gen_get_trackcount(struct wm_drive *d, int *tracks); +int gen_get_cdlen(struct wm_drive *d, int *frames); +int gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe); +int gen_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *ind ); +int gen_set_volume( struct wm_drive *d, int left, int right ); +int gen_get_volume( struct wm_drive *d, int *left, int *right ); +int gen_pause(struct wm_drive *d); +int gen_resume(struct wm_drive *d); +int gen_stop(struct wm_drive *d); +int gen_play(struct wm_drive *d, int start, int end, int realstart); +int gen_eject(struct wm_drive *d); +int gen_closetray(struct wm_drive *d); +int gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght); + +int find_drive_struct(const char *vendor, const char *model, const char *rev); + + +struct cdtext_info* get_glob_cdtext(struct wm_drive*, int); +void free_cdtext(void); +const char* gen_status(int); + +#endif /* WM_STRUCT_H */ diff --git a/kscd/libwm/include/wm_version.h b/kscd/libwm/include/wm_version.h new file mode 100644 index 00000000..82e500c5 --- /dev/null +++ b/kscd/libwm/include/wm_version.h @@ -0,0 +1,35 @@ +#ifndef WM_VERSION_H +#define WM_VERSION_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Version information + * + */ + +#define WM_LIBVER_MAJOR 1 +#define WM_LIBVER_MINOR 4 +#define WM_LIBVER_PL 3 +#define WM_LIBVER_NAME "LibWorkMan" + +#endif /* WM_VERSION_H */ diff --git a/kscd/libwm/include/workman.h b/kscd/libwm/include/workman.h new file mode 100644 index 00000000..e7c3807d --- /dev/null +++ b/kscd/libwm/include/workman.h @@ -0,0 +1,52 @@ +#ifndef WORKMAN_H +#define WORKMAN_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * all-in-one libworkman include file. + * + */ + + +/* + * wm_config should always be included first + */ +#include "wm_config.h" + +#include "workman_defs.h" +#ifdef BUILD_CDDA +#include "wm_cdda.h" +#endif +#include "wm_cddb.h" +#include "wm_cdinfo.h" +#include "wm_cdrom.h" +#include "wm_database.h" +#include "wm_helpers.h" +#include "wm_index.h" +#include "wm_platform.h" +#include "wm_scsi.h" +#include "wm_struct.h" +#include "wm_cdtext.h" + +#endif /* WORKMAN_H */ + diff --git a/kscd/libwm/include/workman_defs.h b/kscd/libwm/include/workman_defs.h new file mode 100644 index 00000000..9da47f8f --- /dev/null +++ b/kscd/libwm/include/workman_defs.h @@ -0,0 +1,30 @@ +#ifndef WORKMAN_DEFS_H +#define WORKMAN_DEFS_H +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player program + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * #defined CONSTANTS + * + * Too bad this file seems to be so empty... + * + */ + +#include "wm_version.h" + +#endif /* WORKMAN_DEFS_H */ diff --git a/kscd/libwm/index.c b/kscd/libwm/index.c new file mode 100644 index 00000000..e15312b7 --- /dev/null +++ b/kscd/libwm/index.c @@ -0,0 +1,383 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Maintain an external index file for a WorkMan CD database. + * Uses the Berkeley libdb library, available from ftp.cs.berkeley.edu. + */ + +#ifdef LIBDB + +#include <stdio.h> +#include <stdlib.h> +#include <db.h> +#include <fcntl.h> +#include <string.h> +#include <netinet/in.h> /* for htonl() */ +#include "include/wm_config.h" +#include "include/wm_index.h" + +extern int suppress_locking; + +/* + * idx_find_entry() + * + * Find an entry in the index file. + * + * Input: + * file Name of database file (text version). + * ntracks Number of tracks on the CD we're looking for. + * tracks Array of track start times. + * len CD length in frames. + * fuzz Fuzz factor (tolerance value). + * pos Pointer to return value. + * + * Output: + * 1 No matching record found. + * 0 Record found; *pos contains offset into text file. + * -1 Index file out of date or inaccessible, or another error. + */ +int +idx_find_entry( char *file, int ntracks, int *tracks, + int len, int fuzz, unsigned long *pos ) +{ + unsigned long dbpos; + char *indexname = NULL, keyval[8]; + int c; + FILE *text; + DB *index; + DBT key, data; + BTREEINFO bti; + + /* + * First, see if the text file is accessible. Lock it if so. + */ + text = fopen(file, "r"); + if (text == NULL) + return (-1); + if ((c = getc(text)) == EOF) + { + fclose(text); + return (-1); + } + if (! suppress_locking) + if (lockit(fileno(text), F_RDLCK)) + { + fclose(text); + return (-1); + } + + /* + * Open the index file. + */ + indexname = malloc(strlen(file) + sizeof(".ind")); + if (indexname == NULL) + { + fclose(text); + return (-1); + } + strcpy(indexname, file); + strcat(indexname, ".ind"); + bti.flags = 0; + bti.cachesize = 0; + bti.minkeypage = bti.maxkeypage = 0; + bti.psize = bti.lorder = 0; + bti.compare = NULL; + bti.prefix = NULL; + index = dbopen(indexname, O_RDONLY, 0666, DB_BTREE, &bti); + free(indexname); + if (index == NULL) + { + fclose(text); + return (-1); + } + + /* + * Search for the first matching entry. + */ + sprintf(keyval, "%07d", tracks[ntracks - 1] - fuzz); + key.data = keyval; + key.size = 7; + if (c = (index->seq)(index, &key, &data, R_CURSOR)) + { + (index->close)(index); + fclose(text); + return (c); + } + + /* + * Now loop through all the possible matches, collecting them into + * memory. + */ + do { + char tracksline[750], *s; + int i, val; + + /* Hit the end of the valid entries? */ + sscanf(key.data, "%d", &val); + if (val > tracks[ntracks - 1] + fuzz) + break; + + dbpos = ntohl(*((unsigned long *) data.data)); + if (fseek(text, dbpos, 0)) + break; + + fgets(tracksline, sizeof(tracksline), text); + if (strncmp(tracksline, "tracks ", 7)) + break; + (void) strtok(tracksline, " \t"); + + /* Got a valid tracks line. See if it matches the CD. */ + s = strtok(NULL, " \t"); + if (s == NULL) + break; + if (atoi(s) != ntracks) + continue; + + for (i = 0; i < ntracks; i++) + { + s = strtok(NULL, " \t"); + if (s == NULL) + break; + val = atoi(s); + if (val + fuzz < tracks[i] || val - fuzz > tracks[i]) + break; + } + if (i != ntracks) + continue; + + s = strtok(NULL, " \t"); + if (s == NULL) + continue; + val = atoi(s); + if (val + fuzz / 75 < len / 75 || val + fuzz / 75 > len / 75) + continue; + + /* XXX - add to sorted list! */ + *pos = dbpos; + (index->close)(index); + fclose(text); + return (0); + } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0); + + if (c == 0) + { + /* An error. */ + (index->close)(index); + fclose(text); + return (-1); + } + + (index->close)(index); + fclose(text); + return (1); +} + +/* + * idx_delete_entry() + * + * Delete an entry from the index file. + * + * Input: + * file Name of database file (text version). + * track Last track's start time (database key). + * fuzz Fuzz factor (tolerance value). + * pos Position of CD in database file. + * + * Output: + * 1 No matching record found. + * 0 Record deleted. + * -1 Index file out of date or inaccessible, or another error. + * + * Note: it is the caller's responsibility to do locking, as it's assumed + * that this operation will accompany a modification of the main database + * file and will need to be atomic with that modification. + */ +int +idx_delete_entry(char *file, int track, int fuzz, unsigned long pos ) +{ + unsigned long dbpos; + char *indexname = NULL, keyval[8]; + int c, status; + DB *index; + DBT key, data; + BTREEINFO bti; + + /* + * Open the index file. + */ + indexname = malloc(strlen(file) + sizeof(".ind")); + if (indexname == NULL) + return (-1); + + strcpy(indexname, file); + strcat(indexname, ".ind"); + + bti.flags = 0; + bti.cachesize = 0; + bti.minkeypage = bti.maxkeypage = 0; + bti.psize = bti.lorder = 0; + bti.compare = NULL; + bti.prefix = NULL; + index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti); + free(indexname); + if (index == NULL) + return (-1); + + /* + * Search for the first matching entry. + */ + sprintf(keyval, "%07d", track - fuzz); + key.data = keyval; + key.size = 7; + if (c = (index->seq)(index, &key, &data, R_CURSOR)) + { + /* + * Nothing matched! + */ + (index->close)(index); + return (c); + } + + /* + * Look for the entry the user wants to delete. + */ + do { + int val; + + /* Hit the end of the valid entries? */ + sscanf(key.data, "%d", &val); + if (val > track + fuzz) + break; + + /* Is this the entry we want? */ + if (pos == ntohl(*((unsigned long *) data.data))) + { + /* + * Yep! Delete it. + */ + status = (index->del)(index, &key, R_CURSOR); + (index->close)(index); + return (status); + } + } while ((c = (index->seq)(index, &key, &data, R_NEXT)) == 0); + + if (c == 0) + { + /* An error. */ + (index->close)(index); + return (-1); + } + + (index->close)(index); + return (1); +} + +/* + * idx_write_entry() + * + * Write out an index file entry. + * + * Input: + * file Name of database file (text version). + * track Start time of last track (database key). + * pos Position of entry in text file. + * + * Output: + * 0 Record written. + * -1 Index file inaccessible, or another error. + * + * Note: it is the caller's responsibility to do locking, as it's assumed + * that this operation will accompany a modification of the main database + * file and will need to be atomic with that modification. + */ +int +idx_write_entry( char *file, int track, unsigned long pos ) +{ + char *indexname, keyval[8]; + int status; + DB *index; + DBT key, data; + BTREEINFO bti; + + /* + * Open the index file. + */ + indexname = malloc(strlen(file) + sizeof(".ind")); + if (indexname == NULL) + return (-1); + + strcpy(indexname, file); + strcat(indexname, ".ind"); + + bti.flags = R_DUP; + bti.cachesize = 0; + bti.minkeypage = 0; + bti.maxkeypage = 0; + bti.psize = 0; + bti.lorder = 4321; /* network byte order */ + bti.compare = NULL; + bti.prefix = NULL; + index = dbopen(indexname, O_RDWR, 0666, DB_BTREE, &bti); + free(indexname); + if (index == NULL) + return (-1); + + /* + * Create a new key and value. + */ + pos = htonl(pos); + data.data = &pos; + data.size = sizeof(pos); + key.data = keyval; + key.size = 7; + + sprintf(keyval, "%07d", track); + + status = (index->put)(index, &key, &data, 0); + + (index->close)(index); + return (status); +} + +#else /* LIBDB */ + +int +idx_find_entry() +{ + return (1); /* no record found; text file will be searched. */ +} + +int +idx_delete_entry() +{ + return (0); +} + +int +idx_write_entry() +{ + return (0); +} + +#endif /* LIBDB */ diff --git a/kscd/libwm/plat_aix.c b/kscd/libwm/plat_aix.c new file mode 100644 index 00000000..93b596a1 --- /dev/null +++ b/kscd/libwm/plat_aix.c @@ -0,0 +1,489 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * plat_aix - AIX 4.x IDE and SCSI support 16 Dec 1998 + * + * AIX 4.x Port: Erik O'Shaughnessy + * Original AIX IDE Code: Cloyce Spradling (xmcd libdi_d/aixioc.c ) + * + * Taken from the ascd distribution. + * + */ + + +#if defined(AIXV3) || defined(__AIXV3) + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/cdrom.h> +#include <sys/devinfo.h> +#include <sys/scsi.h> +#include <sys/scdisk.h> + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +#define LEADOUT 0xaa + +int min_volume = 128; +int max_volume = 255; + + + +/* NAME: gen_init + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_init(struct wm_drive *d) +{ + return 0; +} /* gen_init() */ + + +/* NAME: wmcd_open + * + * FUNCTION: + * + * RETURNS: + */ +int +wmcd_open(struct wm_drive *d) +{ + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + int fd; + + if( ! d ) + { + errno = EFAULT; + return -1; + } + + if(d->fd > -1) /* device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return 0; + } + + if( d->cd_device == (char *)NULL ) + d->cd_device = DEFAULT_CD_DEVICE; + + if( (fd = openx(d->cd_device,O_RDONLY,NULL,SC_SINGLE)) < 0 ) + { + perror("openx"); + return -6; + /* return 1 */ + } + + find_drive_struct(vendor, model, rev); + + d->fd = fd; + d->init(d); + return 0; +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* NAME: wm_scsi + * + * FUNCTION: + * + * RETURNS: + */ +int +wm_scsi(struct wm_drive *d, + uchar_t *cdb, int cdblen, + void *retbuf, int retbuflen, + int getreply) +{ + return 0; +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/* NAME: gen_get_drive_status + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_get_drive_status(struct wm_drive *d, + int oldmode, + int *mode, + int *pos, + int *track, + int *index) +{ + struct cd_audio_cmd cmd; + + *mode = WM_CDM_EJECTED; + + if(d->fd < 0) + switch( wmcd_open(d) ) + { + case -1: + return -1; + case 1: + return 0; + } + + cmd.audio_cmds = CD_INFO_AUDIO; + + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + return -1; + + switch(cmd.status) + { + case CD_PLAY_AUDIO: + *mode = WM_CDM_PLAYING; + *track = cmd.indexing.info_audio.current_track; + *index = cmd.indexing.info_audio.current_index; + *pos = cmd.indexing.info_audio.current_mins * 60 * 75 + + cmd.indexing.info_audio.current_secs * 75 + + cmd.indexing.info_audio.current_frames; + break; + + case CD_PAUSE_AUDIO: + *mode = WM_CDM_PAUSED; + *track = cmd.indexing.info_audio.current_track; + *index = cmd.indexing.info_audio.current_index; + *pos = cmd.indexing.info_audio.current_mins * 60 * 75 + + cmd.indexing.info_audio.current_secs * 75 + + cmd.indexing.info_audio.current_frames; + + break; + case CD_NO_AUDIO: /* no play audio in progress */ + case CD_COMPLETED: /* play operation completed successfully */ + case CD_STATUS_ERROR: /* invalid status or play stopped due to err */ + case CD_NOT_VALID: /* audio status is invalid or not supported */ + *mode = WM_CDM_STOPPED; + break; + default: + *mode = WM_CDM_UNKNOWN; + break; + } + + return 0; +} /* gen_get_drive_status() */ + + + +/* NAME: gen_get_trackcount + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_get_trackcount(struct wm_drive *d,int *tracks) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_TRK_INFO_AUDIO; + cmd.msf_flag = 0; + + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + { + perror("DKAUDIO"); + return -1; + } + + *tracks = cmd.indexing.track_index.last_track; + + return 0; +} /* gen_get_trackcount() */ + +/* NAME: gen_get_trackinfo + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_GET_TRK_MSF; + cmd.msf_flag = 1; + + cmd.indexing.track_msf.track = track; + + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + return -1; + + *startframe = cmd.indexing.track_msf.mins * 60 * 75 + + cmd.indexing.track_msf.secs * 75 + + cmd.indexing.track_msf.frames; + + *data = 0; + + return 0; +} /* gen_get_trackinfo() */ + +/* NAME: gen_get_cdlen + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_get_cdlen(struct wm_drive *d,int *frames) +{ + int tmp; + + return gen_get_trackinfo(d,LEADOUT,&tmp,frames); +} /* gen_get_cdlen() */ + + +/* NAME: gen_play + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_play(struct wm_drive *d,int start,int end) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_PLAY_AUDIO; + cmd.msf_flag = 1; + + cmd.indexing.msf.first_mins = start / (60*75); + cmd.indexing.msf.first_secs = (start % (60*75)) / 75; + cmd.indexing.msf.first_frames = start % 75; + + cmd.indexing.msf.last_mins = end / (60*75); + cmd.indexing.msf.last_secs = (end % (60*75)) / 75; + cmd.indexing.msf.last_frames = end % 75; + + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + { + perror("DKAUDIO:CD_PLAY_AUDIO"); + return -1; + } + return 0; +} /* gen_play() */ + +/* NAME: gen_pause + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_pause(struct wm_drive *d) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_PAUSE_AUDIO; + + return ioctl(d->fd,DKAUDIO,&cmd); +} /* gen_pause() */ + + +/* NAME: gen_resume + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_resume(struct wm_drive *d) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_RESUME_AUDIO; + return ioctl(d->fd,DKAUDIO,&cmd); +} /* gen_resume() */ + +/* NAME: gen_stop + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_stop(struct wm_drive *d) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_STOP_AUDIO; + return ioctl(d->fd,DKAUDIO,&cmd); +} /* gen_stop() */ + +/* NAME: gen_eject + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_eject(struct wm_drive *d) +{ + return ioctl(d->fd,DKEJECT,NULL); +} + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +int +scale_volume(int vol,int max) +{ + return ((vol * (max_volume - min_volume)) / max + min_volume); +} + +int +unscale_volume(int vol,int max) +{ + int n; + n = ( vol - min_volume ) * max_volume / (max - min_volume); + return (n <0)?0:n; +} + +/* NAME: gen_set_volume + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_set_volume(struct wm_drive *d,int left,int right) +{ + struct cd_audio_cmd cmd; + + cmd.audio_cmds = CD_SET_VOLUME; + cmd.volume_type = CD_VOLUME_CHNLS; + + cmd.out_port_0_vol = scale_volume(left,100); + cmd.out_port_1_vol = scale_volume(right,100); + + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + { + perror("CD_SET_VOLUME"); + return -1; + } + + return 0; +} /* gen_set_volume() */ + +/* NAME: gen_get_volume + * + * FUNCTION: + * + * RETURNS: + */ +int +gen_get_volume(struct wm_drive *d,int *left,int *right) +{ + struct cd_audio_cmd cmd; + int l,r; + + fprintf(stderr,"gen_get_volume\n"); + + cmd.audio_cmds = CD_INFO_AUDIO; + if( ioctl(d->fd,DKAUDIO,&cmd) < 0) + return -1; + + *left = unscale_volume(cmd.out_port_0_vol,100); + *right = unscale_volume(cmd.out_port_1_vol,100); + + return 0; +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + + +#endif /* _AIX */ + + + + + diff --git a/kscd/libwm/plat_bsd386.c b/kscd/libwm/plat_bsd386.c new file mode 100644 index 00000000..492d394c --- /dev/null +++ b/kscd/libwm/plat_bsd386.c @@ -0,0 +1,510 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * BSD/386-specific drive control routines. + */ + +static char plat_bsd386_id[] = "$Id$"; + +#if defined(__bsdi__) || defined(__bsdi) + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include "include/wm_config.h" + +/* + * The following is included from the Linux module. However, I didn't + * see a check here if the CD to be ejected is mounted... + */ +#if defined(BSD_MOUNTTEST) + #include <mntent.h> +#endif + + +#include <sys/time.h> +#include <string.h> +#include <cdrom.h> +#ifdef SOUNDBLASTER +# include <sys/soundcard.h> +#endif + +#include "include/wm_struct.h" +#include "include/wm_helpers.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + + +/* + * Since we can't sense the drive type with libcdrom anyway, and since the + * library doesn't provide "pause" or "resume" functions, use the daux field + * to point to the frame number at which we paused. + */ +struct pause_info +{ + int frame; + int endframe; +}; + +#define PAUSE_FRAME (((struct pause_info *) d->daux)->frame) +#define END_FRAME (((struct pause_info *) d->daux)->endframe) +#define CUR_CD ((struct cdinfo *) d->aux) + +void *malloc(); + +#ifdef SOUNDBLASTER + int min_volume = 0; + int max_volume = 100; + int min_volume_drive = 10; /* Toshiba drive does low values. */ + int max_volume_drive = 255; +#else + int min_volume = 10; + int max_volume = 255; +#endif + +/*--------------------------------------------------------* + * Initialize the drive. A no-op for the generic driver. + *--------------------------------------------------------*/ +int +gen_init(struct wm_drive *d) +{ + return (0); +} /* gen_init() */ + +/*-----------------------------------------------------------------------* + * Open the CD device. We can't determine the drive type under BSD/386. + *-----------------------------------------------------------------------*/ +int +wmcd_open(struct wm_drvie *d) +{ + void *aux = NULL, *daux = NULL; + int fd = -1; + + if (d->aux) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (aux=%d)]\n", d->aux); + return (0); + } + + if ((aux = cdopen(d->cd_device)) == NULL) + { + fprintf(stderr, "No cdrom found by libcdrom\n"); + return (-6); + } + + if ((daux = malloc(sizeof(struct pause_info))) == NULL) + return (-1); + +#ifdef SOUNDBLASTER + fd = open("/dev/mixer", O_RDWR, 0); +#endif + + /* Now fill in the relevant parts of the wm_drive structure. */ + find_drive_struct("", "", ""); + d->aux = aux; + d->daux = daux; + d->fd = fd; + PAUSE_FRAME = 0; + END_FRAME = 0; + + (d->init)(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + int tries = 0; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + tries++; + } while ( (status != 0) && (tries < 10) ); + return status; +} /* wmcd_reopen() */ + + +/*---------------------------------------------* + * Send an arbitrary SCSI command to a device. + *---------------------------------------------*/ +int +wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply) +{ + /* Don't know how to do SCSI passthrough... */ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); /* close mixer if open */ + d->fd = -1; + cdclose( d->aux ); /* close the cdrom drive! */ + d->aux = NULL; + free(d->daux); + d->daux = NULL; + } + return 0; +} + +/*--------------------------------------------------------------------------* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + *--------------------------------------------------------------------------*/ +#define DOPOS \ + *pos = status.abs_frame; \ +*track = status.track_num; \ +*index = status.index_num + +int +gen_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct cdstatus status; + extern enum wm_cd_modes cur_cdmode; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + /* Is the device open? */ + if (d->aux == NULL) + { + switch (wmcd_open(d)) + { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + if (cdstatus (CUR_CD, &status) < 0) + { + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + return (0); + } + + switch (status.state) + { + case cdstate_playing: + *mode = WM_CDM_PLAYING; + DOPOS; + break; + + case cdstate_stopped: + /* the MITSUMI drive doesn't have a "paused" state, + so it always comes here and not to the paused section. + The PAUSE_FRAME stuff below (in gen_pause()) + fakes out the paused state. */ + if (oldmode == WM_CDM_PLAYING) + { + *mode = WM_CDM_TRACK_DONE; + break; + } else if (cur_cdmode != WM_CDM_PAUSED) { + *mode = WM_CDM_STOPPED; + DOPOS; + break; + } + /* fall through if paused */ + + case cdstate_paused: + /* the SCSI2 code in the cdrom library only pauses with + cdstop(); it never truly stops a disc (until an in-progress + play reaches the end). So it always comes here. */ + if (cur_cdmode == WM_CDM_STOPPED) + { + *mode = WM_CDM_STOPPED; + DOPOS; + break; + } + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + DOPOS; + } else { + *mode = WM_CDM_STOPPED; + DOPOS; + } + break; + + default: + *mode = WM_CDM_STOPPED; + } + + return (0); +} /* gen_get_drive_status() */ + + +/*-------------------------------------* + * Get the number of tracks on the CD. + *-------------------------------------*/ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + *tracks = CUR_CD->ntracks; + + return (0); +} /* gen_get_trackcount() */ + +/*---------------------------------------------------------* + * Get the start time and mode (data or audio) of a track. + *---------------------------------------------------------*/ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + *data = (CUR_CD->tracks[track - 1].control & 4) ? 1 : 0; + *startframe = CUR_CD->tracks[track - 1].start_frame; + + return (0); +} /* gen_get_trackinfo() */ + +/*-------------------------------------* + * Get the number of frames on the CD. + *-------------------------------------*/ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + *frames = CUR_CD->total_frames; + + return (0); +} /* gen_get_cdlen() */ + +/*------------------------------------------------------------* + * Play the CD from one position to another (both in frames.) + *------------------------------------------------------------*/ +int +gen_play(struct wm_drive *d, int start, int end) +{ + END_FRAME = end; + if (cdplay(d->aux, start, end) < 0) + return (-1); + else + return (0); +} /* gen_play() */ + +/*--------------------------------------------------------------------* + * Pause the CD. This is a bit of a trick since there's no cdpause() + * function in the library. We fake it by saving the frame number + * and stopping. + *--------------------------------------------------------------------*/ +int +gen_pause(struct wm_drive *d) +{ + struct cdstatus status; + + if (cdstatus(d->aux, &status) < 0) + return (-1); + if (status.state != cdstate_playing) + PAUSE_FRAME = CUR_CD->tracks[0].start_frame; + else + PAUSE_FRAME = status.abs_frame; + if (cdstop(d->aux) < 0) + return (-1); + + return (0); +} /* gen_pause() */ + +/*-------------------------------------------------* + * Resume playing the CD (assuming it was paused.) + *-------------------------------------------------*/ +int +gen_resume(struct wm_drive *d) +{ + int status; + + status = (d->play)(d, PAUSE_FRAME, END_FRAME); + PAUSE_FRAME = 0; + return (status); +} /* gen_resume() */ + +/*--------------* + * Stop the CD. + *--------------*/ +int +gen_stop(struct wm_drive *d) +{ + return cdstop(d->aux); +} /* gen_stop() */ + +/*----------------------------------------* + * Eject the current CD, if there is one. + *----------------------------------------*/ +int +gen_eject(struct wm_drive *d) +{ + cdeject(d->aux); + cdclose(d->aux); + d->aux = NULL; + free(d->daux); + d->daux = NULL; + + if (d->fd >= 0) + close(d->fd); /* close mixer */ + d->fd = -1; + return (0); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if (!cdload(d->aux)) + return(0); + return(-1); +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +/*------------------------------------------------------------------------* + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + *------------------------------------------------------------------------*/ +static int +scale_volume(int vol, int max) +{ + /* on Toshiba XM-3401B drive, and on soundblaster, this works fine. */ + return ((vol * (max_volume - min_volume)) / max + min_volume); +} /* scale_volume() */ + +/*---------------------------------------------------------------------------* + * unscale_volume(cd_vol, max) + * + * Given a value between min_volume and max_volume, return the volume slider + * value needed to achieve that value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + *--------------------------------------------------------------------------*/ +static int +unscale_volume(int cd_vol, int max) +{ + int vol = 0, top = max, bot = 0, scaled; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol == scaled) + break; + if (cd_vol < scaled) + top = vol - 1; + else + bot = vol + 1; + } + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} /* unscale_volume() */ + +/*---------------------------------------------------------------------* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + *---------------------------------------------------------------------*/ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + int level; + + left = scale_volume(left, 100); + right = scale_volume(right, 100); + level = right << 8 | left; + + /* Send a Mixer IOCTL */ + if (d->fd >= 0) + (void) ioctl(d->fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &level); +#ifdef notnow + /* NOTE: the cdvolume2() call is an addition to the cdrom library. + Pick it up from the archives on bsdi.com */ + cdvolume2 (CUR_CD, left < 0 ? 0 : left > 255 ? 255 : left, + right < 0 ? 0 : right > 255 ? 255 : right); + +#endif + return (0); +} + +/*---------------------------------------------------------------------* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + *---------------------------------------------------------------------*/ +int +gen_get_volume(struct wm_drive *d, int *left, int *right) +{ + int level; + + /* Most systems can't seem to do this... */ + *left = *right = -1; + + /* Send a Mixer IOCTL */ + if (d->fd >= 0) { + if (ioctl(d->fd, MIXER_READ(SOUND_MIXER_VOLUME), &level) == 0) { + *left = unscale_volume((level & 0xff) & 0xff, 100); + *right = unscale_volume((level >> 8) & 0xff, 100); + } + } + return (0); +} + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + +#endif /* __bsdi__ */ diff --git a/kscd/libwm/plat_freebsd.c b/kscd/libwm/plat_freebsd.c new file mode 100644 index 00000000..b1028a75 --- /dev/null +++ b/kscd/libwm/plat_freebsd.c @@ -0,0 +1,560 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * plat_freebsd.c + * + * FreeBSD-specific drive control routines. + * + * Todd Pfaff, 3/20/94 + * + */ + +#if defined(__FreeBSD__) || defined(__FreeBSD) || defined(__NetBSD__) || defined (__NetBSD) || defined(__DragonFly__) + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> + +#include "include/wm_config.h" + +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/cdio.h> + +#if defined(__OpenBSD__) +# define MSF_MINUTES 1 +# define MSF_SECONDS 2 +# define MSF_FRAMES 3 +# include <sys/scsiio.h> +# include "/sys/scsi/scsi_all.h" +# include "/sys/scsi/scsi_cd.h" +#elif defined(__NetBSD__) +#include <sys/scsiio.h> +#include <dev/scsipi/scsipi_cd.h> +#else +# define LEFT_PORT 0 +# define RIGHT_PORT 1 +# if defined(__FreeBSD_version) && __FreeBSD_version < 300000 +# include <scsi.h> +# endif +#endif + +#include "include/wm_struct.h" +#include "include/wm_platform.h" +#include "include/wm_cdrom.h" +#include "include/wm_scsi.h" +#include "include/wm_helpers.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +void *malloc(); + +int min_volume = 10; +int max_volume = 255; + + +/*--------------------------------------------------------* + * Initialize the drive. A no-op for the generic driver. + *--------------------------------------------------------*/ +int +gen_init(struct wm_drive *d) +{ + return (0); +} /* gen_init() */ + + +/*-------------------------------------------------------------------* + * Open the CD device and figure out what kind of drive is attached. + *-------------------------------------------------------------------*/ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + static int warned = 0; + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + d->fd = open(d->cd_device, 0); + if (d->fd < 0) + { + if (errno == EACCES) + { + return -EACCES; + } + + /* No CD in drive. */ + return (1); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + fd = d->fd; + + find_drive_struct(vendor, model, rev); + + /*(d->init)(d); */ + + d->fd = fd; + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + if(status == -EACCES || status == 1) + return status; + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/*---------------------------------------------* + * Send an arbitrary SCSI command to a device. + * + *---------------------------------------------*/ +int +wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply) +{ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/*--------------------------------------------------------------------------* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + *--------------------------------------------------------------------------*/ +int +gen_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct ioc_read_subchannel sc; + struct cd_sub_channel_info scd; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + sc.address_format = CD_MSF_FORMAT; + sc.data_format = CD_CURRENT_POSITION; + sc.track = 0; + sc.data_len = sizeof(scd); + sc.data = (struct cd_sub_channel_info *)&scd; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) + { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc)) + { + /* + * #ifdef __NetBSD__ + * + * Denis Bourez <denis@rsn.fdn.fr> told me, that closing the + * device is mandatory for FreeBSD, too. + */ + /* we need to release the device so the kernel will notice + reloaded media */ + (void) close(d->fd); + d->fd = -1; + /* + * #endif + */ + return (0); /* ejected */ + } + + switch (scd.header.audio_status) + { + case CD_AS_PLAY_IN_PROGRESS: + *mode = WM_CDM_PLAYING; + dopos: + *pos = scd.what.position.absaddr.msf.minute * 60 * 75 + + scd.what.position.absaddr.msf.second * 75 + + scd.what.position.absaddr.msf.frame; + *track = scd.what.position.track_number; + *index = scd.what.position.index_number; + break; + + case CD_AS_PLAY_PAUSED: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + goto dopos; + } + else + *mode = WM_CDM_STOPPED; + break; + + case CD_AS_PLAY_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + case CD_AS_NO_STATUS: + case 0: + *mode = WM_CDM_STOPPED; + break; + } + + return (0); +} /* gen_get_drive_status() */ + + +/*-------------------------------------* + * Get the number of tracks on the CD. + *-------------------------------------*/ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + struct ioc_toc_header hdr; + + if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1) + return (-1); + + *tracks = hdr.ending_track - hdr.starting_track + 1; + + return (0); +} /* gen_get_trackcount() */ + +/*-----------------------------------------------------------------------* + * Get the start time and mode (data or audio) of a track. + * + * XXX - this should get cached, but that means keeping track of ejects. + *-----------------------------------------------------------------------*/ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + struct ioc_read_toc_entry toc; + struct cd_toc_entry toc_buffer; + + bzero((char *)&toc_buffer, sizeof(toc_buffer)); + toc.address_format = CD_MSF_FORMAT; + toc.starting_track = track; + toc.data_len = sizeof(toc_buffer); + toc.data = &toc_buffer; + + if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc)) + return (-1); + + *data = ((toc_buffer.control & 0x4) != 0); + + *startframe = toc_buffer.addr.msf.minute*60*75 + + toc_buffer.addr.msf.second * 75 + + toc_buffer.addr.msf.frame; + + return (0); +} /* gen_get_trackinfo() */ + +/*-------------------------------------* + * Get the number of frames on the CD. + *-------------------------------------*/ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + struct ioc_toc_header hdr; + int status; + +#define LEADOUT 0xaa /* see scsi.c. what a hack! */ + return gen_get_trackinfo(d, LEADOUT, &tmp, frames); +} /* gen_get_cdlen() */ + + +/*------------------------------------------------------------* + * Play the CD from one position to another (both in frames.) + *------------------------------------------------------------*/ +int +gen_play(struct wm_drive *d, int start, int end, int realstart) +{ + struct ioc_play_msf msf; + + msf.start_m = start / (60*75); + msf.start_s = (start % (60*75)) / 75; + msf.start_f = start % 75; + msf.end_m = end / (60*75); + msf.end_s = (end % (60*75)) / 75; + msf.end_f = end % 75; + + if (ioctl(d->fd, CDIOCSTART)) + return (-1); + + if (ioctl(d->fd, CDIOCPLAYMSF, &msf)) + return (-2); + + return (0); +} /* gen_play() */ + +/*---------------* + * Pause the CD. + *---------------*/ +int +gen_pause( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDIOCPAUSE)); +} /* gen_pause() */ + +/*-------------------------------------------------* + * Resume playing the CD (assuming it was paused.) + *-------------------------------------------------*/ +int +gen_resume( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDIOCRESUME)); +} /* gen_resume() */ + +/*--------------* + * Stop the CD. + *--------------*/ +int +gen_stop( struct wm_drive *d) +{ + return (ioctl(d->fd, CDIOCSTOP)); +} /* gen_stop() */ + +/*----------------------------------------* + * Eject the current CD, if there is one. + *----------------------------------------*/ +int +gen_eject( struct wm_drive *d ) +{ + /* On some systems, we can check to see if the CD is mounted. */ + struct stat stbuf; + struct statfs buf; + int rval; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (fstatfs(stbuf.st_rdev, &buf) == 0) + return (-3); + + rval = ioctl(d->fd, CDIOCALLOW); + + if (rval == 0) + rval = ioctl(d->fd, CDIOCEJECT); + + if (rval == 0) + rval = ioctl(d->fd, CDIOCPREVENT); + + (void) close(d->fd); + + return rval; +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +/*---------------------------------------------------------------------------* + * scale_volume(vol, max) + * + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + * + * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack + * increases much faster toward the top end of the volume scale than it + * does at the bottom. To make up for this, we make the volume scale look + * sort of logarithmic (actually an upside-down inverse square curve) so + * that the volume value passed to the drive changes less and less as you + * approach the maximum slider setting. The actual formula looks like + * + * (max^2 - (max - vol)^2) * (max_volume - min_volume) + * v = --------------------------------------------------- + min_volume + * max^2 + * + * If your system's volume settings aren't broken in this way, something + * like the following should work: + * + * return ((vol * (max_volume - min_volume)) / max + min_volume); + *---------------------------------------------------------------------------*/ +static int +scale_volume(int vol, int max) +{ + return ((vol * (max_volume - min_volume)) / max + min_volume); +} /* scale_volume() */ + +/*---------------------------------------------------------------------------* + * unscale_volume(cd_vol, max) + * + * Given a value between min_volume and max_volume, return the volume slider + * value needed to achieve that value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + *--------------------------------------------------------------------------*/ +static int +unscale_volume( int cd_vol, int max ) +{ + int vol = 0, top = max, bot = 0, scaled; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol == scaled) + break; + if (cd_vol < scaled) + top = vol - 1; + else + bot = vol + 1; + } + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} /* unscale_volume() */ + +/*---------------------------------------------------------------------* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + *---------------------------------------------------------------------*/ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + struct ioc_vol vol; + + if (left < 0) /* don't laugh, I saw this happen once! */ + left = 0; + if (right < 0) + right = 0; + left = scale_volume(left, 100); + right = scale_volume(right, 100); + + bzero((char *)&vol, sizeof(vol)); + + vol.vol[LEFT_PORT] = left; + vol.vol[RIGHT_PORT] = right; + + if (ioctl(d->fd, CDIOCSETVOL, &vol)) + return (-1); + + return (0); +} /* gen_set_volume() */ + +/*---------------------------------------------------------------------* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + *---------------------------------------------------------------------*/ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + struct ioc_vol vol; + + if (d->fd >= 0) + { + bzero((char *)&vol, sizeof(vol)); + + if (ioctl(d->fd, CDIOCGETVOL, &vol)) + *left = *right = -1; + else + { + *left = unscale_volume(vol.vol[LEFT_PORT], 100); + *right = unscale_volume(vol.vol[RIGHT_PORT], 100); + } + } else { + *left = *right = -1; + } + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + +#endif diff --git a/kscd/libwm/plat_hpux.c b/kscd/libwm/plat_hpux.c new file mode 100644 index 00000000..53aaee40 --- /dev/null +++ b/kscd/libwm/plat_hpux.c @@ -0,0 +1,358 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * HP/UX-specific drive control routines. + */ + +#if defined(hpux) || defined(__hpux) + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <ustat.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include "include/wm_config.h" + +/* + * this is for glibc 2.x which the ust structure in + * ustat.h not stat.h + */ +#ifdef __GLIBC__ +#include <sys/ustat.h> +#endif + +#include <sys/time.h> +#include <sys/scsi.h> + +#include "include/wm_struct.h" +#include "include/wm_helpers.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +void *malloc(); +char *strchr(); + +int min_volume = 0; +int max_volume = 255; + +/*--------------------------------------------------------* + * Initialize the drive. A no-op for the generic driver. + *--------------------------------------------------------*/ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + + +/*-------------------------------------------------------------* + * Open the CD and figure out which kind of drive is attached. + *-------------------------------------------------------------*/ +int +wmcd_open( struct wm_drive *d ) +{ + int fd, flag = 1; + static int warned = 0; + /* unsigned ? */ + char vendor[32], model[32], rev[32]; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + d->fd = open(d->cd_device, O_RDWR); + if (d->fd < 0) + { + if (errno == EACCES) + { + return -EACCES; + } + else if (errno != EINTR) + { + return (-6); + } + + /* No CD in drive. */ + return (1); + } + + /* Prepare the device to receive raw SCSI commands. */ + if (ioctl(d->fd, SIOC_CMD_MODE, &flag) < 0) + { + fprintf(stderr, "%s: SIOC_CMD_MODE: true: %s\n", + d->cd_device, strerror(errno)); + /*exit(1);*/ + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + fd = d->fd; + + /* Default drive is the HP one, which might not respond to INQUIRY */ + strcpy(&vendor, "TOSHIBA"); + strcpy(&model, "XM-3301"); + rev[0] = '\0'; + wm_scsi_get_drive_type(d, vendor, model, rev); + find_drive_struct(vendor, model, rev); + + d->fd = fd; + + (d->init)(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/*----------------------------------* + * Send a SCSI command out the bus. + *----------------------------------*/ +int +wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply ) +{ +#ifdef SIOC_IO + struct sctl_io cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cdb_length = cdblen; + cmd.data = retbuf; + cmd.data_length = retbuflen; + cmd.max_msecs = 1000; + cmd.flags = getreply ? SCTL_READ : 0; + memcpy(cmd.cdb, cdb, cdblen); + + return (ioctl(d->fd, SIOC_IO, &cmd)); +#else + /* this code, for pre-9.0, is BROKEN! ugh. */ + char reply_buf[12]; + struct scsi_cmd_parms cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.clock_ticks = 500; + cmd.cmd_mode = 1; + cmd.cmd_type = cdblen; + memcpy(cmd.command, cdb, cdblen); + if (ioctl(d->fd, SIOC_SET_CMD, &cmd) < 0) + return (-1); + + if (! retbuf || ! retbuflen) + (void) read(d->fd, reply_buf, sizeof(reply_buf)); + else if (getreply) + { + if (read(d->fd, retbuf, retbuflen) < 0) + return (-1); + } + else + if (write(d->fd, retbuf, retbuflen) < 0) + return (-1); + + return (0); +#endif +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/*--------------------------------------------------------------------------* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + *--------------------------------------------------------------------------*/ +int +gen_get_drive_status( struct wm_drive *d, int oldmode, int *mode, + int *pos, int *track, int *index ) +{ + return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index)); +} /* gen_get_drive_status() */ + +/*-------------------------------------* + * Get the number of tracks on the CD. + *-------------------------------------*/ +int +gen_get_trackcount(struct wm_drive *d, int *tracks ) +{ + return (wm_scsi2_get_trackcount(d, tracks)); +} /* gen_get_trackcount() */ + +/*---------------------------------------------------------* + * Get the start time and mode (data or audio) of a track. + *---------------------------------------------------------*/ +int +gen_get_trackinfo( struct wm_drive *d, int *track, int *data, int *startframe) +{ + return (wm_scsi2_get_trackinfo(d, track, data, startframe)); +} /* gen_get_trackinfo() */ + +/*-------------------------------------* + * Get the number of frames on the CD. + *-------------------------------------*/ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + + return (wm_scsi2_get_cdlen(d, frames)); +} /* gen_get_cdlen() */ + +/*------------------------------------------------------------* + * Play the CD from one position to another (both in frames.) + *------------------------------------------------------------*/ +int +gen_play( struct wm_drive *d, int start, int end ) +{ + return (wm_scsi2_play(d, start, end)); +} /* gen_play() */ + +/*---------------* + * Pause the CD. + *---------------*/ +int +gen_pause( struct wm_drive *d ) +{ + return (wm_scsi2_pause(d)); +} /* gen_pause() */ + +/*-------------------------------------------------* + * Resume playing the CD (assuming it was paused.) + *-------------------------------------------------*/ +int +gen_resume( struct wm_drive *d ) +{ + return (wm_scsi2_resume(d)); +} /* gen_resume() */ + +/*--------------* + * Stop the CD. + *--------------*/ +int +gen_stop( struct wm_drive *d ) +{ + return (wm_scsi2_stop(d)); +} /* gen_stop() */ + + +/*----------------------------------------* + * Eject the current CD, if there is one. + *----------------------------------------*/ +int +gen_eject( struct wm_drive *d ) +{ + struct stat stbuf; + struct ustat ust; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); + + return (wm_scsi2_eject(d)); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + return (wm_scsi2_closetray(d)); +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +/*---------------------------------------------------------------------* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + *---------------------------------------------------------------------*/ +int +gen_set_volume( struct wm_drive *d, int left, int right ) +{ + return (wm_scsi2_set_volume(d, left, right)); +} /* gen_set_volume() */ + +/*---------------------------------------------------------------------* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + *---------------------------------------------------------------------*/ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + return (wm_scsi2_get_volume(d, left, right)); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + /* This needs to be tested ! */ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + + +#endif + + diff --git a/kscd/libwm/plat_irix.c b/kscd/libwm/plat_irix.c new file mode 100644 index 00000000..7f1e3fc7 --- /dev/null +++ b/kscd/libwm/plat_irix.c @@ -0,0 +1,474 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * IRIX specific. + * + * Taken from the kscd distribution + * + * Paul Kendall + * paul@orion.co.nz, or + * paul@kcbbs.gen.nz + */ + +#if defined(sgi) || defined(__sgi) + +#include "include/wm_config.h" + +/* + * Yes, it was designed for WorkMan 1.4b3 + * Because I did start over from 1.3a, I disable it here. + * There is no guarantee of getting working code by defining + * CDDA yourself. + * + */ +#undef CDDA +/*#define CDDA*/ + +#include <sys/types.h> +#include <sys/time.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sigfpe.h> +#include <dmedia/cdaudio.h> +#include <dmedia/audio.h> +#include <errno.h> + +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +void *malloc(); +char *strchr(); + +int min_volume = 0; +int max_volume = 255; + +#ifdef CDDA +static int playing = STOPPED; +static CDPLAYER *icd; +static CDPARSER *icdp; +static CDFRAME cdbuf[12]; +static ALport audioport; +static ALconfig aconfig; +static struct itimerval audiotimer = { {0,0}, {0,25000} }; +static int cdtrack=0; +static int cdframe=0; +static int cdstopframe=0; + +/* + * Platform specific internal functions for CDDA + */ +void +cbprognum(void *arg, CDDATATYPES type, CDPROGNUM* prognum) +{ + cdtrack = prognum->value; +} /* cbprognum() */ + +void +cbabstime(void *arg, CDDATATYPES type, struct cdtimecode* atime) +{ + cdframe = CDtctoframe(atime); + if( cdframe == cdstopframe ) + playing = STOPPED; +} /* cbabstime() */ + +void +cbplayaudio(void *arg, CDDATATYPES type, short* audio) +{ + if(playing != PLAYING) return; + ALwritesamps(audioport, audio, CDDA_NUMSAMPLES); +} /* cbplayaudio() */ + +static void +alarmsignal() +{ + int n, i; + if(playing != PLAYING) return; + if( ALgetfilled(audioport) < CDDA_NUMSAMPLES*8 ) + { + /* Only get more samples and play them if we're getting low + * this ensures that the CD stays close to the sound + */ + n = CDreadda(icd, cdbuf, 12); + if( n == 0 ) return; + for( i=0 ; i<12 ; i++ ) + CDparseframe(icdp, &cdbuf[i]); + } + signal(SIGALRM, alarmsignal); + setitimer(ITIMER_REAL, &audiotimer, NULL); +} /* alarmsignal() */ +#endif + +/*--------------------------------------------------------* + * Initialize the drive. A no-op for the generic driver. + *--------------------------------------------------------*/ +int +gen_init( struct wm_drive *d ) +{ +#ifdef CDDA + long Param[4]; + /* Set the audio rate to 44100Hz 16bit 2s-comp stereo */ + aconfig = ALnewconfig(); + ALsetwidth(aconfig, AL_SAMPLE_16); + ALsetsampfmt(aconfig, AL_SAMPFMT_TWOSCOMP); + ALsetchannels(aconfig, 2); + Param[0] = AL_OUTPUT_RATE; Param[1] = AL_RATE_44100; + Param[2] = AL_CHANNEL_MODE; Param[3] = AL_STEREO; + ALsetparams(AL_DEFAULT_DEVICE, Param, 4); + audioport = ALopenport("KDE KSCD Audio", "w", aconfig); + + /* setup cdparser */ + icdp = CDcreateparser(); + CDaddcallback(icdp, cd_audio, (CDCALLBACKFUNC)cbplayaudio, 0); + CDaddcallback(icdp, cd_pnum, (CDCALLBACKFUNC)cbprognum, 0); + CDaddcallback(icdp, cd_atime, (CDCALLBACKFUNC)cbabstime, 0); + + /* Lets handle those floating point exceptions expeditiously. */ + sigfpe_[_UNDERFL].repls = _ZERO; + handle_sigfpes(_ON, _EN_UNDERFL, NULL, _ABORT_ON_ERROR, NULL); +#endif + return 0; +} /* gen_init() */ + +/*-------------------------------------------------------------* + * Open the CD and figure out which kind of drive is attached. + *-------------------------------------------------------------*/ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + CDSTATUS s; + + if (d->fd < 0) /* Device already open? */ + { + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + d->fd = 1; + + /* Now fill in the relevant parts of the wm_drive structure. */ + fd = d->fd; + find_drive_struct("", "", ""); + d->fd = fd; + (d->init)(d); + + d->daux = CDopen(d->cd_device,"r"); + if (d->daux == 0) + { + return (-6); + } +#ifdef CDDA + icd = d->daux; +#endif + } else { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + } + + CDgetstatus(d->daux, &s); + if( s.state==CD_NODISC || s.state==CD_ERROR ) + return 1; + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + + +/*----------------------------------* + * Send a SCSI command out the bus. + *----------------------------------*/ +int +wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen, + char *retbuf, int retbuflen, int getreply) +{ + return -1; +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/*--------------------------------------------------------------------------* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + *--------------------------------------------------------------------------*/ +int +gen_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, + int *index ) +{ +#ifdef CDDA + *mode = playing; + *track = cdtrack; + *pos = cdframe; + *index = 0; +#else + CDSTATUS s; + if( CDgetstatus(d->daux, &s)==0 ) + return -1; + *pos = CDmsftoframe(s.min,s.sec,s.frame); + *track = s.track; + *index = 0; + switch( s.state ) + { + case CD_READY: *mode = WM_CDM_STOPPED; + break; + case CD_STILL: + case CD_PAUSED: *mode = WM_CDM_PAUSED; + break; + case CD_PLAYING: *mode = WM_CDM_PLAYING; + break; + default: *mode = WM_CDM_UNKNOWN; + } +#endif + return 0; +} /* gen_get_drive_status() */ + +/*-------------------------------------* + * Get the number of tracks on the CD. + *-------------------------------------*/ +int +gen_get_trackcount( struct wm_drive *d, int *tracks ) +{ + CDSTATUS s; + if( CDgetstatus(d->daux, &s)==0 ) + return -1; + *tracks = s.last; + return 0; +} /* gen_get_trackcount() */ + +/*---------------------------------------------------------* + * Get the start time and mode (data or audio) of a track. + *---------------------------------------------------------*/ +int +gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe) +{ + CDTRACKINFO i; + int ret = CDgettrackinfo(d->daux, track, &i); + if( ret == 0 ) + return -1; + *data = 0; + *startframe = CDmsftoframe(i.start_min,i.start_sec,i.start_frame); + return 0; +} /* gen_get_trackinfo() */ + +/*-------------------------------------* + * Get the number of frames on the CD. + *-------------------------------------*/ +int +gen_get_cdlen( struct wm_drive *d, int *frames ) +{ + CDSTATUS s; + if( CDgetstatus(d->daux, &s)==0 ) + return -1; + *frames = CDmsftoframe(s.total_min,s.total_sec,s.total_frame); + return 0; +} /* gen_get_cdlen() */ + +/*------------------------------------------------------------* + * Play the CD from one position to another (both in frames.) + *------------------------------------------------------------*/ +int +gen_play( struct wm_drive *d, int start, int end ) +{ +#ifdef CDDA + int m, s, f; + CDframetomsf(start, &m, &s, &f); + CDseek(icd, m, s, f); + cdstopframe = end; + playing = PLAYING; + signal(SIGALRM, alarmsignal); + setitimer(ITIMER_REAL, &audiotimer, NULL); +#else + int m, s, f; + CDframetomsf(start, &m, &s, &f); + CDplayabs(d->daux, m, s, f, 1); +#endif + return 0; +} /* gen_play() */ + +/*---------------* + * Pause the CD. + *---------------*/ +int +gen_pause( struct wm_drive *d ) +{ +#ifdef CDDA + playing = WM_CDM_PAUSED; +#else + CDSTATUS s; + if( CDgetstatus(d->daux, &s)==0 ) + return -1; + if(s.state == CD_PLAYING) + CDtogglepause(d->daux); +#endif + return 0; +} /* gen_pause() */ + +/*-------------------------------------------------* + * Resume playing the CD (assuming it was paused.) + *-------------------------------------------------*/ +int +gen_resume( struct wm_drive *d ) +{ +#ifdef CDDA + playing = WM_CDM_PLAYING; + signal(SIGALRM, alarmsignal); + setitimer(ITIMER_REAL, &audiotimer, NULL); +#else + CDSTATUS s; + if( CDgetstatus(d->daux, &s)==0 ) + return -1; + if(s.state == CD_PAUSED) + CDtogglepause(d->daux); +#endif + return 0; +} /* gen_resume() */ + +/*--------------* + * Stop the CD. + *--------------*/ +int +gen_stop( struct wm_drive *d ) +{ +#ifdef CDDA + playing = WM_CDM_STOPPED; +#else + CDstop(d->daux); +#endif + return 0; +} /* gen_stop() */ + +/*----------------------------------------* + * Eject the current CD, if there is one. + *----------------------------------------*/ +int +gen_eject( struct wm_drive *d ) +{ +#ifdef CDDA + playing = WM_CDM_STOPPED; +#endif + CDeject(d->daux); + return 0; +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@Deathsdoor.com + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + +/*---------------------------------------------------------------------* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + *---------------------------------------------------------------------*/ +int +gen_set_volume( struct wm_drive *d, int left, int right ) +{ + long Param[4]; + Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = left*255/100; + Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = right*255/100; + ALsetparams(AL_DEFAULT_DEVICE, Param, 4); + return 0; +} /* gen_set_volume() */ + +/*---------------------------------------------------------------------* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + *---------------------------------------------------------------------*/ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + long Param[4]; + Param[0] = AL_LEFT_SPEAKER_GAIN; Param[1] = 0; + Param[2] = AL_RIGHT_SPEAKER_GAIN; Param[3] = 0; + ALgetparams(AL_DEFAULT_DEVICE, Param, 4); + *left = Param[1] * 100 / 255; + *right = Param[3] * 100 / 255; + return 0; +} /* gen_get_volume() */ + + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + +#endif + diff --git a/kscd/libwm/plat_linux.c b/kscd/libwm/plat_linux.c new file mode 100644 index 00000000..58768a40 --- /dev/null +++ b/kscd/libwm/plat_linux.c @@ -0,0 +1,803 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Linux-specific drive control routines. Very similar to the Sun module. + */ + +#if defined(__linux__) + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/wait.h> +/* Try to get around bug #29274 */ +#include <linux/version.h> +#if 0 +/* this breaks the build on ia64 and s390 for example. + sys/types.h is already included and should provide __u64. + please tell where we really need this and let's try to find + a working #if case for everyone ... adrian@suse.de */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,50)) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) +#undef __GNUC__ +typedef unsigned long long __u64; +#endif +#endif + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#if defined(BSD_MOUNTTEST) + #include <mntent.h> +#else + /* + * this is for glibc 2.x which defines ust structure in + * ustat.h not stat.h + */ + #ifdef __GLIBC__ + #include <sys/ustat.h> + #endif +#endif + + +#include <sys/time.h> +#include <sys/ioctl.h> + +#ifndef __GNUC__ +#define __GNUC__ 1 +#endif +/* needed for vanilla kernel headers, which do provide __u64 only + for ansi */ +#undef __STRICT_ANSI__ +/* needed for non-ansi kernel headers */ +#define asm __asm__ +#define inline __inline__ +#include <asm/types.h> +#include <linux/cdrom.h> +#undef asm +#undef inline + +#include "include/wm_cdda.h" +#include "include/wm_struct.h" +#include "include/wm_platform.h" +#include "include/wm_cdrom.h" +#include "include/wm_scsi.h" +#include "include/wm_helpers.h" + +#ifdef OSS_SUPPORT +#include <linux/soundcard.h> +#define CD_CHANNEL SOUND_MIXER_CD +#endif + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +#define max(a,b) ((a) > (b) ? (a) : (b)) + +#ifdef LINUX_SCSI_PASSTHROUGH +/* this is from <scsi/scsi_ioctl.h> */ +# define SCSI_IOCTL_SEND_COMMAND 1 +#endif + +#ifdef BUILD_CDDA +int gen_cdda_init( struct wm_drive *d ); +#endif + +int min_volume = 0; +int max_volume = 255; + +#ifdef OSS_SUPPORT +int mixer; +char mixer_dev_name[20] = "/dev/mixer"; +#endif + +/*-------------------------------------------------------* + * + * + * CD-ROM drive functions. + * + * + *-------------------------------------------------------*/ + +/*--------------------------------------------------------* + * Initialize the drive. A no-op for the generic driver. + *--------------------------------------------------------*/ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + +/*---------------------------------------------------------------------------* + * Open the CD device and figure out what kind of drive is attached. + *---------------------------------------------------------------------------*/ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + char vendor[32], model[32], rev[32]; + + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + + if (d->fd >= 0) { /* Device already open? */ +/* wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd);*/ + return (0); + } + + fd = open(d->cd_device, O_RDONLY | O_NONBLOCK); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): device=%s fd=%d\n", d->cd_device, fd); + + if (fd < 0) + return -errno; + + /* Now fill in the relevant parts of the wm_drive structure. */ + d->fd = fd; + + /* + * See if we can do digital audio. + */ +#if defined(BUILD_CDDA) + if(d->cdda && gen_cdda_init(d)) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n"); + gen_close(d); + return -1; + } +#endif + + /* Can we figure out the drive type? */ + if (wm_scsi_get_drive_type(d, vendor, model, rev)) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): inquiry failed\n"); + strcpy(vendor, "Generic"); + strcpy(model, "drive type"); + strcpy(rev, ""); + } + + if(find_drive_struct(vendor, model, rev) < 0) { + gen_close(d); + return -1; + } + + if(d->proto->gen_init) + return (d->proto->gen_init)(d); + + return 0; +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + int tries = 0; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + gen_close(d); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calls wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + tries++; + } while ( (status != 0) && (tries < 10) ); + return status; +} /* wmcd_reopen() */ + +/*---------------------------------------------* + * Send an arbitrary SCSI command to a device. + *---------------------------------------------*/ +int +wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply ) +{ +#ifdef LINUX_SCSI_PASSTHROUGH + + char *cmd; + int cmdsize; + + cmdsize = 2 * sizeof(int); + if (retbuf) + { + if (getreply) cmdsize += max(cdblen, retbuflen); + else cmdsize += (cdblen + retbuflen); + } + else cmdsize += cdblen; + + cmd = malloc(cmdsize); + if (cmd == NULL) + return (-1); + + ((int*)cmd)[0] = cdblen + ((retbuf && !getreply) ? retbuflen : 0); + ((int*)cmd)[1] = ((retbuf && getreply) ? retbuflen : 0); + + memcpy(cmd + 2*sizeof(int), cdb, cdblen); + if (retbuf && !getreply) + memcpy(cmd + 2*sizeof(int) + cdblen, retbuf, retbuflen); + + if (ioctl(d->fd, SCSI_IOCTL_SEND_COMMAND, cmd)) + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%s: ioctl() failure\n", __FILE__); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "command buffer is:\n"); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "%02x %02x %02x %02x %02x %02x\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5]); + free(cmd); + return (-1); + } + + if (retbuf && getreply) + memcpy(retbuf, cmd + 2*sizeof(int), retbuflen); + + free(cmd); + return 0; + +#else /* Linux SCSI passthrough*/ +/*----------------------------------------* + * send packet over cdrom interface + * kernel >= 2.2.16 + *----------------------------------------*/ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,2,15)) + + struct cdrom_generic_command cdc; + struct request_sense sense; + int capability; + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi over CDROM_SEND_PACKET entered\n"); + + capability = ioctl(d->fd, CDROM_GET_CAPABILITY); + + if(!(capability & CDC_GENERIC_PACKET)) + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "your CDROM or/and kernel don't support CDC_GENERIC_PACKET ...\n"); + return -1; + } + + memset(&cdc, 0, sizeof(struct cdrom_generic_command)); + memset(&sense, 0, sizeof(struct request_sense)); + + memcpy(cdc.cmd, cdb, cdblen); + + cdc.buffer = retbuf; + cdc.buflen = retbuflen; + cdc.stat = 0; + cdc.sense = &sense; + cdc.data_direction = getreply?CGC_DATA_READ:CGC_DATA_WRITE; + + /* sendpacket_over_cdrom_interface() */ + return ioctl(d->fd, CDROM_SEND_PACKET, &cdc); +#endif /* CDROM_SEND_PACKET */ + printf("ERROR: this binary was compiled without CDROM GENERIC PACKET SUPPORT. kernel version < 2.2.16?\n"); + printf("ERROR: if you have a SCSI CDROM, rebuild it with a #define LINUX_SCSI_PASSTHROUGH\n"); + return (-1); +#endif +} /* wm_scsi */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/*--------------------------------* + * Keep the CD open all the time. + * disabled, analogous to 1.4b3 + *--------------------------------* +void +keep_cd_open( void ) +{ + int fd; + struct flock fl; + extern end; + + + for (fd = 0; fd < 256; fd++) + close(fd); + + if (fork()) + exit(0); + +#if defined(O_NOFOLLOW) + if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT | O_NOFOLLOW, 0666)) < 0) +#else + if ((fd = open("/tmp/cd.lock", O_RDWR | O_CREAT, 0666)) < 0) +#endif + exit(0); + fl.l_type = F_WRLCK; + fl.l_whence = 0; + fl.l_start = 0; + fl.l_len = 0; + if (fcntl(fd, F_SETLK, &fl) < 0) + exit(0); + + if (open(cd_device, 0) >= 0) + { + brk(&end); + pause(); + } + + exit(0); +} +*/ + +/*--------------------------------------------------------------------------* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + *--------------------------------------------------------------------------*/ +int +gen_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *ind ) +{ + struct cdrom_subchnl sc; + int ret; + +#ifdef SBPCD_HACK + static int prevpos = 0; +#endif + + + /* Is the device open? */ + if (d->fd < 0) { + ret = wmcd_open(d); + if(ret < 0) /* error */ + return ret; + + if(ret == 1) { + /* retry */ + *mode = WM_CDM_UNKNOWN; + return 0; + } + } + + /* Try to get rid of the door locking */ + /* Don't care about return value. If it */ + /* works - fine. If not - ... */ + ioctl(d->fd, CDROM_LOCKDOOR, 0); + + *mode = WM_CDM_UNKNOWN; + + sc.cdsc_format = CDROM_MSF; + +#if defined(BUILD_CDDA) + IFCDDA(d) { + if(!cdda_get_drive_status(d, oldmode, mode, pos, track, ind)) { + if(*mode == WM_CDM_STOPPED) + *mode = WM_CDM_UNKNOWN; /* dont believe */ + } + } else +#endif + if(!ioctl(d->fd, CDROMSUBCHNL, &sc)) { + switch (sc.cdsc_audiostatus) { + case CDROM_AUDIO_PLAY: + *mode = WM_CDM_PLAYING; + *track = sc.cdsc_trk; + *ind = sc.cdsc_ind; + *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + + sc.cdsc_absaddr.msf.second * 75 + + sc.cdsc_absaddr.msf.frame; +#ifdef SBPCD_HACK + if( *pos < prevpos ) { + if( (prevpos - *pos) < 75 ) { + *mode = WM_CDM_TRACK_DONE; + } + } + + prevpos = *pos; +#endif + break; + + case CDROM_AUDIO_PAUSED: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) { + *mode = WM_CDM_PAUSED; + *track = sc.cdsc_trk; + *ind = sc.cdsc_ind; + *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + + sc.cdsc_absaddr.msf.second * 75 + + sc.cdsc_absaddr.msf.frame; + } else + *mode = WM_CDM_STOPPED; + break; + + case CDROM_AUDIO_NO_STATUS: + *mode = WM_CDM_STOPPED; + break; + + case CDROM_AUDIO_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + case CDROM_AUDIO_INVALID: /**/ + default: + *mode = WM_CDM_UNKNOWN; + break; + } + } + + if(WM_CDS_NO_DISC(*mode)) { + /* verify status of drive */ + ret = ioctl(d->fd, CDROM_DRIVE_STATUS, 0/* slot */); + if(ret == CDS_DISC_OK) + ret = ioctl(d->fd, CDROM_DISC_STATUS, 0); + switch(ret) { + case CDS_NO_DISC: + *mode = WM_CDM_NO_DISC; + break; + case CDS_TRAY_OPEN: + *mode = WM_CDM_EJECTED; + break; + case CDS_AUDIO: + case CDS_MIXED: + *mode = WM_CDM_STOPPED; + break; + case CDS_DRIVE_NOT_READY: + case CDS_NO_INFO: + case CDS_DATA_1: + case CDS_DATA_2: + case CDS_XA_2_1: + case CDS_XA_2_2: + default: + *mode = WM_CDM_UNKNOWN; + } + } + + return (0); +} /* gen_get_drive_status */ + +/*-------------------------------------* + * Get the number of tracks on the CD. + *-------------------------------------*/ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + struct cdrom_tochdr hdr; + + if (ioctl(d->fd, CDROMREADTOCHDR, &hdr)) + return (-1); + + *tracks = hdr.cdth_trk1; + return (0); +} /* gen_get_trackcount() */ + +/*---------------------------------------------------------* + * Get the start time and mode (data or audio) of a track. + *---------------------------------------------------------*/ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + struct cdrom_tocentry entry; + + entry.cdte_track = track; + entry.cdte_format = CDROM_MSF; + + if (ioctl(d->fd, CDROMREADTOCENTRY, &entry)) + return (-1); + + *startframe = entry.cdte_addr.msf.minute * 60 * 75 + + entry.cdte_addr.msf.second * 75 + + entry.cdte_addr.msf.frame; + *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0; + + return (0); +} + +/*-------------------------------------* + * Get the number of frames on the CD. + *-------------------------------------*/ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + + return gen_get_trackinfo( d, CDROM_LEADOUT, &tmp, frames); +} /* gen_get_cdlen() */ + + +/*------------------------------------------------------------* + * Play the CD from one position to another (both in frames.) + *------------------------------------------------------------*/ +int +gen_play(struct wm_drive *d, int start, int end, int realstart) +{ + struct cdrom_msf msf; + + CDDARETURN(d) cdda_play(d, start, end, realstart); + + msf.cdmsf_min0 = start / (60*75); + msf.cdmsf_sec0 = (start % (60*75)) / 75; + msf.cdmsf_frame0 = start % 75; + msf.cdmsf_min1 = end / (60*75); + msf.cdmsf_sec1 = (end % (60*75)) / 75; + msf.cdmsf_frame1 = end % 75; + + if (ioctl(d->fd, CDROMPLAYMSF, &msf)) { + if (ioctl(d->fd, CDROMSTART)) + return (-1); + if (ioctl(d->fd, CDROMPLAYMSF, &msf)) + return (-2); + } + + /* + * I hope no drive gets really confused after CDROMSTART + * If so, I need to make this run-time configurable. + * +#ifndef FAST_IDE + if (ioctl( d->fd, CDROMSTART)) + return (-1); +#endif + if (ioctl( d->fd, CDROMPLAYMSF, &msf )) + return (-2); + */ + + return (0); +} /* gen_play() */ + +/*---------------* + * Pause the CD. + *---------------*/ +int +gen_pause(struct wm_drive *d) +{ + CDDARETURN(d) cdda_pause(d); + return (ioctl(d->fd, CDROMPAUSE)); +} + +/*-------------------------------------------------* + * Resume playing the CD (assuming it was paused.) + *-------------------------------------------------*/ +int +gen_resume(struct wm_drive *d) +{ + CDDARETURN(d) cdda_pause(d); + return (ioctl(d->fd, CDROMRESUME)); +} + +/*--------------* + * Stop the CD. + *--------------*/ +int +gen_stop(struct wm_drive *d) +{ + CDDARETURN(d) cdda_stop(d); + return (ioctl(d->fd, CDROMSTOP)); +} + + +/*----------------------------------------* + * Eject the current CD, if there is one. + *----------------------------------------*/ +int +gen_eject(struct wm_drive *d) +{ + struct stat stbuf; +#if !defined(BSD_MOUNTTEST) + struct ustat ust; +#else + struct mntent *mnt; + FILE *fp; +#endif + + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "ejecting?\n"); + + if (fstat(d->fd, &stbuf) != 0) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "that weird fstat() thingy\n"); + return (-2); + } + + /* Is this a mounted filesystem? */ +#if !defined(BSD_MOUNTTEST) + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); +#else + /* + * This is the same test as in the WorkBone interface. + * I should eliminate it there, because there is no need + * for it in the UI + */ + /* check if drive is mounted (from Mark Buckaway's cdplayer code) */ + /* Changed it again (look at XPLAYCD from ???? */ + /* It's better to check the device name rather than one device is */ + /* mounted as iso9660. That prevents "no playing" if you have more*/ + /* than one CD-ROM, and one of them is mounted, but it's not the */ + /* audio CD -dirk */ + if ((fp = setmntent (MOUNTED, "r")) == NULL) + { + wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "Could not open %s: %s\n", MOUNTED, strerror (errno)); + return(-3); + } + while ((mnt = getmntent (fp)) != NULL) + { + if (strcmp (mnt->mnt_fsname, d->cd_device) == 0) + { + wm_lib_message(WM_MSG_LEVEL_ERROR|WM_MSG_CLASS, "CDROM already mounted (according to mtab). Operation aborted.\n"); + endmntent (fp); + return(-3); + } + } + endmntent (fp); +#endif /* BSD_MOUNTTEST */ + + IFCDDA(d) { + cdda_eject(d); + } + + ioctl( d->fd, CDROM_LOCKDOOR, 0 ); + + if (ioctl(d->fd, CDROMEJECT)) + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "eject failed (%s).\n", strerror(errno)); + return (-1); + } + + /*------------------ + * Things in "foobar_one" are left over from 1.4b3 + * I put them here for further observation. In 1.4b3, however, + * that workaround didn't help at least for /dev/sbpcd + * (The tray closed just after ejecting because re-opening the + * device causes the tray to close) + *------------------*/ +#ifdef foobar_one + extern int intermittent_dev + /* + * Some drives (drivers?) won't recognize a new CD if we leave the + * device open. + */ + if (intermittent_dev) + gen_close(d); +#endif + + return (0); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE +#ifdef CDROMCLOSETRAY + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "CDROMCLOSETRAY closing tray...\n"); + if (ioctl(d->fd, CDROMCLOSETRAY)) + return (-1); +#else + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen() closing tray...\n"); + if(!gen_close(d)) + { + return(wmcd_reopen(d)); + } else { + return(-1); + } +#endif /* CDROMCLOSETRAY */ +#endif /* CAN_CLOSE */ + /* Always succeed if the drive can't close. */ + return(0); +} /* gen_closetray() */ + + +/*------------------------------------------------------------------------* + * scale_volume(vol, max) + * + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + * This is not used if sound card support is enabled. + * + *------------------------------------------------------------------------*/ +static int +scale_volume( int vol, int max ) +{ +#ifdef CURVED_VOLUME + return ((max * max - (max - vol) * (max - vol)) * + (max_volume - min_volume) / (max * max) + min_volume); +#else + return ((vol * (max_volume - min_volume)) / max + min_volume); +#endif +} /* scale_volume() */ + +static int +unscale_volume( int vol, int max ) +{ +#ifdef CURVED_VOLUME + /* FIXME do it simpler */ + int tmp = (((max_volume - min_volume - vol) * max * max) - (vol + min_volume)); + return max - sqrt((tmp/(max_volume - min_volume))); +#else + return (((vol - min_volume) * max) / (max_volume - min_volume)); +#endif +} /* unscale_volume() */ + +/*---------------------------------------------------------------------* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + *---------------------------------------------------------------------*/ +int +gen_set_volume( struct wm_drive *d, int left, int right ) +{ + struct cdrom_volctrl v; + + CDDARETURN(d) cdda_set_volume(d, left, right); + + /* Adjust the volume to make up for the CD-ROM drive's weirdness. */ + left = scale_volume(left, 100); + right = scale_volume(right, 100); + + v.channel0 = v.channel2 = left < 0 ? 0 : left > 255 ? 255 : left; + v.channel1 = v.channel3 = right < 0 ? 0 : right > 255 ? 255 : right; + + return (ioctl(d->fd, CDROMVOLCTRL, &v)); +} /* gen_set_volume() */ + +/*---------------------------------------------------------------------* + * Read the volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + *---------------------------------------------------------------------*/ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + struct cdrom_volctrl v; + + CDDARETURN(d) cdda_get_volume(d, left, right); + +#if defined(CDROMVOLREAD) + if(!ioctl(d->fd, CDROMVOLREAD, &v)) { + *left = unscale_volume((v.channel0 + v.channel2)/2, 100); + *right = unscale_volume((v.channel1 + v.channel3)/2, 100); + } else +#endif + /* Suns, HPs, Linux, NEWS can't read the volume; oh well */ + *left = *right = -1; + + return 0; +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + * + * Return a buffer with cdtext-stream. buffer will be allocated and filled + * + * needs send packet interface -> for IDE, linux at 2.2.16 + * depends on scsi.c which depends on wm_scsi defined in here + * (which also takes care of IDE drives) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + +#endif /* __linux__ */ diff --git a/kscd/libwm/plat_linux_audio.c b/kscd/libwm/plat_linux_audio.c new file mode 100644 index 00000000..7316a691 --- /dev/null +++ b/kscd/libwm/plat_linux_audio.c @@ -0,0 +1,489 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Linux digital audio functions. + */ + +#include "include/wm_config.h" + +static char plat_linux_audio_id[] = "$Id$"; + +#if defined(__linux__) && defined(BUILD_CDDA) /* { */ + +#include "include/wm_cdda.h" + +/* types.h included by wm_cdda.h */ + +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> +#include <sys/stropts.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * Since there's a lag time between writing audio to the audio device and + * hearing it, we need to make sure the status indicators correlate to what's + * playing out the speaker. Luckily, Solaris gives us some audio + * synchronization facilities that make this pretty easy. + * + * We maintain a circular queue of status information. When we write some + * sound to the audio device, we put its status info into the queue. We write + * a marker into the audio stream; when the audio device driver encounters the + * marker, it increments a field in a status structure. When we see that + * field go up, we grab the next status structure from the queue and send it + * to the parent process. + * + * The minimum size of the queue depends on the latency of the audio stream. + */ +#define QSIZE 500 + +struct cdda_block queue[QSIZE]; +int qtail; +int qstart; + +/* + * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed + * to be playing; this is used to keep track. + */ +extern int playing; + +static int aufd, aucfd; +static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */ + +/* + * For fast linear-to-ulaw mapping, we use a lookup table that's generated + * at startup. + */ +unsigned char *ulawmap, linear_to_ulaw(); + +char *getenv(); + +/* + * Dummy signal handler so writes to /dev/audio will interrupt. + */ +static void +dummy( void ) +{ + signal(SIGALRM, dummy); +} + +/* + * Initialize the audio device. + */ +void +wmaudio_init( void ) +{ + audio_info_t info; + char *audiodev, *acdev; + int linval; + + audiodev = getenv("AUDIODEV"); + if (audiodev == NULL) + audiodev = "/dev/audio"; + + acdev = malloc(strlen(audiodev) + 4); + if (acdev == NULL) + { + perror("Can't allocate audio control filename"); + exit(1); + } + strcpy(acdev, audiodev); + strcat(acdev, "ctl"); + + aucfd = open(acdev, O_WRONLY, 0); + if (aucfd < 0) + { + perror(acdev); + exit(1); + } + free(acdev); + + aufd = open(audiodev, O_WRONLY, 0); + if (aufd < 0) + { + perror(audiodev); + exit(1); + } + + signal(SIGALRM, dummy); + + /* + * Try to set the device to CD-style audio; we can process it + * with the least CPU overhead. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 44100; + info.play.channels = 2; + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + if (errno == EINVAL) + { + /* + * Oh well, so much for that idea. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 8000; + info.play.channels = 1; + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_ULAW; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + { + perror("Can't set up audio device"); + exit(1); + } + + /* + * Initialize the linear-to-ulaw mapping table. + */ + if (ulawmap == NULL) + ulawmap = malloc(65536); + if (ulawmap == NULL) + { + perror("malloc"); + exit(1); + } + for (linval = 0; linval < 65536; linval++) + ulawmap[linval] = linear_to_ulaw(linval-32768); + ulawmap += 32768; + raw_audio = 0; + } + else + { + perror(audiodev); + exit(1); + } +} + +/* + * Get ready to play some sound. + */ +void +wmaudio_ready( void ) +{ + audio_info_t info; + + /* + * Start at the correct queue position. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + qtail = info.play.eof % QSIZE; + qstart = qtail; + + queue[qtail].status = WMCDDA_OK; +} + +/* + * Stop the audio immediately. + */ +void +wmaudio_stop( void ) +{ + if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0) + perror("flush"); +} + +/* + * Close the audio device. + */ +void +wmaudio_close( void ) +{ + wmaudio_stop(); + close(aufd); + close(aucfd); +} + +/* + * Set the volume level. + */ +void +wmaudio_volume(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + info.play.gain = level; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO"); +} + +/* + * Set the balance level. + */ +void +wmaudio_balance(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + level *= AUDIO_RIGHT_BALANCE; + info.play.balance = level / 255; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO"); +} + +/* + * Mark the most recent audio block on the queue as the last one. + */ +void +wmaudio_mark_last( void ) +{ + queue[qtail].status = WMCDDA_DONE; +} + +/* + * Figure out the most recent status information and send it upstream. + */ +int +wmaudio_send_status( void ) +{ + audio_info_t info; + int qhead; + + /* + * Now send the most current status information to our parent. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + qhead = info.play.eof % QSIZE; + + if (qhead != qstart && playing) + { + int balance; + + if (queue[qhead].status != WMCDDA_DONE) + queue[qhead].status = WMCDDA_PLAYED; + queue[qhead].volume = info.play.gain; + queue[qhead].balance = (info.play.balance * 255) / + AUDIO_RIGHT_BALANCE; + + send_status(queue + qhead); + qstart = -1; + } + + return (queue[qhead].status == WMCDDA_DONE); +} + +/* + * Play some audio and pass a status message upstream, if applicable. + * Returns 0 on success. + */ +int +wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + int i; + short *buf16; + int alarmcount = 0; + struct itimerval it; + + alarm(1); + + while (write(aufd, rawbuf, buflen) <= 0) + if (errno == EINTR) + { + if (! raw_audio && alarmcount++ < 5) + { + /* + * 8KHz /dev/audio blocks for several seconds + * waiting for its queue to drop below a low + * water mark. + */ + wmaudio_send_status(); + timerclear(&it.it_interval); + timerclear(&it.it_value); + it.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &it, NULL); + continue; + } + +/* close(aufd); + close(aucfd); + wmaudio_init(); +*/ wmaudio_stop( void ); + alarm(2); + continue; + } + else + { + blk->status = WMCDDA_ERROR; + return (-1); + } + alarm(0); + + /* + * Mark this spot in the audio stream. + * + * Marks don't always succeed (if the audio buffer is empty + * this call will block forever) so do it asynchronously. + */ + fcntl(aufd, F_SETFL, O_NONBLOCK); + if (write(aufd, rawbuf, 0) < 0) + { + if (errno != EAGAIN) + perror("audio mark"); + } + else + qtail = (qtail + 1) % QSIZE; + + fcntl(aufd, F_SETFL, 0); + + queue[qtail] = *blk; + + if (wmaudio_send_status() < 0) + return (-1); + else + return (0); +} + +/* + * Get the current audio state. + */ +void +wmaudio_state(struct cdda_block *blk) +{ + audio_info_t info; + int balance; + + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + blk->volume = info.play.gain; + blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE; +} + +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ +#define ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + +unsigned char +linear_to_ulaw( sample ) +int sample; +{ + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if ( sign != 0 ) sample = -sample; /* get magnitude */ + if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F; + ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa ); +#ifdef ZEROTRAP + if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return ulawbyte; +} + +/* + * Downsample a block of CDDA data, if necessary, for playing out an old-style + * audio device. + */ +long +wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + short *buf16 = (short *)rawbuf; + int i, j, samples; + int mono_value; + unsigned char *rbend = rawbuf + buflen; + + /* Don't do anything if the audio device can take the raw values. */ + if (raw_audio) + return (buflen); + + for (i = 0; buf16 < (short *)(rbend); i++) + { + /* Downsampling to 8KHz is a little irregular. */ + samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12; + + /* And unfortunately, we don't always end on a nice boundary. */ + if (buf16 + samples > (short *)(rbend)) + samples = ((short *)rbend) - buf16; + + /* + * No need to average all the values; taking the first one + * is sufficient and less CPU-intensive. But we do need to + * do both channels. + */ + mono_value = (buf16[0] + buf16[1]) / 2; + buf16 += samples; + rawbuf[i] = ulawmap[mono_value]; + } + + return (i); +} + +#endif /* ... && BUILD_CDDA */ diff --git a/kscd/libwm/plat_linux_cdda.c b/kscd/libwm/plat_linux_cdda.c new file mode 100644 index 00000000..6456e10a --- /dev/null +++ b/kscd/libwm/plat_linux_cdda.c @@ -0,0 +1,253 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk F�sterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Linux CDDA functions. Derived from the SUN module. + */ + +#include "include/wm_cdda.h" + +#if defined(__linux__) && defined(BUILD_CDDA) + +#ifndef __GNUC__ +#define __GNUC__ 1 +#endif +/* needed for vanilla kernel headers, which do provide __u64 only + for ansi */ +#undef __STRICT_ANSI__ +/* needed for non-ansi kernel headers */ +#define asm __asm__ +#define inline __inline__ +#include <asm/types.h> +#include <linux/cdrom.h> +#undef asm +#undef inline + +#include <stdio.h> +#include <math.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#include "include/wm_struct.h" +#include "include/wm_cdda.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +#define CDDABLKSIZE 2352 + +/* Address of next block to read. */ +static int current_position; + +/* Address of last block to read. */ +static int ending_position; + +static struct cdrom_read_audio cdda; +static long wmcdda_normalize(struct cdda_block *block); + + +/* + * Initialize the CDDA data buffer and open the appropriate device. + * + */ +int +wmcdda_init(struct cdda_device* pdev) +{ + int i; + + if (pdev->fd > -1) + return -1; + + if(!pdev->devname) + return -1; + + for (i = 0; i < pdev->numblocks; i++) { + /* in Linux const */ + pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE; + pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen); + if (!pdev->blocks[i].buf) { + ERRORLOG("wmcdda_init ENOMEM\n"); + return -ENOMEM; + } + } + + pdev->fd = open(pdev->devname, O_RDONLY | O_NONBLOCK); + + if (pdev->fd > -1) { + cdda.addr_format = CDROM_LBA; + cdda.addr.lba = 200; + cdda.nframes = 1; + cdda.buf = (unsigned char*)pdev->blocks[0].buf; + + pdev->status = WM_CDM_STOPPED; + if((ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0)) { + if (errno == ENXIO) { + /* CD ejected! */ + pdev->status = WM_CDM_EJECTED; + return 0; + } else { + /* Sometimes it fails once, dunno why */ + pdev->status = WM_CDM_CDDAERROR; + return 0; + } + } + } else { + ERRORLOG("canot open device, errno %i\n", errno); + pdev->status = WM_CDM_UNKNOWN; + return -1; + } + + pdev->status = WM_CDM_UNKNOWN; + return 0; +} + +/* + * Close the CD-ROM device in preparation for exiting. + */ +int +wmcdda_close(struct cdda_device* pdev) +{ + int i; + + if(-1 == pdev->fd) + return -1; + + close(pdev->fd); + pdev->fd = -1; + + for (i = 0; i < pdev->numblocks; i++) { + free(pdev->blocks[i].buf); + pdev->blocks[i].buf = 0; + pdev->blocks[i].buflen = 0; + } + + return 0; +} + +/* + * Set up for playing the CD. Actually this doesn't play a thing, just sets a + * couple variables so we'll know what to do when we're called. + */ +int +wmcdda_setup(int start, int end, int realstart) +{ + current_position = start; + ending_position = end; + + return 0; +} + +/* + * Read some blocks from the CD. Stop if we hit the end of the current region. + * + * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason. + */ +long +wmcdda_read(struct cdda_device* pdev, struct cdda_block *block) +{ + if (pdev->fd < 0 && wmcdda_init(pdev)) { + return -1; + } + + /* Hit the end of the CD, probably. */ + if (current_position >= ending_position) { + block->status = WM_CDM_TRACK_DONE; + return 0; + } + + cdda.addr_format = CDROM_LBA; + cdda.addr.lba = current_position - CD_MSF_OFFSET; + if (ending_position && current_position + pdev->frames_at_once > ending_position) + cdda.nframes = ending_position - current_position; + else + cdda.nframes = pdev->frames_at_once; + + cdda.buf = (unsigned char*)block->buf; + + if (ioctl(pdev->fd, CDROMREADAUDIO, &cdda) < 0) { + if (errno == ENXIO) { + /* CD ejected! */ + block->status = WM_CDM_EJECTED; + return 0; + } else { + /* Sometimes it fails once, dunno why */ + block->status = WM_CDM_CDDAERROR; + return 0; + } + } + + block->track = -1; + block->index = 0; + block->frame = current_position; + block->status = WM_CDM_PLAYING; + block->buflen = cdda.nframes * CDDABLKSIZE; + + current_position = current_position + cdda.nframes; + + return wmcdda_normalize(block); +} + +/* + * Normalize a bunch of CDDA data. Basically this means doing byte-swapping, since the CD audio is in + * littleendian format. + */ +long +wmcdda_normalize(struct cdda_block *block) +{ +#if WM_BIG_ENDIAN + int i; + int blocks = block->buflen / CDDABLKSIZE; + char *rawbuf = block->buf; + char *dest = block->buf; + + while (blocks--) { + for (i = 0; i < CDDABLKSIZE / 2; i++) { + *dest++ = rawbuf[1]; + *dest++ = rawbuf[0]; + rawbuf += 2; + } + } +#endif + return block->buflen; +} + +/* + * Set the playback direction. + */ +void +wmcdda_direction(int newdir) +{ +} + +/* + * Do system-specific stuff to get ready to play at a particular speed. + */ +void +wmcdda_speed(int speed) +{ +} + +#endif diff --git a/kscd/libwm/plat_news.c b/kscd/libwm/plat_news.c new file mode 100644 index 00000000..0e95b01d --- /dev/null +++ b/kscd/libwm/plat_news.c @@ -0,0 +1,442 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Sony NEWS-specific drive control routines. + */ + +static char plat_news_id[] = "$Id$"; + +#if defined( __sony_news) || defined(sony_news) + +#include <errno.h> +#include <stdio.h> +#include <fcntl.h> +#include <ustat.h> +#include <CD.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +void *malloc(); +char *strchr(); + +extern int intermittent_dev; + +int min_volume = 128; +int max_volume = 255; + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + +/* + * Open the CD device and figure out what kind of drive is attached. + */ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + static int warned = 0; + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + intermittent_dev = 1; + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + if ((d->fd = CD_Open(d->cd_device, 0)) < 0) + { + /* Solaris 2.2 volume manager moves links around */ + if (errno == ENOENT && intermittent_dev) + return (0); + + if (errno == EACCES) + { + if (!warned) + { + /* + char realname[MAXPATHLEN]; + + if (realpath(cd_device, realname) == NULL) + { + perror("realpath"); + return 1; + } + */ + return -EACCES; + } + } + else if (errno != EIO) /* defined at top */ + { + return (-6); + } + + /* No CD in drive. */ + return (1); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + fd = d->fd; + + /* Figure out the drive type, if possible */ + wm_scsi_get_drive_type(d, vendor, model, rev); + find_drive_struct(vendor, model, rev); + + d->fd = fd; + + (d->init)(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* + * Pass SCSI commands to the device. + */ +int +wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen, + unsigned char *buf, int buflen, int getreply) +{ + /* NEWS can't do SCSI passthrough... or can it? */ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + int ret = 0; + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device.\n"); + ret = CD_Close(d->fd); + d->fd = -1; + wm_susleep(3000000); + } + return (ret); +} + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct CD_Status sc; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + /* Disc is ejected. Close the device. */ + if (CD_GetStatus(d->fd, &sc)) + { + gen_close(d); + return (0); + } + + switch (sc.status) { + case CDSTAT_PLAY: + *mode = PLAYING; + *track = sc.tno; + *index = sc.index; + *pos = sc.baddr; + break; + + case CDSTAT_PAUSE: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + *track = sc.tno; + *index = sc.index; + *pos = sc.baddr; + } + else + *mode = WM_CDM_STOPPED; + break; + + case CDSTAT_STOP: + if (oldmode == WM_CDM_PLAYING) + { + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + } + /* fall through */ + + default: + *mode = WM_CDM_STOPPED; + break; + } + + return (0); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + struct CD_Capacity cc; + + if (CD_GetCapacity(d->fd, &cc)) + return (-1); + + *tracks = cc.etrack - 1; + return (0); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + */ +int +gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe ) +{ + struct CD_TOCinfo hdr; + struct CD_TOCdata ent; + + hdr.strack = track; + hdr.ntrack = 1; + hdr.data = &ent; + if (CD_ReadTOC(d->fd, &hdr)) + return (-1); + + *data = (ent.control & 4) ? 1 : 0; + *startframe = ent.baddr; + + return (0); +} /* gen_get_trackinfo */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen( struct wm_drive *d, int *frames ) +{ + int tmp; + + if ((d->get_trackcount)(d, &tmp)) + return (-1); + + return (gen_get_trackinfo(d, tmp + 1, &tmp, frames)); +} /* gen_get_cdlen() */ + + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play( struct wm_drive *d, int start, int end ) +{ + struct CD_PlayAddr msf; + + msf.addrmode = CD_MSF; + msf.addr.msf.startmsf.min = start / (60*75); + msf.addr.msf.startmsf.sec = (start % (60*75)) / 75; + msf.addr.msf.startmsf.frame = start % 75; + msf.addr.msf.endmsf.min = end / (60*75); + msf.addr.msf.endmsf.sec = (end % (60*75)) / 75; + msf.addr.msf.endmsf.frame = end % 75; + + if (CD_Play(d->fd, &msf)) + { + printf("wm_cd_play_chunk(%d,%d)\n",start,end); + printf("msf = %d:%d:%d %d:%d:%d\n", + msf.addr.msf.startmsf.min, + msf.addr.msf.startmsf.sec, + msf.addr.msf.startmsf.frame, + msf.addr.msf.endmsf.min, + msf.addr.msf.endmsf.sec, + msf.addr.msf.endmsf.frame); + perror("CD_Play"); + return (-1); + } + + return (0); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause( struct wm_drive *d ) +{ + CD_Pause(d->fd); + return (0); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume( struct wm_drive *d ) +{ + CD_Restart(d->fd); + return (0); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop( struct wm_drive *d ) +{ + CD_Stop(d->fd); + return (0); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject( struct wm_drive *d ) +{ + struct stat stbuf; + struct ustat ust; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); + + if (CD_AutoEject(d->fd)) + return (-1); + + /* Close the device if it needs to vanish. */ + if (intermittent_dev) + { + gen_close(d); + } + + return (0); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@DeathsDoor.com + *----------------------------------------*/ +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!gen_close(d)) + { + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume( struct wm_drive *d, int left, int right) +{ + /* NEWS can't adjust volume! */ + return (0); +} + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume( struct wm_drive *d, omt *left, int *right) +{ + /* Suns, HPs, Linux, NEWS can't read the volume; oh well */ + *left = *right = -1; + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + +#endif diff --git a/kscd/libwm/plat_openbsd.c b/kscd/libwm/plat_openbsd.c new file mode 100644 index 00000000..a3b0fd47 --- /dev/null +++ b/kscd/libwm/plat_openbsd.c @@ -0,0 +1,545 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * OpenBSD-specific drive control routines. (Based on plat_freebsd.c) + * + * Michael Shalayeff, 7/24/96 + * Todd Pfaff, 3/20/94 + * + */ + +#if defined(__OpenBSD__) || defined(__OpenBSD) + + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include "include/wm_config.h" +#include "include/wm_cdrom.h" +#include "include/wm_helpers.h" + +/* this is for glibc 2.x which defines the ust structure in ustat.h not stat.h */ +#ifdef __GLIBC__ +#include <sys/ustat.h> +#endif + +#include <sys/time.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/cdio.h> +#include <sys/scsiio.h> +#include <scsi/scsi_all.h> +#include <scsi/cd.h> +#include <scsi/scsi_cd.h> + +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +void *malloc(); + +int min_volume = 10; +int max_volume = 255; + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init(struct wm_drive *d) +{ + return (0); +} /* gen_init() */ + + +char *cds[] = {"/dev/rcd0c", "/dev/rcd1c", "/dev/acd0c", NULL}; + + +/* + * Open the CD device and figure out what kind of drive is attached. + */ +int +wmcd_open(struct wm_drive *d) +{ + int fd; + static int warned = 0; + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + int i; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + { + for (i = 0; cds[i] != NULL; i++) + { + d->cd_device = cds[i]; + d->fd = open(d->cd_device, O_RDONLY); + if (d->fd >= 0) + break; + } + } + else + d->fd = open(d->cd_device, O_RDONLY); + if (d->fd < 0) + { + if (errno == EIO) + /* No CD in drive. */ + return 1; + else + return -errno; + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + fd = d->fd; + + find_drive_struct(vendor, model, rev); + + (d->init)(d); + + d->fd = fd; + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* + * Send an arbitrary SCSI command to a device. + * + */ +int +wm_scsi(struct wm_drive *d, unsigned char *cdb, + int cdblen, void *retbuf, int retbuflen, int getreply) +{ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + } + d->fd = -1; + return 0; +} + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct ioc_read_subchannel sc; + struct cd_sub_channel_info scd; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + sc.address_format = CD_MSF_FORMAT; + sc.data_format = CD_CURRENT_POSITION; + sc.track = 0; + sc.data_len = sizeof(scd); + sc.data = (struct cd_sub_channel_info *)&scd; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + if (ioctl(d->fd, CDIOCREADSUBCHANNEL, &sc)) + { + /* we need to release the device so the kernel will notice + reloaded media */ + (void) close(d->fd); + d->fd = -1; + return (0); /* ejected */ + } + + switch (scd.header.audio_status) + { + case CD_AS_PLAY_IN_PROGRESS: + *mode = WM_CDM_PLAYING; + dopos: + *pos = scd.what.position.absaddr.msf.minute * 60 * 75 + + scd.what.position.absaddr.msf.second * 75 + + scd.what.position.absaddr.msf.frame; + *track = scd.what.position.track_number; + *index = scd.what.position.index_number; + break; + + case CD_AS_PLAY_PAUSED: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + goto dopos; + } + else + *mode = WM_CDM_STOPPED; + break; + + case CD_AS_PLAY_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + case CD_AS_NO_STATUS: + case 0: + *mode = WM_CDM_STOPPED; + break; + } + + return (0); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + struct ioc_toc_header hdr; + + if (ioctl(d->fd, CDIOREADTOCHEADER, &hdr) == -1) + return (-1); + + *tracks = hdr.ending_track - hdr.starting_track + 1; + return (0); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + * + * XXX - this should get cached, but that means keeping track of ejects. + */ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + struct ioc_read_toc_entry toc; + struct cd_toc_entry toc_buffer; + + bzero((char *)&toc_buffer, sizeof(toc_buffer)); + toc.address_format = CD_MSF_FORMAT; + toc.starting_track = track; + toc.data_len = sizeof(toc_buffer); + toc.data = &toc_buffer; + + if (ioctl(d->fd, CDIOREADTOCENTRYS, &toc)) + return (-1); + + *data = ((toc_buffer.control & 0x4) != 0); + + *startframe = toc_buffer.addr.msf.minute*60*75 + + toc_buffer.addr.msf.second * 75 + + toc_buffer.addr.msf.frame; + + return (0); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + struct ioc_toc_header hdr; + int status; + +#define LEADOUT 0xaa /* see scsi.c. what a hack! */ + return gen_get_trackinfo(d, LEADOUT, &tmp, frames); +} /* gen_get_cdlen() */ + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play(struct wm_drive *d, int start, int end) +{ + struct ioc_play_msf msf; + + msf.start_m = start / (60*75); + msf.start_s = (start % (60*75)) / 75; + msf.start_f = start % 75; + msf.end_m = end / (60*75); + msf.end_s = (end % (60*75)) / 75; + msf.end_f = end % 75; + + if (ioctl(d->fd, CDIOCSTART)) + return (-1); + + if (ioctl(d->fd, CDIOCPLAYMSF, &msf)) + return (-2); + + return (0); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause(struct wm_drive *d) +{ + return (ioctl(d->fd, CDIOCPAUSE)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume(struct wm_drive *d) +{ + return (ioctl(d->fd, CDIOCRESUME)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop(struct wm_drive *d) +{ + return (ioctl(d->fd, CDIOCSTOP)); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject(struct wm_drive *d) +{ + /* On some systems, we can check to see if the CD is mounted. */ + struct stat stbuf; + struct statfs buf; + int rval; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (fstatfs(stbuf.st_rdev, &buf) == 0) + return (-3); + + rval = ioctl(d->fd, CDIOCALLOW); + if (rval == 0) + rval = ioctl(d->fd, CDIOCEJECT); + if (rval == 0) + rval = ioctl(d->fd, CDIOCPREVENT); + if (rval == 0) + rval = close(d->fd); + if (rval == 0) + d->fd = -1; + return rval; +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@DeathsDoor.com + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + +/* + * scale_volume(vol, max) + * + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + * + * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack + * increases much faster toward the top end of the volume scale than it + * does at the bottom. To make up for this, we make the volume scale look + * sort of logarithmic (actually an upside-down inverse square curve) so + * that the volume value passed to the drive changes less and less as you + * approach the maximum slider setting. The actual formula looks like + * + * (max^2 - (max - vol)^2) * (max_volume - min_volume) + * v = --------------------------------------------------- + min_volume + * max^2 + * + * If your system's volume settings aren't broken in this way, something + * like the following should work: + * + * return ((vol * (max_volume - min_volume)) / max + min_volume); + */ +static int +scale_volume(int vol, int max) +{ + return ((vol * (max_volume - min_volume)) / max + min_volume); +} /* scale_volume() */ + +/* + * unscale_volume(cd_vol, max) + * + * Given a value between min_volume and max_volume, return the volume slider + * value needed to achieve that value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + */ +static int +unscale_volume(int cd_vol, int max) +{ + int vol = 0, top = max, bot = 0, scaled; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol == scaled) + break; + if (cd_vol < scaled) + top = vol - 1; + else + bot = vol + 1; + } + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} /* unscale_volume() */ + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + struct ioc_vol vol; + + if (left < 0) /* don't laugh, I saw this happen once! */ + left = 0; + if (right < 0) + right = 0; + left = scale_volume(left, 100); + right = scale_volume(right, 100); + + bzero((char *)&vol, sizeof(vol)); + + vol.vol[LEFT_PORT] = left; + vol.vol[RIGHT_PORT] = right; + + if (ioctl(d->fd, CDIOCSETVOL, &vol)) + return (-1); + + return (0); +} /* gen_set_volume() */ + + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume(struct wm_drive *d, int *left, int *right) +{ + struct ioc_vol vol; + + if (d->fd >= 0) + { + bzero((char *)&vol, sizeof(vol)); + + if (ioctl(d->fd, CDIOCGETVOL, &vol)) + *left = *right = -1; + else + { + *left = unscale_volume(vol.vol[LEFT_PORT], 100); + *right = unscale_volume(vol.vol[RIGHT_PORT], 100); + } + } + else + *left = *right = -1; + + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + +#endif diff --git a/kscd/libwm/plat_osf1.c b/kscd/libwm/plat_osf1.c new file mode 100644 index 00000000..a2cc0afb --- /dev/null +++ b/kscd/libwm/plat_osf1.c @@ -0,0 +1,674 @@ +/* + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * OSF drive control routines. + */ + + +#if defined(__osf__) || defined(__osf) + + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <ustat.h> +#include <string.h> +/* #include <sys/rzdisk.h> +#include <sys/cdrom.h> */ +#include <io/cam/rzdisk.h> +#include <io/cam/cdrom.h> + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_helpers.h" +#include "include/wm_cdtext.h" + + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * This structure will be filled with the TOC header and all entries. + * Ultrix doesn't seem to allow getting single TOC entries. + * - Chris Ross (cross@eng.umd.edu) + */ +struct cd_toc_header_and_entries +{ + struct cd_toc_header cdth; + struct cd_toc_entry cdte[CDROM_MAX_TRACK+1]; +}; + +void *malloc(); +char *strchr(); + +int min_volume = 128; +int max_volume = 255; +static char* osf_cd_device = NULL; + +/* + * fgetline() + * + * Simulate fgets, but joining continued lines in the output of uerf. + * + * platform specific internal function + * + */ + +#define BUF_SIZE 85 /* Max length of a (real) line */ + +char * +fgetline( FILE *fp ) +{ + static char *retval = NULL; + static char holdbuf[BUF_SIZE + 1]; + char tmp[BUF_SIZE + 1]; + char *stmp; + + if (!retval) + { + retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */ + if (!retval) + return(NULL); + else + *retval = '\0'; + } + + if (*holdbuf) + { + strcpy(retval, holdbuf); + retval[strlen(retval)-1] = '\0'; + memset(holdbuf, 0, BUF_SIZE+1); + } + + while (fgets(tmp, BUF_SIZE, fp)) + { + stmp = tmp + strspn(tmp, " \t"); + if (*stmp == '_') { /* Continuation line */ + retval[strlen(retval)-1] = '\0'; /* Trim off C/R */ + strcat(retval, stmp+1); + } else { + if (*retval) + { + strcpy(holdbuf, tmp); + holdbuf[strlen(holdbuf)-1] = -1; + return retval; + } else { /* First line read, keep reading */ + strcat(retval, stmp); + retval[strlen(retval)-1] = '\0'; + } + } + } + return NULL; +} /* fgetline() */ + + +/* + * find_cdrom + * + * Determine the name of the CD-ROM device. + * + * Read through the boot records (via a call to uerf) and find the SCSI + * address of the CD-ROM. + */ +const char* +find_cdrom() +{ + char *data; + FILE *uerf; + int fds[2]; + int pid; + extern char *getenv(); + const char *device = NULL; + + pipe(fds); + + device = getenv("CDROM"); + /* + ** the path of the device has to start w/ /dev + ** otherwise we are vulnerable to race conditions + ** Thomas Biege <thomas@suse.de> + */ + if ( device == NULL || + strncmp("/dev/", device, 5) || + strstr(device, "/../") + ) + return NULL; + + if ((pid = fork()) == 0) + { + close(fds[0]); + dup2(fds[1], 1); + execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0); + execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0); + return NULL; /* _exit(1); */ + } else if (pid < 0) { + perror("fork"); + return NULL; + } + + close(fds[1]); + uerf = fdopen(fds[0], "r"); + + while (data = fgetline(uerf)) + if (strstr(data, "RRD42")) + { + char *device_p; + + osf_cd_device = (char *)malloc(sizeof("/dev/rrz##c")); + strcpy(osf_cd_device, "/dev/r"); + device_p = strstr(data, "rz"); + device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0'; + strcat(osf_cd_device, device_p); + strcat(osf_cd_device, "c"); + device = osf_cd_device; + break; + } + + fclose(uerf); + + if (device == NULL) + { + fprintf(stderr, + "No cdrom (RRD42) is installed on this system\n"); + return NULL; + } + + kill(pid, 15); + (void)wait((int *)NULL); + return device; +} /* find_cdrom() */ + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + +/* + * Open the CD device and figure out what kind of drive is attached. + */ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + static int warned = 0; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = find_cdrom(); + + d->fd = open(d->cd_device, O_RDWR); + if (d->fd < 0) + { + if (errno == EACCES) + { + return -EACCES; + } + else if (errno != EINTR) + { + return (-6); + } + + /* No CD in drive. */ + return (1); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + find_drive_struct("", "", ""); + d->fd = fd; + + (d->init)(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* + * Send an arbitrary SCSI command to a device. + */ +int +wm_scsi(struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply) +{ + /* OSF1 doesn't have a SCSI passthrough interface, does it? */ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct cd_sub_channel sc; + struct cd_subc_channel_data scd; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + sc.sch_address_format = CDROM_MSF_FORMAT; + sc.sch_data_format = CDROM_CURRENT_POSITION; + sc.sch_track_number = 0; + sc.sch_alloc_length = sizeof(scd); + sc.sch_buffer = (caddr_t)&scd; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) + { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc)) + return (0); /* ejected */ + + switch (scd.scd_header.sh_audio_status) + { + case AS_PLAY_IN_PROGRESS: + *mode = WM_CDM_PLAYING; + dopos: + *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 + + scd.scd_position_data.scp_absaddr.msf.s_units * 75 + + scd.scd_position_data.scp_absaddr.msf.f_units; + *track = scd.scd_position_data.scp_track_number; + *index = scd.scd_position_data.scp_index_number; + break; + + case AS_PLAY_PAUSED: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + goto dopos; + } + else + *mode = WM_CDM_STOPPED; + break; + + case AS_PLAY_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + case AS_NO_STATUS: + *mode = WM_CDM_STOPPED; + break; + default: + abort(); + } + + return (0); +} /* gen_get_drive_status() */ + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play( struct wm_drive *d, int start, int end ) +{ + struct cd_play_audio_msf msf; + + msf.msf_starting_M_unit = start / (60*75); + msf.msf_starting_S_unit = (start % (60*75)) / 75; + msf.msf_starting_F_unit = start % 75; + msf.msf_ending_M_unit = end / (60*75); + msf.msf_ending_S_unit = (end % (60*75)) / 75; + msf.msf_ending_F_unit = end % 75; + + if (ioctl(d->fd, SCSI_START_UNIT)) + return (-1); + if (ioctl(d->fd, CDROM_PLAY_AUDIO_MSF, &msf)) + return (-2); + + return (0); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDROM_PAUSE_PLAY, 0)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDROM_RESUME_PLAY, 0)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop( struct wm_drive *d ) +{ + return (ioctl(d->fd, SCSI_STOP_UNIT, 0)); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject(struct wm_drive *d) +{ + /* On some systems, we can check to see if the CD is mounted. */ + struct stat stbuf; + struct ustat ust; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); + + return (ioctl(d->fd, CDROM_EJECT_CADDY, 0)); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@DeathsDoor.com + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + struct cd_toc_header hdr; + + if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) + return (-1); + + *tracks = hdr.th_ending_track; + + return (0); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + * + * XXX - this should get cached, but that means keeping track of ejects. + */ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + struct cd_toc toc; + struct cd_toc_header hdr; + struct cd_toc_header_and_entries toc_buffer; + + if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) + return (-1); + + bzero((char *)&toc_buffer, sizeof(toc_buffer)); + toc.toc_address_format = CDROM_MSF_FORMAT; + toc.toc_starting_track = 0; + toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) + + hdr.th_data_len0) & 0xfff) + 2; + toc.toc_buffer = (caddr_t)&toc_buffer; + + if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc)) + return (-1); + + if (track == 0) + track = hdr.th_ending_track + 1; + + *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0; + *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 + + toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 + + toc_buffer.cdte[track - 1].te_absaddr.msf.f_units; + + return (0); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen( struct wm_drive *d, int *frames ) +{ + int tmp; + + return (gen_get_trackinfo(d, 0, &tmp, frames)); +} /* gen_get_cdlen() */ + +/* + * scale_volume(vol, max) + * + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + * + * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack + * increases much faster toward the top end of the volume scale than it + * does at the bottom. To make up for this, we make the volume scale look + * sort of logarithmic (actually an upside-down inverse square curve) so + * that the volume value passed to the drive changes less and less as you + * approach the maximum slider setting. The actual formula looks like + * + * (max^2 - (max - vol)^2) * (max_volume - min_volume) + * v = --------------------------------------------------- + min_volume + * max^2 + * + * If your system's volume settings aren't broken in this way, something + * like the following should work: + * + * return ((vol * (max_volume - min_volume)) / max + min_volume); + */ +scale_volume(int vol, int max) +{ + return ((max * max - (max - vol) * (max - vol)) * + (max_volume - min_volume) / (max * max) + min_volume); +} /* scale_volume() */ + +/* + * unscale_volume(cd_vol, max) + * + * Given a value between min_volume and max_volume, return the volume slider + * value needed to achieve that value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + */ +static int +unscale_volume( int cd_vol, int max ) +{ + int vol = 0, top = max, bot = 0, scaled; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol == scaled) + break; + if (cd_vol < scaled) + top = vol - 1; + else + bot = vol + 1; + } + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} /* unscale_volume() */ + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + struct cd_playback pb; + struct cd_playback_status ps; + struct cd_playback_control pc; + + left = scale_volume(left, 100); + right = scale_volume(right, 100); + + bzero((char *)&pb, sizeof(pb)); + bzero((char *)&ps, sizeof(ps)); + bzero((char *)&pc, sizeof(pc)); + + pb.pb_alloc_length = sizeof(ps); + pb.pb_buffer = (caddr_t)&ps; + + if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) + return (-1); + + pc.pc_chan0_select = ps.ps_chan0_select; + pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ? + CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ? + CDROM_MAX_VOLUME : left; + pc.pc_chan1_select = ps.ps_chan1_select; + pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ? + CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ? + CDROM_MAX_VOLUME : right; + + pb.pb_alloc_length = sizeof(pc); + pb.pb_buffer = (caddr_t)&pc; + + if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb)) + return (-1); + + return (0); +} /* gen_set_volume() */ + + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + struct cd_playback pb; + struct cd_playback_status ps; + + bzero((char *)&pb, sizeof(pb)); + bzero((char *)&ps, sizeof(ps)); + + pb.pb_alloc_length = sizeof(ps); + pb.pb_buffer = (caddr_t)&ps; + + if (d->fd >= 0) + { + if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) + *left = *right = -1; + else + { + *left = unscale_volume(ps.ps_chan0_volume, 100); + *right = unscale_volume(ps.ps_chan1_volume, 100); + } + } + else + *left = *right = -1; + + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* no SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + +#endif diff --git a/kscd/libwm/plat_scor5.c b/kscd/libwm/plat_scor5.c new file mode 100644 index 00000000..1e5be6e2 --- /dev/null +++ b/kscd/libwm/plat_scor5.c @@ -0,0 +1,426 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * SCO Openserver R5 specific. Derived from the KSCD plat_scor5.c + * + */ + + +static char plat_linux_id[] = "$Id$"; + +#if defined(M_UNIX) || defined(__M_UNIX) + + +#include "include/wm_config.h" + +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/scsi.h> +#include <sys/scsicmd.h> +#include <errno.h> +#include <macros.h> + +#include "include/wm_struct.h" +#include "include/wm_scsi.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +#define SENSE_SZ EXTD_SENSE_LEN + +void *malloc(); +char *strchr(); + +int min_volume = 0; +int max_volume = 255; + +/* + * platformspecific internal function + */ +static int +create_cdrom_node(char *dev_name) +{ + char pass_through[100]; + int file_des; + struct stat sbuf; + int err; + int ccode; + + + strncpy(pass_through, dev_name, sizeof(pass_through)-2); + strcat(pass_through, "p" ); + + if (setreuid(-1,0) < 0) + { + perror("setregid/setreuid/access"); + return -1; + } + + ccode = access(pass_through, F_OK); + + if (ccode < 0) + { + + if (stat(dev_name, &sbuf) < 0) + { + perror("Call to get pass-through device number failed"); + return -1; + } + + if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE), + sbuf.st_rdev) < 0) + { + perror("Unable to make pass-through node"); + return -1; + } + + if (chown(pass_through, 0 , 0) < 0) + { + perror("chown"); + return -1; + } + + if (chmod(pass_through, 0660 ) < 0) + { + perror("chmod"); + return -1; + } + } + + file_des = open( pass_through, O_RDONLY); + err = errno; + + /* + if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) ) + { + perror("setreuid/setregid"); + exit(1); + } + + */ + + errno = err; + return file_des; +} /* create_cdrom_node() */ + + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + +/* + * Open the CD and figure out which kind of drive is attached. + */ +int +wmcd_open( struct wm_drive *d) +{ + int fd; + static int warned = 0; + char vendor[9], model[17], rev[5]; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + { + fprintf(stderr,"cd_device string empty\n"); + return (-1); + } + + + d->fd = create_cdrom_node(d->cd_device); /* this will do open */ + + if (d->fd < 0) + { + if (errno == EACCES) + { + if (! warned) + { + fprintf(stderr,"Cannot access %s\n",d->cd_device); + warned++; + } + } + else if (errno != EINTR) + { + perror(d->cd_device); + return( -6 ); + } + + /* can not acces CDROM device */ + return (-1); + } + + if (warned) + { + warned = 0; + fprintf(stderr, "Thank you.\n"); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + + fd = d->fd; + + if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0) + { + perror("Cannot inquiry drive for it's type"); + return (-1); + } + find_drive_struct(vendor, model, rev); + + d->fd = fd; + + return (0); +} /* wmcd_open */ + +/* + * Re-Open the device + */ +wmcd_reopen( struct wm_drive *d ) +{ + int status; + int tries = 0; + do { + gen_close(d); + susleep( 1000 ); + status = wmcd_open( d ); + susleep( 1000 ); + tries++; + } while ( (status != 0) && (tries < 10) ); + return status; +} /* wmcd_reopen() */ + +/* + * Send a SCSI command out the bus. + */ +int +wm_scsi(struct wm_drive *d, unsigned char *xcdb, int cdblen, + char *retbuf, int retbuflen, int getreply) +{ + int ccode; + int file_des = d->fd; + unsigned char sense_buffer[ SENSE_SZ ]; + + /* getreply == 1 is read, == 0 is write */ + + struct scsicmd2 sb; /* Use command with automatic sense */ + + if (cdblen > SCSICMDLEN) + { + fprintf(stderr,"Cannot handle longer commands than %d bytes.\n", SCSICMDLEN); + exit(-1); + } + + /* Get the command */ + memcpy(sb.cmd.cdb, xcdb, cdblen); + sb.cmd.cdb_len = cdblen; + + /* Point to data buffer */ + sb.cmd.data_ptr = retbuf; + sb.cmd.data_len = retbuflen; + + /* Is this write or read ? */ + sb.cmd.is_write = (getreply==1) ? 0 : 1; + + /* Zero out return status fields */ + sb.cmd.host_sts = 0; + sb.cmd.target_sts = 0; + + /* Set up for possible sense info */ + + sb.sense_ptr = sense_buffer; + sb.sense_len = sizeof(sense_buffer); + + ccode = ioctl(file_des, SCSIUSERCMD2, &sb); + + if ( sb.cmd.target_sts != 02 ) + return ccode; + + return 0; +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +void +keep_cd_open() { } + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status(wm_drive *d, int oldmode, int *mode, + int *pos, int *track, int *index) +{ + return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index)); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + return (wm_scsi2_get_trackcount(d, tracks)); +} /* gen_get_trackcount() */ + + +/* + * Get the start time and mode (data or audio) of a track. + */ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + return (wm_scsi2_get_trackinfo(d, track, data, startframe)); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + return (wm_scsi2_get_cdlen(d, frames)); +} /* gen_get_cdlen() */ + + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play(struct wm_drive *d, int start, int end) +{ + return (wm_scsi2_play(d, start, end)); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause( struct wm_drive *d ) +{ + return (wm_scsi2_pause(d)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume( struct wm_drive *d ) +{ + return (wm_scsi2_resume(d)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop(struct wm_drive *d) +{ + return (wm_scsi2_stop(d)); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject(struct wm_drive *d) +{ + int stat; + + stat = wm_scsi2_eject(d); + sleep(2); + return (stat); +} /* gen_eject() */ + + +int +gen_closetray(struct wm_drive *d) +{ + return(wm_scsi2_closetray(d)); +} /* gen_closetray() */ + + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + return (wm_scsi2_set_volume(d, left, right)); +} /* gen_set_volume() */ + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume(struct wm_drive *d, int *left, int *right) +{ + return (wm_scsi2_get_volume(d, left, right)); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + /* This needs to be tested! */ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + + + +#endif /* M_UNIX */ + + + + diff --git a/kscd/libwm/plat_sun.c b/kscd/libwm/plat_sun.c new file mode 100644 index 00000000..f83a6cd8 --- /dev/null +++ b/kscd/libwm/plat_sun.c @@ -0,0 +1,972 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Sun-specific drive control routines. + */ + +static char plat_sun_id[] = "$Id$"; + +#if defined(sun) || defined(__sun) + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/ioctl.h> + +#include "include/wm_config.h" +#include "include/wm_helpers.h" +#include "include/wm_cdrom.h" +#include "include/wm_cdtext.h" + +#include <ustat.h> +#include <unistd.h> +#include <signal.h> +#ifdef solbourne +# include <mfg/dklabel.h> +# include <mfg/dkio.h> +# include <sys/unistd.h> +# include <dev/srvar.h> +#else /* A real Sun */ +# ifdef SYSV +# include <poll.h> +# include <stdlib.h> +# include <sys/cdio.h> +# include <sys/socket.h> +# include <sys/scsi/impl/uscsi.h> +# include "include/wm_cdda.h" +# else +# include <sys/buf.h> +# include <sun/dkio.h> +# include <scsi/targets/srdef.h> +# include <scsi/impl/uscsi.h> +# include <scsi/generic/commands.h> +# endif +#endif + +#include "include/wm_struct.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +int min_volume = 0; +int max_volume = 255; + +static const char *sun_cd_device = NULL; +extern int intermittent_dev; + +int current_end; + +#if defined(SYSV) && defined(SIGTHAW) +#ifdef __GNUC__ +void sigthawinit(void) __attribute__ ((constructor)); +#else +#pragma init(sigthawinit) +#endif /* GNUC */ + +static int last_left, last_right; +static struct wm_drive *thecd = NULL; + +/* + * Handling for Sun's Suspend functionality + */ +static void +thawme(int sig) +{ +// Just leave this line in as a reminder for a missing +// functionality in the GUI. +// change_mode(NULL, WM_CDM_STOPPED, NULL); + codec_init(); + if( thecd ) + gen_set_volume(thecd, last_left, last_right); +} /* thawme() */ + +void +sigthawinit( void ) +{ + struct sigaction sa; + + sa.sa_handler = thawme; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + sigaction(SIGTHAW, &sa, NULL); +} /* sigthawinit() */ + +#endif /* SYSV && SIGTHAW */ + +/* + * find_cdrom + * + * Determine the name of the CD-ROM device. + * + * Use the first of /vol/dev/aliases/cdrom0, /dev/rdsk/c0t6d0s2, and /dev/rsr0 + * that exists. (Check for /vol/dev/aliases, not cdrom0, since it won't be + * there if there's no CD in the drive.) This is done so a single SunOS 4.x + * binary can be used on any 4.x or higher Sun system. + */ +const char* +find_cdrom( void ) +{ + if (access("/vol/dev/aliases", X_OK) == 0) + { + /* Volume manager. Device might not be there. */ + intermittent_dev = 1; + + /* If vold is running us, it'll tell us the device name. */ + sun_cd_device = getenv("VOLUME_DEVICE"); + /* + ** the path of the device has to include /dev + ** otherwise we are vulnerable to race conditions + ** Thomas Biege <thomas@suse.de> + */ + if (sun_cd_device == NULL || + strncmp("/vol/dev/", sun_cd_device, 9) || + strstr(sun_cd_device, "/../") ) + return "/vol/dev/aliases/cdrom0"; + else + return sun_cd_device; + } + else if (access("/dev/rdsk/c0t6d0s2", F_OK) == 0) + { + /* Solaris 2.x w/o volume manager. */ + return "/dev/rdsk/c0t6d0s2"; + } + else if (access("/dev/rcd0", F_OK) == 0) + { + return "/dev/rcd0"; + } + else if (access("/dev/rsr0", F_OK) == 0) + return "/dev/rsr0"; + else + { + fprintf(stderr, "Couldn't find a CD device!\n"); + return NULL; + } +} /* find_cdrom() */ + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init( struct wm_drive *d ) +{ + codec_init(); + return (0); +} /* gen_init() */ + + +/* + * Open the CD device and figure out what kind of drive is attached. + */ +int +wmcd_open( struct wm_drive *d ) +{ + static int warned = 0; + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = find_cdrom(); + + d->fd = open(d->cd_device, 0); + if (d->fd < 0) + { + /* Solaris 2.2 volume manager moves links around */ + if (errno == ENOENT && intermittent_dev) + return (1); + + if (errno == EACCES) + { + if (!warned) + { + /* + char realname[MAXPATHLEN]; + + if (realpath(cd_device, realname) == NULL) + { + perror("realpath"); + return (1); + } + */ + return -EACCES; + } + } + else if (errno != ENXIO) + { + return( -6 ); + } + + /* No CD in drive. */ + return (1); + } + + /* + * See if we can do digital audio. + */ +#if defined(BUILD_CDDA) + if(d->cdda) { + if (!gen_cdda_init(d)) + /* WARNING: Old GUI call. How could this survive? */ + enable_cdda_controls(1); + else { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): failed in gen_cdda_init\n"); + gen_close(d); + return -1; + } +#endif + + /* Can we figure out the drive type? */ + if (wm_scsi_get_drive_type(d, vendor, model, rev)) + { + if (errno == EPERM) + { + /* + * Solaris 2.4 seems to refuse to do USCSICMD ioctls + * when not running as root. SunOS 4.x allows it + * as an unprivileged user, though. + */ + fprintf(stderr, "Warning: WorkMan can't adapt itself to your drive unless it runs as root.\n"); + } else { + fprintf(stderr, "Warning: WorkMan couldn't determine drive type\n"); + } + strcpy(vendor, "Generic"); + strcpy(model, "drive type"); + strcpy(rev, ""); + } + + find_drive_struct(vendor, model, rev); + + (d->proto->gen_init)(d); + thecd = d; + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + gen_close(d); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + + +#ifndef solbourne +/* + * Send an arbitrary SCSI command out the bus and optionally wait for + * a reply if "retbuf" isn't NULL. + */ +int +wm_scsi( struct wm_drive *d, + unsigned char *cdb, + int cdblen, void *retbuf, + int retbuflen, int getreply ) +{ + char x; + struct uscsi_cmd cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.uscsi_cdb = (void *) cdb; + cmd.uscsi_cdblen = cdblen; + cmd.uscsi_bufaddr = retbuf ? retbuf : (void *)&x; + cmd.uscsi_buflen = retbuf ? retbuflen : 0; + cmd.uscsi_flags = USCSI_ISOLATE | USCSI_SILENT; + if (getreply) + cmd.uscsi_flags |= USCSI_READ; + + if (ioctl(d->fd, USCSICMD, &cmd)) + return (-1); + + if (cmd.uscsi_status) + return (-1); + + return (0); +} +#else +int wm_scsi() { return (-1); } +#endif + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/* Alarm signal handler. */ +static void do_nothing( int x ) { x++; } + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status( struct wm_drive *d, + int oldmode, + int *mode, + int *pos, int *track, int *index ) +{ + struct cdrom_subchnl sc; + struct itimerval old_timer, new_timer; + struct sigaction old_sig, new_sig; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) + { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + +#if defined(BUILD_CDDA) + if (oldmode == WM_CDM_PAUSED || oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_STOPPED) { + CDDARETURN(d) cdda_get_drive_status(d, oldmode, mode, pos, track, index); + } +#endif + + /* + * Solaris 2.2 hangs on this ioctl if someone else ejects the CD. + * So we schedule a signal to break out of the hang if the call + * takes an unreasonable amount of time. The signal handler and + * timer are restored immediately to avoid interfering with XView. + */ + if (intermittent_dev) + { + /* + * First clear out the timer so XView's signal doesn't happen + * while we're diddling with the signal handler. + */ + timerclear(&new_timer.it_interval); + timerclear(&new_timer.it_value); + setitimer(ITIMER_REAL, &new_timer, &old_timer); + + /* + * Now install the no-op signal handler. + */ + new_sig.sa_handler = do_nothing; + memset(&new_sig.sa_mask, 0, sizeof(new_sig.sa_mask)); + new_sig.sa_flags = 0; + if (sigaction(SIGALRM, &new_sig, &old_sig)) + perror("sigaction"); + + /* + * And finally, set the timer. + */ + new_timer.it_value.tv_sec = 2; + setitimer(ITIMER_REAL, &new_timer, NULL); + } + + sc.cdsc_format = CDROM_MSF; + + if (ioctl(d->fd, CDROMSUBCHNL, &sc)) + { + if (intermittent_dev) + { + sigaction(SIGALRM, &old_sig, NULL); + setitimer(ITIMER_REAL, &old_timer, NULL); + + /* If the device can disappear, let it do so. */ + close(d->fd); + d->fd = -1; + } + + return (0); + } + + if (intermittent_dev) + { + sigaction(SIGALRM, &old_sig, NULL); + setitimer(ITIMER_REAL, &old_timer, NULL); + } + + switch (sc.cdsc_audiostatus) { + case CDROM_AUDIO_PLAY: + *mode = WM_CDM_PLAYING; + *track = sc.cdsc_trk; + *index = sc.cdsc_ind; + *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + + sc.cdsc_absaddr.msf.second * 75 + + sc.cdsc_absaddr.msf.frame; + break; + + case CDROM_AUDIO_PAUSED: + case CDROM_AUDIO_INVALID: + case CDROM_AUDIO_NO_STATUS: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + *track = sc.cdsc_trk; + *index = sc.cdsc_ind; + *pos = sc.cdsc_absaddr.msf.minute * 60 * 75 + + sc.cdsc_absaddr.msf.second * 75 + + sc.cdsc_absaddr.msf.frame; + } + else + *mode = WM_CDM_STOPPED; + break; + + /* CD ejected manually during play. */ + case CDROM_AUDIO_ERROR: + break; + + case CDROM_AUDIO_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + default: + *mode = WM_CDM_UNKNOWN; + break; + } + + return (0); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount( struct wm_drive *d, int *tracks ) +{ + struct cdrom_tochdr hdr; + + if (ioctl(d->fd, CDROMREADTOCHDR, &hdr)) + return (-1); + + *tracks = hdr.cdth_trk1; + return (0); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + */ +int +gen_get_trackinfo( struct wm_drive *d, int track, int *data, int *startframe) +{ + struct cdrom_tocentry entry; + + entry.cdte_track = track; + entry.cdte_format = CDROM_MSF; + + if (ioctl(d->fd, CDROMREADTOCENTRY, &entry)) + return (-1); + + *startframe = entry.cdte_addr.msf.minute * 60 * 75 + + entry.cdte_addr.msf.second * 75 + + entry.cdte_addr.msf.frame; + *data = entry.cdte_ctrl & CDROM_DATA_TRACK ? 1 : 0; + + return (0); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen(struct wm_drive *d, int *frames ) +{ + int tmp; + + return (gen_get_trackinfo(d, CDROM_LEADOUT, &tmp, frames)); +} /* gen_get_cdlen() */ + +/* + * Play the CD from one position to another. + * + * d Drive structure. + * start Frame to start playing at. + * end End of this chunk. + * realstart Beginning of this chunk (<= start) + */ +int +gen_play( struct wm_drive *d, int start, int end, int realstart) +{ + struct cdrom_msf msf; + unsigned char cmdbuf[10]; + + current_end = end; + + CDDARETURN(d) cdda_play(d, start, end, realstart); + + msf.cdmsf_min0 = start / (60*75); + msf.cdmsf_sec0 = (start % (60*75)) / 75; + msf.cdmsf_frame0 = start % 75; + msf.cdmsf_min1 = end / (60*75); + msf.cdmsf_sec1 = (end % (60*75)) / 75; + msf.cdmsf_frame1 = end % 75; + + codec_start(); + if (ioctl(d->fd, CDROMSTART)) + return (-1); + if (ioctl(d->fd, CDROMPLAYMSF, &msf)) + return (-2); + + return (0); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause( struct wm_drive *d ) +{ + CDDARETURN(d) cdda_pause(d); + + codec_stop(); + return (ioctl(d->fd, CDROMPAUSE)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume( struct wm_drive *d ) +{ + CDDARETURN(d) cdda_pause(d); + + codec_start(); + return (ioctl(d->fd, CDROMRESUME)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop( struct wm_drive *d ) +{ + CDDARETURN(d) cdda_stop(d); + + codec_stop(); + return (ioctl(d->fd, CDROMSTOP)); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject( struct wm_drive *d ) +{ + struct stat stbuf; + struct ustat ust; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); + + IFCDDA(d) { + cdda_eject(d); + } + + if (ioctl(d->fd, CDROMEJECT)) + return (-1); + + /* Close the device if it needs to vanish. */ + if (intermittent_dev) + { + close(d->fd); + d->fd = -1; + /* Also remember to tell the cddaslave since volume + manager switches links around on us */ + if (d->cdda_slave > -1) + { + write(d->cdda_slave, "E", 1); + cdda_get_ack(d->cdda_slave); + } + } + + return (0); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@DeathsDoor.com + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume( struct wm_drive *d, int left, int right ) +{ + struct cdrom_volctrl v; + +#if defined(SIGTHAW) && defined(SYSV) + last_left = left; + last_right = right; + thecd = d; +#endif + + CDDARETURN(d) cdda_set_volume(d, left, right); + + left = (left * (max_volume - min_volume)) / 100 + min_volume; + right = (right * (max_volume - min_volume)) / 100 + min_volume; + + v.channel0 = left < 0 ? 0 : left > 255 ? 255 : left; + v.channel1 = right < 0 ? 0 : right > 255 ? 255 : right; + + return (ioctl(d->fd, CDROMVOLCTRL, &v)); +} /* gen_set_volume() */ + +/* + * Read the volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume( struct wm_drive *d, int *left, int *right ) +{ + CDDARETURN(d) cdda_get_volume(d, left, right); + + *left = *right = -1; + + return (wm_scsi2_get_volume(d, left, right)); +} /* gen_get_volume() */ + +#ifdef BUILD_CDDA + +/* + * Try to initialize the CDDA slave. Returns 0 on success. + */ +int +gen_cdda_init( struct wm_drive *d ) +{ + int slavefds[2]; + + if (d->cdda_slave > -1) + return (0); + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, slavefds)) + { + perror("socketpair"); + return (-1); + } + + switch (fork()) + { + case 0: + close(slavefds[0]); + dup2(slavefds[1], 1); + dup2(slavefds[1], 0); + close(slavefds[1]); + close(d->fd); + /* Try the default path first. */ + execl(cddaslave_path, cddaslave_path, d->cd_device, (void *)0); + /* Search $PATH if that didn't work. */ + execlp("cddaslave", "cddaslave", d->cd_device, (void *)0); + perror(cddaslave_path); + exit(1); + + case -1: + close(slavefds[0]); + close(slavefds[1]); + perror("fork"); + return (-2); + } + + close(slavefds[1]); + d->cdda_slave = slavefds[0]; + + if (!cdda_get_ack(d->cdda_slave)) + { + d->cdda_slave = -1; + codec_start(); + return (-3); + } + + return (0); +} + +#endif /* BUILD_CDDA */ + +/* + * The following code activates the internal CD audio passthrough on + * SPARCstation 5 systems (and possibly others.) + * + * Thanks to <stevep@ctc.ih.att.com>, Roger Oscarsson <roger@cs.umu.se> + * and Steve McKinty <> + * + * Most CD drives have a headphone socket on the front, but it + * is often more convenient to route the audio though the + * built-in audio device. That way the user can leave their + * headphones plugged-in to the base system, for use with + * other audio stuff like ShowMeTV + */ + +#ifdef CODEC /* { */ +#ifdef SYSV /* { */ + +# include <sys/ioctl.h> +# include <sys/audioio.h> +# include <stdlib.h> + +#else /* } { */ + +# include <sun/audioio.h> +# define AUDIO_DEV_SS5STYLE 5 +typedef int audio_device_t; + +#endif /* } */ +#endif /* } */ + +/* + * Don't do anything with /dev/audio if we can't set it to high quality. + * Also, don't do anything real if it's not Solaris. + */ +#if !defined(AUDIO_ENCODING_LINEAR) || !defined(CODEC) || !defined(SYSV) /* { */ +codec_init() { return 0; } +codec_start() { return 0; } +codec_stop() { return 0; } +#else + +#ifndef AUDIO_INTERNAL_CD_IN +#define AUDIO_INTERNAL_CD_IN 0x4 +#endif + +static char* devname = 0; +static char* ctlname = 0; +static int ctl_fd = -1; +static int port = AUDIO_LINE_IN; +int internal_audio = 1; + +codec_init( void ) +{ + register int i; + char* ctlname; + audio_info_t foo; + audio_device_t aud_dev; + + if (internal_audio == 0) + { + ctl_fd = -1; + return(0); + } + + if (!(devname = getenv("AUDIODEV"))) devname = "/dev/audio"; + ctlname = strcat(strcpy(malloc(strlen(devname) + 4), devname), "ctl"); + if ((ctl_fd = open(ctlname, O_WRONLY, 0)) < 0) + { + perror(ctlname); + return -1; + } + if (ioctl(ctl_fd, AUDIO_GETDEV, &aud_dev) < 0) + { + close(ctl_fd); + ctl_fd = -1; + return -1; + } + /* + * Instead of filtering the "OLD_SUN_AUDIO", try to find the new ones. + * Not sure if this is all correct. + */ +#ifdef SYSV + if (strcmp(aud_dev.name, "SUNW,CS4231") && + strcmp(aud_dev.name, "SUNW,sb16") && + strcmp(aud_dev.name, "SUNW,sbpro")) +#else + if (aud_dev != AUDIO_DEV_SS5STYLE) +#endif + { + close(ctl_fd); + ctl_fd = -1; + return 0; /* but it's okay */ + } + + /* + * Does the chosen device have an internal CD port? + * If so, use it. If not then try and use the + * Line In port. + */ + if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0) + { + perror("AUDIO_GETINFO"); + close(ctl_fd); + ctl_fd = -1; + return(-1); + } + if (foo.record.avail_ports & AUDIO_INTERNAL_CD_IN) + port = AUDIO_INTERNAL_CD_IN; + else + port = AUDIO_LINE_IN; + + /* + * now set it up to use it. See audio(7I) + */ + + AUDIO_INITINFO(&foo); + foo.record.port = port; + foo.record.balance = foo.play.balance = AUDIO_MID_BALANCE; +#ifdef BUILD_CDDA + if (d->cdda_slave > -1) + foo.monitor_gain = 0; + else +#endif + foo.monitor_gain = AUDIO_MAX_GAIN; + /* + * These next ones are tricky. The voulme will depend on the CD drive + * volume (set by the knob on the drive and/or by workman's volume + * control), the audio device record gain and the audio device + * play gain. For simplicity we set the latter two to something + * reasonable, but we don't force them to be reset if the user + * wants to change them. + */ + foo.record.gain = (AUDIO_MAX_GAIN * 80) / 100; + foo.play.gain = (AUDIO_MAX_GAIN * 40) / 100; + + ioctl(ctl_fd, AUDIO_SETINFO, &foo); + return 0; +} + +static int +kick_codec( void ) +{ + audio_info_t foo; + int dev_fd; + int retval = 0; + + /* + * Open the audio device, not the control device. This + * will fail if someone else has taken it. + */ + + if ((dev_fd = open(devname, O_WRONLY|O_NDELAY, 0)) < 0) + { + perror(devname); + return -1; + } + + AUDIO_INITINFO(&foo); + foo.record.port = port; + foo.monitor_gain = AUDIO_MAX_GAIN; + + /* These can only be set on the real device */ + foo.play.sample_rate = 44100; + foo.play.channels = 2; + foo.play.precision = 16; + foo.play.encoding = AUDIO_ENCODING_LINEAR; + + if ((retval = ioctl(dev_fd, AUDIO_SETINFO, &foo)) < 0) + perror(devname); + + close(dev_fd); + return retval; +} /* kick_codec() */ + +codec_start( void ) +{ + audio_info_t foo; + + if (ctl_fd < 0) + return 0; + + if (ioctl(ctl_fd, AUDIO_GETINFO, &foo) < 0) + return -1; + + if (foo.play.channels != 2) return kick_codec(); + if (foo.play.encoding != AUDIO_ENCODING_LINEAR) return kick_codec(); + if (foo.play.precision != 16) return kick_codec(); + if (foo.play.sample_rate != 44100) return kick_codec(); + + if (foo.record.channels != 2) return kick_codec(); + if (foo.record.encoding != AUDIO_ENCODING_LINEAR) return kick_codec(); + if (foo.record.precision != 16) return kick_codec(); + if (foo.record.sample_rate != 44100) return kick_codec(); + + if (foo.monitor_gain != AUDIO_MAX_GAIN) return kick_codec(); + if (foo.record.port != port) return kick_codec(); + + return 0; +} /* codec_start() */ + +codec_stop( void ) { return 0; } + +#endif /* CODEC } */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + /* This needs to be tested */ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + +#endif /* sun */ diff --git a/kscd/libwm/plat_sun_audio.c b/kscd/libwm/plat_sun_audio.c new file mode 100644 index 00000000..da9fe12f --- /dev/null +++ b/kscd/libwm/plat_sun_audio.c @@ -0,0 +1,493 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Sun (really Solaris) digital audio functions. + */ + +#include "include/wm_config.h" + +static char plat_sun_audio_id[] = "$Id$"; + +#if defined(sun) || defined(__sun) && defined(SYSV) && defined(BUILD_CDDA) && defined(WMCDDA_DONE) + + +#include "include/wm_cdda.h" + +/* types.h included by wmcdda.h */ + +#include <stdio.h> +#include <malloc.h> +#include <sys/ioctl.h> +#include <sys/audioio.h> +#include <sys/stropts.h> +#include <sys/time.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * Since there's a lag time between writing audio to the audio device and + * hearing it, we need to make sure the status indicators correlate to what's + * playing out the speaker. Luckily, Solaris gives us some audio + * synchronization facilities that make this pretty easy. + * + * We maintain a circular queue of status information. When we write some + * sound to the audio device, we put its status info into the queue. We write + * a marker into the audio stream; when the audio device driver encounters the + * marker, it increments a field in a status structure. When we see that + * field go up, we grab the next status structure from the queue and send it + * to the parent process. + * + * The minimum size of the queue depends on the latency of the audio stream. + */ +#define QSIZE 500 + +struct cdda_block queue[QSIZE]; +int qtail; +int qstart; + +/* + * We only send WMCDDA_PLAYED status messages upstream when the CD is supposed + * to be playing; this is used to keep track. + */ +extern int playing; + +static int aufd, aucfd; +static int raw_audio = 1; /* Can /dev/audio take 44.1KHz stereo? */ + +/* + * For fast linear-to-ulaw mapping, we use a lookup table that's generated + * at startup. + */ +unsigned char *ulawmap, linear_to_ulaw(); + +char *getenv(); + +/* + * Dummy signal handler so writes to /dev/audio will interrupt. + */ +static void +dummy( void ) +{ + signal(SIGALRM, dummy); +} + +/* + * Initialize the audio device. + */ +void +wmaudio_init( void ) +{ + audio_info_t info; + char *audiodev, *acdev; + int linval; + + audiodev = getenv("AUDIODEV"); + if (audiodev == NULL || + strncmp("/dev/", audiodev, 5) || + strstr(audiodev, "/../") ) + audiodev = "/dev/audio"; + + acdev = malloc(strlen(audiodev) + 4); + if (acdev == NULL) + { + perror("Can't allocate audio control filename"); + exit(1); + } + strcpy(acdev, audiodev); + strcat(acdev, "ctl"); + + aucfd = open(acdev, O_WRONLY, 0); + if (aucfd < 0) + { + perror(acdev); + exit(1); + } + free(acdev); + + aufd = open(audiodev, O_WRONLY, 0); + if (aufd < 0) + { + perror(audiodev); + exit(1); + } + + signal(SIGALRM, dummy); + + /* + * Try to set the device to CD-style audio; we can process it + * with the least CPU overhead. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 44100; + info.play.channels = 2; + info.play.precision = 16; + info.play.encoding = AUDIO_ENCODING_LINEAR; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + if (errno == EINVAL) + { + /* + * Oh well, so much for that idea. + */ + AUDIO_INITINFO(&info); + info.play.sample_rate = 8000; + info.play.channels = 1; + info.play.precision = 8; + info.play.encoding = AUDIO_ENCODING_ULAW; + info.play.pause = 0; + info.record.pause = 0; + info.monitor_gain = 0; + if (ioctl(aufd, AUDIO_SETINFO, &info) < 0) + { + perror("Can't set up audio device"); + exit(1); + } + + /* + * Initialize the linear-to-ulaw mapping table. + */ + if (ulawmap == NULL) + ulawmap = malloc(65536); + if (ulawmap == NULL) + { + perror("malloc"); + exit(1); + } + for (linval = 0; linval < 65536; linval++) + ulawmap[linval] = linear_to_ulaw(linval-32768); + ulawmap += 32768; + raw_audio = 0; + } + else + { + perror(audiodev); + exit(1); + } +} + +/* + * Get ready to play some sound. + */ +void +wmaudio_ready( void ) +{ + audio_info_t info; + + /* + * Start at the correct queue position. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + qtail = info.play.eof % QSIZE; + qstart = qtail; + + queue[qtail].status = WMCDDA_OK; +} + +/* + * Stop the audio immediately. + */ +void +wmaudio_stop( void ) +{ + if (ioctl(aufd, I_FLUSH, FLUSHRW) < 0) + perror("flush"); +} + +/* + * Close the audio device. + */ +void +wmaudio_close( void ) +{ + wmaudio_stop(); + close(aufd); + close(aucfd); +} + +/* + * Set the volume level. + */ +void +wmaudio_volume(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + info.play.gain = level; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO"); +} + +/* + * Set the balance level. + */ +void +wmaudio_balance(int level) +{ + audio_info_t info; + + AUDIO_INITINFO(&info); + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) perror("AUDIO_GETINFO"); + level *= AUDIO_RIGHT_BALANCE; + info.play.balance = level / 255; + if (ioctl(aucfd, AUDIO_SETINFO, &info) < 0) perror("AUDIO_SETINFO"); +} + +/* + * Mark the most recent audio block on the queue as the last one. + */ +void +wmaudio_mark_last( void ) +{ + queue[qtail].status = WMCDDA_DONE; +} + +/* + * Figure out the most recent status information and send it upstream. + */ +int +wmaudio_send_status( void ) +{ + audio_info_t info; + int qhead; + + /* + * Now send the most current status information to our parent. + */ + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + qhead = info.play.eof % QSIZE; + + if (qhead != qstart && playing) + { + int balance; + + if (queue[qhead].status != WMCDDA_DONE) + queue[qhead].status = WMCDDA_PLAYED; + queue[qhead].volume = info.play.gain; + queue[qhead].balance = (info.play.balance * 255) / + AUDIO_RIGHT_BALANCE; + + send_status(queue + qhead); + qstart = -1; + } + + return (queue[qhead].status == WMCDDA_DONE); +} + +/* + * Play some audio and pass a status message upstream, if applicable. + * Returns 0 on success. + */ +int +wmaudio_play(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + int i; + short *buf16; + int alarmcount = 0; + struct itimerval it; + + alarm(1); + + while (write(aufd, rawbuf, buflen) <= 0) + if (errno == EINTR) + { + if (! raw_audio && alarmcount++ < 5) + { + /* + * 8KHz /dev/audio blocks for several seconds + * waiting for its queue to drop below a low + * water mark. + */ + wmaudio_send_status(); + timerclear(&it.it_interval); + timerclear(&it.it_value); + it.it_value.tv_usec = 500000; + setitimer(ITIMER_REAL, &it, NULL); + continue; + } + +/* close(aufd); + close(aucfd); + wmaudio_init(); +*/ wmaudio_stop(); + alarm(2); + continue; + } + else + { + blk->status = WMCDDA_ERROR; + return (-1); + } + alarm(0); + + /* + * Mark this spot in the audio stream. + * + * Marks don't always succeed (if the audio buffer is empty + * this call will block forever) so do it asynchronously. + */ + fcntl(aufd, F_SETFL, O_NONBLOCK); + if (write(aufd, rawbuf, 0) < 0) + { + if (errno != EAGAIN) + perror("audio mark"); + } + else + qtail = (qtail + 1) % QSIZE; + + fcntl(aufd, F_SETFL, 0); + + queue[qtail] = *blk; + + if (wmaudio_send_status() < 0) + return (-1); + else + return (0); +} + +/* + * Get the current audio state. + */ +void +wmaudio_state(struct cdda_block *blk) +{ + audio_info_t info; + int balance; + + if (ioctl(aucfd, AUDIO_GETINFO, &info) < 0) + perror("AUDIO_GETINFO"); + blk->volume = info.play.gain; + blk->balance = (info.play.balance * 255) / AUDIO_RIGHT_BALANCE; +} + +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ +#define ZEROTRAP /* turn on the trap as per the MIL-STD */ +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + +unsigned char +linear_to_ulaw( sample ) +int sample; +{ + static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + int sign, exponent, mantissa; + unsigned char ulawbyte; + + /* Get the sample into sign-magnitude. */ + sign = (sample >> 8) & 0x80; /* set aside the sign */ + if ( sign != 0 ) sample = -sample; /* get magnitude */ + if ( sample > CLIP ) sample = CLIP; /* clip the magnitude */ + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F; + ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa ); +#ifdef ZEROTRAP + if ( ulawbyte == 0 ) ulawbyte = 0x02; /* optional CCITT trap */ +#endif + + return ulawbyte; +} + +/* + * Downsample a block of CDDA data, if necessary, for playing out an old-style + * audio device. + */ +long +wmaudio_convert(unsigned char *rawbuf, long buflen, struct cdda_block *blk) +{ + short *buf16 = (short *)rawbuf; + int i, j, samples; + int mono_value; + unsigned char *rbend = rawbuf + buflen; + + /* Don't do anything if the audio device can take the raw values. */ + if (raw_audio) + return (buflen); + + for (i = 0; buf16 < (short *)(rbend); i++) + { + /* Downsampling to 8KHz is a little irregular. */ + samples = (i & 1) ? ((i % 20) ? 10 : 12) : 12; + + /* And unfortunately, we don't always end on a nice boundary. */ + if (buf16 + samples > (short *)(rbend)) + samples = ((short *)rbend) - buf16; + + /* + * No need to average all the values; taking the first one + * is sufficient and less CPU-intensive. But we do need to + * do both channels. + */ + mono_value = (buf16[0] + buf16[1]) / 2; + buf16 += samples; + rawbuf[i] = ulawmap[mono_value]; + } + + return (i); +} + +#endif /* ... && BUILD_CDDA */ diff --git a/kscd/libwm/plat_sun_cdda.c b/kscd/libwm/plat_sun_cdda.c new file mode 100644 index 00000000..3f669a8b --- /dev/null +++ b/kscd/libwm/plat_sun_cdda.c @@ -0,0 +1,380 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Sun (really Solaris) CDDA functions. + */ + +#include "include/wm_cdda.h" + +#if defined(sun) || defined(__sun__) && defined(SYSV) && defined(BUILD_CDDA) + + +#include "include/wm_struct.h" +#include "include/wm_cdda.h" +/* types.h and cdio.h are included by wm_cdda.h */ + +#include <stdio.h> +#include <math.h> +#include <sys/ioctl.h> +#include <malloc.h> +#include <errno.h> + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +#define CDDABLKSIZE 2368 +#define SAMPLES_PER_BLK 588 + +/* Address of next block to read. */ +int current_position = 0; + +/* Address of first and last blocks to read. */ +int starting_position = 0; +int ending_position = 0; + +/* Playback direction. */ +int direction = 1; + +/* Number of blocks to read at once; initialize to the maximum. */ +/* (was 30. Set to 15 for INTeL. Maybe config option? */ +int numblocks = 15; + +/* + * This is the fastest way to convert from BCD to 8-bit. + */ +unsigned char unbcd[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,0,0,0,0,0, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0,0,0,0,0,0, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 0,0,0,0,0,0, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 0,0,0,0,0,0, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 0,0,0,0,0,0, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 0,0,0,0,0,0, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 0,0,0,0,0,0, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 0,0,0,0,0,0, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 0,0,0,0,0,0, + 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static long wmcdda_normalize(struct cdda_block *block); + +/* + * Initialize the CDDA data buffer and open the appropriate device. + * + * NOTE: We allocate twice as much space as we need to actually read a block; + * this lets us do audio manipulations without bothering to malloc a second + * buffer. + * + * Also, test to see if we can actually *do* CDDA on this drive; if not, we + * need to exit right away so the UI doesn't show the user any CDDA controls. + */ +int +wmcdda_init(struct cdda_device* pdev, struct cdda_block *block) +{ + struct cdrom_cdda cdda; + int i; + + if (pdev->fd > -1) + return -1; + + for (i = 0; i < pdev->numblocks; i++) { + /* in Linux const */ + pdev->blocks[i].buflen = pdev->frames_at_once * CDDABLKSIZE; + pdev->blocks[i].buf = malloc(pdev->blocks[i].buflen); + if (!pdev->blocks[i].buf) + return -ENOMEM; + } + + pdev->fd = open(pdev->devname, 0); + if (pdev->fd == -1) + pdev->fd = open("/dev/rdsk/c0t6d0s2", 0); + + if (pdev->fd > -1) + { + cdda.cdda_addr = 200; + cdda.cdda_length = 1; + cdda.cdda_data = pdev->blocks[0].buf; + cdda.cdda_subcode = CDROM_DA_SUBQ; + + if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0) + { + block->status = WM_CDM_STOPPED; + return -1; + } else { + block->status = WM_CDM_STOPPED; + return 0; + } + } else { + block->status = WM_CDM_EJECTED; + return -1; + } +} + +/* + * Close the CD-ROM device in preparation for exiting. + */ +int +wmcdda_close(struct cdda_device* pdev) +{ + int i; + + if(-1 == pdev->fd) + return -1; + + close(pdev->fd); + pdev->fd = -1; + + for (i = 0; i < pdev->numblocks; i++) { + free(pdev->blocks[i].buf); + pdev->blocks[i].buf = 0; + pdev->blocks[i].buflen = 0; + } + + return 0; +} + +/* + * Set up for playing the CD. Actually this doesn't play a thing, just sets a + * couple variables so we'll know what to do when we're called. + */ +int +wmcdda_setup(int start, int end, int realstart) +{ + current_position = start - 150; + ending_position = end - 150; + starting_position = realstart - 150; + + /* + * Special case: don't start at the "end" of a track if we're + * playing backwards! + */ + if (direction == -1 && start == realstart) + current_position = ending_position - numblocks; + return 0; +} + +/* + * Read some blocks from the CD. Stop if we hit the end of the current region. + * + * Returns number of bytes read, -1 on error, 0 if stopped for a benign reason. + */ +long +wmcdda_read(struct cdda_device* pdev, struct cdda_block *block) +{ + struct cdrom_cdda cdda; + int blk; + unsigned char *q; + extern int speed; + unsigned char* rawbuf = block->buf; + + if(pdev->fd < 0 && (wmcdda_init(pdev, block) < 0)) { + return -1; + } + + /* + * Hit the end of the CD, probably. + */ + if ((direction > 0 && current_position >= ending_position) || + (direction < 0 && current_position < starting_position)) + { + block->status = WM_CDM_TRACK_DONE; + return (0); + } + + cdda.cdda_addr = current_position; + if (ending_position && current_position + pdev->frames_at_once > ending_position) + cdda.cdda_length = ending_position - current_position; + else + cdda.cdda_length = pdev->frames_at_once; + cdda.cdda_data = (unsigned char*)block->buf; + cdda.cdda_subcode = CDROM_DA_SUBQ; + + if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0) + { + if (errno == ENXIO) /* CD ejected! */ + { + block->status = WM_CDM_EJECTED; + return (-1); + } + + /* Sometimes it fails once, dunno why */ + if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0) + { + if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0) + { + if (ioctl(pdev->fd, CDROMCDDA, &cdda) < 0) + { + perror("CDROMCDDA"); + block->status = WM_CDM_CDDAERROR; + return (-1); + } + } + } + } + + if (speed > 148) + { + /* + * We want speed=148 to advance by cdda_length, but + * speed=256 to advance cdda_length * 4. + */ + current_position = current_position + + (cdda.cdda_length * direction * (speed - 112)) / 36; + } + else + current_position = current_position + cdda.cdda_length * direction; + + for (blk = 0; blk < numblocks; blk++) + { + /* + * New valid Q-subchannel information? Update the block + * status. + */ + q = &rawbuf[blk * CDDABLKSIZE + SAMPLES_PER_BLK * 4]; + if (*q == 1) + { + block->track = unbcd[q[1]]; + block->index = unbcd[q[2]]; + /*block->minute = unbcd[q[7]]; + block->second = unbcd[q[8]];*/ + block->frame = unbcd[q[9]]; + block->status = WM_CDM_PLAYING; + block->buflen = cdda.cdda_length; + } + } + + return wmcdda_normalize(block); +} + +/* + * Normalize a bunch of CDDA data. Basically this means ripping out the + * Q subchannel data and doing byte-swapping, since the CD audio is in + * littleendian format. + * + * Scanning is handled here too. + * + * XXX - do byte swapping on Intel boxes? + */ +long +wmcdda_normalize(struct cdda_block *block) +{ + int i, nextq; + long buflen = block->buflen; + int blocks = buflen / CDDABLKSIZE; + unsigned char *rawbuf = block->buf; + unsigned char *dest = rawbuf; + unsigned char tmp; + long *buf32 = (long *)rawbuf, tmp32; + +/* + * this was #ifndef LITTLEENDIAN + * in wmcdda it was called LITTLE_ENDIAN. Was this a flaw? + */ +#if WM_BIG_ENDIAN + if (blocks--) + for (i = 0; i < SAMPLES_PER_BLK * 2; i++) + { + /* Only need to use temp buffer on first block. */ + tmp = *rawbuf++; + *dest++ = *rawbuf++; + *dest++ = tmp; + } +#endif + + while (blocks--) + { + /* Skip over Q data. */ + rawbuf += 16; + + for (i = 0; i < SAMPLES_PER_BLK * 2; i++) + { +#if WM_LITTLE_ENDIAN + *dest++ = *rawbuf++; + *dest++ = *rawbuf++; +#else + *dest++ = rawbuf[1]; + *dest++ = rawbuf[0]; + rawbuf += 2; +#endif + } + } + + buflen -= ((buflen / CDDABLKSIZE) * 16); + + /* + * Reverse the data here if we're playing backwards. + * XXX - ideally this should be done above. + */ + if (direction < 0) + { + buflen /= 4; /* we can move 32 bits at a time. */ + + for (i = 0; i < buflen / 2; i++) + { + tmp32 = buf32[i]; + buf32[i] = buf32[buflen - i - 1]; + buf32[buflen - i - 1] = tmp32; + } + + buflen *= 4; + } + + return (buflen); +} + +/* + * Set the playback direction. + */ +void +wmcdda_direction(int newdir) +{ + if (newdir == 0) + { + numblocks = 20; + direction = 1; + } + else + { + numblocks = 30; + direction = -1; + } +} + +/* + * Do system-specific stuff to get ready to play at a particular speed. + */ +void +wmcdda_speed(int speed) +{ + if (speed > 128) + numblocks = 12; + else + numblocks = direction > 0 ? 20 : 30; +} + +#endif /* } */ diff --git a/kscd/libwm/plat_svr4.c b/kscd/libwm/plat_svr4.c new file mode 100644 index 00000000..ad6e488c --- /dev/null +++ b/kscd/libwm/plat_svr4.c @@ -0,0 +1,482 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * SVR4 specific. Much of this is similar to plat_hpux.c. + */ + +static char plat_svr4_id[] = "$Id$"; + +#if (defined(SVR4) || defined(__SVR4)) && !defined(sun) && !defined(__sun) && !defined(sony_news) && !defined(__sony_news) + + +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <sys/mkdev.h> +#include <sys/stat.h> +#include <sys/sdi.h> +#include <sys/sdi_edt.h> +#include <sys/scsi.h> +#include <errno.h> + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + + +void *malloc(); +char *strchr(); + +int min_volume = 0; +int max_volume = 255; + +static int +create_cdrom_node(char *dev_name) +{ + char pass_through[100]; + int file_des; + dev_t pass_thru_device; + int err; + int ccode; + + + strncpy(pass_through, dev_name, sizeof(pass_through) - 2); + strcat(pass_through, "p" ); + + if (setreuid(-1,0) < 0) + { + perror("setregid/setreuid/access"); + return -1; + } + + ccode = access(pass_through, F_OK); + + if (ccode < 0) + { + if ((file_des = open(dev_name, O_RDONLY)) < 0) + { + perror("open cdrom devices failed"); + return -1; + } + + if (ioctl(file_des, B_GETDEV, &pass_thru_device) < 0) + { + perror("Call to get pass-through device number failed"); + return -1; + } + + (void)close(file_des); + + if (mknod(pass_through, (S_IFCHR | S_IREAD | S_IWRITE), + pass_thru_device) < 0) + { + perror("Unable to make pass-through node"); + return -1; + } + + if (chown(pass_through, 0 , 0) < 0) + { + perror("chown"); + return -1; + } + + if (chmod(pass_through, 0660 ) < 0) + { + perror("chmod"); + return -1; + } + } + + file_des = open( pass_through, O_RDWR); + err = errno; + + if ( (setreuid(-1,getuid()) < 0) || (setregid(-1,getgid()) < 0) ) + { + perror("setreuid/setregid"); + return -1; + } + errno = err; + return file_des; +} /* create_cdrom_node() */ + +const char* +find_cdrom() +{ + /* + ** the path of the device has to start w/ /dev + ** otherwise we are vulnerable to race conditions + ** Thomas Biege <thomas@suse.de> + */ + const char* device = NULL; + + device = getenv("CDROM"); + if ( (device != NULL) && + !(strncmp("/dev/", device, 5) || + strstr(_device, "/../") )) + return device; + + if (access("/dev/cdrom/cdrom1", F_OK) == 0) + { + return "/dev/cdrom/cdrom1"; + } + else if (access("/dev/cdrom/cdrom2", F_OK) == 0) + { + return "/dev/cdrom/cdrom2"; + } + else + { + fprintf(stderr, "Couldn't find a CD device!\n"); + return NULL; + } +} /* find_cdrom() */ + +/* + * Initialize the drive. A no-op for the generic driver. + */ +int +gen_init(struct wm_drive *d) +{ + return (0); +} /* gen_init() */ + +/* + * Open the CD and figure out which kind of drive is attached. + */ +int +wmcd_open(struct wm_drive *d) +{ + int fd, flag = 1; + static int warned = 0; + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = DEFAULT_CD_DEVICE; + + d->fd = create_cdrom_node(d->cd_device); /* this will do open */ + + if (d->fd < 0) + { + if (errno == EACCES) + { + if (! warned) + { + fprintf(stderr,"Cannot access %s\n",d->cd_device); + warned++; + } + } + else if (errno != EINTR) + { + return ( -6 ); + } + + /* No CD in drive. (Is this true also for svr4 ? XXX ) */ + return (1); + } + + if (warned) + { + warned = 0; + fprintf(stderr, "Thank you.\n"); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + + fd = d->fd; + + if (wm_scsi_get_drive_type(d, vendor, model, rev) < 0) + { + perror("Cannot inquiry drive for it's type"); + exit(1); + } + find_drive_struct(vendor, model, rev); + + d->fd = fd; + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + + +/* + * Send a SCSI command out the bus. + */ +int +wm_scsi( struct wm_drive *d, unsigned char *xcdb, int cdblen, + char *retbuf, int retbuflen, int getreply) +{ + int ccode; + int file_des = d->fd; + int i,j; + unsigned char sense_buffer[ SENSE_SZ ]; + int errno_save; + + /* getreply == 1 is read, == 0 is write */ + + struct sb sb; + struct scs scs; + + sb.sb_type = ISCB_TYPE; + + sb.SCB.sc_comp_code = SDI_PROGRES; + sb.SCB.sc_int = NULL; + sb.SCB.sc_wd = 0; + sb.SCB.sc_dev.sa_major = 0; + sb.SCB.sc_dev.sa_minor = 0; + sb.SCB.sc_dev.sa_lun = 0; + sb.SCB.sc_dev.sa_exlun = 0; + sb.SCB.sc_status = 0; + sb.SCB.sc_link = (struct sb *) NULL; + sb.SCB.sc_resid = 0; + + sb.SCB.sc_cmdpt = (void *)xcdb; + sb.SCB.sc_cmdsz = cdblen; + + sb.SCB.sc_datapt = retbuf ; + sb.SCB.sc_datasz = retbuflen ; + + if (getreply == 1) + sb.SCB.sc_mode = SCB_READ; + else + sb.SCB.sc_mode = SCB_WRITE; + + sb.SCB.sc_time = 500; + + ccode = ioctl(file_des, SDI_SEND, &sb); + + if ( (sb.SCB.sc_comp_code != 0xd000000e ) || + ( sb.SCB.sc_status != 02) ) + return ccode; + + errno_save = errno; + + sb.SCB.sc_comp_code = SDI_PROGRES; + sb.SCB.sc_int = NULL; + sb.SCB.sc_wd = 0; + sb.SCB.sc_dev.sa_major = 0; + sb.SCB.sc_dev.sa_minor = 0; + sb.SCB.sc_dev.sa_lun = 0; + sb.SCB.sc_dev.sa_exlun = 0; + sb.SCB.sc_status = 0; + sb.SCB.sc_link = (struct sb *) NULL; + sb.SCB.sc_resid = 0; + + scs.ss_op = SS_REQSEN; + scs.ss_lun = 0; + scs.ss_addr1 = 0; + scs.ss_addr = 0; + scs.ss_len = SENSE_SZ; + scs.ss_cont = 0; + + sb.SCB.sc_cmdpt = SCS_AD(&scs); + sb.SCB.sc_cmdsz = SCS_SZ; + sb.SCB.sc_datapt = sense_buffer; + sb.SCB.sc_datasz = 18; + sb.SCB.sc_mode = SCB_READ; + sb.SCB.sc_time = 5000; + + if (ioctl(file_des, SDI_SEND, &sb) < 0) + { + fprintf(stderr,"Cannot read sense.\n"); + exit(-1); + } + + errno=errno_save; + return -1; +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + return (wm_scsi2_get_drive_status(d, oldmode, mode, pos, track, index)); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount(struct wm_drive *d, int *tracks) +{ + return (wm_scsi2_get_trackcount(d, tracks)); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + */ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + return (wm_scsi2_get_trackinfo(d, track, data, startframe)); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + + return (wm_scsi2_get_cdlen(d, frames)); +} /* gen_get_cdlen() */ + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play(struct wm_drive *d, int start, int end) +{ + return (wm_scsi2_play(d, start, end)); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause(struct wm_drive *d) +{ + return (wm_scsi2_pause(d)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume(struct wm_drive *d) +{ + return (wm_scsi2_resume(d)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop(struct wm_drive *d) +{ + return (wm_scsi2_stop(d)); +} /* gen_stop() */ + + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject(struct wm_drive *d) +{ + return (wm_scsi2_eject(d)); +} /* gen_eject() */ + +/* + * Close the tray. + * please review scsi.c / wm_scsi2_closetray() + * and send changes to milliByte@DeathsDoor.com + */ +int +gen_closetray( struct wm_drive *d ) +{ + return(wm_scsi2_closetray(d)); +} /* gen_closetray() */ + + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume(struct wm_drive *d, int left, int right) +{ + return (wm_scsi2_set_volume(d, left, right)); +} /* gen_set_volume() */ + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume(struct wm_drive *d, int *left, int *right) +{ + return (wm_scsi2_get_volume(d, left, right)); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + /* This needs to be tested */ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + +#endif diff --git a/kscd/libwm/plat_template.c b/kscd/libwm/plat_template.c new file mode 100644 index 00000000..bd47c20b --- /dev/null +++ b/kscd/libwm/plat_template.c @@ -0,0 +1,295 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * This file surely contains nonsense. It's the porter's part to fill + * the gaps and assure that the resulting code makes sense. + * + */ + + +static char plat_template_id[] = "$Id$" + +#if [TEMPLATESYSTEM] + + +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_cdtext.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * gen_init(); + * + */ +int +gen_init(struct wm_drive *d) +{ + return (0); +} /* gen_init() */ + +/* + * wmcd_open() + * + */ +int +wmcd_open(struct wm_drive *d) +{ + char vendor[32] = WM_STR_GENVENDOR; + char model[32] = WM_STR_GENMODEL; + char rev[32] = WM_STR_GENREV; + + if( ! d ) + { + errno = EFAULT; + return -1; + } + + if(d->fd > -1) /* device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return 0; + } + + if( d->cd_device == (char *)NULL ) + d->cd_device = DEFAULT_CD_DEVICE; + + /* open() goes here */ + + if(find_drive_struct(vendor, model, rev)) { + gen_close(d); + return -1; + } + + d->init(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* + * wm_scsi() + * + */ +int +wm_scsi(struct wm_drive *d, + uchar_t *cdb, int cdblen,void *retbuf,int retbuflen,int getreply) +{ + return (0); +} /* wm_scsi() */ + +/* + * close the CD device + */ + +int +gen_close(struct wm_drive *d) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} /* gen_close() */ + +/* + * gen_get_drive_status() + * + */ +int +gen_get_drive_status(struct wm_drive *d, + int oldmode, + int *mode, + int *pos, + int *track, + int *index) +{ + return (0); +} /* gen_get_drive_status() */ + +/* + * gen_get_trackcount() + * + */ +int +gen_get_trackcount(struct wm_drive *d,int *tracks) +{ + return (0); +} /* gen_get_trackcount() */ + +/* + * gen_get_trackinfo() + * + */ +int +gen_get_trackinfo(struct wm_drive *d,int track,int *data,int *startframe) +{ + return (0); +} /* gen_get_trackinfo() */ + +/* + * gen_get_cdlen() + * + */ +int +gen_get_cdlen(struct wm_drive *d,int *frames) +{ + return (0); +} /* gen_get_cdlen() */ + +/* + * gen_play() + * + */ +int +gen_play(struct wm_drive *d,int start,int end) +{ + return (0); +} /* gen_play() */ + +/* + * gen_pause() + * + */ +int +gen_pause(struct wm_drive *d) +{ + return ioctl( 0 ); +} /* gen_pause() */ + +/* + * gen_resume + * + */ +int +gen_resume(struct wm_drive *d) +{ + return (0); +} /* gen_resume() */ + +/* + * gen_stop() + * + */ +int +gen_stop(struct wm_drive *d) +{ + return (0); +} /* gen_stop() */ + +/* + * gen_eject() + * + */ +int +gen_eject(struct wm_drive *d) +{ + return (0); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + *----------------------------------------*/ +int gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!wmcd_close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + +int +scale_volume(int vol,int max) +{ + return ((vol * (max_volume - min_volume)) / max + min_volume); +} /* scale_volume() */ + +int +unscale_volume(int vol,int max) +{ + int n; + n = ( vol - min_volume ) * max_volume / (max - min_volume); + return (n <0)?0:n; +} /* unscale_volume() */ + +/* + * gen_set_volume() + * + */ +int +gen_set_volume(struct wm_drive *d,int left,int right) +{ + return (0); +} /* gen_set_volume() */ + +/* + * gen_get_volume() + * + */ +int +gen_get_volume(struct wm_drive *d,int *left,int *right) +{ + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + * + * For systems without working wm_scsi(), this should return -1 + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return wm_scsi_get_cdtext(d, pp_buffer, p_buffer_lenght); +} /* gen_get_cdtext() */ + +#endif /* TEMPLATESYSTEM */ diff --git a/kscd/libwm/plat_ultrix.c b/kscd/libwm/plat_ultrix.c new file mode 100644 index 00000000..2685b0f5 --- /dev/null +++ b/kscd/libwm/plat_ultrix.c @@ -0,0 +1,663 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * ULTRIX 4.2 drive control routines. + */ + +static char plat_ultrix_id[] = "$Id$"; + +#if defined(ultrix) || defined(__ultrix) + + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> +#include <fcntl.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <ustat.h> +#include <string.h> +#include <sys/rzdisk.h> +#include <sys/cdrom.h> + +#include "include/wm_config.h" +#include "include/wm_cdtext.h" +#include "include/wm_struct.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM + +/* + * This structure will be filled with the TOC header and all entries. + * Ultrix doesn't seem to allow getting single TOC entries. + * - Chris Ross (cross@eng.umd.edu) + */ +struct cd_toc_header_and_entries +{ + struct cd_toc_header cdth; + struct cd_toc_entry cdte[CDROM_MAX_TRACK+1]; +}; + +void *malloc(); +char *strchr(); + +int min_volume = 128; +int max_volume = 255; + +char * ultrix_cd_device = NULL; +/* + * fgetline() + * + * Simulate fgets, but joining continued lines in the output of uerf. + */ + +#define BUF_SIZE 85 /* Max length of a (real) line */ + +char * +fgetline( FILE *fp ) +{ + static char *retval = NULL; + static char holdbuf[BUF_SIZE + 1]; + char tmp[BUF_SIZE + 1]; + char *stmp; + + if (!retval) + { + retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */ + if (!retval) + return(NULL); + else + *retval = '\0'; + } + + if (*holdbuf) + { + strcpy(retval, holdbuf); + retval[strlen(retval)-1] = '\0'; + memset(holdbuf, 0, BUF_SIZE+1); + } + + while (fgets(tmp, BUF_SIZE, fp)) + { + stmp = tmp + strspn(tmp, " \t"); + if (*stmp == '_') + { /* Continuation line */ + retval[strlen(retval)-1] = '\0'; /* Trim off C/R */ + strcat(retval, stmp+1); + } else { + if (*retval) + { + strcpy(holdbuf, tmp); + holdbuf[strlen(holdbuf)-1] = -1; + return retval; + } else { /* First line read, keep reading */ + strcat(retval, stmp); + retval[strlen(retval)-1] = '\0'; + } + } + } + return NULL; +} /* fgetline() */ + +/* + * find_cdrom + * + * Determine the name of the CD-ROM device. + * + * Read through the boot records (via a call to uerf) and find the SCSI + * address of the CD-ROM. If the "CDROM" environment variable is set, + * use that instead. + */ +const char* +find_cdrom() +{ + char *data; + FILE *uerf; + int fds[2]; + int pid; + const char* device = NULL; + + device = getenv("CDROM"); + + if (device != NULL) + { + if(strncmp("/dev/", device, 5) || strstr(device, "/../")) + return NULL; + } + + pipe(fds); + + if ((pid = fork()) == 0) + { + close(fds[0]); + dup2(fds[1], 1); + execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0); + execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0); + return NULL; /* _exit(1); */ + } else if (pid < 0) { + perror("fork"); + return NULL; /* exit(1); */ + } + + close(fds[1]); + uerf = fdopen(fds[0], "r"); + + while (data = fgetline(uerf)) + if (strstr(data, "RRD42")) + { + char *device_p; + + ultrix_cd_device = (char *)malloc(sizeof("/dev/rrz##c")); + strcpy(ultrix_cd_device, "/dev/r"); + device_p = strstr(data, "rz"); + device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0'; + strcat(ultrix_cd_device, device_p); + strcat(ultrix_cd_device, "c"); + device = ultrix_cd_device; + break; + } + + fclose(uerf); + + if (device == NULL) + { + fprintf(stderr, "No cdrom (RRD42) is installed on this system\n"); + return NULL; /* exit(1); */ + } + + kill(pid, 15); + (void)wait((int *)NULL); + return device; +} /* find_cdrom() */ + +/* + * initialize the drive. a no-op for the generic driver. + */ +int +gen_init( struct wm_drive *d ) +{ + return (0); +} /* gen_init() */ + + +/* + * Open the CD device and figure out what kind of drive is attached. + */ +int +wmcd_open( struct wm_drive *d ) +{ + int fd; + static int warned = 0; + + if (d->fd >= 0) /* Device already open? */ + { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_open(): [device is open (fd=%d)]\n", d->fd); + return (0); + } + + if (d->cd_device == NULL) + d->cd_device = find_cdrom(); + + d->fd = open(d->cd_device, 0); + if (d->fd < 0) + { + if (errno == EACCES) + { + return -EACCES; + } + else if (errno != EINTR) + { + return( -6 ); + } + + /* No CD in drive. */ + return (1); + } + + /* Now fill in the relevant parts of the wm_drive structure. */ + find_drive_struct("", "", ""); + d->fd = fd; + + (d->init)(d); + + return (0); +} /* wmcd_open() */ + +/* + * Re-Open the device if it is open. + */ +int +wmcd_reopen( struct wm_drive *d ) +{ + int status; + + do { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wmcd_reopen\n"); + status = gen_close( d ); + wm_susleep( 1000 ); + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "calling wmcd_open()\n"); + status = wmcd_open( d ); /* open it as usual */ + wm_susleep( 1000 ); + } while ( status != 0 ); + return status; +} /* wmcd_reopen() */ + +/* + * Send an arbitrary SCSI command to a device. + */ +int +wm_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen, + void *retbuf, int retbuflen, int getreply ) +{ + /* ULTRIX doesn't have a SCSI passthrough interface, does it? */ + return (-1); +} /* wm_scsi() */ + +int +gen_close( struct wm_drive *d ) +{ + if(d->fd != -1) { + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n"); + close(d->fd); + d->fd = -1; + } + return 0; +} + +/* + * Get the current status of the drive: the current play mode, the absolute + * position from start of disc (in frames), and the current track and index + * numbers if the CD is playing or paused. + */ +int +gen_get_drive_status( struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *index) +{ + struct cd_sub_channel sc; + struct cd_subc_channel_data scd; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + sc.sch_address_format = CDROM_MSF_FORMAT; + sc.sch_data_format = CDROM_CURRENT_POSITION; + sc.sch_track_number = 0; + sc.sch_alloc_length = sizeof(scd); + sc.sch_buffer = (caddr_t)&scd; + + /* Is the device open? */ + if (d->fd < 0) + { + switch (wmcd_open(d)) + { + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc)) + return (0); /* ejected */ + + switch (scd.scd_header.sh_audio_status) + { + case AS_PLAY_IN_PROGRESS: + *mode = WM_CDM_PLAYING; +dopos: + *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 + + scd.scd_position_data.scp_absaddr.msf.s_units * 75 + + scd.scd_position_data.scp_absaddr.msf.f_units; + *track = scd.scd_position_data.scp_track_number; + *index = scd.scd_position_data.scp_index_number; + break; + + case AS_PLAY_PAUSED: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + goto dopos; + } + else + *mode = WM_CDM_STOPPED; + break; + + case AS_PLAY_COMPLETED: + *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */ + break; + + case AS_NO_STATUS: + *mode = WM_CDM_STOPPED; + break; + } + return (0); +} /* gen_get_drive_status() */ + +/* + * Get the number of tracks on the CD. + */ +int +gen_get_trackcount( struct wm_drive *d, int *tracks ) +{ + struct cd_toc_header hdr; + + if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) + return (-1); + + *tracks = hdr.th_ending_track; + + return (0); +} /* gen_get_trackcount() */ + +/* + * Get the start time and mode (data or audio) of a track. + * + * XXX - this should get cached, but that means keeping track of ejects. + */ +int +gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe) +{ + struct cd_toc toc; + struct cd_toc_header hdr; + struct cd_toc_header_and_entries toc_buffer; + + if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr)) + return (-1); + + bzero((char *)&toc_buffer, sizeof(toc_buffer)); + toc.toc_address_format = CDROM_MSF_FORMAT; + toc.toc_starting_track = 0; + toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) + + hdr.th_data_len0) & 0xfff) + 2; + toc.toc_buffer = (caddr_t)&toc_buffer; + + if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc)) + return (-1); + + if (track == 0) + track = hdr.th_ending_track + 1; + + *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0; + *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 + + toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 + + toc_buffer.cdte[track - 1].te_absaddr.msf.f_units; + + return (0); +} /* gen_get_trackinfo() */ + +/* + * Get the number of frames on the CD. + */ +int +gen_get_cdlen(struct wm_drive *d, int *frames) +{ + int tmp; + return (gen_get_trackinfo(d, 0, &tmp, frames)); +} /* gen_get_cdlen() */ + +/* + * Play the CD from one position to another (both in frames.) + */ +int +gen_play( struct wm_drive *d, int start, int end ) +{ + struct cd_play_audio_msf msf; + + msf.msf_starting_M_unit = start / (60*75); + msf.msf_starting_S_unit = (start % (60*75)) / 75; + msf.msf_starting_F_unit = start % 75; + msf.msf_ending_M_unit = end / (60*75); + msf.msf_ending_S_unit = (end % (60*75)) / 75; + msf.msf_ending_F_unit = end % 75; + + if (ioctl(d->fd, SCSI_START_UNIT)) + return (-1); + if (ioctl(d->fd, CDROM_PLAY_MSF, &msf)) + return (-2); + + return (0); +} /* gen_play() */ + +/* + * Pause the CD. + */ +int +gen_pause( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDROM_PAUSE_PLAY)); +} /* gen_pause() */ + +/* + * Resume playing the CD (assuming it was paused.) + */ +int +gen_resume( struct wm_drive *d ) +{ + return (ioctl(d->fd, CDROM_RESUME_PLAY)); +} /* gen_resume() */ + +/* + * Stop the CD. + */ +int +gen_stop( struct wm_drive *d ) +{ + return (ioctl(d->fd, SCSI_STOP_UNIT)); +} /* gen_stop() */ + +/* + * Eject the current CD, if there is one. + */ +int +gen_eject(struct wm_drive *d) +{ + /* On some systems, we can check to see if the CD is mounted. */ + struct stat stbuf; + struct ustat ust; + + if (fstat(d->fd, &stbuf) != 0) + return (-2); + + /* Is this a mounted filesystem? */ + if (ustat(stbuf.st_rdev, &ust) == 0) + return (-3); + + return (ioctl(d->fd, CDROM_EJECT_CADDY)); +} /* gen_eject() */ + +/*----------------------------------------* + * Close the CD tray + * + * Please edit and send changes to + * milliByte@DeathsDoor.com + *----------------------------------------*/ + +int +gen_closetray(struct wm_drive *d) +{ +#ifdef CAN_CLOSE + if(!close(d->fd)) + { + d->fd=-1; + return(wmcd_reopen(d)); + } else { + return(-1); + } +#else + /* Always succeed if the drive can't close */ + return(0); +#endif /* CAN_CLOSE */ +} /* gen_closetray() */ + + +/* + * scale_volume(vol, max) + * + * Return a volume value suitable for passing to the CD-ROM drive. "vol" + * is a volume slider setting; "max" is the slider's maximum value. + * + * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack + * increases much faster toward the top end of the volume scale than it + * does at the bottom. To make up for this, we make the volume scale look + * sort of logarithmic (actually an upside-down inverse square curve) so + * that the volume value passed to the drive changes less and less as you + * approach the maximum slider setting. The actual formula looks like + * + * (max^2 - (max - vol)^2) * (max_volume - min_volume) + * v = --------------------------------------------------- + min_volume + * max^2 + * + * If your system's volume settings aren't broken in this way, something + * like the following should work: + * + * return ((vol * (max_volume - min_volume)) / max + min_volume); + */ +scale_volume( int vol, int max ) +{ + return ((max * max - (max - vol) * (max - vol)) * + (max_volume - min_volume) / (max * max) + min_volume); +} /* scale_volume() */ + + +/* + * unscale_volume(cd_vol, max) + * + * Given a value between min_volume and max_volume, return the volume slider + * value needed to achieve that value. + * + * Rather than perform floating-point calculations to reverse the above + * formula, we simply do a binary search of scale_volume()'s return values. + */ +static int +unscale_volume( int cd_vol, int max ) +{ + int vol = 0, top = max, bot = 0, scaled; + + while (bot <= top) + { + vol = (top + bot) / 2; + scaled = scale_volume(vol, max); + if (cd_vol == scaled) + break; + if (cd_vol < scaled) + top = vol - 1; + else + bot = vol + 1; + } + + if (vol < 0) + vol = 0; + else if (vol > max) + vol = max; + + return (vol); +} /* unscale_volume() */ + +/* + * Set the volume level for the left and right channels. Their values + * range from 0 to 100. + */ +int +gen_set_volume( struct wm_drive *d, int left, int right ) +{ + struct cd_playback pb; + struct cd_playback_status ps; + struct cd_playback_control pc; + + left = scale_volume(left, 100); + right = scale_volume(right, 100); + + bzero((char *)&pb, sizeof(pb)); + bzero((char *)&ps, sizeof(ps)); + bzero((char *)&pc, sizeof(pc)); + + pb.pb_alloc_length = sizeof(ps); + pb.pb_buffer = (caddr_t)&ps; + + if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) + return (-1); + + pc.pc_chan0_select = ps.ps_chan0_select; + pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ? + CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ? + CDROM_MAX_VOLUME : left; + pc.pc_chan1_select = ps.ps_chan1_select; + pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ? + CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ? + CDROM_MAX_VOLUME : right; + + pb.pb_alloc_length = sizeof(pc); + pb.pb_buffer = (caddr_t)&pc; + + if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb)) + return (-1); + + return (0); +} /* gen_set_volume() */ + +/* + * Read the initial volume from the drive, if available. Each channel + * ranges from 0 to 100, with -1 indicating data not available. + */ +int +gen_get_volume(struct wm_drive *d, int *left, int *right) +{ + struct cd_playback pb; + struct cd_playback_status ps; + + bzero((char *)&pb, sizeof(pb)); + bzero((char *)&ps, sizeof(ps)); + + pb.pb_alloc_length = sizeof(ps); + pb.pb_buffer = (caddr_t)&ps; + + if (d->fd >= 0) + { + if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb)) + *left = *right = -1; + else + { + *left = unscale_volume(ps.ps_chan0_volume, 100); + *right = unscale_volume(ps.ps_chan1_volume, 100); + } + } + else + *left = *right = -1; + + return (0); +} /* gen_get_volume() */ + +/*------------------------------------------------------------------------* + * gen_get_cdtext(drive, buffer, lenght) + *------------------------------------------------------------------------*/ + +int +gen_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_lenght) +{ + return -1; /* No SCSI, no CDTEXT */ +} /* gen_get_cdtext() */ + + +#endif diff --git a/kscd/libwm/scsi.c b/kscd/libwm/scsi.c new file mode 100644 index 00000000..4e0dd759 --- /dev/null +++ b/kscd/libwm/scsi.c @@ -0,0 +1,667 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Frontend functions for sending raw SCSI commands to the CD-ROM drive. + * These depend on wm_scsi(), which should be defined in each platform + * module. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "include/wm_config.h" +#include "include/wm_struct.h" +#include "include/wm_scsi.h" +#include "include/wm_platform.h" +#include "include/wm_helpers.h" +#include "include/wm_cdrom.h" +#include "include/wm_cdtext.h" + +#define SCMD_INQUIRY 0x12 +#define SCMD_MODE_SELECT 0x15 +#define SCMD_MODE_SENSE 0x1a +#define SCMD_START_STOP 0x1b +#define SCMD_PREVENT 0x1e +#define SCMD_READ_SUBCHANNEL 0x42 +#define SCMD_READ_TOC 0x43 +#define SCMD_PLAY_AUDIO_MSF 0x47 +#define SCMD_PAUSE_RESUME 0x4b + +#define SUBQ_STATUS_INVALID 0x00 +#define SUBQ_STATUS_PLAY 0x11 +#define SUBQ_STATUS_PAUSE 0x12 +#define SUBQ_STATUS_DONE 0x13 +#define SUBQ_STATUS_ERROR 0x14 +#define SUBQ_STATUS_NONE 0x15 +#define SUBQ_STATUS_NO_DISC 0x17 /* Illegal, but Toshiba returns it. */ +#define SUBQ_ILLEGAL 0xff + +#define PAGE_AUDIO 0x0e +#define LEADOUT 0xaa + +#define WM_MSG_CLASS WM_MSG_CLASS_SCSI + +/* local prototypes */ +int wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len ); +int wm_scsi2_pause_resume(struct wm_drive *d, int resume); +int wm_scsi2_prevent(struct wm_drive *d, int prevent); +int wm_scsi2_play(struct wm_drive *d, int sframe, int eframe); +int wm_scsi2_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe); +int wm_scsi2_get_cdlen(struct wm_drive *d, int frames); +int wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *ind); +int wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks); +int wm_scsi2_pause(struct wm_drive *d); +int wm_scsi2_resume(struct wm_drive *d); +int wm_scsi2_stop(struct wm_drive *d); +int wm_scsi2_eject(struct wm_drive *d); +int wm_scsi2_closetray(struct wm_drive *d); +int wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right); +int wm_scsi2_set_volume(struct wm_drive *d, int left, int right); +/* local prototypes END */ + +/* + * Send a SCSI command over the bus, with all the CDB bytes specified + * as unsigned char parameters. This doesn't use varargs because some + * systems have stdargs instead and the number of bytes in a CDB is + * limited to 12 anyway. + * + * d Drive structure + * buf Buffer for data, both sending and receiving + * len Size of buffer + * dir TRUE if the command expects data to be returned in the buffer. + * a0- CDB bytes. Either 6, 10, or 12 of them, depending on the command. + */ +/*VARARGS4*/ +int +sendscsi( struct wm_drive *d, void *buf, + unsigned int len, int dir, + unsigned char a0, unsigned char a1, + unsigned char a2, unsigned char a3, + unsigned char a4, unsigned char a5, + unsigned char a6, unsigned char a7, + unsigned char a8, unsigned char a9, + unsigned char a10, unsigned char a11 ) + +{ + int cdblen = 0; + unsigned char cdb[12]; + + cdb[0] = a0; + cdb[1] = a1; + cdb[2] = a2; + cdb[3] = a3; + cdb[4] = a4; + cdb[5] = a5; + + switch ((a0 >> 5) & 7) { + case 0: + cdblen = 6; + break; + + case 5: + cdb[10] = a10; + cdb[11] = a11; + cdblen = 12; + + case 1: + case 2: + case 6: /* assume 10-byte vendor-specific codes for now */ + cdb[6] = a6; + cdb[7] = a7; + cdb[8] = a8; + cdb[9] = a9; + if (! cdblen) + cdblen = 10; + break; + } + + return (wm_scsi(d, cdb, cdblen, buf, len, dir)); +} + +/* + * Send a MODE SENSE command and return the results (minus header cruft) + * in a user buffer. + * + * d Drive structure + * page Number of page to query (plus page control bits, if any) + * buf Result buffer + */ +int +wm_scsi_mode_sense( struct wm_drive *d, unsigned char page, unsigned char *buf ) +{ + unsigned char pagebuf[255]; + int status, i, len, offset; + + status = sendscsi(d, pagebuf, sizeof(pagebuf), 1, SCMD_MODE_SENSE, 0, + page, 0, sizeof(pagebuf), 0,0,0,0,0,0,0); + if (status < 0) + return (status); + + /* + * The first byte of the returned data is the transfer length. Then + * two more bytes and the length of whatever header blocks are in + * front of the page we want. + */ + len = pagebuf[0] - pagebuf[3] - 3; + offset = pagebuf[3] + 4; + for (i = 0; i < len; i++) + buf[i] = pagebuf[offset + i]; + + return (0); +} + +/* + * Send a MODE SELECT command. + * + * d Drive structure + * buf Page buffer (no need to put on block descriptors) + * len Size of page + */ +int +wm_scsi_mode_select( struct wm_drive *d, unsigned char *buf, unsigned char len ) +{ + unsigned char pagebuf[255]; + int i; + + pagebuf[0] = pagebuf[1] = pagebuf[2] = pagebuf[3] = 0; + for (i = 0; i < (int) len; i++) + pagebuf[i + 4] = buf[i]; + + return (sendscsi(d, pagebuf, len + 4, 0, SCMD_MODE_SELECT, 0x10, 0, + 0, len + 4, 0,0,0,0,0,0,0)); +} + +/* + * Send an INQUIRY command to get the drive type. + * + * d Drive structure + * vendor Buffer for vendor name (8 bytes + null) + * model Buffer for model name (16 bytes + null) + * rev Buffer for revision level (4 bytes + null) + * + * The above string lengths apply to the SCSI INQUIRY command. The + * actual WorkMan drive structure reserves 31 bytes + NULL per entry. + * + * If the model name begins with "CD-ROM" and zero or more spaces, that will + * all be stripped off since it's just extra junk to WorkMan. + */ +int +wm_scsi_get_drive_type( struct wm_drive *d, char *vendor, + char *model, char *rev ) +{ +/* removed unsigned*/ + char *s, *t, buf[36]; + memset(buf, 0, 36); + + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "Sending SCSI inquiry command...\n"); + if (sendscsi(d, buf, 36, 1, SCMD_INQUIRY, 0, 0, 0, 36, 0,0,0,0,0,0,0)) + { + sprintf( vendor, WM_STR_GENVENDOR); + sprintf( model, WM_STR_GENMODEL); + sprintf( rev, WM_STR_GENREV); + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_ERROR, "SCSI Inquiry command not supported in this context\n"); + return -1; + } + + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_DEBUG, "sent.\n"); + + memcpy(vendor, buf + 8, 8); + vendor[8] = '\0'; + memcpy(model, buf + 16, 16); + model[16] = '\0'; + memcpy(rev, buf + 32, 4); + rev[4] = '\0'; + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "SCSI Inquiry result: [%s|%s|%s]\n", vendor, model, rev); + + + /* Remove "CD-ROM " from the model. */ + if (! strncmp(model, "CD-ROM", 6)) + { + s = model + 6; + t = model; + while (*s == ' ' || *s == '\t') + s++; + while( (*t++ = *s++) ) + ; + } + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_INFO, "scsi: Cooked data: %s %s rev. %s\n", vendor, model,rev); + return (0); +} /* wm_scsi_get_drive_type() */ + +/* + * Send a SCSI-2 PAUSE/RESUME command. "resume" is 1 to resume, 0 to pause. + */ +int +wm_scsi2_pause_resume(struct wm_drive *d, int resume) +{ + return (sendscsi(d, NULL, 0, 0, SCMD_PAUSE_RESUME, 0, 0, 0, 0, 0, 0, + 0, resume ? 1 : 0, 0,0,0)); +} + +/* + * Send a SCSI-2 "prevent media removal" command. "prevent" is 1 to lock + * caddy in. + */ +int +wm_scsi2_prevent(struct wm_drive *d, int prevent) +{ + return (sendscsi(d, NULL, 0, 0, SCMD_PREVENT, 0, 0, 0, 0, 0, 0, + 0, prevent ? 1 : 0, 0,0,0)); +} + +/* + * Send a SCSI-2 PLAY AUDIO MSF command. Pass the starting and ending + * frame numbers. + */ +int +wm_scsi2_play(struct wm_drive *d, int sframe, int eframe) +{ + return (sendscsi(d, NULL, 0, 0, SCMD_PLAY_AUDIO_MSF, 0, 0, + sframe / (60 * 75), (sframe / 75) % 60, sframe % 75, + eframe / (60 * 75), (eframe / 75) % 60, eframe % 75, + 0,0,0)); +} + +/* + * Send a SCSI-2 READ TOC command to get the data for a particular track. + * Fill in track information from the returned data. + */ +int +wm_scsi2_get_trackinfo(struct wm_drive *d, int track, + int *data, int *startframe) +{ + unsigned char buf[12]; /* one track's worth of info */ + + if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 2, + 0, 0, 0, 0, track, sizeof(buf) / 256, + sizeof(buf) % 256, 0,0,0)) + return (-1); + + *data = buf[5] & 4 ? 1 : 0; + *startframe = buf[9] * 60 * 75 + buf[10] * 75 + buf[11]; + + return (0); +} + +/* + * Get the starting frame for the leadout area (which should be the same as + * the length of the disc as far as WorkMan is concerned). + */ +int +wm_scsi2_get_cdlen(struct wm_drive *d, int frames) +{ + int tmp; + return (wm_scsi2_get_trackinfo(d, LEADOUT, &tmp, &frames)); +} + +/* + * Get the current status of the drive by sending the appropriate SCSI-2 + * READ SUB-CHANNEL command. + */ +int +wm_scsi2_get_drive_status(struct wm_drive *d, int oldmode, + int *mode, int *pos, int *track, int *ind) +{ + unsigned char buf[48]; + + /* If we can't get status, the CD is ejected, so default to that. */ + *mode = WM_CDM_EJECTED; + + /* Is the device open? */ + if (d->fd < 0) + { +/* + * stupid somehow, but necessary this time + */ + switch( wmcd_open( d ) ) { + + case -1: /* error */ + return (-1); + + case 1: /* retry */ + return (0); + } + } + + /* If we can't read status, the CD has been ejected. */ + buf[1] = SUBQ_ILLEGAL; + if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_SUBCHANNEL, 2, 64, 1, + 0, 0, 0, sizeof(buf) / 256, sizeof(buf) % 256, 0,0,0)) + return (0); + + switch (buf[1]) { + case SUBQ_STATUS_PLAY: + *mode = WM_CDM_PLAYING; + *track = buf[6]; + *ind = buf[7]; + *pos = buf[9] * 60 * 75 + buf[10] * 75 + buf[11]; + break; + + case SUBQ_STATUS_PAUSE: + if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED) + { + *mode = WM_CDM_PAUSED; + *track = buf[6]; + *ind = buf[7]; + *pos = buf[9] * 60 * 75 + + buf[10] * 75 + + buf[11]; + } + else + *mode = WM_CDM_STOPPED; + break; + + /* + * SUBQ_STATUS_DONE is sometimes returned when the CD is idle, + * even though the spec says it should only be returned when an + * audio play operation finishes. + */ + case SUBQ_STATUS_DONE: + case SUBQ_STATUS_NONE: + case SUBQ_STATUS_INVALID: + if (oldmode == WM_CDM_PLAYING) + *mode = WM_CDM_TRACK_DONE; + else + *mode = WM_CDM_STOPPED; + break; + + /* + * This usually means there's no disc in the drive. + */ + case SUBQ_STATUS_NO_DISC: + break; + + /* + * This usually means the user ejected the CD manually. + */ + case SUBQ_STATUS_ERROR: + break; + + case SUBQ_ILLEGAL: /* call didn't really succeed */ + break; + + default: + *mode = WM_CDM_UNKNOWN; +#ifndef NDEBUG + if( getenv( "WORKMAN_DEBUG" ) != NULL ) + printf("wm_scsi2_get_drive_status: status is 0x%x\n", + buf[1]); +#endif + break; + } + + return (0); +} + +/* + * Get the number of tracks on the CD using the SCSI-2 READ TOC command. + */ +int +wm_scsi2_get_trackcount(struct wm_drive *d, int *tracks) +{ + unsigned char buf[4]; + + if (sendscsi(d, buf, sizeof(buf), 1, SCMD_READ_TOC, 0, + 0, 0, 0, 0, 0, sizeof(buf) / 256, + sizeof(buf) % 256, 0,0,0)) + return (-1); + + *tracks = buf[3] - buf[2] + 1; + return (0); +} + +/* + * Pause the CD. + */ +int +wm_scsi2_pause(struct wm_drive *d) +{ + return (wm_scsi2_pause_resume(d, 0)); +} + +/* + * Resume playing after a pause. + */ +int +wm_scsi2_resume(struct wm_drive *d) +{ + return (wm_scsi2_pause_resume(d, 1)); +} + +/* + * Stop playing the CD by sending a START STOP UNIT command. + */ +int +wm_scsi2_stop(struct wm_drive *d) +{ + return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 0, 0,0,0,0,0,0,0,0,0,0)); +} + +/* + * Eject the CD by sending a START STOP UNIT command. + */ +int +wm_scsi2_eject(struct wm_drive *d) +{ + /* Unlock the disc (possibly unnecessary). */ + if (wm_scsi2_prevent(d, 0)) + return (-1); + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for ejecting...\n"); + return (sendscsi(d, NULL, 0, 0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0)); +} + +/* + * Something like a dummy. The SCSI-2 specs are too hard for me to + * understand here... + * + * If you have the correct command handy, please send the code to + * milliByte@DeathsDoor.com + */ +int +wm_scsi2_closetray(struct wm_drive *d) +{ + wm_lib_message(WM_MSG_CLASS_SCSI | WM_MSG_LEVEL_VERB, "Issuing START_STOP for closing...\n"); + return (sendscsi(d, NULL, 0,0, SCMD_START_STOP, 2, 0,0,0,0,0,0,0,0,0,0)); +} + +/* + * Get the volume by doing a MODE SENSE command. + */ +int +wm_scsi2_get_volume(struct wm_drive *d, int *left, int *right) +{ + unsigned char mode[16]; + + *left = *right = -1; + + /* Get the current audio parameters first. */ + if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode)) + return (-1); + + *left = ((int) mode[9] * 100) / 255; + *right = ((int) mode[11] * 100) / 255; + + return (0); +} + +/* + * Set the volume by doing a MODE SELECT command. + */ +int +wm_scsi2_set_volume(struct wm_drive *d, int left, int right) +{ + unsigned char mode[16]; + + /* Get the current audio parameters first. */ + if (wm_scsi_mode_sense(d, PAGE_AUDIO, mode)) + return (-1); + + /* Tweak the volume part of the parameters. */ + mode[9] = (left * 255) / 100; + mode[11] = (right * 255) / 100; + + /* And send them back to the drive. */ + return (wm_scsi_mode_select(d, mode, sizeof(mode))); +} + +/*------------------------------------------------------------------------* + * wm_scsi_get_cdtext(drive, buffer, lenght) + * + * Return a buffer with cdtext-stream. buffer mus be allocated and filled + * + * + *------------------------------------------------------------------------*/ + +int +wm_scsi_get_cdtext(struct wm_drive *d, unsigned char **pp_buffer, int *p_buffer_length) +{ + int ret; + unsigned char temp[8]; + unsigned char *dynamic_temp; + int cdtext_possible; + unsigned short cdtext_data_length; + unsigned long feature_list_length; +#define IGNORE_FEATURE_LIST +#ifndef IGNORE_FEATURE_LIST + struct feature_list_header *pHeader; + struct feature_descriptor_cdread *pDescriptor; +#endif /* IGNORE_FEATURE_LIST */ + + dynamic_temp = NULL; + cdtext_possible = 0; + wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "wm_scsi_get_cdtext entered\n"); + + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: use GET_FEATURY_LIST(0x46)...\n"); + ret = sendscsi(d, temp, 8, 1, + 0x46, 0x02, 0x00, 0x1E, 0, + 0, 0, 0, 8, 0, 0, 0); + + if(ret) + { + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: GET_FEATURY_LIST(0x46) not implemented or broken. ret = %i!\n", ret); +#ifndef IGNORE_FEATURE_LIST + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT ERROR: Try #define IGNORE_FEATURE_LIST in libwm/scsi.c\n"); +#else + cdtext_possible = 1; + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) ignored. It's OK, because many CDROMS don't implement this feature\n"); +#endif /* IGNORE_FEATURE_LIST */ + } + else + { + feature_list_length = temp[0]*0xFFFFFF + temp[1]*0xFFFF + temp[2]*0xFF + temp[3] + 4; + + dynamic_temp = malloc(feature_list_length); + + if(!dynamic_temp) + return -1; + + memset(dynamic_temp, 0, feature_list_length); + ret = sendscsi(d, dynamic_temp, feature_list_length, 1, + 0x46, 0x02, 0x00, 0x1E, 0, 0, + 0, (feature_list_length>>8) & 0xFF, feature_list_length & 0xFF, 0, 0, 0); + + +#ifndef IGNORE_FEATURE_LIST + if(!ret) + { + pHeader = (struct feature_list_header*)dynamic_temp; +/* printf("length = %i, profile = 0x%02X%02X\n", pHeader->lenght_lsb, pHeader->profile_msb, pHeader->profile_lsb);*/ + pDescriptor = (struct feature_descriptor_cdread*)(dynamic_temp + sizeof(struct feature_list_header)); +/* printf("code = 0x%02X%02X, settings = 0x%02X, add_length = %i, add_settings = 0x%02X \n", + pDescriptor->feature_code_msb, pDescriptor->feature_code_lsb, pDescriptor->settings, + pDescriptor->add_lenght, pDescriptor->add_settings);*/ + + cdtext_possible = pDescriptor->add_settings & 0x01; + } + else + { + cdtext_possible = 0; + } + +#else + cdtext_possible = 1; +#endif /* IGNORE_FEATURE_LIST */ + + free (dynamic_temp); + dynamic_temp = 0; + } + + if(!cdtext_possible) + { + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: GET_FEATURY_LIST(0x46) says, CDTEXT is not present!\n"); + return EXIT_SUCCESS; + } + + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read, how long CDTEXT is?\n"); + ret = sendscsi(d, temp, 4, 1, + SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0, + 0, 0, 4, 0, 0, 0); + + if(ret) + { + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, + "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret); + } + else + { + cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */ + /* cdtext_data_length%18 == 0;? */ + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: CDTEXT is %i byte(s) long\n", cdtext_data_length); + /* cdc_buffer[2]; cdc_buffer[3]; reserwed */ + dynamic_temp = malloc(cdtext_data_length); + if(!dynamic_temp) + return -1; + + memset(dynamic_temp, 0, cdtext_data_length); + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: try to read CDTEXT\n"); + ret = sendscsi(d, dynamic_temp, cdtext_data_length, 1, + SCMD_READ_TOC, 0x00, 0x05, 0, 0, 0, + 0, (cdtext_data_length>>8) & 0xFF, cdtext_data_length & 0xFF, 0, 0, 0); + + if(ret) + { + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, + "CDTEXT ERROR: READ_TOC(0x43) with format code 0x05 not implemented or broken. ret = %i!\n", ret); + } + else + { + cdtext_data_length = temp[0]*0xFF + temp[1] + 4 + 1; /* divide by 18 + 4 ? */ + wm_lib_message(WM_MSG_LEVEL_INFO|WM_MSG_CLASS, "CDTEXT INFO: read %i byte(s) of CDTEXT\n", cdtext_data_length); + + /* send cdtext only 18 bytes packs * ? */ + *(p_buffer_length) = cdtext_data_length - 4; + *pp_buffer = malloc(*p_buffer_length); + if(!(*pp_buffer)) + { + return -1; + } + memcpy(*pp_buffer, dynamic_temp + 4, *p_buffer_length); + } + free(dynamic_temp); + dynamic_temp = 0; + } + + return ret; +} /* wm_scsi_get_cdtext() */ diff --git a/kscd/libwm/wm_helpers.c b/kscd/libwm/wm_helpers.c new file mode 100644 index 00000000..109efe1b --- /dev/null +++ b/kscd/libwm/wm_helpers.c @@ -0,0 +1,238 @@ +/* + * $Id$ + * + * This file is part of WorkMan, the civilized CD player library + * (c) 1991-1997 by Steven Grimm (original author) + * (c) by Dirk Frsterling (current 'author' = maintainer) + * The maintainer can be contacted by his e-mail address: + * milliByte@DeathsDoor.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * Some helpful functions... + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/time.h> +#include "include/workman_defs.h" +#include "include/wm_config.h" +#include "include/wm_helpers.h" +#include "include/wm_struct.h" + +#define WM_MSG_CLASS WM_MSG_CLASS_MISC + +int wm_lib_verbosity = WM_MSG_LEVEL_NONE; + +/* + * Some seleced functions of version reporting follow... + */ + +int wm_libver_major( void ){return WM_LIBVER_MAJOR;} +int wm_libver_minor( void ){return WM_LIBVER_MINOR;} +int wm_libver_pl( void ){return WM_LIBVER_PL;} + +char *wm_libver_name( void ) +{ + char *s = NULL; + + wm_strmcat(&s, WM_LIBVER_NAME); + return s; +} /* wm_libver_name() */ + +char *wm_libver_number( void ) +{ + char *s = NULL; + + s = malloc(10); + /* this is not used very often, so don't care about speed...*/ + sprintf(s, "%d.%d.%d", wm_libver_major(), wm_libver_minor(), wm_libver_pl()); + return s; +} /* wm_libver_number() */ + +char *wm_libver_date( void ) +{ + char *s = NULL; + wm_strmcat(&s, __DATE__); + return s; +} /* wm_libver_date() */ + +char *wm_libver_string( void ) +{ + char *s = NULL; + + wm_strmcat( &s, wm_libver_name() ); + wm_strmcat( &s, " " ); + wm_strmcat( &s, wm_libver_number() ); + return s; +} /* wm_libver_string() */ + + +/* + * + * Now for some memory management... + * + */ + +/* Free some memory and set a pointer to null. */ +void freeup( char **x ) +{ + if (*x != NULL) + { + free(*x); + *x = NULL; + } +} /* freeup() */ + +/* Copy into a malloced string. */ +void +wm_strmcpy( char **t, const char *s ) +{ + wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy(%s, '%s')\n", *t, s); + if (*t != NULL) + { + wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy freeing pointer %p\n", *t); + free(*t); + } + + *t = malloc(strlen(s) + 1); + if (*t == NULL) + { + perror("wm_strmcpy"); + exit(1); + } + + wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcpy finally copying (%p, '%s')\n", *t, s); + strncpy(*t, s, strlen(s)); +} /* wm_strmcpy() */ + +/* Add to a malloced string. */ +void +wm_strmcat( char **t, const char *s) +{ + int len = strlen(s) + 1; + + wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "wm_strmcat(%s, %s)\n", *t, s); + + if (*s == '\0') + return; + + if (*t != NULL) + { + len += strlen(*t); + *t = realloc(*t, len); + if (*t == NULL) + { + perror("wm_strmcat"); + exit(1); + } + strcat(*t, s); + } + else + wm_strmcpy(t, s); +} /* wm_strmcat() */ + +/* Duplicate a string. Some systems have this in libc, but not all. */ +char * +wm_strdup( char *s ) +{ + char *new; + + new = malloc(strlen(s) + 1); + if (new) + strcpy(new, s); + return (new); +} /* wm_strdup() */ + + +/* + * set and get verbosity level. + */ +void wm_lib_set_verbosity( int level ) +{ + if( WM_MSG_LEVEL_NONE <= level && level <= WM_MSG_LEVEL_DEBUG ) + { + wm_lib_verbosity = level; + wm_lib_message(WM_MSG_CLASS_MISC | WM_MSG_LEVEL_DEBUG, "Verbosity set to %d|%d\n", WM_MSG_LEVEL_DEBUG, level & WM_MSG_CLASS_ALL); + } +} /* wm_lib_set_verbosity */ + +int wm_lib_get_verbosity( void ) +{ + return wm_lib_verbosity; +} + +/* + * wm_lib_message(). + * + * any message that falls into allowed classes and has at least + * verbosity level wm_lib_verbosity & 0xf will be printed. + * + * Usage: + * + * wm_lib_message( WM_MSG_LEVEL | WM_MSG_CLASS, "format", contents); + * + * To simplify the usage, you may simply use WM_MSG_CLASS. It should be + * defined in each module to reflect the correct message class. + * + */ +void wm_lib_message( unsigned int level, const char *fmt, ... ) +{ + va_list ap; + /* verbosity level */ + unsigned int vlevel = wm_lib_verbosity & 0xf; + /* allowed classes */ + unsigned int vclass = (level & WM_MSG_CLASS_ALL) & (wm_lib_verbosity & WM_MSG_CLASS_ALL); + + /* + * just give me the level + */ + level &= 0xf; + if(level <= WM_MSG_LEVEL_NONE) + { + fprintf(stderr, "LibWorkMan warning: A LibWorkMan programmer specified an invalid message level.\n"); + } + /* + * print it only if level and class are allowed. + */ + if( (level <= vlevel) && (vclass != 0) ) + { + fprintf(stderr, "libWorkMan: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } +} /* wm_lib_message() */ + +/* + * Simulate usleep() using select(). + */ +int +wm_susleep( int usec ) +{ + struct timeval tv; + + timerclear(&tv); + tv.tv_sec = usec / 1000000; + tv.tv_usec = usec % 1000000; + return (select(0, NULL, NULL, NULL, &tv)); +} /* wm_susleep() */ + + diff --git a/kscd/panel.ui b/kscd/panel.ui new file mode 100644 index 00000000..3b6b218a --- /dev/null +++ b/kscd/panel.ui @@ -0,0 +1,468 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>kscdPanelDlg</class> +<widget class="QWidget"> + <property name="name"> + <cstring>kscdPanelDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>516</width> + <height>198</height> + </rect> + </property> + <property name="caption"> + <string>KsCD</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QComboBox"> + <property name="name"> + <cstring>songListCB</cstring> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>timeIcon</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Track progress</string> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>timeSlider</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>100</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="tracking"> + <bool>false</bool> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Track progress</string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer7</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Fixed</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLabel"> + <property name="name"> + <cstring>volumeIcon</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Volume control</string> + </property> + </widget> + <widget class="QSlider"> + <property name="name"> + <cstring>volumeSlider</cstring> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="toolTip" stdset="0"> + <string>Volume control</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="spacing"> + <number>1</number> + </property> + <widget class="QPushButton" row="1" column="1"> + <property name="name"> + <cstring>ejectPB</cstring> + </property> + <property name="text"> + <string>E&ject</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QFrame" row="0" column="2" rowspan="3" colspan="2"> + <property name="name"> + <cstring>backdrop</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>3</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>270</width> + <height>80</height> + </size> + </property> + <property name="paletteForegroundColor"> + <color> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </property> + <property name="paletteBackgroundColor"> + <color> + <red>0</red> + <green>0</green> + <blue>0</blue> + </color> + </property> + <property name="font"> + <font> + <pointsize>9</pointsize> + </font> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>2</number> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame" row="0" column="0" rowspan="2" colspan="2"> + <property name="name"> + <cstring>frameleds</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>100</width> + <height>35</height> + </size> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + </widget> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>statuslabel</cstring> + </property> + <property name="text"> + <string>Status</string> + </property> + </widget> + <widget class="QLabel" row="1" column="2"> + <property name="name"> + <cstring>volumelabel</cstring> + </property> + <property name="text"> + <string>Vol: --</string> + </property> + </widget> + <widget class="QLabel" row="0" column="3"> + <property name="name"> + <cstring>totaltimelabel</cstring> + </property> + </widget> + <widget class="QLabel" row="1" column="3"> + <property name="name"> + <cstring>tracklabel</cstring> + </property> + <property name="text"> + <string>--/--</string> + </property> + </widget> + <widget class="QWidget" row="0" column="4" rowspan="2" colspan="1"> + <property name="name"> + <cstring>symbols</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>30</width> + <height>0</height> + </size> + </property> + <property name="text" stdset="0"> + <string>Vol: --</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>artistlabel</cstring> + </property> + <property name="text"> + <string>Artist</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0" rowspan="1" colspan="5"> + <property name="name"> + <cstring>titlelabel</cstring> + </property> + <property name="text"> + <string>Title</string> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + <widget class="QPushButton" row="3" column="0"> + <property name="name"> + <cstring>shufflePB</cstring> + </property> + <property name="text"> + <string>R&andom</string> + </property> + <property name="toggleButton"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="3" column="3"> + <property name="name"> + <cstring>infoPB</cstring> + </property> + <property name="text"> + <string>E&xtras</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>playPB</cstring> + </property> + <property name="text"> + <string>Pla&y</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="1" column="0"> + <property name="name"> + <cstring>stopPB</cstring> + </property> + <property name="text"> + <string>Stop</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="3" column="2"> + <property name="name"> + <cstring>cddbPB</cstring> + </property> + <property name="text"> + <string>&CDDB</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="3" column="1"> + <property name="name"> + <cstring>repeatPB</cstring> + </property> + <property name="text"> + <string>Loop</string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toggleButton"> + <bool>true</bool> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="2" column="1"> + <property name="name"> + <cstring>nextPB</cstring> + </property> + <property name="text"> + <string>&Next</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + <widget class="QPushButton" row="2" column="0"> + <property name="name"> + <cstring>prevPB</cstring> + </property> + <property name="text"> + <string>Pre&vious</string> + </property> + <property name="flat"> + <bool>true</bool> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>playPB</sender> + <signal>clicked()</signal> + <receiver>kscdPanelDlg</receiver> + <slot>playClicked()</slot> + </connection> + <connection> + <sender>nextPB</sender> + <signal>clicked()</signal> + <receiver>kscdPanelDlg</receiver> + <slot>nextClicked()</slot> + </connection> + <connection> + <sender>prevPB</sender> + <signal>clicked()</signal> + <receiver>kscdPanelDlg</receiver> + <slot>prevClicked()</slot> + </connection> + <connection> + <sender>stopPB</sender> + <signal>clicked()</signal> + <receiver>kscdPanelDlg</receiver> + <slot>stopClicked()</slot> + </connection> + <connection> + <sender>ejectPB</sender> + <signal>clicked()</signal> + <receiver>kscdPanelDlg</receiver> + <slot>ejectClicked()</slot> + </connection> +</connections> +<tabstops> + <tabstop>songListCB</tabstop> + <tabstop>timeSlider</tabstop> + <tabstop>volumeSlider</tabstop> + <tabstop>playPB</tabstop> + <tabstop>stopPB</tabstop> + <tabstop>ejectPB</tabstop> + <tabstop>prevPB</tabstop> + <tabstop>nextPB</tabstop> + <tabstop>shufflePB</tabstop> + <tabstop>repeatPB</tabstop> + <tabstop>cddbPB</tabstop> + <tabstop>infoPB</tabstop> +</tabstops> +<slots> + <slot>playClicked()</slot> + <slot>stopClicked()</slot> + <slot>prevClicked()</slot> + <slot>ejectClicked()</slot> + <slot returnType="bool">nextClicked()</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/kscd/prefs.kcfgc b/kscd/prefs.kcfgc new file mode 100644 index 00000000..89d94319 --- /dev/null +++ b/kscd/prefs.kcfgc @@ -0,0 +1,5 @@ +# Code generation options for kconfig_compiler +File=kscd.kcfg +ClassName=Prefs +Singleton=true +Mutators=true diff --git a/kscd/version.h b/kscd/version.h new file mode 100644 index 00000000..9a87fa75 --- /dev/null +++ b/kscd/version.h @@ -0,0 +1 @@ +#define KSCDVERSION "1.6" diff --git a/kscd/workman2cddb.pl b/kscd/workman2cddb.pl new file mode 100755 index 00000000..539ce929 --- /dev/null +++ b/kscd/workman2cddb.pl @@ -0,0 +1,86 @@ +#!/usr/bin/perl +# +# usage: just start it. Then copy the files to the local database. +# +# I have a extra cddb category (x-converted) for them, becauce I'm to +# lazy to sort them all into the right category... +# +# make sure the output directory does *not* exist, the script refuses +# to work if it does (just to make sure that it does not overwrite +# something important) +# +# (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> / Copying: GNU GPL +# + +# config: output directory +$dir="/tmp/cddb"; + +############################################################################# + +$out=0; +open(IN,"$ENV{'HOME'}/.workmandb") ||\ + die "open $ENV{'HOME'}/.workmandb: $!"; +mkdir("$dir",0777) || die "mkdir $dir: $!"; + +while (<IN>) { + if (/^tracks/) { + if ($out) { + print OUT "EXTD=\n"; + for ($i = 2, $n = 0; $i < $t[1]+2; $i++) { + printf OUT "EXTT%d=\n",$i-2; + } + print OUT "PLAYORDER=\n"; + close OUT; + } + @t = split; + for ($i = 2, $n = 0; $i < $t[1]+2; $i++) { + $len = $t[$i]/75; + for ($quer = 0; $len > 0; $quer += $len % 10, $len /= 10) {}; + $n += $quer + ($len % 60); + } + $m = ($t[$t[1]+2] - int($t[2]/75)); + $id = (($n % 0xff) << 24) | ($m << 8) | $t[1]; + $magic = sprintf("%08x",$id); + open(OUT,">$dir/$magic") || die "open $dir/$magic: $!"; + $out=1; + } + + $cdname = $1 if (/^cdname (.*)/); + if (/^artist (.*)/) { + $artist = $1; + + print OUT "# xmcd 2.0 CD database file\n"; + print OUT "#\n"; + print OUT "# Track frame offsets:\n"; + for ($i = 2, $n = 0; $i < $t[1]+2; $i++) { + print OUT "#\t$t[$i]\n"; + } + print OUT "#\n"; + print OUT "# Disc length: $t[$t[1]+2] seconds\n"; + print OUT "#\n"; + print OUT "# Revision: 1\n"; + print OUT "# Submitted via: wdb2cddb 1.0\n"; + print OUT "#\n"; + + print OUT "DISCID=$magic\n"; + print OUT "DTITLE=$artist / $cdname\n"; + print "$magic - $artist / $cdname\n"; + $track=0; + } + if (/^track (.*)/) { + print OUT "TTITLE$track=$1\n"; + $track++; + } +} + +close IN; + +print OUT "EXTD=\n"; +for ($i = 2, $n = 0; $i < $t[1]+2; $i++) { + printf OUT "EXTT%d=\n",$i-2; +} +print OUT "PLAYORDER=\n"; +close OUT; + +print "\n*** The CDDB files are in $dir ***\n\n"; + diff --git a/kscd/xmcd.desktop b/kscd/xmcd.desktop new file mode 100644 index 00000000..a89db6f2 --- /dev/null +++ b/kscd/xmcd.desktop @@ -0,0 +1,75 @@ +[Desktop Entry] +Comment=CD Database File +Comment[af]=Cd Databasis Lêer +Comment[az]=CD Databeyzi Faylı +Comment[bg]=Файл с база от данни за КД +Comment[bn]=সিডি ডেটাবেস ফাইল +Comment[br]=Restr Stlennvon CD +Comment[bs]=CD baza podataka +Comment[ca]=Fitxer de base de dades de CD +Comment[cs]=Soubor CD databáze +Comment[cy]=Ffeil Cronfa Ddata CD +Comment[da]=Cd-databasefil +Comment[de]=CD-Datenbank-Datei +Comment[el]=Αρχείο βάσης δεδομένων CD +Comment[eo]=Lumdiskdatumbazo-dosiero +Comment[es]=Base de datos de CDs +Comment[et]=CD andmebaasi fail +Comment[eu]=CD datubase fitxategia +Comment[fa]=پروندۀ دادگان دیسک فشرده +Comment[fi]=CD-tietokantatiedosto +Comment[fr]=Banque de données pour CD +Comment[gl]=Ficheiro de base de datos de CD +Comment[he]=קובץ מסד נתוני תקליטור +Comment[hi]=सीडी डाटाबेस फ़ाइल +Comment[hr]=CD baza datoteka +Comment[hu]=CD-adatbázis fájl +Comment[id]=File Database CD +Comment[is]=CD gagnasafnsskrá +Comment[it]=File di database CD +Comment[ja]=CD データベースファイル +Comment[kk]=CD деректер қоры файлы +Comment[km]=ឯកសារមូលដ្ឋានទិន្នន័យស៊ីឌី +Comment[ko]=CD 데이터베이스 파일 +Comment[lt]=CD duomenų bazės Byla +Comment[lv]=CD Datubāzes Fails +Comment[mk]=Датотека за база на податоци за CD +Comment[ms]=Fail Pangkalan Data CD +Comment[mt]=Fajl Database tas-CDs +Comment[nb]=CD-databasefil +Comment[nds]=CD-Datenbankdatei +Comment[ne]=CD डाटाबेस फाइल +Comment[nl]=Bestand met cd-database +Comment[nn]=CD-databasefil +Comment[pa]=CD ਡਾਟਾਬੇਸ ਫਾਇਲ +Comment[pl]=Baza danych CD +Comment[pt]=Ficheiro de dados de CDs +Comment[pt_BR]=Arquivo de Dados de CDs +Comment[ro]=Fişier bază de date CD +Comment[ru]=Файл базы данных CD +Comment[se]=CD-diehtovuođđofiila +Comment[sk]=Databázový súbor CD +Comment[sl]=Zbirka podatkov o CD +Comment[sr]=Фајл CD базе података +Comment[sr@Latn]=Fajl CD baze podataka +Comment[sv]=Cd-databasfil +Comment[ta]=குறுந்தகடு தரவுத்தளக் கோப்பு +Comment[tg]=Файли Манбаъи Додаҳои Диски Фишурда +Comment[th]=แฟ้มฐานข้อมูลซีดี +Comment[tr]=CD Veritabanı Dosyası +Comment[uk]=Файл бази даних CD +Comment[uz]=CD maʼlumot baza fayli +Comment[uz@cyrillic]=CD маълумот база файли +Comment[ven]=CD ya databeizi ya faela +Comment[wa]=Båze di dnêyes des plakes +Comment[xh]=Ifayile yesiseko sedata ye CD +Comment[zh_CN]=CD 数据库文件 +Comment[zh_HK]=CD 資料庫檔案 +Comment[zh_TW]=CD 資料庫檔案 +Comment[zu]=Ifayela yesiseko sedata ye CD +DefaultApp=kedit +Icon=cdtrack +Type=MimeType +MimeType=text/xmcd +Patterns= + |