summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-rw-r--r--TODO113
-rw-r--r--gd_content_interface.c121
-rw-r--r--gd_content_interface.h187
-rw-r--r--i18n.c66
-rw-r--r--i18n.h2
-rwxr-xr-xmg_content_interface.c155
-rwxr-xr-xmg_content_interface.h247
-rw-r--r--mg_database.c13
-rw-r--r--mg_database.h37
-rw-r--r--mg_filters.c14
-rw-r--r--mg_filters.h6
-rw-r--r--mg_media.c70
-rw-r--r--mg_media.h67
-rw-r--r--mg_playlist.c209
-rw-r--r--mg_playlist.h143
-rw-r--r--mg_tools.h61
-rw-r--r--muggle.c122
-rw-r--r--muggle.h25
-rw-r--r--vdr_config.h119
-rw-r--r--vdr_decoder.c137
-rw-r--r--vdr_decoder.h158
-rw-r--r--vdr_decoder_mp3.c450
-rw-r--r--vdr_decoder_mp3.h114
-rw-r--r--vdr_menu.c373
-rw-r--r--vdr_menu.h92
-rw-r--r--vdr_network.h45
-rw-r--r--vdr_player.c1073
-rw-r--r--vdr_player.h137
-rw-r--r--vdr_setup.c70
-rw-r--r--vdr_setup.h76
-rw-r--r--vdr_sound.c609
-rw-r--r--vdr_stream.c332
-rw-r--r--vdr_stream.h57
34 files changed, 4907 insertions, 602 deletions
diff --git a/Makefile b/Makefile
index de4a3f2..46bac1e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#
# Makefile for a Video Disk Recorder plugin
#
-# $Id: Makefile,v 1.8 2004/02/23 15:41:21 RaK Exp $
+# $Id: Makefile,v 1.9 2004/05/28 15:29:18 lvw Exp $
# The official name of this plugin.
# This name will be used in the '-P...' option of VDR to load the plugin.
@@ -46,8 +46,9 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
### The object files (add further files here):
-OBJS = $(PLUGIN).o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o i18n.o
-BINOBJS = mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o
+OBJS = $(PLUGIN).o i18n.o vdr_menu.o mg_database.o mg_content_interface.o gd_content_interface.o mg_tools.o mg_media.o mg_filters.o mg_playlist.o vdr_decoder_mp3.o vdr_stream.o vdr_decoder.o vdr_player.o vdr_setup.o
+
+LIBS = -lmad -lmysqlclient
### Targets:
@@ -68,7 +69,7 @@ $(DEPFILE): Makefile
$(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
libvdr-$(PLUGIN).so: $(OBJS)
- $(CXX) $(CXXFLAGS) -shared $(OBJS) -lmysqlclient -o $@
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@
@cp $@ $(LIBDIR)/$@.$(VDRVERSION)
dist: clean
diff --git a/TODO b/TODO
index 2ce638a..93a5255 100644
--- a/TODO
+++ b/TODO
@@ -3,45 +3,126 @@ TODO File for Muggle
Code polishing
==============
-- check for memory leaks
-- check for (reasonably) consistent usage of char*/string
-- mg_database is not used
+- Check for memory leaks
+- Check for (reasonably) consistent usage of char*/string
+- mgDatabase is not used? mgPlayer used what for?
+- Check for unnecessary log commands
+- Add logs, documentation (generate in HTML)
+- Generate HTML documentation using doxygen, dotty/gv for state machines
Short term items
================
+- Adapt scripts
+ - use working directory by default or argument
+ - how to install into correct path
OSD
---
-- DisplayTrackInfo
-- DisplayAlbumInfo
-- Edit playlist (means moving tracks around?)
Content
-------
+- Handle loop mode in mgPlaylist
+- Handle shuffle mode in mgPlaylist
+- Save/load playlists to database
+
+Player
+------
+- Determine max. framecount (needed for rewinding) ?
+- Init scale/level/normalize?
+- Check play speed
+- Add ogg decoder
+
+- Add a simple progress display (song title, artist, ...)
+- DisplayTrackInfo (part of the player!)
+- DisplayAlbumInfo (part of the player!)
Medium term items
=================
-- Add a player for various formats
- - from mp3 plugin
- - use mplayer in slave mode
- - check mpg123 (used in GD)
+- really abstract from specific queries etc.
+- mgDatabase should abstract database stuff!?
-Long term items
-===============
+OSD
+---
+- Type numbers to enter characters and jump to first title accordingly
+- Add separators for first letter (A, B, C, ...) that can be skipped using left/right
+Content
+-------
+- Save/load filter sets
+- Apply filter set as dynamic playlist
+
+Player
+------
+- Shuffle: toggle and loop keys. Shuffle only songs not already played, not easy though
- Display covers as still pictures
+- Add flac decoder
+
+Web interface
+-------------
+- Look at PHP stuff from c't 11/04 and adapt to schema
+
+Long term ideas
+===============
+- daapd integration?
+- Display arbitrary images while playing music
Visions
=======
-
-- handle films
-- handle off-line media (CDs, DVDs)
-- handle EPG
+- handle off-line films (CDs, DVDs, recordings)
+- handle streams (live TV with channel list, MP3 radio,..., EPG)
+- handle images (possibly in sync with music/radio)
+- muggle content syndication (e.g. via DAAPD)
+- provide a stream (e.g. icecast) of the currently played music?
Already Done
============
+- Delete selected item
+- Add command line option for top level directory
+ - prepended to filename in non-GD-mode
+ - searched in GD-mode
+- Edit playlist (move tracks like channels in VDR channel list)
+ (OK in playlist view)
+- Instant play = empty current playlist, append tracks of current node and play
+ (easy, in submenu of browser)
+- Clear playlist (submenu action)
+- Find files from database entry based on GD compatibility flag
+- Handle Next/PrevFile in mgPlaylist (vdr_player.c)
+- Add plugin parameters for database name/host/user/pass
+- Add plugin parameter for GD filename compatibility
- handle filters:
- create tracklist from filter
- create tree from filter
- i18n (english and german)
+
+************************************************************
+*
+* $Log: TODO,v $
+* Revision 1.7 2004/05/28 15:29:18 lvw
+* Merged player branch back on HEAD branch.
+*
+* Revision 1.1.2.12 2004/05/27 07:58:38 lvw
+* Removed bugs in moving and removing tracks from playlists
+*
+* Revision 1.1.2.11 2004/05/25 21:57:58 lvw
+* Updated TODO list
+*
+* Revision 1.1.2.10 2004/05/25 00:10:45 lvw
+* Code cleanup and added use of real database source files
+*
+* Revision 1.1.2.9 2004/05/24 11:48:35 lvw
+* Extended TODO list
+*
+* Revision 1.1.2.8 2004/05/13 06:48:00 lvw
+* Updated TODO list
+*
+* Revision 1.1.2.7 2004/05/12 22:38:37 lvw
+* Some cleanup
+*
+* Revision 1.1.2.6 2004/05/11 06:35:16 lvw
+* Added debugging while hunting stop bug.
+*
+* Revision 1.1.2.5 2004/05/07 06:46:41 lvw
+* Removed a bug in playlist deallocation. Added infrastructure to display information while playing.
+*
+***********************************************************/
diff --git a/gd_content_interface.c b/gd_content_interface.c
index ba5429a..9c339d6 100644
--- a/gd_content_interface.c
+++ b/gd_content_interface.c
@@ -1,19 +1,16 @@
-/*******************************************************************/
/*! \file content_interface.cpp
- * \brief Data Objects for content (e.g. mp3 files, movies)
- * for the vdr muggle plugindatabase
- ********************************************************************
- * \version $Revision: 1.21 $
- * \date $Date: 2004/02/23 17:03:24 $
+ * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugindatabase
+ *
+ * \version $Revision: 1.22 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
*
- * DUMMY
* Implements main classes of for content items and interfaces to SQL databases
*
* This file implements the following classes
* - GdPlaylist a playlist
- * - mgGdTrack a single track (content item). e.g. an mp3 file
+ * - mgGdTrack a single track (content item). e.g. an mp3 file
* - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
*
*/
@@ -21,8 +18,10 @@
#define DEBUG
#include "gd_content_interface.h"
+
#include "mg_tools.h"
#include "i18n.h"
+#include "vdr_setup.h"
using namespace std;
@@ -35,16 +34,23 @@ using namespace std;
// non-member function
int GdInitDatabase(MYSQL *db)
{
- if(mysql_init(db) == NULL)
+ if( mysql_init(db) == NULL )
{
return -1;
}
- if(mysql_real_connect(db,"localhost","root","",
- "GiantDisc",0,NULL,0) == NULL)
- {
+ // if(mysql_real_connect( db, "localhost", "root", "",
+ // "GiantDisc2", 0, NULL, 0) == NULL)
+ if( mysql_real_connect( db,
+ the_setup.DbHost,
+ the_setup.DbUser,
+ the_setup.DbPass,
+ the_setup.DbName,
+ the_setup.DbPort,
+ NULL, 0 ) == NULL )
+ {
return -2;
- }
+ }
return 0;
}
@@ -63,20 +69,12 @@ vector<string> *GdGetStoredPlaylists(MYSQL db)
return list;
}
-//------------------------------------------------------------------
-//------------------------------------------------------------------
-//
-// class gdTrackFilters
-//
-//------------------------------------------------------------------
-//------------------------------------------------------------------
-
/*!
- *******************************************************************
+ *
* \brief constructor, constracts a number >=1 of filter sets
*
* the first set (index 0 ) is active by default
- ********************************************************************/
+ */
gdFilterSets::gdFilterSets()
{
mgFilter* filter;
@@ -102,8 +100,9 @@ gdFilterSets::gdFilterSets()
filter = new mgFilterString(tr("artist"), ""); set->push_back(filter);
// genre
filter = new mgFilterString(tr("genre"), ""); set->push_back(filter);
- // rating
- filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter);
+
+ // rating. TODO: Currently buggy. LVW
+ // filter = new mgFilterChoice(tr("rating"), 1, rating); set->push_back(filter);
m_sets.push_back(set);
@@ -170,7 +169,8 @@ string gdFilterSets::computeRestriction(int *viewPrt)
{
string sql_str = "1";
- switch (m_activeSetId) {
+ switch (m_activeSetId)
+ {
case 0:
*viewPrt = 100; // tracks (flatlist for mountain man ;-))
break;
@@ -261,7 +261,7 @@ string gdFilterSets::computeRestriction(int *viewPrt)
//------------------------------------------------------------------
//------------------------------------------------------------------
//
-// class mgTack
+// class mgTrack
//
//------------------------------------------------------------------
//------------------------------------------------------------------
@@ -269,7 +269,7 @@ mgGdTrack mgGdTrack::UNDEFINED = mgGdTrack();
/*!
*****************************************************************************
- * \brief Constructor: creates a mgGdTrack obect
+ * \brief Constructor: creates a mgGdTrack object
*
* \param sqlIdentifier identifies a unique row in the track database
* \param dbase database which stores the track table
@@ -296,16 +296,15 @@ mgGdTrack::mgGdTrack(const mgGdTrack& org)
m_db = org.m_db;
m_retrieved = org.m_retrieved;
if(m_retrieved)
- {
- m_artist = org.m_artist;
- m_title = org.m_title;
- m_mp3file = org.m_mp3file;
- m_album = org.m_album;
- m_genre = org.m_genre;
- m_year = org.m_year;
- m_rating = org.m_rating;
- }
-
+ {
+ m_artist = org.m_artist;
+ m_title = org.m_title;
+ m_mp3file = org.m_mp3file;
+ m_album = org.m_album;
+ m_genre = org.m_genre;
+ m_year = org.m_year;
+ m_rating = org.m_rating;
+ }
}
@@ -459,6 +458,7 @@ vector<mgFilter*> *mgGdTrack::getTrackInfo()
{
return new vector<mgFilter*>();
}
+
bool mgGdTrack::setTrackInfo(vector<mgFilter*> *info)
{
return false;
@@ -766,7 +766,7 @@ GdPlaylist::GdPlaylist(unsigned int sql_identifier, MYSQL db_handle)
m_listname = row[0];
m_author = row[1];
m_sqlId = sql_identifier;
- // now read allentries of the playlist and
+ // now read all entries of the playlist and
// write them into the tracklist
insertDataFromSQL();
}
@@ -1391,6 +1391,46 @@ mgContentItem* GdTreeNode::getSingleTrack()
/* -------------------- begin CVS log ---------------------------------
* $Log: gd_content_interface.c,v $
+ * Revision 1.22 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ *
+ * Revision 1.21 2004/02/23 17:03:24 RaK
+ * - error in filter view while trying to switch or using the colour keys
+ * workaround: first filter criteria is inttype. than it works, dont ask why ;-(
+ *
+ * Revision 1.20 2004/02/23 16:30:58 RaK
+ * - album search error because of i18n corrected
+ *
+ * Revision 1.19 2004/02/23 15:56:19 RaK
+ * - i18n
+ *
+ * Revision 1.18 2004/02/23 15:17:51 RaK
+ * - i18n
+ *
+ * Revision 1.17 2004/02/23 15:41:21 RaK
+ * - first i18n attempt
+ *
+ * Revision 1.16 2004/02/14 22:02:45 RaK
+ * - mgFilterChoice Debuged
+ * fehlendes m_type = CHOICE in mg_filters.c
+ * falscher iterator in vdr_menu.c
+ *
+ * Revision 1.15 2004/02/12 09:15:07 LarsAC
+ * Moved filter classes into separate files
+ *
+ * Revision 1.14.2.4 2004/05/26 14:34:58 lvw
+ * Formatting changed
+ *
+ * Revision 1.14.2.3 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.14.2.2 2004/03/14 17:57:30 lvw
+ * Linked against libmad. Introduced config options into code.
+ *
+ * Revision 1.14.2.1 2004/03/02 07:05:50 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.21 2004/02/23 17:03:24 RaK
* - error in filter view while trying to switch or using the colour keys
* workaround: first filter criteria is inttype. than it works, dont ask why ;-(
@@ -1415,6 +1455,7 @@ mgContentItem* GdTreeNode::getSingleTrack()
* Revision 1.15 2004/02/12 09:15:07 LarsAC
* Moved filter classes into separate files
*
+>>>>>>> 1.14.2.4
* Revision 1.14 2004/02/12 07:56:46 RaK
* - SQL Fehler bei der Playlist Search korrigiert
*
diff --git a/gd_content_interface.h b/gd_content_interface.h
index c572c75..8e95267 100644
--- a/gd_content_interface.h
+++ b/gd_content_interface.h
@@ -1,24 +1,23 @@
-/*******************************************************************/
-/*! \file gd_content_interface.h
- * \brief Data Objects for content (e.g. mp3 files, movies)
- * for the vdr muggle plugindatabase
- ********************************************************************
- * \version $Revision: 1.6 $
- * \date $Date: 2004/02/23 15:41:21 $
+/*!
+ * \file gd_content_interface.h
+ * \brief Data objects for content (e.g. mp3 files, movies)
+ * for the vdr muggle plugin database
+ *
+ * \version $Revision: 1.7 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
*
- * Declares main classes of for content items and interfaces to SQL databases
+ * Declares main classes for content items and interfaces to SQL databases
*
* This file defines the following classes
- * - mgPlaylist a playlist
- * - mgGdTrack a single track (content item). e.g. an mp3 file
- * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ * - gdFilterSets
+ * - mgGdTrack a single track (content item). e.g. an mp3 file
+ * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
*
*/
-/*******************************************************************/
-/* makes sure we dont use parse the same declarations twice */
+/* makes sure we dont use the same declarations twice */
#ifndef _GD_CONTENT_INTERFACE_H
#define _GD_CONTENT_INTERFACE_H
@@ -29,75 +28,158 @@
#include "mg_content_interface.h"
#include "mg_media.h"
-#include "mg_filters.h"
+#include "mg_playlist.h"
+#include "mg_filters.h"
#include "i18n.h"
-// non-member function
+// non-member functions
int GdInitDatabase(MYSQL *db);
std::vector<std::string> *GdGetStoredPlaylists(MYSQL db);
+/*!
+ * \brief A set of filters to search for content
+ */
class gdFilterSets : public mgFilterSets
{
public:
+
+ /*!
+ * \brief the default constructor
+ *
+ * Constructs a number ( >= 1 ) of filter sets where
+ * the first (index 0) is active by default.
+ */
gdFilterSets();
- // constructor, constracts a number >=1 of filter sets
- // the first set (index 0 ) is active by default
+ /*!
+ * \brief the destructor
+ */
virtual ~gdFilterSets();
- // destructor
+ /*!
+ * \brief compute restriction w.r.t active filters
+ *
+ * Computes the (e.g. sql) restrictions specified by
+ * the active filter sets.
+ *
+ * \param viewPort - after call, contains the index of the appropriate default view in
+ */
virtual std::string computeRestriction(int *viewPrt);
- // computes the (e.g. sql-) restrictions specified by the active filter set
- // and returns the index of the appropriate defualt view in viewPrt
-
};
/*!
- *******************************************************************
- * \class mgGdTrack
- *
* \brief represents a a single track
+
* This may be any content item. e.g. a mp3 fileselection
- *
* The object is initially created with a database identifier.
* The actual data is only read when a content field is accessed for
- * The first time
- ********************************************************************/
+ * the first time. For subsequent access, cached values are used.
+ */
class mgGdTrack : public mgContentItem
{
private:
+
+ /*!
+ * \brief the database in which the track resides
+ * */
MYSQL m_db;
- bool m_retrieved; // false if content field values have not yet been
- // retrieved from database. This is done on demand
+
+ /*!
+ * \brief a dirty flag
+ *
+ * false, if content field values have not yet been retrieved
+ * from the database. Set to true when contents are retrieved
+ * (on demand only).
+ */
+ bool m_retrieved;
// content fields
+ /*!
+ * \brief the artist name
+ */
std::string m_artist;
+
+ /*!
+ * \brief the track title
+ */
std::string m_title;
+
+ /*!
+ * \brief the filename
+ */
std::string m_mp3file;
+
+ /*!
+ * \brief The album to which the file belongs
+ */
std::string m_album;
+
+ /*!
+ * \brief The genre of the music
+ */
std::string m_genre;
+
+ /*!
+ * \brief The year in which the track appeared
+ */
int m_year;
+
+ /*!
+ * \brief The rating by the user
+ */
int m_rating;
+
+ /*!
+ * \brief The length of the file in bytes.
+ * \todo TODO: is this true? Or what length are we talking about?
+ */
int m_length;
+ /*!
+ * \brief Access the data of the item from the database
+ */
bool readData();
public:
-
- /* constructor */
- mgGdTrack(){ m_uniqID = -1;} // creates invalid item
- mgGdTrack(int sqlIdentifier, MYSQL dbase);
+ /*!
+ * \brief a constructor
+ *
+ * Creates an invalid item.
+ *
+ * \todo does this make sense?
+ */
+ mgGdTrack()
+ {
+ m_uniqID = -1;
+ }
+
+ /*!
+ * \brief a constructor for a specific item
+ *
+ * The constructor creates a specific item in a given database
+ *
+ * \param sqlIdentifier - a unique ID of the item which will be represented
+ * \param dbase - the database in which the item exists
+ */
+ mgGdTrack( int sqlIdentifier, MYSQL dbase );
+
+ /*!
+ * \brief a copy constructor
+ */
mgGdTrack(const mgGdTrack&);
- /* destructor */
+ /*!
+ * \brief the destructor
+ */
virtual ~mgGdTrack();
virtual mgContentItem::contentType getContentType(){return mgContentItem::GD_AUDIO;}
+
virtual mgMediaPlayer getPlayer()
{
return mgMediaPlayer();
@@ -106,13 +188,17 @@ private:
/* data acess */
//virtual functions of the base class
virtual std::string getSourceFile();
+
virtual std::string getTitle();
- virtual std::string getLabel(int col);
+
+ virtual std::string getLabel(int col = 0);
virtual std::vector<mgFilter*> *getTrackInfo();
+
virtual bool setTrackInfo(std::vector<mgFilter*>*);
virtual std::string getGenre();
+
virtual int getRating();
// additional class-specific functions
@@ -221,12 +307,41 @@ public:
/* -------------------- begin CVS log ---------------------------------
* $Log: gd_content_interface.h,v $
+ * Revision 1.7 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ *
+ * Revision 1.6 2004/02/23 15:41:21 RaK
+ * - first i18n attempt
+ *
+ * Revision 1.5 2004/02/12 09:15:07 LarsAC
+ * Moved filter classes into separate files
+ *
+ * Revision 1.4.2.6 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.4.2.5 2004/04/01 21:35:32 lvw
+ * Minor corrections, some debugging aid.
+ *
+ * Revision 1.4.2.4 2004/03/14 17:57:30 lvw
+ * Linked against libmad. Introduced config options into code.
+ *
+ * Revision 1.4.2.3 2004/03/10 13:11:24 lvw
+ * Added documentation
+ *
+ * Revision 1.4.2.2 2004/03/08 07:14:27 lvw
+ * Preliminary changes to muggle player
+ *
+ * Revision 1.4.2.1 2004/03/02 07:05:50 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.6 2004/02/23 15:41:21 RaK
* - first i18n attempt
*
* Revision 1.5 2004/02/12 09:15:07 LarsAC
* Moved filter classes into separate files
*
+>>>>>>> 1.4.2.6
* Revision 1.4 2004/02/09 19:27:52 MountainMan
* filter set implemented
*
diff --git a/i18n.c b/i18n.c
index 3a0cbd8..cb36790 100644
--- a/i18n.c
+++ b/i18n.c
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: i18n.c,v 1.5 2004/02/23 17:07:08 RaK Exp $
+ * $Id: i18n.c,v 1.6 2004/05/28 15:29:18 lvw Exp $
*/
#include "i18n.h"
@@ -554,5 +554,69 @@ const tI18nPhrase Phrases[] = {
"",// TODO
"",// TODO
},
+ { "Load",
+ "Laden",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
+ { "Save",
+ "Speichern",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
+ { "Clear",
+ "Loeschen",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
+ { "Mainmenu",
+ "Hauptmenue",
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ "",// TODO
+ },
{ NULL }
};
diff --git a/i18n.h b/i18n.h
index e4e6993..ef5a31f 100644
--- a/i18n.h
+++ b/i18n.h
@@ -3,7 +3,7 @@
*
* See the README file for copyright information and how to reach the author.
*
- * $Id: i18n.h,v 1.1 2004/02/23 15:41:21 RaK Exp $
+ * $Id: i18n.h,v 1.2 2004/05/28 15:29:18 lvw Exp $
*/
#ifndef _I18N__H
diff --git a/mg_content_interface.c b/mg_content_interface.c
index 5d60358..437d8b5 100755
--- a/mg_content_interface.c
+++ b/mg_content_interface.c
@@ -1,14 +1,11 @@
-/*******************************************************************/
-/*! \file content_interface.c
- * \brief Data Objects for content (e.g. mp3 files, movies)
- * for the vdr muggle plugindatabase
- ********************************************************************
- * \version $Revision: 1.4 $
- * \date $Date: 2004/02/23 15:41:21 $
+/*! \file mg_content_interface.c
+ * \brief Data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin
+ *
+ * \version $Revision: 1.5 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
*
- * DUMMY
* Implements main classes of for content items and interfaces to SQL databases
*
* This file implements the following classes
@@ -17,7 +14,6 @@
* - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
*
*/
-/*******************************************************************/
#define DEBUG
#include "mg_content_interface.h"
@@ -31,22 +27,20 @@ mgContentItem mgContentItem::UNDEFINED = mgContentItem();
using namespace std;
/*!
- *****************************************************************************
- * \brief construcor
+ * \brief constructor
*
- * creates empty tracklist
- ****************************************************************************/
+ * create an empty tracklist
+ */
mgTracklist::mgTracklist()
{
}
/*!
- *****************************************************************************
* \brief destrucor
*
* Deletes all items in the tracklist and removes the list itself
- ****************************************************************************/
+ */
mgTracklist::~mgTracklist()
{
mgContentItem* ptr;
@@ -183,11 +177,15 @@ mgContentItem* mgTracklist::getItem(unsigned int position)
****************************************************************************/
bool mgTracklist::remove(unsigned int position)
{
- if(position >= m_list.size()) return false;
+ if( position >= m_list.size() )
+ {
+ return false;
+ }
vector<mgContentItem*>::iterator iter;
iter = m_list.begin()+ position;
m_list.erase(iter);
+
return true;
}
@@ -213,104 +211,6 @@ int mgTracklist::remove(mgContentItem* item)
}
-/*=================================================================*/
-/* */
-/* class mgPlaylist */
-/* */
-/*=================================================================*/
-mgPlaylist::mgPlaylist()
-{
-}
-mgPlaylist::mgPlaylist(string listname)
-{
- m_listname = listname;
-}
-
- /*==== destructor ====*/
-mgPlaylist::~mgPlaylist()
-{
-
-}
-/*==== add/ remove tracks ====*/
-
-/* adds a song at the end of the playlist */
-void mgPlaylist::append(mgContentItem* item)
-{
- m_list.push_back(item);
-}
-
-void mgPlaylist::appendList(vector<mgContentItem*> *tracks)
-{
- vector<mgContentItem*>::iterator iter;
- mgDebug(3, "Adding %d tracks to the playlist",tracks->size());
- for(iter = tracks->begin(); iter != tracks->end(); iter++)
- {
- m_list.push_back(*iter);
- }
- tracks->clear();
-}
-
-
-/* adds a song after 'position' */
-void mgPlaylist::insert(mgContentItem* item, unsigned int position)
-{
- if(position >= m_list.size())
- m_list.push_back(item);
- else
- m_list.insert(m_list.begin()+position, item);
-}
-
-
-
-/*==== access tracks ====*/
-string mgPlaylist::getListname() { return m_listname; }
-void mgPlaylist::setListname(string name){ m_listname = name;}
-
-
-
-// returns the first item of the list
-mgContentItem* mgPlaylist::getFirst()
-{
- m_current = m_list.begin();
- return *m_current;
-}
-
-// returns the nth track from the playlist
-mgContentItem* mgPlaylist::getPosition(unsigned int position)
-{
- if(position >= m_list.size())
- return &(mgContentItem::UNDEFINED); //invalid
- m_current = m_list.begin()+position;
- return *m_current;
-}
-
-// proceeds to the next item
-mgContentItem* mgPlaylist::skipFwd()
-{
- if(m_current+1 == m_list.end())
- return &(mgContentItem::UNDEFINED); //invalid
- else
- return * (++m_current);
-}
-
-// goes back to the previous item
-mgContentItem* mgPlaylist::skipBack()
-{
- if(m_current == m_list.begin())
- return &(mgContentItem::UNDEFINED); //invalid
- else
- return * (--m_current);
-}
-
-// get next track, do not update data structures
-mgContentItem* mgPlaylist::sneakNext()
-{
- if(m_current+1 == m_list.end())
- return &(mgContentItem::UNDEFINED); //invalid
- else
- return * (m_current+1);
-}
-
/*=================================================================*/
/* */
@@ -402,6 +302,31 @@ string mgSelectionTreeNode::getRestrictions()
/* -------------------- begin CVS log ---------------------------------
* $Log: mg_content_interface.c,v $
+ * Revision 1.5 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ *
+ * Revision 1.4 2004/02/23 15:41:21 RaK
+ * - first i18n attempt
+ *
+ * Revision 1.3.2.6 2004/05/27 07:58:38 lvw
+ * Removed bugs in moving and removing tracks from playlists
+ *
+ * Revision 1.3.2.5 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.3.2.4 2004/05/04 16:51:53 lvw
+ * Debugging aids added.
+ *
+ * Revision 1.3.2.3 2004/03/08 21:42:22 lvw
+ * Added count method. Some comments for further todos added.
+ *
+ * Revision 1.3.2.2 2004/03/08 07:14:27 lvw
+ * Preliminary changes to muggle player
+ *
+ * Revision 1.3.2.1 2004/03/02 07:08:12 lvw
+ * 118 additions
+ *
* Revision 1.4 2004/02/23 15:41:21 RaK
* - first i18n attempt
*
diff --git a/mg_content_interface.h b/mg_content_interface.h
index b7bc7a3..bb99999 100755
--- a/mg_content_interface.h
+++ b/mg_content_interface.h
@@ -1,19 +1,21 @@
/*******************************************************************/
-/*! \file content_interface.h
- * \brief Data Objects for content (e.g. mp3 files, movies)
- * for the vdr muggle plugindatabase
+/*! \file mg_content_interface.h
+ * \brief data Objects for content (e.g. mp3 files, movies) for the vdr muggle plugin
+ *
********************************************************************
- * \version $Revision: 1.3 $
- * \date $Date: 2004/02/09 19:27:52 $
- * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: MountainMan $
+ *
+ * \version $Revision: 1.4 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author file owner: $Author: lvw $
*
- * Declares main classes of for content items and interfaces to SQL databases
+ * Declares generic classes of for content items and interfaces to SQL databases
*
- * This file defines the following classes
- * - mgPlaylist a playlist
- * - mgTrack a single track (content item). e.g. an mp3 file
- * - mgSelection a set of tracks (e.g. a database subset matching certain criteria)
+ * This file defines the following classes
+ * - mgMediaMplayer
+ * - mgContentItem
+ * - mgTracklist
+ * - mgSelectionTreeNode
*
*/
/*******************************************************************/
@@ -28,82 +30,150 @@
#include <mysql/mysql.h>
#define ILLEGAL_ID -1
+
class mgFilter;
+class mgPlaylist;
/*!
- *******************************************************************
* \class mgMediaPlayer
- *
* \brief dummy player class
- ********************************************************************/
+ */
class mgMediaPlayer
{
- public:
- mgMediaPlayer(){;}
- ~mgMediaPlayer(){;}
-
+ public:
+
+ mgMediaPlayer()
+ { }
+
+ ~mgMediaPlayer()
+ { }
};
/*!
- *******************************************************************
- * \class mgContentItem
- *
* \brief Generic class that represents a single content item.
+ *
* This is the parent class from which classes like mgGdTrack are derived
- ********************************************************************/
+ *
+ */
class mgContentItem
{
+ public:
+ typedef enum contentType
+ {
+ ABSTRACT =0,
+ GD_AUDIO,
+ EPG
+ };
- public:
- typedef enum contentType{
- ABSTRACT =0,
- GD_AUDIO,
- EPG
- }contentType;
-
- protected:
+ protected:
int m_uniqID; // internal identifier to uniquely identify a content item;
- public:
- /* constructor */
- mgContentItem(){ m_uniqID = -1;}
- mgContentItem(int id){m_uniqID = id;}
- mgContentItem(const mgContentItem& org){m_uniqID = org.m_uniqID;}
- /* destructor */
- virtual ~mgContentItem(){};
-
- /* data acess */
- int getId(){ return m_uniqID;}
+ public:
- // what type of content are we looking at (e.g. audio, video, epg)
- virtual contentType getContentType(){return ABSTRACT;}
+ /*! \brief default constructor
+ */
+ mgContentItem()
+ : m_uniqID( -1 )
+ { }
+
+ /*! \brief constructor with explicit id
+ */
+ mgContentItem( int id )
+ : m_uniqID( id )
+ { }
+
+ /*! \brief copy constructor
+ */
+ mgContentItem( const mgContentItem& org )
+ : m_uniqID( org.m_uniqID )
+ { }
+
+ /*! \brief destructor
+ */
+ virtual ~mgContentItem()
+ { };
+
+ /*! \brief acess unique id
+ */
+ int getId()
+ {
+ return m_uniqID;
+ }
- // return (global?) object that is used to play the content items
- virtual mgMediaPlayer getPlayer(){return mgMediaPlayer();}
+ /*! \brief determine what type of content are we looking at (e.g. audio, video, epg)
+ */
+ virtual contentType getContentType()
+ {
+ return ABSTRACT;
+ }
+ /*! \brief return a (global?) object that is used to play content items
+ * \todo What for? Interesting properties? Last state, play info, ...?
+ */
+ virtual mgMediaPlayer getPlayer()
+ {
+ return mgMediaPlayer();
+ }
- // get the "file" (or URL) that is passed to the player
- virtual std::string getSourceFile(){return "";}
+ /*! \brief get the "file" (or URL) that is passed to the player
+ */
+ virtual std::string getSourceFile()
+ {
+ return "";
+ }
// ============ data access =================
- virtual std::string getTitle(){return "";} // return the title
- virtual std::string getLabel(int col){return "";} // return the title
-
- virtual std::string getDescription()// return a short textual description
- {return "";}
- virtual std::vector<mgFilter*> *getTrackInfo(){return NULL;}
- virtual bool updateTrackInfo(std::vector<mgFilter*>*){return false;}
+ /*! \brief return the title
+ */
+ virtual std::string getTitle()
+ {
+ return "";
+ }
+
+ /*! \brief return a specific label
+ */
+ virtual std::string getLabel(int col = 0)
+ {
+ return "";
+ }
+
+ /*! \brief return a short textual description
+ */
+ virtual std::string getDescription()
+ {
+ return "";
+ }
+
+ virtual std::vector<mgFilter*> *getTrackInfo()
+ {
+ return NULL;
+ }
+
+ virtual bool updateTrackInfo(std::vector<mgFilter*>*)
+ {
+ return false;
+ }
+
+ virtual std::string getGenre()
+ {
+ return "";
+ }
- virtual std::string getGenre(){return "";}
virtual int getRating()
{
return 0;
}
- virtual bool operator == (mgContentItem o){return m_uniqID == o.m_uniqID;}
+ virtual bool operator == (mgContentItem o)
+ {
+ return m_uniqID == o.m_uniqID;
+ }
// check, if usable
- virtual bool isValid() {return (m_uniqID >=0);}
+ virtual bool isValid()
+ {
+ return ( m_uniqID >= 0 );
+ }
static mgContentItem UNDEFINED;
};
@@ -136,54 +206,6 @@ class mgTracklist
virtual bool remove(unsigned int position); // remove item at position
};
-/*!
- *******************************************************************
- * \class mgPlaylist
- *
- * Represents a generic playlist, i.e. an ordered collection of tracks
- * The derived classes take care of specifics of certain media types
- ********************************************************************/
-class mgPlaylist : public mgTracklist
-{
- protected:
- std::string m_listname;
- std::vector<mgContentItem*>::iterator m_current;
- public:
-
- /*==== constructors ====*/
- mgPlaylist();
- mgPlaylist(std::string listname);
-
- /*==== destructor ====*/
- virtual ~mgPlaylist();
-
- /*==== add/ remove tracks ====*/
-
- /* adds a song at the end of the playlist */
- virtual void append(mgContentItem* item);
- virtual void appendList(std::vector<mgContentItem*> *tracks);
- /* adds a song after 'position' */
- virtual void insert(mgContentItem* item, unsigned int position);
-
- /*==== access tracks ====*/
- std::string getListname() ;
- void setListname(std::string name);
-
- // returns the first item of the list
- virtual mgContentItem* getFirst();
-
- // returns the nth track from the playlist
- virtual mgContentItem* getPosition(unsigned int position);
-
- // proceeds to the next item
- virtual mgContentItem* skipFwd();
-
- // goes back to the previous item
- virtual mgContentItem* skipBack();
-
- virtual mgContentItem* sneakNext();
-
-};
class mgSelectionTreeNode
{
@@ -247,6 +269,21 @@ public:
/* -------------------- begin CVS log ---------------------------------
* $Log: mg_content_interface.h,v $
+ * Revision 1.4 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.3.2.4 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.3.2.3 2004/04/01 21:35:32 lvw
+ * Minor corrections, some debugging aid.
+ *
+ * Revision 1.3.2.2 2004/03/08 21:42:22 lvw
+ * Added count method. Some comments for further todos added.
+ *
+ * Revision 1.3.2.1 2004/03/08 07:14:28 lvw
+ * Preliminary changes to muggle player
+ *
* Revision 1.3 2004/02/09 19:27:52 MountainMan
* filter set implemented
*
diff --git a/mg_database.c b/mg_database.c
index 9a85cb8..eabff6d 100644
--- a/mg_database.c
+++ b/mg_database.c
@@ -2,10 +2,10 @@
/*! \file mg_database.c
* \brief A capsule around MySql database access
********************************************************************
- * \version $Revision: 1.1 $
- * \date $Date: 2004/02/01 18:22:53 $
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author file owner: $Author: lvw $
*/
/*******************************************************************/
@@ -17,7 +17,9 @@ mgDB::mgDB()
{
}
-mgDB::mgDB(string user, string pass)
+mgDB::mgDB(string host, string name,
+ string user, string pass,
+ int port)
{
}
@@ -27,9 +29,8 @@ mgDB::~mgDB()
MYSQL mgDB::getDBHandle()
{
- MYSQL m;
- return m;
+ return m_dbase;
}
diff --git a/mg_database.h b/mg_database.h
index b979df4..4e00826 100644
--- a/mg_database.h
+++ b/mg_database.h
@@ -1,34 +1,47 @@
-/*******************************************************************/
-/*! \file mg_database.h
- * \brief A capsule around MySql database access
- ********************************************************************
- * \version $Revision: 1.1 $
- * \date $Date: 2004/02/01 18:22:53 $
+/*!
+ * \file mg_database.h
+ * \brief A capsule around MySql database access
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author Responsible author: $Author: lvw $
*/
-/*******************************************************************/
-
#ifndef __MG_DATABASE_H
#define __MG_DATABASE_H
#include <string>
-
#include <mysql/mysql.h>
class mgDB
{
public:
+ /*! \brief default constructor
+ */
mgDB( );
- mgDB( std::string user, std::string pass );
+
+ /*! \brief constructor
+ *
+ * \param host
+ * \param name
+ * \param user
+ * \param pass
+ * \param port
+ */
+ mgDB( std::string host, std::string name,
+ std::string user, std::string pass,
+ int port );
+
+ /*! \brief constructor */
~mgDB();
+ /*! \brief obtain database handle*/
MYSQL getDBHandle();
private:
- // MYSQL m_dbase;
+ MYSQL m_dbase;
};
#endif
diff --git a/mg_filters.c b/mg_filters.c
index c5859e0..f8c164d 100644
--- a/mg_filters.c
+++ b/mg_filters.c
@@ -2,10 +2,10 @@
/*! \file mg_filters.c
* \brief
********************************************************************
- * \version $Revision: 1.2 $
- * \date $Date: 2004/02/14 22:02:45 $
+ * \version $Revision: 1.3 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author file owner: $Author: lvw $
*/
/*******************************************************************/
@@ -284,14 +284,14 @@ void mgFilterChoice::restore()
void mgFilterChoice::clear()
{
m_stored_val = m_default_val;
- m_selval = m_default_val;
+ m_selval = m_default_val;
}
bool mgFilterChoice::isSet()
{
if(m_stored_val == m_default_val)
- {
- return false;
- }
+ {
+ return false;
+ }
return true;
}
diff --git a/mg_filters.h b/mg_filters.h
index 8a983f7..5501d63 100644
--- a/mg_filters.h
+++ b/mg_filters.h
@@ -3,10 +3,10 @@
* \brief Top level access to media in vdr plugin muggle
* for the vdr muggle plugindatabase
********************************************************************
- * \version $Revision: 1.1 $
- * \date $Date: 2004/02/12 09:15:07 $
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author file owner: $Author: lvw $
*/
/*******************************************************************/
diff --git a/mg_media.c b/mg_media.c
index bbb682b..18e44f3 100644
--- a/mg_media.c
+++ b/mg_media.c
@@ -1,40 +1,38 @@
-/*******************************************************************/
/*! \file mg_media.c
* \brief Top level access to media in vdr plugin muggle
- * for the vdr muggle plugindatabase
- ********************************************************************
- * \version $Revision: 1.12 $
- * \date $Date: 2004/02/23 16:30:58 $
+ *
+ * \version $Revision: 1.13 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
*/
-/*******************************************************************/
/* makes sure we dont parse the same declarations twice */
#include "mg_media.h"
+
#include "mg_tools.h"
#include "mg_content_interface.h"
+
#include "gd_content_interface.h"
+#include "vdr_setup.h"
+
using namespace std;
//-------------------------------------------------------------------
// mgFilterSets
//-------------------------------------------------------------------
/*!
- *******************************************************************
- * \brief constructor
- *
- * constructor of the base class
- ********************************************************************/
+ * \brief constructor
+ *
+ * constructor of the base class
+ */
+
mgFilterSets::mgFilterSets()
{
// nothing to be done in the base class
}
- /*!
- *******************************************************************
- * \brief destructor
- ********************************************************************/
+
mgFilterSets::~mgFilterSets()
{
vector<mgFilter*> *set;
@@ -52,7 +50,7 @@ mgFilterSets::~mgFilterSets()
}
m_sets.clear();
}
- /*!
+/*!
*******************************************************************
* \brief returns the number of available sets
********************************************************************/
@@ -139,9 +137,8 @@ void mgFilterSets::select(int n)
/*!
- *******************************************************************
* \brief return title of the active filter set
- ********************************************************************/
+ */
string mgFilterSets::getTitle()
{
if(m_activeSetId < (int) m_titles.size())
@@ -156,15 +153,11 @@ string mgFilterSets::getTitle()
}
}
-//-------------------------------------------------------------------
-// mgFilterSets
-//-------------------------------------------------------------------
/*!
- *******************************************************************
* \class mgMedia
*
* \brief mein class to access content in the vdr plugin muggle
- ********************************************************************/
+ */
mgMedia::mgMedia(contentType mediatype)
{
int errval = 0;
@@ -180,7 +173,7 @@ mgMedia::mgMedia(contentType mediatype)
case GD_MP3:
{
errval = GdInitDatabase(&m_db);
- mgDebug(3, "Successfully conntected to sql database 'GiantDisc'");
+ mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName );
}
}
if(errval < 0)
@@ -193,8 +186,8 @@ mgMedia::mgMedia(contentType mediatype)
{
case GD_MP3:
{
- errval = GdInitDatabase(&m_db);
- mgDebug(3, "Successfully conntected to sql database 'GiantDisc'");
+ errval = GdInitDatabase( &m_db ); // TODO: why duplicate this? LVW
+ mgDebug(3, "Successfully conntected to sql database %s", the_setup.DbName );
}
}
}
@@ -220,7 +213,9 @@ string mgMedia::getMediaTypeName()
switch(m_mediatype)
{
case GD_MP3:
+ {
return "GiantDisc";
+ } break;
}
mgError("implementation Error"); // we should never get here
return "";
@@ -429,6 +424,24 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter()
/* -------------------- begin CVS log ---------------------------------
* $Log: mg_media.c,v $
+ * Revision 1.13 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.12 2004/02/23 16:30:58 RaK
+ * - album search error because of i18n corrected
+ *
+ * Revision 1.11 2004/02/12 09:15:07 LarsAC
+ * Moved filter classes into separate files
+ *
+ * Revision 1.10.2.3 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.10.2.2 2004/03/14 17:57:30 lvw
+ * Linked against libmad. Introduced config options into code.
+ *
+ * Revision 1.10.2.1 2004/03/02 07:05:50 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.12 2004/02/23 16:30:58 RaK
* - album search error because of i18n corrected
*
@@ -451,8 +464,5 @@ mgSelectionTreeNode *mgMedia::applyActiveFilter()
* Revision 1.7 2004/02/02 22:48:04 MountainMan
* added CVS $Log
*
- *
* --------------------- end CVS log ----------------------------------
*/
-
-
diff --git a/mg_media.h b/mg_media.h
index cb685c3..394db13 100644
--- a/mg_media.h
+++ b/mg_media.h
@@ -1,15 +1,14 @@
-/*******************************************************************/
-/*! \file mgmedia.h
+/*!
+ * \file mgmedia.h
* \brief Top level access to media in vdr plugin muggle
- * for the vdr muggle plugindatabase
- ********************************************************************
- * \version $Revision: 1.10 $
- * \date $Date: 2004/02/12 09:15:07 $
+ *
+ * \version $Revision: 1.11 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author Responsible author: $Author: lvw $
*/
-/*******************************************************************/
-/* makes sure we dont use parse the same declarations twice */
+
+// makes sure we dont use parse the same declarations twice
#ifndef _MG_MEDIA_H
#define _MG_MEDIA_H
@@ -25,63 +24,61 @@ class mgFilter;
class mgFilterSets;
/*!
- *******************************************************************
* \class mgFilterSets
*
- * Represents one or several sets of filters to set and memorize
- * search constraint
- ********************************************************************/
-class mgFilterSets {
+ * Represents one or several sets of filters to set and memorize search constraint
+ */
+class mgFilterSets
+{
protected:
int m_activeSetId;
- std::vector<mgFilter*> *m_activeSet; // pointer to the active filter set
+ std::vector<mgFilter*> *m_activeSet; // pointer to the active filter set
- std::vector< std::vector<mgFilter*>*> m_sets;
// stores name-value pairs, even if a different set is active
+ std::vector< std::vector<mgFilter*>*> m_sets;
- std::vector<std::string> m_titles;
// stores the titles for all filters
+ std::vector<std::string> m_titles;
public:
- mgFilterSets();
// constructor, constracts a number >=1 of filter sets
// the first set (index 0 ) is active by default
+ mgFilterSets();
- virtual ~mgFilterSets();
// destructor
+ virtual ~mgFilterSets();
- int numSets();
// returns the number of available sets
+ int numSets();
- void nextSet();
// proceeds to the next one in a circular fashion
+ void nextSet();
- void select(int n);
// activates a specific set by index
+ void select(int n);
- virtual void clear();
// restores the default values for all filter values in the active set
// normally, the default values represent 'no restrictions'
+ virtual void clear();
- void accept();
// stores the current filter values
+ void accept();
- std::vector<mgFilter*> *getFilters();
// returns the active set to the application
// the application may temporarily modify the filter values
// accept() needs to be called to memorize the changed values
+ std::vector<mgFilter*> *getFilters();
- virtual std::string computeRestriction(int *viewPrt)=0;
// computes the (e.g. sql-) restrictions specified by the active filter set
// and returns the index of the appropriate defualt view in viewPrt
+ virtual std::string computeRestriction(int *viewPrt)=0;
- std::string getTitle();
// returns title of active filter set
+ std::string getTitle();
};
/*!
- *******************************************************************
* \class mgMedia
*
* \brief main class to access content in the vdr plugin muggle
@@ -90,7 +87,7 @@ class mgFilterSets {
* where the data type is explicitelymentioned.
* The class provides a set of objects that abstract from the data
* type and source
- ********************************************************************/
+ */
class mgMedia
{
@@ -149,6 +146,18 @@ class mgMedia
/* -------------------- begin CVS log ---------------------------------
* $Log: mg_media.h,v $
+ * Revision 1.11 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.10 2004/02/12 09:15:07 LarsAC
+ * Moved filter classes into separate files
+ *
+ * Revision 1.9.2.2 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.9.2.1 2004/03/02 07:05:50 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.10 2004/02/12 09:15:07 LarsAC
* Moved filter classes into separate files
*
diff --git a/mg_playlist.c b/mg_playlist.c
new file mode 100644
index 0000000..f6387b0
--- /dev/null
+++ b/mg_playlist.c
@@ -0,0 +1,209 @@
+/*!
+ * \file mg_playlist.c
+ * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * This file implements the class mgPlaylist which maintains a playlist
+ * and supports editing (e.g. adding or moving tracks), navigating it
+ * (e.g. obtaining arbitrary items or accessing them sequentially) or
+ * making it persistent in some database.
+ */
+
+#include "mg_playlist.h"
+#include "mg_tools.h"
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+/* ==== constructors ==== */
+
+mgPlaylist::mgPlaylist()
+{
+ m_current_idx = 0;
+
+ char *buffer;
+ asprintf( &buffer, "Playlist-%ld", random() );
+
+ m_listname = buffer;
+}
+
+mgPlaylist::mgPlaylist(string listname)
+{
+ m_current_idx = 0;
+ m_listname = listname;
+}
+
+/* ==== destructor ==== */
+
+mgPlaylist::~mgPlaylist()
+{
+}
+
+void mgPlaylist::toggleShuffle()
+{
+}
+
+void mgPlaylist::toggleLoop()
+{
+}
+
+void mgPlaylist::initialize()
+{
+ m_current = m_list.begin();
+}
+
+/* ==== add/remove tracks ==== */
+
+/* adds a song at the end of the playlist */
+void mgPlaylist::append(mgContentItem* item)
+{
+ m_list.push_back(item);
+}
+
+/* append a list of tracks at the end of the playlist */
+void mgPlaylist::appendList(vector<mgContentItem*> *tracks)
+{
+ vector<mgContentItem*>::iterator iter;
+
+ mgDebug( 3, "Adding %d tracks to the playlist", tracks->size() );
+
+ for( iter = tracks->begin(); iter != tracks->end(); iter++ )
+ {
+ m_list.push_back(*iter);
+ }
+
+ // TODO: why is this vector cleared? shouldn't the caller take care of this? LVW
+ tracks->clear();
+}
+
+/* add a song after 'position' */
+void mgPlaylist::insert(mgContentItem* item, unsigned int position)
+{
+ if( position >= m_list.size() )
+ {
+ m_list.push_back(item);
+ }
+ else
+ {
+ m_list.insert( m_list.begin() + position, item );
+ }
+}
+
+void mgPlaylist::clear()
+{
+ // TODO: who takes care of memory allocation/deallocation of mgItems?
+
+ vector<mgContentItem*>::iterator iter;
+
+ for( iter = m_list.begin(); iter != m_list.end(); iter++ )
+ { // delete each item in the list
+ delete *iter;
+ }
+
+ // finally clear the list itself
+ m_list.clear();
+}
+
+void mgPlaylist::move( int from, int to )
+{
+ vector<mgContentItem*>::iterator from_iter = m_list.begin() + from;
+ vector<mgContentItem*>::iterator to_iter = m_list.begin() + to;
+
+ m_list.insert( to_iter, *from_iter);
+ m_list.erase( from_iter );
+}
+
+/*==== access tracks ====*/
+string mgPlaylist::getListname()
+{
+ return m_listname;
+}
+
+void mgPlaylist::setListname(string name)
+{
+ m_listname = name;
+}
+
+// returns the count of items in the list
+int mgPlaylist::count()
+{
+ return m_list.size();
+}
+
+// returns the first item of the list
+mgContentItem* mgPlaylist::getFirst()
+{
+ m_current = m_list.begin();
+
+ return *m_current;
+}
+
+// returns the nth track from the playlist
+mgContentItem* mgPlaylist::getPosition(unsigned int position)
+{
+ if( position >= m_list.size() )
+ {
+ // TODO: why not return a NULL pointer? LVW
+ return &(mgContentItem::UNDEFINED); //invalid
+ }
+ m_current = m_list.begin() + position;
+
+ return *m_current;
+}
+
+// proceeds to the next item
+mgContentItem* mgPlaylist::skipFwd()
+{
+ mgContentItem* next;
+
+ if( m_current + 1 == m_list.end() ) // unless loop mode
+ {
+ // TODO: why not return a NULL pointer? LVW
+ next = &(mgContentItem::UNDEFINED); //invalid item
+ }
+ else
+ {
+ return * (++ m_current);
+ }
+
+ return next;
+}
+
+// goes back to the previous item
+mgContentItem* mgPlaylist::skipBack()
+{
+ mgContentItem* prev;
+
+ if( m_current == m_list.begin() )
+ {
+ // TODO: why not return a NULL pointer? LVW
+ prev = &(mgContentItem::UNDEFINED); //invalid
+ }
+ else
+ {
+ prev = * (--m_current);
+ }
+
+ return prev;
+}
+
+// get next track, do not update data structures
+mgContentItem* mgPlaylist::sneakNext()
+{
+ if( m_current+1 == m_list.end() )
+ {
+ // TODO: why not return a NULL pointer? LVW
+ return &(mgContentItem::UNDEFINED); //invalid
+ }
+ else
+ {
+ return * (m_current+1);
+ }
+}
+
diff --git a/mg_playlist.h b/mg_playlist.h
new file mode 100644
index 0000000..dcec71c
--- /dev/null
+++ b/mg_playlist.h
@@ -0,0 +1,143 @@
+/*!
+ * \file mg_playlist.c
+ * \brief defines functions to be executed on playlists for the vdr muggle plugindatabase
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * This file implements the class mgPlaylist which maintains a playlist
+ * and supports editing (e.g. adding or moving tracks), navigating it
+ * (e.g. obtaining arbitrary items or accessing them sequentially) or
+ * making it persistent in some database.
+ */
+#ifndef __MG_PLAYLIST
+#define __MG_PLAYLIST
+
+#include "mg_content_interface.h"
+
+/*!
+ * \class mgPlaylist
+ *
+ * \brief Represents a generic playlist, i.e. an ordered collection of tracks
+ * Derived classes take care of specifics of certain media types
+ */
+class mgPlaylist : public mgTracklist
+{
+
+private:
+ //! \brief current index in the playlist
+ // TODO: should be a property of the player?
+ int m_current_idx;
+
+protected:
+
+ // TODO: Why not make these private? Subclasses should use access functions. LVW
+
+ //! \brief the name of the playlist
+ std::string m_listname;
+
+ //! \brief the current item as an iterator
+ std::vector<mgContentItem*>::iterator m_current;
+
+public:
+
+ /* ==== constructors and initialization ==== */
+
+ //! \brief the default constructor (random listname)
+ mgPlaylist();
+
+ /*! \brief constructor with a specified listname
+ *
+ * \param listname - initial name of the list
+ */
+ mgPlaylist( std::string listname );
+
+ void initialize();
+
+ /* ==== destructor ==== */
+ //! \brief the destructor
+ virtual ~mgPlaylist();
+
+ /* === control behavior */
+
+ //! \brief toggle the loop mode. TODO.
+ void toggleLoop();
+
+ //! \brief toggle the shuffle mode. TODO.
+ void toggleShuffle();
+
+ /* ==== add/ remove tracks ==== */
+
+ /*! \brief adds a song at the end of the playlist
+ *
+ * \param item - the item to be appended
+ */
+ virtual void append(mgContentItem* item);
+
+ /*! \brief add a vector of songs at the end of the playlist
+ *
+ * \param tracks - the vector of tracks to be added
+ */
+ virtual void appendList(std::vector<mgContentItem*> *tracks);
+
+ /*! \brief add a song after a specified position
+ *
+ * Might be merged with append by using a default argument for the position
+ *
+ * \param item - the item to be added
+ * \param position - the position where the item should be added
+ */
+ virtual void insert(mgContentItem* item, unsigned int position);
+
+ //! \brief clear all tracks
+ virtual void clear();
+
+ /*! \brief move tracks within playlist
+ *
+ * \param from - the item of the index to be moved
+ * \param to - the target index of the item
+ */
+ void move( int from, int to );
+
+ /*! \brief remove a track from the playlist
+ *
+ * \param pos - the index of the track to be removed
+ */
+ // bool remove( int pos );
+
+ /* ==== access tracks ==== */
+
+ //! \brief obtain the listname
+ std::string getListname() ;
+
+ /*! \brief set the listname
+ *
+ * \param name - the new name of this list
+ */
+ void setListname(std::string name);
+
+ //! \brief returns the count of items in the list
+ int count();
+
+ //! \brief returns the first item of the list
+ virtual mgContentItem* getFirst();
+
+ /*! \brief returns the nth track from the playlist
+ *
+ * \param position - the position to skip to
+ */
+ virtual mgContentItem* getPosition(unsigned int position);
+
+ //! \brief proceeds to the next item
+ virtual mgContentItem* skipFwd();
+
+ //! \brief goes back to the previous item
+ virtual mgContentItem* skipBack();
+
+ //! \brief obtain the next item without skipping the current position
+ virtual mgContentItem* sneakNext();
+};
+
+#endif
diff --git a/mg_tools.h b/mg_tools.h
index f913667..d56f7d8 100644
--- a/mg_tools.h
+++ b/mg_tools.h
@@ -3,10 +3,10 @@
* \brief A few util functions for standalone and plugin messaging
* for the vdr muggle plugindatabase
********************************************************************
- * \version $Revision: 1.2 $
- * \date $Date: 2004/02/02 22:48:04 $
+ * \version $Revision: 1.3 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: MountainMan $
+ * \author file owner: $Author: lvw $
*
*/
/*******************************************************************/
@@ -14,6 +14,9 @@
#ifndef _MUGGLE_TOOLS_H
#define _MUGGLE_TOOLS_H
+#include <iostream>
+#include <string>
+
#include "mysql/mysql.h"
#define STANDALONE 1
@@ -24,12 +27,62 @@ void mgDebug( const char *fmt, ... );
void mgWarning(const char *fmt, ...);
void mgError(const char *fmt, ...);
-
MYSQL_RES* mgSqlReadQuery(MYSQL *db, const char *fmt, ...);
void mgSqlWriteQuery(MYSQL *db, const char *fmt, ...);
+#ifdef DEBUG
+#define MGLOG(x) mgLog __thelog(x)
+#else
+#define MGLOG(x) {}
+#endif
+
+#ifdef DEBUG
+#define MGLOGSTREAM __thelog.getStream()
+#else
+#define MGLOGSTREAM __thelog.getStream()
+#endif
+
+
+class mgLog
+{
+ public:
+ enum
+ {
+ LOG, WARNING, ERROR, FATAL
+ } mgLogLevel;
+
+ std::ostream& getStream()
+ {
+ return std::cout;
+ }
+
+ mgLog( std::string methodname ) : m_methodname( methodname )
+ {
+ getStream() << m_methodname << " entered" << std::endl;
+ };
+
+ ~mgLog()
+ {
+ getStream() << m_methodname << " terminated" << std::endl;
+ }
+
+ private:
+
+ std::string m_methodname;
+
+};
+
/* -------------------- begin CVS log ---------------------------------
* $Log: mg_tools.h,v $
+ * Revision 1.3 2004/05/28 15:29:18 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.2.2.2 2004/04/18 14:08:41 lvw
+ * Added some more logging/debugging code
+ *
+ * Revision 1.2.2.1 2004/04/09 16:14:48 lvw
+ * Added further code for logging/debugging.
+ *
* Revision 1.2 2004/02/02 22:48:04 MountainMan
* added CVS $Log
*
diff --git a/muggle.c b/muggle.c
index c8d1c76..86e5c9d 100644
--- a/muggle.c
+++ b/muggle.c
@@ -1,28 +1,33 @@
-/*******************************************************************/
-/*! \file muggle.c
- * \brief Implements a plugin for browsing media libraries within VDR
- ********************************************************************
- * \version $Revision: 1.6 $
- * \date $Date: 2004/02/23 16:18:15 $
+/*!
+ * \file muggle.c
+ * \brief Implements a plugin for browsing media libraries within VDR
+ *
+ * \version $Revision: 1.7 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: muggle.c,v 1.7 2004/05/28 15:29:18 lvw Exp $
*/
-/*******************************************************************/
-static const char *VERSION = "0.0.1";
-static const char *DESCRIPTION = "Access GiantDisc database contents";
-static const char *MAINMENUENTRY = "Muggle";
+#include <getopt.h>
#include "muggle.h"
#include "vdr_menu.h"
-
+#include "vdr_setup.h"
#include "mg_tools.h"
#include "mg_content_interface.h"
#include "mg_media.h"
#include "i18n.h"
+static const char *VERSION = "0.0.1";
+static const char *DESCRIPTION = "Media juggle plugin for VDR";
+static const char *MAINMENUENTRY = "Muggle";
+
+using namespace std;
+
const char* mgMuggle::Version(void)
{
return VERSION;
@@ -40,9 +45,14 @@ const char* mgMuggle::MainMenuEntry(void)
mgMuggle::mgMuggle(void)
{
- // Initialize any member variables here.
- // DON'T DO ANYTHING ELSE THAT MAY HAVE SIDE EFFECTS, REQUIRE GLOBAL
- // VDR OBJECTS TO EXIST OR PRODUCE ANY OUTPUT!
+ // defaults for database arguments
+ the_setup.DbHost = "localhost";
+ the_setup.DbPort = 0;
+ the_setup.DbName = "GiantDisc2";
+ the_setup.DbUser = "";
+ the_setup.DbPass = "" ;
+ the_setup.GdCompatibility = false;
+ the_setup.ToplevelDir = "";
}
mgMuggle::~mgMuggle()
@@ -53,12 +63,70 @@ mgMuggle::~mgMuggle()
const char *mgMuggle::CommandLineHelp(void)
{
// Return a string that describes all known command line options.
- return NULL;
+ return
+ " -h HHHH, --host=HHHH specify database host (default is localhost)\n"
+ " -n NNNN, --name=NNNN specify database name (overridden by -g)\n"
+ " -p PPPP, --port=PPPP specify port of database server (default is )\n"
+ " -u UUUU, --user=UUUU specify database user (default is )\n"
+ " -w WWWW, --password=WWWW specify database password (default is empty)\n"
+ " -t TTTT, --toplevel=TTTT specify toplevel directory for music\n"
+ " -g, --giantdisc enable full Giantdisc compatibility mode\n";
}
bool mgMuggle::ProcessArgs(int argc, char *argv[])
{
+ cout << "mgMuggle::ProcessArgs" << endl << flush;
+
// Implement command line argument processing here if applicable.
+ static struct option long_options[] =
+ {
+ { "host", required_argument, NULL, 'h' },
+ { "name", required_argument, NULL, 'n' },
+ { "port", required_argument, NULL, 'p' },
+ { "user", required_argument, NULL, 'u' },
+ { "password", required_argument, NULL, 'w' },
+ { "toplevel", required_argument, NULL, 't' },
+ { "giantdisc", no_argument, NULL, 'g' },
+ { NULL }
+ };
+
+ int c, option_index = 0;
+ while( ( c = getopt_long( argc, argv, "h:p:u:w:g:", long_options, &option_index ) ) != -1 )
+ {
+ switch (c)
+ {
+ case 'h':
+ {
+ the_setup.DbHost = optarg;
+ } break;
+ case 'n':
+ {
+ the_setup.DbName = optarg;
+ }
+ case 'p':
+ {
+ the_setup.DbPort = atoi( optarg );
+ } break;
+ case 'u':
+ {
+ the_setup.DbUser = optarg;
+ } break;
+ case 'w':
+ {
+ the_setup.DbPass = optarg;
+ } break;
+ case 't':
+ {
+ the_setup.ToplevelDir = optarg;
+ } break;
+ case 'g':
+ {
+ the_setup.GdCompatibility = true;
+ } break;
+ default: return false;
+ }
+ }
+
return true;
}
@@ -73,9 +141,9 @@ bool mgMuggle::Start(void)
// Start any background activities the plugin shall perform.
mgSetDebugLevel( 99 );
- RegisterI18n(Phrases);
- m_media = new mgMedia( mgMedia::GD_MP3 );
- m_root = m_media->getSelectionRoot();
+ RegisterI18n( Phrases );
+ m_media = new mgMedia( mgMedia::GD_MP3 );
+ m_root = m_media->getSelectionRoot();
m_playlist = m_media->createTemporaryPlaylist();
m_media->initFilterSet();
return true;
@@ -96,14 +164,22 @@ cOsdObject *mgMuggle::MainMenuAction(void)
cMenuSetupPage *mgMuggle::SetupMenu(void)
{
- // Return a setup menu in case the plugin supports one.
- return NULL;
+ return new mgMenuSetup();
}
bool mgMuggle::SetupParse(const char *Name, const char *Value)
{
- // Parse your own setup parameters and store their values.
- return false;
+ if (!strcasecmp(Name, "InitLoopMode")) the_setup.InitLoopMode = atoi(Value);
+ else if (!strcasecmp(Name, "InitShuffleMode")) the_setup.InitShuffleMode = atoi(Value);
+ else if (!strcasecmp(Name, "AudioMode")) the_setup.AudioMode = atoi(Value);
+ else if (!strcasecmp(Name, "DisplayMode")) the_setup.DisplayMode = atoi(Value);
+ else if (!strcasecmp(Name, "BackgrMode")) the_setup.BackgrMode = atoi(Value);
+ else if (!strcasecmp(Name, "TargetLevel")) the_setup.TargetLevel = atoi(Value);
+ else if (!strcasecmp(Name, "LimiterLevel")) the_setup.LimiterLevel = atoi(Value);
+ else if (!strcasecmp(Name, "Only48kHz")) the_setup.Only48kHz = atoi(Value);
+ else return false;
+
+ return true;
}
VDRPLUGINCREATOR(mgMuggle); // Don't touch this!
diff --git a/muggle.h b/muggle.h
index c5c0d04..957ce5e 100644
--- a/muggle.h
+++ b/muggle.h
@@ -1,24 +1,19 @@
-/*******************************************************************/
-/*! \file muggle.h
- * \brief Implements a plugin for browsing media libraries within VDR
- ********************************************************************
- * \version $Revision: 1.4 $
- * \date $Date: 2004/02/03 19:28:46 $
+/*!
+ * \file muggle.h
+ * \brief Implements a plugin for browsing media libraries within VDR
+ *
+ * \version $Revision: 1.5 $
+ * \date $Date: 2004/05/28 15:29:18 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: LarsAC $
+ * \author file owner: $Author: lvw $
+ *
+ * $Id: muggle.h,v 1.5 2004/05/28 15:29:18 lvw Exp $
*/
-/*******************************************************************/
#ifndef _MUGGLE_H
#define _MUGGLE_H
-#undef SHELL_TEST
-
-#ifdef SHELL_TEST
- #include "shell_plugin.h"
-#else
- #include <plugin.h>
-#endif
+#include <plugin.h>
class mgMedia;
class mgSelectionTreeNode;
diff --git a/vdr_config.h b/vdr_config.h
new file mode 100644
index 0000000..6178a34
--- /dev/null
+++ b/vdr_config.h
@@ -0,0 +1,119 @@
+/*!
+ * \file vdr_menu.c
+ * \brief Implements menu handling for browsing media libraries within VDR
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_config.h,v 1.2 2004/05/28 15:29:18 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+// This files contains some option switches/values for compile time
+// configuration of the MP3/MPlayer plugin. You should only alter
+// this file, if you have understand the source code parts which deal
+// with the changed value.
+
+// After changing this file you should do a "make plugins-clean ; make plugins"
+// to recompile vdr.
+
+#ifndef ___CONFIG_H
+#define ___CONFIG_H
+
+// The buffer size in bytes for the decoded audio data which is about to
+// be send to the dvb driver. Should not be made to big, as this delays the
+// pause function too much.
+#define MP3BUFSIZE (100*1024)
+
+// The number of consecutive frame decoding error that may occure during
+// decoding before aborting the file.
+#define MAX_FRAME_ERR 10
+
+// Defines the min. gain required to launch the normalizer. Don't bother normalizing
+// songs which wouldn't change much in volumen.
+#define MIN_GAIN 0.03
+
+// Comment this out to disable the fast limiter function in the normalizer. The fast function
+// uses a lookup table and is 12-14 times faster than the normal function, but less
+// accurate (rel. error about 1e-7).
+#define USE_FAST_LIMITER
+
+// Defines the filename extention to use for playlist files.
+#define PLAYLISTEXT ".m3u"
+
+// Defines the text to identify WinAmp-Style playlists.
+#define WINAMPEXT "#EXTM3U"
+
+// Comment this out, if you don't want to use mmap() to access the data files.
+// Normaly there is no need to disable mmap(), as the code switches to normal
+// file i/o if mmap() is not available for a specific file/filesystem.
+#define USE_MMAP
+
+// Defines the max. memory size used for mmapping. This should not exceed the
+// available free memory on your machine.
+#define MAX_MMAP_SIZE (32*1024*1024)
+
+// The buffer size in bytes to use for normal file i/o if a file cannot be
+// mmap()ed. If set to large on slow media, may cause audio drop outs as the
+// audio buffer may underrun while filling this buffer.
+#define MP3FILE_BUFSIZE (32*1024)
+
+// The filename to save the cached id3 information. The file is located in the
+// video directory and this definition must not contain any path (no "/" )
+#define CACHEFILENAME "id3info.cache"
+
+// The interval in seconds in which the id3 cache is saved to disk (only
+// if any changes occured).
+#define CACHESAVETIMEOUT 120
+
+// How many days to keep unused entries in the id3 cache. Unused entries from
+// undefined mp3sources are purged after timeout/10 days.
+#define CACHEPURGETIMEOUT 120
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// Defines the timeout in seconds for functions which use a single key
+// (e.g. openning the playlist window). If the key is repressed during
+// the timeout, the secondary function is activated.
+#define MULTI_TIMEOUT 3
+
+// Defines the timeout in ms for entering the single digits in direct song
+// selection.
+#define SELECT_TIMEOUT 1000
+
+// If the progress display is closed on direct song selection, the display
+// is opend temporarily. This defines the time in seconds after the display
+// is closed again.
+#define SELECTHIDE_TIMEOUT 3
+
+// Defines the time in seconds to jump inside a song with left/right.
+#define JUMPSIZE 3
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
+
+// DEBUGGING OPTIONS
+// (not needed for normal operation)
+
+// Uncomment to enable generic debugging messages to the console. This may slow
+// down operation in some cases.
+#define DEBUG
+
+// Uncomment to disable audio output to the dvb driver. The audio data is
+// simply discarded.
+//#define DUMMY_OUTPUT
+
+// Uncomment to enable dumping the normalize limiter lookup table to the file
+// "/tmp/limiter". The generated file will be about 3MB in size. This option shouldn't
+// be enabled for day-by-day operation.
+//#define ACC_DUMP
+
+#endif //___CONFIG_H
diff --git a/vdr_decoder.c b/vdr_decoder.c
new file mode 100644
index 0000000..8b4cf70
--- /dev/null
+++ b/vdr_decoder.c
@@ -0,0 +1,137 @@
+/*!
+ * \file vdr_decoder.h
+ * \brief A generic decoder for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_decoder.c,v 1.2 2004/05/28 15:29:18 lvw Exp $
+ *
+ * Adapted from:
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001-2003 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include <videodir.h>
+#include <interface.h>
+
+// #include "common.h"
+// #include "data-mp3.h"
+// #include "decoder-core.h"
+// #include "decoder-mp3-stream.h"
+// #include "decoder-snd.h"
+// #include "decoder-ogg.h"
+
+#include "vdr_decoder.h"
+#include "vdr_decoder_mp3.h"
+
+using namespace std;
+
+// --- mgDecoders ---------------------------------------------------------------
+
+mgMediaType mgDecoders::getMediaType( string s )
+{
+ // TODO: currently handles only mp3. LVW
+ return MT_MP3;
+}
+
+mgDecoder *mgDecoders::findDecoder( string filename )
+{
+ mgDecoder *decoder = 0;
+
+ switch( getMediaType( filename ) )
+ {
+ case MT_MP3:
+ {
+ // prepend filename with path to music library?? TODO
+ printf( "mp3 file detected, launching mgMP3Decoder\n" );
+ decoder = new mgMP3Decoder(filename);
+ } break;
+
+ /*
+ case MT_MP3_STREAM: decoder = new mgMP3StreamDecoder(full); break;
+ #ifdef HAVE_SNDFILE
+ case MT_SND: decoder = new cSndDecoder(full); break;
+ #endif
+ #ifdef HAVE_VORBISFILE
+ case MT_OGG: decoder = new mgOggDecoder(full); break;
+ #endif
+ */
+ default:
+ {
+ esyslog("ERROR: unknown media type" );
+ } break;
+ }
+
+ if( decoder && !decoder->valid() )
+ {
+ // no decoder found or decoder doesn't match
+
+ delete decoder; // might be carried out on NULL pointer!
+ decoder = 0;
+
+ esyslog("ERROR: no valid decoder found for %s", filename.c_str() );
+ }
+ return decoder;
+}
+
+// --- mgDecoder ----------------------------------------------------------------
+
+mgDecoder::mgDecoder(string filename)
+{
+ m_filename = filename;
+ m_locked = 0;
+ m_urgentLock = false;
+ m_playing = false;
+}
+
+mgDecoder::~mgDecoder()
+{
+}
+
+void mgDecoder::lock(bool urgent)
+{
+ m_locklock.Lock();
+
+ if( urgent && m_locked )
+ {
+ m_urgentLock = true; // signal other locks to release quickly
+ }
+ m_locked ++;
+
+ m_locklock.Unlock(); // don't hold the "locklock" when locking "lock", may cause a deadlock
+ m_lock.Lock();
+ m_urgentLock = false;
+}
+
+void mgDecoder::unlock(void)
+{
+ m_locklock.Lock();
+
+ m_locked--;
+
+ m_lock.Unlock();
+ m_locklock.Unlock();
+}
+
+bool mgDecoder::tryLock(void)
+{
+ bool res = false;
+ m_locklock.Lock();
+
+ if( !m_locked && !m_playing )
+ {
+ lock();
+ res = true;
+ }
+ m_locklock.Unlock();
+ return res;
+}
diff --git a/vdr_decoder.h b/vdr_decoder.h
new file mode 100644
index 0000000..74b200e
--- /dev/null
+++ b/vdr_decoder.h
@@ -0,0 +1,158 @@
+/*!
+ * \file vdr_decoder.h
+ * \brief A generic decoder for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:18 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_decoder.h,v 1.2 2004/05/28 15:29:18 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___DECODER_H
+#define ___DECODER_H
+
+#include <string>
+
+#include <thread.h>
+#include <string>
+
+#define DEC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+
+// --------From decoder_core.h ------------------------------------
+
+/*!
+ * \brief The current status of the decoder
+ */
+enum eDecodeStatus
+ {
+ dsOK=0, dsPlay, dsSkip, dsEof, dsError, dsSoftError
+ };
+
+// ----------------------------------------------------------------
+
+/*!
+ * \brief A data structure to put decoded PCM data
+ */
+struct mgDecode
+{
+ eDecodeStatus status;
+ int index;
+ struct mad_pcm *pcm;
+};
+
+// ----------------------------------------------------------------
+
+/*!
+ * \brief Information about ???
+ */
+class mgPlayInfo
+{
+public:
+ int m_index, m_total;
+};
+
+// ----------------------------------------------------------------
+
+/*!
+ * \brief Media types
+ */
+enum mgMediaType
+{
+ MT_MP3, MT_MP3_STREAM, MT_OGG, MT_FLAC
+};
+
+/*!
+ * \brief A generic decoder class to handle conversion into PCM format
+ */
+class mgDecoder
+{
+protected:
+
+ /*! \brief The currently playing file */
+ std::string m_filename;
+
+ /*! \brief Mutexes to coordinate threads */
+ cMutex m_lock, m_locklock;
+ int m_locked;
+ bool m_urgentLock;
+
+ /*! \brief Whether the decoder is currently active */
+ bool m_playing;
+
+ /*! \brief ??? */
+ mgPlayInfo m_playinfo;
+
+ /*! \brief Place a lock */
+ virtual void lock( bool urgent = false );
+
+ /*! \brief Release a lock */
+ virtual void unlock(void);
+
+ /*! \brief Try to obtain a lock */
+ virtual bool tryLock(void);
+
+public:
+
+ /*! \brief The constructor */
+ mgDecoder( std::string filename );
+
+ /*! \brief The destructor */
+ virtual ~mgDecoder();
+
+ /*! \brief Whether a decoder instance is able to play the given file */
+ virtual bool valid() = 0;
+
+ /*! \brief Whether a stream (i.e. from the network is being decoded */
+ virtual bool isStream()
+ {
+ return false;
+ }
+
+ /*! \brief Start decoding */
+ virtual bool start() = 0;
+
+ /*! \brief Stop decoding */
+ virtual bool stop() = 0;
+
+ /*! \brief Skip an amount of time. Impossible by default */
+ virtual bool skip( int seconds, int avail, int rate)
+ {
+ return false;
+ }
+
+ /*! \brief Return decoded data */
+ virtual struct mgDecode *decode() = 0;
+
+ /*! \brief Information about the current playback status */
+ virtual mgPlayInfo *playInfo()
+ {
+ return 0;
+ }
+};
+
+// ----------------------------------------------------------------
+
+/*!
+ * \brief A generic decoder manager class to handle different decoders
+ */
+class mgDecoders
+{
+public:
+
+ /*! \brief Try to find a valid decoder for a file
+ */
+ static mgDecoder *findDecoder( std::string filename );
+
+ /*! \brief determine the media type for a given source
+ */
+ static mgMediaType getMediaType( std::string filename );
+
+};
+
+#endif //___DECODER_H
diff --git a/vdr_decoder_mp3.c b/vdr_decoder_mp3.c
new file mode 100644
index 0000000..436d7b6
--- /dev/null
+++ b/vdr_decoder_mp3.c
@@ -0,0 +1,450 @@
+/*!
+ * \file vdr_decoder_mp3.h
+ * \brief An mp3 decoder for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_decoder_mp3.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <cmath>
+#include <iostream>
+
+#include "vdr_config.h"
+#include "vdr_decoder_mp3.h"
+#include "vdr_stream.h"
+
+#include "mg_tools.h"
+
+#define d(x) x
+
+using namespace std;
+
+// ----------------------------------------------------------------
+
+int mgMadStream(struct mad_stream *stream, mgStream *str)
+{
+ unsigned char *data;
+ unsigned long len;
+ if( str->stream( data, len, stream->next_frame ) )
+ {
+ if( len > 0 )
+ {
+ mad_stream_buffer(stream, data, len);
+ }
+ return len;
+ }
+ return -1;
+}
+
+// --- mgMP3Decoder -------------------------------------------------------------
+
+mgMP3Decoder::mgMP3Decoder(string filename, bool preinit)
+ : mgDecoder(filename)
+{
+ MGLOG( "mgMP3Decoder::mgMP3Decoder" );
+
+ m_stream = 0;
+ m_isStream = false;
+
+ if( preinit )
+ {
+ m_stream = new mgStream( filename );
+ }
+ m_madframe = 0;
+ m_madsynth = 0;
+
+ memset( &m_madstream, 0, sizeof(m_madstream) );
+
+ init();
+}
+
+mgMP3Decoder::~mgMP3Decoder()
+{
+ MGLOG( "mgMP3Decoder::~mgMP3Decoder" );
+
+ clean();
+ delete m_stream;
+}
+
+void mgMP3Decoder::init()
+{
+ MGLOG( "mgMP3Decoder::init" );
+
+ clean();
+ mad_stream_init( &m_madstream );
+
+ m_madframe = new mad_frame;
+ mad_frame_init( m_madframe );
+
+ m_madsynth = new mad_synth;
+ mad_synth_init( m_madsynth );
+
+ mad_stream_options( &m_madstream, MAD_OPTION_IGNORECRC);
+
+ m_playtime = mad_timer_zero;
+ m_skiptime = mad_timer_zero;
+ m_framenum = m_framemax = 0;
+ m_mute = m_errcount = 0;
+}
+
+void mgMP3Decoder::clean()
+{
+ MGLOG( "mgMP3Decoder::clean" );
+
+ m_playing = false;
+ if( m_madsynth )
+ {
+ mad_synth_finish( m_madsynth );
+ delete m_madsynth;
+ m_madsynth = 0;
+ }
+
+ if( m_madframe )
+ {
+ mad_frame_finish( m_madframe );
+ delete m_madframe;
+ m_madframe = 0;
+ }
+ mad_stream_finish( &m_madstream );
+
+ /*
+ if( m_frameinfo )
+ {
+ cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush;
+ delete m_frameinfo;
+ cout << "mgMP3Decoder::clean: m_frameinfo deleted." << endl << flush;
+ m_frameinfo = 0;
+ }
+ */
+}
+
+bool mgMP3Decoder::valid(void)
+{
+ MGLOG( "mgMP3Decoder::valid" );
+
+ bool res = false;
+ if( tryLock() )
+ {
+ if( start() )
+ {
+ struct mgDecode *dd;
+ int count = 10;
+ do
+ {
+ dd = decode();
+ if( dd->status == dsEof )
+ {
+ count=0;
+ }
+ if( dd->status != dsPlay)
+ {
+ break;
+ }
+ } while( --count );
+ if( !count )
+ {
+ res=true;
+ }
+ stop();
+ }
+ unlock();
+ }
+ return res;
+}
+
+mgPlayInfo *mgMP3Decoder::playInfo()
+{
+ MGLOG( "mgMP3Decoder::playInfo" );
+ if( m_playing )
+ {
+ m_playinfo.m_index = mad_timer_count( m_playtime, MAD_UNITS_SECONDS );
+ // m_playinfo.Total = m_scan->Total; // TODO
+
+ return &m_playinfo;
+ }
+ return 0;
+}
+
+bool mgMP3Decoder::start()
+{
+ MGLOG( "mgMP3Decoder::start" );
+
+ lock(true);
+ init();
+ m_playing = true;
+
+ if( m_stream->open(true) )
+ {
+ if(!m_isStream)
+ {
+ m_stream->seek();
+
+ printf( "mgMP3Decoder::start: m_framemax not determined, rewinding disabled!!!\n" );
+
+ /* m_framemax = scan->Frames+20; // TODO
+
+ m_frameinfo = new struct FrameInfo[m_framemax];
+ if(!m_frameinfo)
+ {
+ printf( "mgMP3Decoder::start: no memory for frame index, rewinding disabled" );
+ }
+ */
+ }
+ unlock();
+
+ printf( "mgMP3Decoder::start: true\n" );
+ return true;
+ }
+ m_stream->close();
+ clean();
+ unlock();
+
+ printf( "mgMP3Decoder::start: false" );
+ return false;
+}
+
+bool mgMP3Decoder::stop(void)
+{
+ MGLOG( "mgMP3Decoder::stop" );
+
+ lock();
+
+ if( m_playing )
+ {
+ m_stream->close();
+ clean();
+ }
+ unlock();
+ return true;
+}
+
+struct mgDecode *mgMP3Decoder::done(eDecodeStatus status)
+{
+ // MGLOG( "mgMP3Decoder::done" );
+
+ m_ds.status = status;
+ m_ds.index = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS );
+ m_ds.pcm = &m_madsynth->pcm;
+ unlock(); // release the lock from Decode()
+
+ return &m_ds;
+}
+
+eDecodeStatus mgMP3Decoder::decodeError(bool hdr)
+{
+ MGLOG( "mgMP3Decoder::decodeError" );
+
+ if( m_madstream.error == MAD_ERROR_BUFLEN || m_madstream.error == MAD_ERROR_BUFPTR )
+ {
+ int s = mgMadStream( &m_madstream, m_stream );
+ if( s < 0 )
+ {
+ printf( "mgMP3Decoder::decodeError: dsError returned\n" );
+ return dsError;
+ }
+ if( s == 0 )
+ {
+ printf( "mgMP3Decoder::decodeError: dsEof returned\n" );
+ return dsEof;
+ }
+ }
+ else if( !MAD_RECOVERABLE( m_madstream.error ) )
+ {
+ printf( "mgMP3Decoder::decodeError: mad decode %sfailed, frame=%d: %s. Returning dsError\n",
+ hdr? "hdr " : "", m_framenum,
+ mad_stream_errorstr( &m_madstream ) );
+ return dsError;
+ }
+ else
+ {
+ m_errcount += hdr? 1: 100;
+ printf( "mgMP3Decoder::decodeError: mad decode %s error, frame=%d count=%d: %s. Returning dsOK\n",
+ hdr? "hdr ": "", m_framenum, m_errcount,
+ mad_stream_errorstr( &m_madstream ) );
+ }
+ return dsOK;
+}
+
+struct mgDecode *mgMP3Decoder::decode()
+{
+ // MGLOG( "mgMP3Decoder::decode" );
+
+ lock(); // this is released in Done()
+ eDecodeStatus r;
+
+ while( m_playing )
+ {
+ if( m_errcount >= MAX_FRAME_ERR*100 )
+ {
+ printf( "mgMP3Decoder::decode: excessive decoding errors,"
+ " aborting file %s\n", m_filename.c_str() );
+ return done(dsError);
+ }
+
+ if( mad_header_decode( &m_madframe->header, &m_madstream) == -1)
+ {
+ if( (r = decodeError(true) ) )
+ {
+ return done(r);
+ }
+ }
+ else
+ {
+ if(!m_isStream)
+ {
+#ifdef DEBUG2
+ if( m_framenum >= m_framemax )
+ {
+ printf( "mgMP3Decoder::start: framenum >= framemax!!!!\n" );
+ }
+#endif
+ if( m_frameinfo && m_framenum < m_framemax )
+ {
+ m_frameinfo[m_framenum].Pos =
+ m_stream->bufferPos() +
+ ( m_madstream.this_frame - m_madstream.buffer );
+ m_frameinfo[m_framenum].Time = m_playtime;
+ }
+ }
+
+ mad_timer_add( &m_playtime, m_madframe->header.duration);
+ m_framenum ++;
+
+ if( mad_timer_compare(m_playtime, m_skiptime) >= 0 )
+ {
+ m_skiptime = mad_timer_zero;
+ }
+ else
+ {
+ return done(dsSkip); // skipping, decode next header
+ }
+
+ if( mad_frame_decode( m_madframe, &m_madstream ) == -1 )
+ {
+ if( ( r = decodeError(false) ) )
+ {
+ return done(r);
+ }
+ }
+ else
+ {
+ m_errcount = 0;
+
+ // TODO: // m_scan->InfoHook( &frame->header );
+
+ mad_synth_frame( m_madsynth, m_madframe);
+
+ if( m_mute )
+ {
+ m_mute--;
+ return done( dsSkip );
+ }
+ return done( dsPlay );
+ }
+ }
+ }
+ return done( dsError );
+}
+
+void mgMP3Decoder::makeSkipTime( mad_timer_t *skiptime,
+ mad_timer_t playtime,
+ int secs, int avail, int dvbrate)
+{
+ mad_timer_t time;
+
+ *skiptime = playtime;
+
+ mad_timer_set( &time, abs(secs), 0, 0 );
+
+ if( secs < 0 )
+ {
+ mad_timer_negate(&time);
+ }
+
+ mad_timer_add( skiptime, time );
+
+ float bufsecs = (float)avail / (float)(dvbrate * (16/8 * 2)); // Byte/s = samplerate * 16 bit * 2 chan
+
+ printf( "mgMP3Decoder::makeSkipTime: skip: avail=%d bufsecs=%f\n", avail, bufsecs );
+
+ int full = (int)bufsecs;
+ bufsecs -= (float)full;
+
+ mad_timer_set( &time, full, (int)(bufsecs*1000.0), 1000);
+
+ mad_timer_negate(&time);
+
+ mad_timer_add( skiptime, time );
+
+ printf( "mgMP3Decoder::makeSkipTime: skip: playtime=%ld secs=%d full=%d bufsecs=%f skiptime=%ld\n",
+ mad_timer_count(playtime,MAD_UNITS_MILLISECONDS),
+ secs, full, bufsecs,
+ mad_timer_count(*skiptime,MAD_UNITS_MILLISECONDS ) );
+}
+
+bool mgMP3Decoder::skip( int seconds, int avail, int rate )
+{
+ lock();
+
+ bool res = false;
+ if( m_playing && !m_isStream )
+ {
+ if( !mad_timer_compare( m_skiptime, mad_timer_zero ) )
+ { // allow only one skip at any time
+ mad_timer_t time;
+ makeSkipTime( &time, m_playtime, seconds, avail, rate);
+
+ if( mad_timer_compare( m_playtime, time ) <= 0 )
+ { // forward skip
+#ifdef DEBUG
+ int i = mad_timer_count( time, MAD_UNITS_SECONDS );
+ printf( "mgMP3Decoder::skip: forward skipping to %02d:%02d\n", i/60, i%60 );
+#endif
+ m_skiptime = time;
+ m_mute=1;
+ res = true;
+ }
+ else
+ { // backward skip
+ if( m_frameinfo )
+ {
+#ifdef DEBUG
+ int i = mad_timer_count( time, MAD_UNITS_SECONDS );
+ printf( "mgMP3Decoder::skip: rewinding to %02d:%02d\n", i/60, i%60 );
+#endif
+ while( m_framenum && mad_timer_compare( time, m_frameinfo[--m_framenum].Time) < 0) ;
+ m_mute = 2;
+ if( m_framenum >= 2)
+ {
+ m_framenum-=2;
+ }
+ m_playtime = m_frameinfo[m_framenum].Time;
+ m_stream->seek( m_frameinfo[m_framenum].Pos );
+ mad_stream_finish( &m_madstream ); // reset stream buffer
+ mad_stream_init( &m_madstream );
+#ifdef DEBUG
+ i = mad_timer_count( m_playtime, MAD_UNITS_MILLISECONDS );
+ printf( "mgMP3Decoder::skip: new playtime=%d framenum=%d filepos=%lld\n", i, m_framenum, m_frameinfo[m_framenum].Pos);
+#endif
+ res = true;
+ }
+ }
+ }
+ }
+ unlock();
+ return res;
+}
diff --git a/vdr_decoder_mp3.h b/vdr_decoder_mp3.h
new file mode 100644
index 0000000..abc8eed
--- /dev/null
+++ b/vdr_decoder_mp3.h
@@ -0,0 +1,114 @@
+/*!
+ * \file vdr_decoder_mp3.h
+ * \brief An mp3 decoder for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_decoder_mp3.h,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___DECODER_MP3_H
+#define ___DECODER_MP3_H
+
+#define DEC_MP3 DEC_ID('M','P','3',' ')
+#define DEC_MP3_STR "MP3"
+
+#include <mad.h>
+#include <string>
+
+#include "vdr_decoder.h"
+
+#if MAD_F_FRACBITS != 28
+#warning libmad with MAD_F_FRACBITS != 28 not tested
+#endif
+
+class mgStream;
+
+// ----------------------------------------------------------------
+
+/*!
+ * \brief A class to decode mp3 songs into PCM using libmad
+ */
+class mgMP3Decoder : public mgDecoder
+{
+private:
+ struct mgDecode m_ds;
+
+ //
+ struct mad_stream m_madstream;
+ struct mad_frame *m_madframe;
+ struct mad_synth *m_madsynth;
+ mad_timer_t m_playtime, m_skiptime;
+
+ //
+ struct FrameInfo
+ {
+ unsigned long long Pos;
+ mad_timer_t Time;
+ } *m_frameinfo;
+
+ int m_framenum, m_framemax, m_errcount, m_mute;
+ //
+ void init();
+
+ void clean();
+
+ struct mgDecode *done( eDecodeStatus status );
+
+ virtual mgPlayInfo *playInfo();
+
+ eDecodeStatus decodeError(bool hdr);
+
+ void makeSkipTime(mad_timer_t *skiptime, mad_timer_t playtime,
+ int secs, int avail, int dvbrate);
+
+protected:
+ mgStream *m_stream;
+ bool m_isStream;
+
+public:
+
+ /*!
+ * \brief construct a decoder from a filename
+ */
+ mgMP3Decoder( std::string filename, bool preinit = true );
+
+ /*!
+ * \brief the destructor
+ */
+ virtual ~mgMP3Decoder();
+
+ /*!
+ * \brief check, whether the file contains useable MP3 content
+ */
+ virtual bool valid();
+
+ /*!
+ * \brief start the decoding process
+ */
+ virtual bool start();
+
+ /*!
+ * \brief stop the decoding process
+ */
+ virtual bool stop();
+
+ /*!
+ * \brief skip an amount of seconds
+ */
+ virtual bool skip( int seconds, int avail, int rate );
+
+ /*!
+ * \brief the actual decoding function (uses libmad)
+ */
+ virtual struct mgDecode *decode();
+};
+
+#endif //___DECODER_MP3_H
diff --git a/vdr_menu.c b/vdr_menu.c
index 8d4cf06..dcc442f 100644
--- a/vdr_menu.c
+++ b/vdr_menu.c
@@ -1,34 +1,36 @@
-/*******************************************************************/
-/*! \file vdr_menu.c
- * \brief Implements menu handling for browsing media libraries within VDR
- ********************************************************************
- * \version $Revision: 1.19 $
- * \date $Date: 2004/02/23 17:03:24 $
+/*!
+ * \file vdr_menu.c
+ * \brief Implements menu handling for browsing media libraries within VDR
+ *
+ * \version $Revision: 1.20 $
+ * \date $Date: 2004/05/28 15:29:19 $
* \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+ * \author Responsible author: $Author: lvw $
*
- * $Id: vdr_menu.c,v 1.19 2004/02/23 17:03:24 RaK Exp $
+ * $Id: vdr_menu.c,v 1.20 2004/05/28 15:29:19 lvw Exp $
*/
-/*******************************************************************/
+
+#include <string>
+#include <vector>
+
+#include <mysql/mysql.h>
#include <menuitems.h>
#include <tools.h>
-#include <mysql/mysql.h>
#include "vdr_menu.h"
+#include "vdr_player.h"
+#include "i18n.h"
#include "mg_content_interface.h"
+#include "mg_playlist.h"
+#define DEBUG
#include "mg_tools.h"
#include "mg_media.h"
#include "mg_filters.h"
#include "gd_content_interface.h"
-#include "i18n.h"
-
-#include <string>
-#include <vector>
-
using namespace std;
// ----------------------- mgMenuTreeItem ------------------
@@ -51,9 +53,6 @@ void mgMenuTreeItem::Set()
SetText( buffer, false );
}
-// ----------------------- mgMenuTrackItem ------------------
-
-
// ----------------------- mgMainMenu ----------------------
mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *playlist)
@@ -64,28 +63,6 @@ mgMainMenu::mgMainMenu(mgMedia *media, mgSelectionTreeNode *root, mgPlaylist *pl
SetTitle( tr("Muggle Media Database") );
SetButtons();
- m_filtername = new char[32];
- strcpy( m_filtername, "none" );
-
- m_title = new char[32];
- strcpy( m_title, "none" );
-
- m_interpret = new char[32];
- strcpy( m_interpret, "none" );
-
- m_album = new char[32];
- strcpy( m_album, "none" );
-
- m_playlist = new char[32];
- strcpy( m_playlist, "none" );
-
- m_year_min = 1900;
- m_year_max = 2100;
-
- m_filter = 0;
-
- m_tracklist = NULL;
-
DisplayTree( m_root );
}
@@ -110,17 +87,17 @@ void mgMainMenu::SetButtons( )
{
SetHelp( tr("Add"), tr("Cycle tree"), tr("Playlist"), tr("Submenu") );
}
- else if( m_state == PLAYLIST )
+ else if( m_state == TREE_SUBMENU )
{
- SetHelp( tr("Edit PL"), tr("Track info"), tr("Filter"), tr("Submenu") );
+ SetHelp( tr("Instant Play"), tr("2"), tr("3"), tr("Mainmenu") );
}
- else if( m_state == PLAYLIST_TRACKINFO )
+ else if( m_state == PLAYLIST )
{
- SetHelp( tr("Edit PL?"), tr("Album info"), tr("Filter"), tr("Submenu") );
+ SetHelp( tr("Play"), tr("Move"), tr("Filter"), tr("Submenu") );
}
- else if( m_state == PLAYLIST_ALBUMINFO )
+ else if( m_state == PLAYLIST_SUBMENU )
{
- SetHelp( tr("Edit PL?"), tr("Playlist"), tr("Filter"), tr("Submenu") );
+ SetHelp( tr("Load"), tr("Save"), tr("Delete"), tr("Mainmenu") );
}
else if( m_state == FILTER )
{
@@ -132,9 +109,17 @@ void mgMainMenu::SetButtons( )
}
}
+void mgMainMenu::Move( int from, int to )
+{
+ m_current_playlist->move( from, to );
+
+ cOsdMenu::Move( from, to );
+ Display();
+}
+
eOSState mgMainMenu::ProcessKey(eKeys key)
{
- mgDebug( 1, "mgMainMenu::ProcessKey" );
+ MGLOG( "mgMainMenu::ProcessKey" );
eOSState state = cOsdMenu::ProcessKey(key);
if( m_state == TREE )
@@ -169,10 +154,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
if( tracks )
{
- m_current_playlist->appendList(tracks);
char buffer[256];
sprintf( buffer, "%d tracks sent to current playlist", (int) tracks->size() );
+ m_current_playlist->appendList(tracks);
Interface->Status( buffer );
Interface->Flush();
}
@@ -201,8 +186,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
} break;
case kBlue:
{
- mgDebug( 1, "mgMainMenu: submenu (todo)" );
-
+ m_last_osd_index = Current();
DisplayTreeSubmenu();
state = osContinue;
@@ -248,18 +232,25 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
{
case k0 ... k9:
{
- int n = key - k0;
-
+ int n = key - k0;
TreeSubmenuAction( n );
state = osContinue;
- }
- case kOk:
+ } break;
+ case kBlue:
{
- TreeSubmenuAction( Current() );
+ m_state = TREE;
+ // restore last selected entry
+ int last = m_history.back();
+ DisplayTree( m_node, last );
+
state = osContinue;
} break;
+ case kOk:
+ {
+ state = TreeSubmenuAction( Current() );
+ } break;
default:
{
state = osContinue;
@@ -268,11 +259,10 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
}
else if( state == osBack )
{
- mgDebug( 1, "mgMainMenu: return from tree view submenu" );
-
+ m_state = TREE;
+
// restore last selected entry
int last = m_history.back();
-
DisplayTree( m_node, last );
state = osContinue;
@@ -280,65 +270,91 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
}
else if( m_state == PLAYLIST )
{
- mgDebug( 1, "mgMainMenu: in state PLAYLIST" );
if( state == osUnknown )
{
switch( key )
{
case kOk:
{
- mgDebug( 1, "mgMainMenu: playlist ok" );
+ // show some more information?
state = osContinue;
} break;
case kRed:
{
- mgDebug( 1, "mgMainMenu: edit playlist" );
- Mark(); // Mark (to move), moving done by VDR, calls Move
- }
+ // TODO: what happens if the user presses play and the player is already active?
+ Play( m_current_playlist );
+ state = osEnd;
+ } break;
case kGreen:
{
- if( m_state == PLAYLIST )
- {
- mgDebug( 1, "mgMainMenu: switch to TrackInfo" );
- DisplayTrackInfo();
- }
- else if( m_state == PLAYLIST_TRACKINFO )
- {
- mgDebug( 1, "mgMainMenu: switch to AlbumInfo" );
- DisplayAlbumInfo();
- }
- else if( m_state == PLAYLIST_ALBUMINFO )
- {
- mgDebug( 1, "mgMainMenu: switch to Playlist" );
- DisplayPlaylist();
- }
+ Mark();
+
state = osContinue;
} break;
case kYellow:
{
- mgDebug( 1, "mgMainMenu: cycle playlist to filter" );
DisplayFilter();
state = osContinue;
} break;
case kBlue:
{
// Submenu
- mgDebug( 1, "mgMainMenu: playlist submenu (todo)" );
-
+ m_last_osd_index = Current();
+ DisplayPlaylistSubmenu();
+
state = osContinue;
} break;
default:
{
- mgDebug( 1, "mgMainMenu: default" );
state = osContinue;
};
}
}
}
+ else if( m_state == PLAYLIST_SUBMENU )
+ {
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case k0 ... k9:
+ {
+ int n = key - k0;
+ PlaylistSubmenuAction( n );
+
+ state = osContinue;
+ } break;
+ case kYellow:
+ {
+ PlaylistSubmenuAction( 3 );
+ } break;
+ case kBlue:
+ {
+ m_state = PLAYLIST;
+ DisplayPlaylist( m_last_osd_index );
+
+ state = osContinue;
+ } break;
+ case kOk:
+ {
+ state = PlaylistSubmenuAction( Current() );
+ } break;
+ default:
+ {
+ state = osContinue;
+ } break;
+ }
+ }
+ else if( state == osBack )
+ {
+ m_state = PLAYLIST;
+ DisplayPlaylist( m_last_osd_index );
+
+ state = osContinue;
+ }
+ }
else if( m_state == FILTER )
{
- mgDebug( 1, "mgMainMenu: in state FILTER" );
-
if( state == osUnknown )
{
switch( key )
@@ -399,8 +415,7 @@ eOSState mgMainMenu::ProcessKey(eKeys key)
else
{
mgDebug(1, "Process key: else");
- mgDebug(1, "Process key: %d", (int) state);
-
+ mgDebug(1, "Process key: %d", (int) state);
}
return state;
@@ -461,22 +476,43 @@ void mgMainMenu::DisplayTreeSubmenu()
SetTitle( strcat("Muggle - ",tr("Tree View Commands") ) );
// Add items
- Add( new cOsdItem( "1 - Test1" ) );
+ Add( new cOsdItem( "1 - Instant play" ) );
Add( new cOsdItem( "9 - Test9" ) );
Add( new cOsdItem( "0 - Test0" ) );
Display();
}
-void mgMainMenu::TreeSubmenuAction( int n )
+eOSState mgMainMenu::TreeSubmenuAction( int n )
{
mgDebug( "mgMainMenu: TreeSubmenuAction( %d )", n );
+ eOSState state = osContinue;
switch( n )
{
case 0:
{
- // action 0
+ // action 0: instant play of current node, might need a security question
+
+ mgSelectionTreeNode *current = CurrentNode();
+ if( current )
+ {
+ // clear playlist
+ m_current_playlist->clear();
+
+ // append current node
+ vector<mgContentItem*> *tracks = current->getTracks();
+
+ if( tracks )
+ {
+ m_current_playlist->appendList( tracks );
+
+ // play
+ Play( m_current_playlist );
+
+ state = osEnd;
+ }
+ }
} break;
case 1:
{
@@ -487,16 +523,16 @@ void mgMainMenu::TreeSubmenuAction( int n )
// undefined action
} break;
}
+
+ return state;
}
-void mgMainMenu::DisplayPlaylist()
+void mgMainMenu::DisplayPlaylist( int index_current )
{
m_state = PLAYLIST;
- mgDebug( 1, "mgBrowseMenu::DisplayPlaylist");
// make sure we have a current playlist
Clear();
-
SetButtons();
vector<mgContentItem*>* list = m_current_playlist-> getAll();
@@ -506,68 +542,109 @@ void mgMainMenu::DisplayPlaylist()
tr("items") );
SetTitle( titlestr );
- mgDebug( 1, "mgBrowseMenu::DisplayPlaylist: %d elements received",
- list->size() );
-
for( unsigned int i = 0; i < m_current_playlist->getNumItems(); i++)
{
string label = m_current_playlist->getLabel( i, " " );
Add( new cOsdItem( label.c_str() ) );
}
+
+ if( index_current >= 0 )
+ {
+ cOsdItem *item = Get( m_last_osd_index );
+ SetCurrent( item );
+ RefreshCurrent();
+ DisplayCurrent( true );
+ }
Display();
}
-void mgMainMenu::DisplayTrackInfo()
+void mgMainMenu::DisplayPlaylistSubmenu()
{
- m_state = PLAYLIST_TRACKINFO;
- SetButtons();
-
- // show info of the currently playing track
-}
+ m_state = PLAYLIST_SUBMENU;
-void mgMainMenu::DisplayAlbumInfo()
-{
- m_state = PLAYLIST_ALBUMINFO;
+ Clear();
SetButtons();
+ SetTitle( "Muggle - Playlist View Commands" );
- // show info of the currently playing album
+ // Add items
+ Add( new cOsdItem( "1 - Load playlist" ) );
+ Add( new cOsdItem( "2 - Save playlist" ) );
+ Add( new cOsdItem( "3 - Clear playlist" ) );
+ Add( new cOsdItem( "4 - Remove entry from list" ) );
+
+ Display();
}
-void mgMainMenu::Move( int from, int to )
+eOSState mgMainMenu::PlaylistSubmenuAction( int n )
{
- // check current view, perform move in the content view
- if( m_state == PLAYLIST )
+ cout << "mgMainMenu::PlaylistSubmenuAction: " << n << endl << flush;
+ eOSState state = osContinue;
+
+ switch( n )
{
- // resort
+ case 0:
+ {
+ Interface->Status( "Load not yet implemented" );
+ Interface->Flush();
+ } break;
+ case 1:
+ {
+ Interface->Status( "Save not yet implemented" );
+ Interface->Flush();
+ } break;
+ case 2:
+ { // clear playlist
+ m_current_playlist->clear();
+
+ // confirmation
+ Interface->Status( "Playlist cleared" );
+ Interface->Flush();
+
+ state = osContinue;
+ } break;
+ case 3:
+ { // remove selected title
+ m_current_playlist->remove( m_last_osd_index );
+ if( m_last_osd_index > 0 )
+ {
+ m_last_osd_index --;
+ }
+ DisplayPlaylist( m_last_osd_index );
+
+ // confirmation
+ Interface->Status( "Entry removed" );
+ Interface->Flush();
+ }
+ default:
+ {
+ // undefined action
+ } break;
}
- // what now?
+ return state;
}
void mgMainMenu::DisplayFilter()
{
m_state = FILTER;
- Clear();
-
- mgDebug( 1, "Creating Muggle filter view" );
+
+ Clear();
SetButtons();
SetTitle( m_media->getActiveFilterTitle().c_str() );
- mgDebug( 1, "filter view2" );
-
vector<mgFilter*> *filter_list = m_media->getActiveFilters();
- mgDebug( 1, "filter view3" );
int i=0;
for( vector<mgFilter*>::iterator iter = filter_list->begin();
iter != filter_list->end();
iter ++ )
{
- mgDebug( 1, "Filter %d/%dint filter %s='%s'",
- i, filter_list->size(),
- (*iter)->getName(),
- (*iter)->getStrVal().c_str());
+ mgDebug( 1, "Filter %d/%dint filter %s='%s'",
+ i, filter_list->size(),
+ (*iter)->getName(),
+ (*iter)->getStrVal().c_str());
+
switch( (*iter)->getType() )
{
case mgFilter::INT:
@@ -637,9 +714,29 @@ void mgMainMenu::DisplayFilterSelector()
// show available filters, load on OK?
}
+void mgMainMenu::Play(mgPlaylist *plist)
+{
+ MGLOG( "mgMainMenu::Play" );
+ cControl *control = cControl::Control();
+
+ if( control && typeid(*control) == typeid(mgPlayerControl) )
+ { // is there a running MP3 player?
+ cout << "mgMainMenu::Play: signal new playlist to existing control" << endl;
+ static_cast<mgPlayerControl*>(control)->NewPlaylist(plist); // signal the running player to load the new playlist
+ }
+ else
+ {
+ cout << "mgMainMenu::Play: starting new control" << endl;
+ cControl::Launch( new mgPlayerControl(plist) );
+ }
+}
+
/************************************************************
*
* $Log: vdr_menu.c,v $
+ * Revision 1.20 2004/05/28 15:29:19 lvw
+ * Merged player branch back on HEAD branch.
+ *
* Revision 1.19 2004/02/23 17:03:24 RaK
* - error in filter view while trying to switch or using the colour keys
* workaround: first filter criteria is inttype. than it works, dont ask why ;-(
@@ -661,6 +758,42 @@ void mgMainMenu::DisplayFilterSelector()
* Revision 1.14 2004/02/12 09:08:48 LarsAC
* Added handling of filter choices (untested)
*
+ * Revision 1.13.2.12 2004/05/27 07:58:38 lvw
+ * Removed bugs in moving and removing tracks from playlists
+ *
+ * Revision 1.13.2.11 2004/05/26 14:31:04 lvw
+ * Added submenu for playlist view
+ *
+ * Revision 1.13.2.10 2004/05/25 21:58:45 lvw
+ * Handle submenus for views
+ *
+ * Revision 1.13.2.9 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.13.2.8 2004/05/11 06:35:16 lvw
+ * Added debugging while hunting stop bug.
+ *
+ * Revision 1.13.2.7 2004/05/04 16:51:53 lvw
+ * Debugging aids added.
+ *
+ * Revision 1.13.2.6 2004/04/29 06:48:21 lvw
+ * Output statements to aid debugging added
+ *
+ * Revision 1.13.2.5 2004/04/25 18:44:07 lvw
+ * Removed bugs in menu handling
+ *
+ * Revision 1.13.2.4 2004/03/14 12:30:56 lvw
+ * Menu now calls player
+ *
+ * Revision 1.13.2.3 2004/03/11 07:22:32 lvw
+ * Added setup menu
+ *
+ * Revision 1.13.2.2 2004/03/08 22:28:40 lvw
+ * Added documentation headers.
+ *
+ * Revision 1.13.2.1 2004/03/02 07:07:27 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.13 2004/02/09 19:27:52 MountainMan
* filter set implemented
*
diff --git a/vdr_menu.h b/vdr_menu.h
index e0ef30b..a4a1f7c 100644
--- a/vdr_menu.h
+++ b/vdr_menu.h
@@ -1,30 +1,23 @@
-/*******************************************************************/
-/*! \file vdr_menu.h
- * \brief Implements menu handling for broswing media libraries within VDR
- ********************************************************************
- * \version $Revision: 1.9 $
- * \date $Date: 2004/02/23 15:41:21 $
- * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
- * \author file owner: $Author: RaK $
+/*!
+ * \file vdr_menu.h
+ * \brief Implements menu handling for broswing media libraries within VDR
*
- * $Id: vdr_menu.h,v 1.9 2004/02/23 15:41:21 RaK Exp $
+ * \version $Revision: 1.10 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
*
+ * $Id: vdr_menu.h,v 1.10 2004/05/28 15:29:19 lvw Exp $
*/
-/*******************************************************************/
#ifndef _VDR_MENU_H
#define _VDR_MENU_H
-#undef SHELL_TEST
+#include <list>
-#ifdef SHELL_TEST
- #include "myosd.h"
- #include "mymenuitems.h"
-#else
- #include <osd.h>
-#endif
+#include <osd.h>
-#include <list>
+#include "i18n.h"
#include "i18n.h"
@@ -33,6 +26,9 @@ class mgSelectionTreeNode;
class mgPlaylist;
class mgTracklist;
+/*!
+ * \brief a special menu item
+ */
class mgMenuTreeItem : public cOsdItem
{
public:
@@ -49,6 +45,9 @@ class mgMenuTreeItem : public cOsdItem
};
+/*!
+ * \brief the muggle main OSD
+ */
class mgMainMenu : public cOsdMenu
{
public:
@@ -59,50 +58,51 @@ class mgMainMenu : public cOsdMenu
mgMenuTreeItem *CurrentItem();
eOSState ProcessKey(eKeys Key);
- void Move( int from, int to);
protected:
enum MuggleStatus
{
TREE, TREE_SUBMENU,
- PLAYLIST, PLAYLIST_TRACKINFO, PLAYLIST_ALBUMINFO,
- FILTER
+ PLAYLIST, PLAYLIST_SUBMENU,
+ FILTER, FILTER_SUBMENU
};
void SetButtons();
+ void Move( int from, int to );
// Tree view handling
void DisplayTree( mgSelectionTreeNode *node, int select = 0 );
void DisplayTreeViewSelector();
- void DisplayTreeSubmenu();
- void TreeSubmenuAction( int n );
- // Filter view handling
- void DisplayFilter();
- void DisplayFilterSelector();
+ void DisplayTreeSubmenu();
+ eOSState TreeSubmenuAction( int n );
// Playlist view handling
- void DisplayPlaylist();
+ void DisplayPlaylist( int index_current = -1 );
void DisplayTrackInfo();
void DisplayAlbumInfo();
+ void DisplayPlaylistSubmenu();
+ eOSState PlaylistSubmenuAction( int n );
+
+ // Filter view handling
+ void DisplayFilter();
+ void DisplayFilterSelector();
+
private:
+ void Play(mgPlaylist *plist);
// content stuff
mgMedia *m_media;
mgSelectionTreeNode *m_root;
mgSelectionTreeNode *m_node;
mgPlaylist *m_current_playlist;
- mgTracklist *m_tracklist;
-
- // filter items
- char *m_title, *m_interpret, *m_album, *m_playlist, *m_filtername;
- int m_year_min, m_year_max, m_filter;
MuggleStatus m_state;
-
std::list<int> m_history;
+
+ int m_last_osd_index;
};
#endif
@@ -110,9 +110,33 @@ class mgMainMenu : public cOsdMenu
/************************************************************
*
* $Log: vdr_menu.h,v $
+ * Revision 1.10 2004/05/28 15:29:19 lvw
+ * Merged player branch back on HEAD branch.
+ *
* Revision 1.9 2004/02/23 15:41:21 RaK
* - first i18n attempt
*
+ * Revision 1.8.2.7 2004/05/27 07:58:38 lvw
+ * Removed bugs in moving and removing tracks from playlists
+ *
+ * Revision 1.8.2.6 2004/05/26 14:31:04 lvw
+ * Added submenu for playlist view
+ *
+ * Revision 1.8.2.5 2004/05/25 21:58:54 lvw
+ * Handle submenus for views
+ *
+ * Revision 1.8.2.4 2004/03/14 12:30:56 lvw
+ * Menu now calls player
+ *
+ * Revision 1.8.2.3 2004/03/11 07:22:32 lvw
+ * Added setup menu
+ *
+ * Revision 1.8.2.2 2004/03/08 22:28:40 lvw
+ * Added documentation headers.
+ *
+ * Revision 1.8.2.1 2004/03/02 07:07:27 lvw
+ * Initial adaptations from MP3 plugin added (untested)
+ *
* Revision 1.8 2004/02/08 10:48:44 LarsAC
* Made major revisions in OSD behavior
*
@@ -125,6 +149,4 @@ class mgMainMenu : public cOsdMenu
* Revision 1.5 2004/02/03 00:13:24 LarsAC
* Improved OSD handling of collapse/back
*
- *
- ************************************************************
*/
diff --git a/vdr_network.h b/vdr_network.h
new file mode 100644
index 0000000..182fa23
--- /dev/null
+++ b/vdr_network.h
@@ -0,0 +1,45 @@
+/*
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___NETWORK_H
+#define ___NETWORK_H
+
+#include <thread.h>
+#include <ringbuffer.h>
+#include <config.h>
+
+class cRingBufferLinear;
+
+// ----------------------------------------------------------------
+
+class mgNet : public cRingBufferLinear, cThread
+{
+private:
+ int m_fd;
+ bool m_connected, m_netup;
+ int m_deferedErrno;
+ int m_rwTimeout, m_conTimeout;
+ unsigned char m_lineBuff[4096];
+ int m_count;
+ //
+ void close(void);
+ int ringRead(unsigned char *dest, int len);
+ void copyFromBuff(unsigned char *dest, int n);
+protected:
+ virtual void action(void);
+public:
+ mgNet(int size, int ConTimeoutMs, int RwTimeoutMs);
+ ~mgNet();
+ bool connect(const char *hostname, const int port);
+ void disconnect(void);
+ bool connected(void) { return m_connected; }
+ int gets(char *dest, int len);
+ int puts(char *dest);
+ int read(unsigned char *dest, int len);
+ int write(unsigned char *dest, int len);
+};
+
+#endif //___NETWORK_H
diff --git a/vdr_player.c b/vdr_player.c
new file mode 100644
index 0000000..090bbc6
--- /dev/null
+++ b/vdr_player.c
@@ -0,0 +1,1073 @@
+/*!
+ * \file vdr_player.c
+ * \brief A generic PCM player for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_player.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <string>
+#include <iostream>
+
+#include <mad.h>
+#include <id3tag.h>
+
+#include <player.h>
+#include <device.h>
+#include <thread.h>
+#include <ringbuffer.h>
+#include <tools.h>
+#include <recording.h>
+
+#include "vdr_player.h"
+#include "vdr_decoder.h"
+#include "vdr_config.h"
+#include "vdr_setup.h"
+
+#include "mg_tools.h"
+#include "mg_playlist.h"
+#include "mg_content_interface.h"
+
+using namespace std;
+
+// ----------------------------------------------------------------
+
+// TODO: check for use of constants
+#define OUT_BITS 16 // output 16 bit samples to DVB driver
+#define OUT_FACT (OUT_BITS/8*2) // output factor is 16 bit & 2 channels -> 4 bytes
+
+// cResample
+#define MAX_NSAMPLES (1152*7) // max. buffer for resampled frame
+
+// cNormalize
+#define MAX_GAIN 3.0 // max. allowed gain
+#define LIM_ACC 12 // bit, accuracy for lookup table
+#define F_LIM_MAX (mad_fixed_t)((1<<(MAD_F_FRACBITS+2))-1) // max. value covered by lookup table
+#define LIM_SHIFT (MAD_F_FRACBITS-LIM_ACC) // shift value for table lookup
+#define F_LIM_JMP (mad_fixed_t)(1<<LIM_SHIFT) // lookup table jump between values
+
+// cLevel
+#define POW_WIN 100 // window width for smoothing power values
+#define EPSILON 0.00000000001 // anything less than EPSILON is considered zero
+
+// cMP3Player
+#define MAX_FRAMESIZE 2048 // max. frame size allowed for DVB driver
+#define HDR_SIZE 9
+#define LPCM_SIZE 7
+#define LEN_CORR 3
+#define SPEEDCHECKSTART ((MP3BUFSIZE*1000/32000/2/2)+1000) // play time to fill buffers before speed check starts (ms)
+#define SPEEDCHECKTIME 3000 // play time for speed check (ms)
+
+/*
+struct LPCMHeader { int id:8; // id
+ int frame_count:8; // number of frames
+ int access_ptr:16; // first acces unit pointer, i.e. start of audio frame
+ bool emphasis:1; // audio emphasis on-off
+ bool mute:1; // audio mute on-off
+ bool reserved:1; // reserved
+ int frame_number:5; // audio frame number
+ int quant_wlen:2; // quantization word length
+ int sample_freq:2; // audio sampling frequency (48khz=0, 96khz=1, 44,1khz=2, 32khz=3)
+ bool reserved2:1; // reserved
+ int chan_count:3; // number of audio channels - 1 (e.g. stereo = 1)
+ int dyn_range_ctrl:8; // dynamic range control (0x80 if off)
+ };
+*/
+
+struct LPCMFrame
+{
+ unsigned char PES[HDR_SIZE];
+ unsigned char LPCM[LPCM_SIZE];
+ unsigned char Data[MAX_FRAMESIZE-HDR_SIZE-LPCM_SIZE];
+};
+
+#include "vdr_sound.c"
+
+
+// --- mgPCMPlayer ----------------------------------------------------------
+
+/*!
+ * \brief a generic PCM player class
+ *
+ * this class implements a state machine that obtains decoded data from a generic data
+ * and moves it to the DVB device. It inherits from cPlayer in order to be hooked into
+ * VDR as a player and inherits from cThread in order to implement a separate thread
+ * for the decoding process.
+ */
+class mgPCMPlayer : public cPlayer, cThread
+{
+private:
+
+ //! \brief indicates, whether the player is currently active
+ bool m_active;
+
+ //! \brief indicates, whether the player has been started
+ bool m_started;
+
+ //! \brief a buffer for decoded sound
+ cRingBufferFrame *m_ringbuffer;
+
+ //! \brief a mutex for the playmode
+ cMutex m_playmode_mutex;
+
+ //! \brief a condition to signal playmode changes
+ cCondVar m_playmode_cond;
+
+ //! \brief the current playlist
+ mgPlaylist *m_playlist;
+
+ //! \brief the currently played or to be played item
+ mgContentItem *m_current;
+
+ //! \brief the currently playing item
+ mgContentItem *m_playing;
+
+ //! \brief the decoder responsible for the currently playing item
+ mgDecoder *m_decoder;
+
+ cFrame *m_rframe, *m_pframe;
+
+ enum ePlayMode
+ {
+ pmPlay,
+ pmStopped,
+ pmPaused,
+ pmStartup
+ };
+ ePlayMode m_playmode;
+
+ enum eState
+ {
+ msStart, msStop,
+ msDecode, msNormalize,
+ msResample, msOutput,
+ msError, msEof, msWait
+ };
+ eState m_state;
+
+ bool levelgood;
+ unsigned int dvbSampleRate;
+
+ //
+ int m_index;
+
+ void Empty(void);
+ bool NextFile(void);
+ bool PrevFile(void);
+ void StopPlay(void);
+
+ void SetPlayMode(ePlayMode mode);
+ void WaitPlayMode(ePlayMode mode, bool inv);
+
+ string getSourceFile();
+protected:
+ virtual void Activate(bool On);
+ virtual void Action(void);
+
+public:
+ mgPCMPlayer(mgPlaylist *plist);
+ virtual ~mgPCMPlayer();
+
+ bool Active(void) { return m_active; }
+ void Pause(void);
+ void Play(void);
+ void Forward(void);
+ void Backward(void);
+ void Goto(int Index, bool Still=false);
+ void SkipSeconds(int secs);
+ void ToggleShuffle(void);
+ void ToggleLoop(void);
+ virtual bool GetIndex(int &Current, int &Total, bool SnapToIFrame=false);
+ // bool GetPlayInfo(cMP3PlayInfo *rm); // LVW
+ void NewPlaylist(mgPlaylist *plist);
+ };
+
+mgPCMPlayer::mgPCMPlayer(mgPlaylist *plist)
+ : cPlayer( the_setup.BackgrMode? pmAudioOnly: pmAudioOnlyBlack )
+{
+ m_playlist = plist;
+
+ m_active = true;
+ m_started = false;
+
+ m_ringbuffer = new cRingBufferFrame( MP3BUFSIZE );
+
+ m_rframe = 0;
+ m_pframe = 0;
+ m_decoder = 0;
+
+ m_playmode = pmStartup;
+ m_state = msStop;
+
+ m_index = 0;
+ m_playing = 0;
+}
+
+mgPCMPlayer::~mgPCMPlayer()
+{
+ Detach();
+
+ delete m_ringbuffer;
+}
+
+void mgPCMPlayer::Activate(bool on)
+{
+ MGLOG( "mgPCMPlayer::Activate" );
+ if( on )
+ {
+ if( m_playlist && !m_started )
+ {
+ m_playmode = pmStartup;
+ Start();
+
+ m_started = true;
+ m_current = 0;
+
+ m_playmode_mutex.Lock();
+ WaitPlayMode( pmStartup, true ); // wait for the decoder to become ready
+ m_playmode_mutex.Unlock();
+
+ Lock();
+ m_playlist->initialize();
+ if( NextFile() )
+ {
+ Play();
+ }
+ Unlock();
+ }
+ }
+ else if( m_started && m_active )
+ {
+ Lock();
+ StopPlay();
+ Unlock();
+
+ m_active = false;
+ SetPlayMode( pmStartup );
+
+ Cancel(2);
+ }
+}
+
+void mgPCMPlayer::NewPlaylist( mgPlaylist *plist )
+{
+ MGLOG( "mgPCMPlayer::NewPlaylist" );
+
+ Lock();
+ StopPlay();
+ Unlock();
+
+ // memory management of playlists should happen elsewhere (menu, content)
+ m_playlist = plist;
+ m_current = 0;
+
+ if( NextFile() )
+ {
+ Play();
+ }
+}
+
+void mgPCMPlayer::SetPlayMode(ePlayMode mode)
+{
+ m_playmode_mutex.Lock();
+ if( mode != m_playmode )
+ {
+ m_playmode = mode;
+ m_playmode_cond.Broadcast();
+ }
+ m_playmode_mutex.Unlock();
+}
+
+void mgPCMPlayer::WaitPlayMode(ePlayMode mode, bool inv)
+{
+ // must be called with m_playmode_mutex LOCKED !!!
+
+ while( m_active && ( (!inv && mode != m_playmode) || (inv && mode == m_playmode) ) )
+ {
+ m_playmode_cond.Wait(m_playmode_mutex);
+ }
+}
+
+void mgPCMPlayer::Action(void)
+{
+ MGLOG( "mgPCMPlayer::Action" );
+
+ struct mgDecode *ds=0;
+ struct mad_pcm *pcm=0;
+ cResample resample[2];
+ unsigned int nsamples[2];
+ const mad_fixed_t *data[2];
+ cScale scale;
+ cLevel level;
+ cNormalize norm;
+ bool haslevel=false;
+ struct LPCMFrame lpcmFrame;
+ const unsigned char *p=0;
+ int pc = 0, only48khz = the_setup.Only48kHz;
+ cPoller poll;
+#ifdef DEBUG
+ int beat=0;
+#endif
+
+ dsyslog( "muggle: player thread started (pid=%d)", getpid() );
+
+ memset( &lpcmFrame, 0, sizeof(lpcmFrame) );
+ lpcmFrame.PES[2]=0x01;
+ lpcmFrame.PES[3]=0xbd;
+ lpcmFrame.PES[6]=0x87;
+ lpcmFrame.LPCM[0]=0xa0; // substream ID
+ lpcmFrame.LPCM[1]=0xff;
+ lpcmFrame.LPCM[5]=0x01;
+ lpcmFrame.LPCM[6]=0x80;
+
+ dvbSampleRate = 48000;
+ m_state = msStop;
+ SetPlayMode( pmStopped );
+ cout << "Playmode set." << endl << flush;
+
+ while( m_active )
+ {
+#ifdef DEBUG
+ if(time(0)>=beat+30)
+ {
+ cout << "mgPCMPlayer::Action: heartbeat buffer=" << m_ringbuffer->Available() << endl << flush;
+ scale.Stats(); if(haslevel) norm.Stats();
+ beat=time(0);
+ }
+#endif
+
+ Lock();
+
+ if( !m_rframe && m_playmode == pmPlay )
+ {
+ switch( m_state )
+ {
+ case msStart:
+ {
+ m_index = 0;
+ m_playing = m_current;
+
+ string filename = getSourceFile();
+
+ if( ( m_decoder = mgDecoders::findDecoder( filename ) ) && m_decoder->start() )
+ {
+ levelgood = true;
+ haslevel = false;
+
+ scale.Init();
+ level.Init();
+
+ m_state = msDecode;
+
+ break;
+ }
+
+ m_state = msEof;
+ } break;
+ case msDecode:
+ {
+ ds = m_decoder->decode();
+ switch( ds->status )
+ {
+ case dsPlay:
+ {
+ pcm = ds->pcm;
+ m_index = ds->index/1000;
+ m_state = msNormalize;
+ } break;
+ case dsSkip:
+ case dsSoftError:
+ {
+ // skipping, state unchanged, next decode
+ } break;
+ case dsEof:
+ {
+ m_state = msEof;
+ } break;
+ case dsOK:
+ case dsError:
+ {
+ m_state = msError;
+ } break;
+ }
+ } break;
+ case msNormalize:
+ {
+ if(!haslevel)
+ {
+ if( levelgood )
+ {
+ level.GetPower( pcm );
+ }
+ }
+ else
+ {
+ norm.AddGain( pcm );
+ }
+ m_state = msResample;
+ } break;
+ case msResample:
+ {
+#ifdef DEBUG
+ {
+ static unsigned int oldrate=0;
+ if(oldrate!=pcm->samplerate)
+ {
+ cout << "mgPCMPlayer::Action: new input sample rate " << pcm->samplerate << endl << flush;
+ oldrate = pcm->samplerate;
+ }
+ }
+#endif
+ nsamples[0] = nsamples[1] = pcm->length;
+ data[0] = pcm->samples[0];
+ data[1] = pcm->channels > 1 ? pcm->samples[1]: 0;
+
+ lpcmFrame.LPCM[5]&=0xcf;
+ dvbSampleRate=48000;
+ if(!only48khz)
+ {
+ switch(pcm->samplerate)
+ { // If one of the supported frequencies, do it without resampling.
+ case 96000:
+ { // Select a "even" upsampling frequency if possible, too.
+ lpcmFrame.LPCM[5] |= 1 << 4;
+ dvbSampleRate = 96000;
+ } break;
+
+ //case 48000: // this is already the default ...
+ // lpcmFrame.LPCM[5]|=0<<4;
+ // dvbSampleRate=48000;
+ // break;
+ case 11025:
+ case 22050:
+ case 44100:
+ {
+ lpcmFrame.LPCM[5]|=2<<4;
+ dvbSampleRate = 44100;
+ } break;
+ case 8000:
+ case 16000:
+ case 32000:
+ {
+ lpcmFrame.LPCM[5]|=3<<4;
+ dvbSampleRate = 32000;
+ } break;
+ }
+ }
+
+ if( dvbSampleRate != pcm->samplerate )
+ {
+ if( resample[0].SetInputRate( pcm->samplerate, dvbSampleRate ) )
+ {
+ nsamples[0] = resample[0].ResampleBlock( nsamples[0], data[0] );
+ data[0] = resample[0].Resampled();
+ }
+ if(data[1] && resample[1].SetInputRate( pcm->samplerate, dvbSampleRate ) )
+ {
+ nsamples[1] = resample[1].ResampleBlock( nsamples[1], data[1] );
+ data[1] = resample[1].Resampled();
+ }
+ }
+ m_state=msOutput;
+ } break;
+ case msOutput:
+ {
+ if( nsamples[0] > 0 )
+ {
+ unsigned int outlen = scale.ScaleBlock( lpcmFrame.Data,
+ sizeof(lpcmFrame.Data),
+ nsamples[0], data[0],
+ data[1],
+ the_setup.AudioMode? amDither: amRound );
+ if( outlen )
+ {
+ outlen += sizeof(lpcmFrame.LPCM)+LEN_CORR;
+ lpcmFrame.PES[4] = outlen >> 8;
+ lpcmFrame.PES[5] = outlen;
+ m_rframe = new cFrame( (unsigned char *)&lpcmFrame,
+ outlen + sizeof( lpcmFrame.PES ) - LEN_CORR );
+ }
+ }
+ else
+ {
+ m_state=msDecode;
+ }
+ } break;
+ case msError:
+ case msEof:
+ {
+ if( NextFile() )
+ {
+ m_state = msStart;
+ }
+ else
+ {
+ m_state = msWait;
+ }
+ } // fall through
+ case msStop:
+ {
+ m_playing = 0;
+ if( m_decoder )
+ { // who deletes decoder?
+ m_decoder->stop();
+ m_decoder = 0;
+ }
+
+ levelgood = false;
+
+ scale.Stats();
+ if( haslevel )
+ {
+ norm.Stats();
+ }
+ if( m_state == msStop )
+ { // might be unequal in case of fall through from eof/error
+ SetPlayMode( pmStopped );
+ }
+ } break;
+ case msWait:
+ {
+ if( m_ringbuffer->Available() == 0 )
+ {
+ m_active = false;
+ SetPlayMode(pmStopped);
+ }
+ } break;
+ }
+ }
+
+ if( m_rframe && m_ringbuffer->Put( m_rframe ) )
+ {
+ m_rframe = 0;
+ }
+
+ if( !m_pframe && m_playmode == pmPlay )
+ {
+ m_pframe = m_ringbuffer->Get();
+ if( m_pframe )
+ {
+ p = m_pframe->Data();
+ pc = m_pframe->Count();
+ }
+ }
+
+ if( m_pframe )
+ {
+ int w = PlayVideo( p, pc );
+ if( w > 0 )
+ {
+ p += w;
+ pc -= w;
+
+ if( pc <= 0 )
+ {
+ m_ringbuffer->Drop(m_pframe);
+ m_pframe=0;
+ }
+ }
+ else if( w < 0 && FATALERRNO )
+ {
+ LOG_ERROR;
+ break;
+ }
+ }
+
+ Unlock();
+
+ if( (m_rframe || m_state == msWait) && m_pframe )
+ {
+ // Wait for output to become ready
+ DevicePoll( poll, 500 );
+ }
+ else
+ {
+ if( m_playmode != pmPlay )
+ {
+ m_playmode_mutex.Lock();
+
+ if( m_playmode != pmPlay )
+ {
+ WaitPlayMode( m_playmode, true ); // Wait on playMode change
+ }
+ m_playmode_mutex.Unlock();
+ }
+ }
+ }
+
+ Lock();
+
+ if( m_rframe )
+ {
+ delete m_rframe;
+ m_rframe=0;
+ }
+
+ if( m_decoder )
+ { // who deletes decoder?
+ m_decoder->stop();
+ m_decoder = 0;
+ }
+
+ m_playing = 0;
+
+ SetPlayMode(pmStopped);
+
+ Unlock();
+
+ m_active = false;
+
+ dsyslog( "muggle: player thread ended (pid=%d)", getpid() );
+}
+
+void mgPCMPlayer::Empty(void)
+{
+ MGLOG( "mgPCMPlayer::Empty" );
+
+ Lock();
+
+ m_ringbuffer->Clear();
+ DeviceClear();
+
+ delete m_rframe;
+ m_rframe = 0;
+ m_pframe = 0;
+
+ Unlock();
+}
+
+void mgPCMPlayer::StopPlay()
+{ // StopPlay() must be called in locked state!!!
+ MGLOG( "mgPCMPlayer::StopPlay" );
+ if( m_playmode != pmStopped )
+ {
+ Empty();
+ m_state = msStop;
+ SetPlayMode( pmPlay );
+ Unlock(); // let the decode thread process the stop signal
+
+ m_playmode_mutex.Lock();
+ WaitPlayMode( pmStopped, false );
+ m_playmode_mutex.Unlock();
+
+ Lock();
+ }
+}
+
+string mgPCMPlayer::getSourceFile()
+{
+ string filename;
+
+ if( !the_setup.GdCompatibility )
+ { // use filename itself
+ filename = string( the_setup.ToplevelDir ) + m_playing->getSourceFile();
+ }
+ else
+ { // find the unique name within any directory, but what is top? video?
+ char *cmd = NULL;
+ asprintf( &cmd, "find %s -follow -type f -name '%s'|sort ",
+ the_setup.ToplevelDir, m_playing->getSourceFile().c_str() );
+
+ FILE *p = popen(cmd, "r");
+ if (p)
+ {
+ char *s;
+ if( (s = readline(p) ) != NULL )
+ {
+ filename = string( s );
+ }
+ }
+ pclose( p );
+ delete cmd;
+ }
+
+ cout << "mgPCMPlayer::getSourceFile: found filename " << filename << endl << flush;
+ return "/test.mp3";
+}
+
+bool mgPCMPlayer::NextFile()
+{
+ bool res = false;
+
+ mgContentItem *newcurr = m_playlist->skipFwd();
+
+ if( newcurr && newcurr != &(mgContentItem::UNDEFINED) )
+ {
+ m_current = newcurr;
+ res = true;
+ }
+
+ return res;
+}
+
+bool mgPCMPlayer::PrevFile(void)
+{
+ bool res = false;
+
+ mgContentItem *newcurr = m_playlist->skipBack();
+
+ if( newcurr && newcurr != &(mgContentItem::UNDEFINED) )
+ {
+ m_current = newcurr;
+ res = true;
+ }
+
+ return res;
+}
+
+void mgPCMPlayer::ToggleShuffle()
+{
+ m_playlist->toggleShuffle();
+}
+
+void mgPCMPlayer::ToggleLoop(void)
+{
+ m_playlist->toggleLoop();
+}
+
+void mgPCMPlayer::Pause(void)
+{
+ if( m_playmode == pmPaused )
+ {
+ Play();
+ }
+ else
+ {
+ if( m_playmode == pmPlay )
+ {
+ // DeviceFreeze();
+ SetPlayMode( pmPaused );
+ }
+ }
+}
+
+void mgPCMPlayer::Play(void)
+{
+ MGLOG( "mgPCMPlayer::Play" );
+
+ Lock();
+ if( m_playmode != pmPlay && m_current )
+ {
+ if( m_playmode == pmStopped )
+ {
+ m_state = msStart;
+ }
+ // DevicePlay(); // TODO? Commented out in original code, too
+ SetPlayMode( pmPlay );
+ }
+ Unlock();
+}
+
+void mgPCMPlayer::Forward(void)
+{
+ MGLOG( "mgPCMPlayer::Forward" );
+
+ Lock();
+ if( NextFile() )
+ {
+ StopPlay();
+ Play();
+ }
+ Unlock();
+}
+
+void mgPCMPlayer::Backward(void)
+{
+ Lock();
+ if( PrevFile() )
+ {
+ StopPlay();
+ Play();
+ }
+ Unlock();
+}
+
+void mgPCMPlayer::Goto( int index, bool still )
+{
+ mgContentItem *next = m_playlist->getPosition( index-1 );
+
+ if( next != &(mgContentItem::UNDEFINED) ) //invalid
+ {
+ Lock();
+ StopPlay();
+ m_current = next;
+ Play();
+ Unlock();
+ }
+}
+
+void mgPCMPlayer::SkipSeconds(int secs)
+{
+ if( m_playmode != pmStopped )
+ {
+ Lock();
+ if( m_playmode == pmPaused )
+ {
+ SetPlayMode( pmPlay );
+ }
+ if( m_decoder && m_decoder->skip( secs, m_ringbuffer->Available(), dvbSampleRate ) )
+ {
+ levelgood=false;
+ }
+ Empty();
+ Unlock();
+ }
+}
+
+bool mgPCMPlayer::GetIndex( int &current, int &total, bool snaptoiframe )
+{
+ bool res = false;
+ current = SecondsToFrames(m_index);
+ total = -1;
+
+ return res;
+}
+
+// --- mgPlayerControl -------------------------------------------------------
+
+mgPlayerControl::mgPlayerControl( mgPlaylist *plist )
+ : cControl( m_player = new mgPCMPlayer(plist) )
+{
+ MGLOG( "mgPlayerControl::mgPlayerControl" );
+
+ m_visible = false;
+ m_has_osd = false;
+}
+
+mgPlayerControl::~mgPlayerControl()
+{
+ Hide();
+ Stop();
+}
+
+bool mgPlayerControl::Active(void)
+{
+ MGLOG( "mgPlayerControl::Active" );
+
+ return m_player && m_player->Active();
+}
+
+void mgPlayerControl::Stop(void)
+{
+ if( m_player )
+ {
+ delete m_player;
+ m_player = 0;
+ }
+}
+
+void mgPlayerControl::Pause(void)
+{
+ if( m_player )
+ {
+ m_player->Pause();
+ }
+}
+
+void mgPlayerControl::Play(void)
+{
+ if( m_player )
+ {
+ m_player->Play();
+ }
+}
+
+void mgPlayerControl::Forward(void)
+{
+ if( m_player )
+ {
+ m_player->Forward();
+ }
+}
+
+void mgPlayerControl::Backward(void)
+{
+ if( m_player )
+ {
+ m_player->Backward();
+ }
+}
+
+void mgPlayerControl::SkipSeconds(int Seconds)
+{
+ if( m_player )
+ {
+ m_player->SkipSeconds(Seconds);
+ }
+}
+
+void mgPlayerControl::Goto(int Position, bool Still)
+{
+ if( m_player )
+ {
+ m_player->Goto(Position, Still);
+ }
+}
+
+void mgPlayerControl::ToggleShuffle(void)
+{
+ if( m_player )
+ {
+ m_player->ToggleShuffle();
+ }
+}
+
+void mgPlayerControl::ToggleLoop(void)
+{
+ if( m_player )
+ {
+ m_player->ToggleLoop();
+ }
+}
+
+void mgPlayerControl::NewPlaylist(mgPlaylist *plist)
+{
+ if( m_player )
+ {
+ m_player->NewPlaylist(plist);
+ }
+}
+
+void mgPlayerControl::ShowProgress()
+{
+ if( m_visible )
+ {
+ if( !m_has_osd )
+ {
+ // open the osd if its not already there...
+ Interface->Open();
+ m_has_osd = true;
+ }
+
+ // now an osd is open, go on
+
+ int w = Interface->Width();
+ int h = Interface->Height();
+
+ Interface->WriteText( w/2, h/2, "Muggle is active!" );
+
+ // Add: song info (name, artist, pos in playlist, time, ...)
+ // Add: progress bar
+
+ Interface->Flush();
+ }
+ else
+ {
+ Hide();
+ }
+}
+
+void mgPlayerControl::Hide()
+{
+ if( m_has_osd )
+ {
+ Interface->Close();
+ m_has_osd = false;
+ }
+}
+
+eOSState mgPlayerControl::ProcessKey(eKeys key)
+{
+ if( !Active() )
+ {
+ return osEnd;
+ }
+
+ ShowProgress();
+
+ eOSState state = cControl::ProcessKey(key);
+
+ if( state == osUnknown )
+ {
+ switch( key )
+ {
+ case kUp:
+ {
+ Forward();
+ } break;
+ case kDown:
+ {
+ Backward();
+ } break;
+ case kPause:
+ case kYellow:
+ {
+ Pause();
+ } break;
+ case kStop:
+ case kBlue:
+ {
+ Hide();
+ Stop();
+
+ return osEnd;
+ } break;
+ case kOk:
+ {
+ m_visible = !m_visible;
+ ShowProgress();
+
+ return osContinue;
+ } break;
+ case kBack:
+ {
+ Hide();
+ Stop();
+
+ return osEnd;
+ } break;
+ default:
+ {
+ return osUnknown;
+ }
+ }
+ }
+ return osContinue;
+}
+
+/************************************************************
+ *
+ * $Log: vdr_player.c,v $
+ * Revision 1.2 2004/05/28 15:29:19 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.1.2.19 2004/05/26 14:30:27 lvw
+ * Removed bug in finding correct mp3 file in GD mode
+ *
+ * Revision 1.1.2.18 2004/05/25 06:48:24 lvw
+ * Documentation and code polishing.
+ *
+ * Revision 1.1.2.17 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.1.2.16 2004/05/24 11:48:52 lvw
+ * Debugging info added to find deadlock
+ *
+ * Revision 1.1.2.15 2004/05/12 22:38:37 lvw
+ * Some cleanup
+ *
+ * Revision 1.1.2.14 2004/05/11 06:35:16 lvw
+ * Added debugging while hunting stop bug.
+ *
+ * Revision 1.1.2.13 2004/05/07 06:46:41 lvw
+ * Removed a bug in playlist deallocation. Added infrastructure to display information while playing.
+ *
+ *
+ ***********************************************************/
diff --git a/vdr_player.h b/vdr_player.h
new file mode 100644
index 0000000..6ce2f1b
--- /dev/null
+++ b/vdr_player.h
@@ -0,0 +1,137 @@
+/*!
+ * \file vdr_player.h
+ * \brief A player/control combination to let VDR play music
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_player.h,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+
+#ifndef ___DVB_MP3_H
+#define ___DVB_MP3_H
+
+#include <player.h>
+
+// -------------------------------------------------------------------
+
+class mgPCMPlayer;
+class mgPlaylist;
+
+// -------------------------------------------------------------------
+
+/*!
+ * \brief exerts control over the player itself
+ *
+ * This control is launched from the main menu and manages a link
+ * to the player. Key events are caught and signaled to the player.
+ */
+class mgPlayerControl : public cControl
+{
+private:
+
+ //! \brief the reference to the player
+ mgPCMPlayer *m_player;
+
+ //! \brief indicates, whether the osd should be visible
+ bool m_visible;
+
+ //! \brief indicates, whether an osd is currently displayed
+ bool m_has_osd;
+
+public:
+
+ /*! \brief construct a control with a playlist
+ *
+ * \param plist - the playlist to be played
+ */
+ mgPlayerControl(mgPlaylist *plist);
+
+ /*! \brief destructor
+ */
+ virtual ~mgPlayerControl();
+
+ //! \brief indicate, whether the corresponding player is active
+ bool Active();
+
+ //! \brief stop the corresponding player
+ void Stop();
+
+ //! \brief toggle the pause mode of the corresponding player
+ void Pause();
+
+ //! \brief start playing
+ void Play();
+
+ //! \brief skip to the next song
+ void Forward();
+
+ //! \brief skip to the previous song
+ void Backward();
+
+ /*! \brief skip a specified number of seconds
+ *
+ * \param seconds - the number of seconds to skip
+ */
+ void SkipSeconds(int seconds);
+
+ /*! \brief goto a certain position in the playlist
+ *
+ * \param index - the position in the playlist to skip to
+ * \param still - currently unused
+ */
+ void Goto(int index, bool still = false);
+
+ //! \brief toggle the shuffle mode of the corresponding player
+ void ToggleShuffle();
+
+ //! \brief toggle the loop mode of the corresponding player
+ void ToggleLoop();
+
+ /*! \brief signal a new playlist
+ *
+ * The caller has to take care of deallocating the previous list
+ *
+ * \param plist - the new playlist to be played
+ */
+ void NewPlaylist( mgPlaylist *plist );
+
+ //! \brief a progress display
+ void ShowProgress();
+
+ //! \brief hide the osd, if present
+ void Hide();
+
+ //! \brief process key events
+ eOSState ProcessKey(eKeys key);
+};
+
+#endif //___DVB_MP3_H
+
+/************************************************************
+ *
+ * $Log: vdr_player.h,v $
+ * Revision 1.2 2004/05/28 15:29:19 lvw
+ * Merged player branch back on HEAD branch.
+ *
+ * Revision 1.1.2.9 2004/05/25 06:48:24 lvw
+ * Documentation and code polishing.
+ *
+ * Revision 1.1.2.8 2004/05/25 00:10:45 lvw
+ * Code cleanup and added use of real database source files
+ *
+ * Revision 1.1.2.7 2004/05/11 06:35:16 lvw
+ * Added debugging while hunting stop bug.
+ *
+ * Revision 1.1.2.6 2004/05/07 06:46:41 lvw
+ * Removed a bug in playlist deallocation. Added infrastructure to display information while playing.
+ *
+ *
+ ***********************************************************/
diff --git a/vdr_setup.c b/vdr_setup.c
new file mode 100644
index 0000000..04ba06b
--- /dev/null
+++ b/vdr_setup.c
@@ -0,0 +1,70 @@
+/*!
+ * \file vdr_setup.c
+ * \brief A setup class for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_setup.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Partially adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include <string.h>
+
+#include "vdr_setup.h"
+#include "i18n.h"
+
+mgSetup the_setup;
+
+// --- mgMenuSetup -----------------------------------------------------------
+
+mgMenuSetup::mgMenuSetup()
+{
+ static const char allowed[] = { "abcdefghijklmnopqrstuvwxyz0123456789-_" };
+
+ m_data = the_setup;
+
+ SetSection( tr("Muggle") );
+
+ Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial loop mode"), &m_data.InitLoopMode));
+ Add(new cMenuEditBoolItem(tr("Setup.Muggle$Initial shuffle mode"), &m_data.InitShuffleMode));
+ Add(new cMenuEditBoolItem(tr("Setup.Muggle$Audio mode"), &m_data.AudioMode, tr("Round"), tr("Dither")));
+ Add(new cMenuEditBoolItem(tr("Setup.Muggle$Use 48kHz mode only"), &m_data.Only48kHz));
+ Add(new cMenuEditIntItem( tr("Setup.Muggle$Display mode"), &m_data.DisplayMode, 1, 3));
+ Add(new cMenuEditBoolItem(tr("Setup.Muggle$Background mode"), &m_data.BackgrMode, tr("Black"), tr("Live")));
+ Add(new cMenuEditIntItem( tr("Setup.Muggle$Normalizer level"), &m_data.TargetLevel, 0, MAX_TARGET_LEVEL));
+ Add(new cMenuEditIntItem( tr("Setup.Muggle$Limiter level"), &m_data.LimiterLevel, MIN_LIMITER_LEVEL, 100));
+}
+
+void mgMenuSetup::Store(void)
+{
+ the_setup = m_data;
+
+ SetupStore("InitLoopMode", the_setup.InitLoopMode );
+ SetupStore("InitShuffleMode", the_setup.InitShuffleMode);
+ SetupStore("AudioMode", the_setup.AudioMode );
+ SetupStore("DisplayMode", the_setup.DisplayMode );
+ SetupStore("BackgrMode", the_setup.BackgrMode );
+ SetupStore("TargetLevel", the_setup.TargetLevel );
+ SetupStore("LimiterLevel", the_setup.LimiterLevel );
+ SetupStore("Only48kHz", the_setup.Only48kHz );
+}
+
+// --- mgSetup ---------------------------------------------------------------
+
+mgSetup::mgSetup()
+{
+ InitLoopMode = 0;
+ InitShuffleMode = 0;
+ AudioMode = 1;
+ DisplayMode = 3;
+ BackgrMode = 1;
+ TargetLevel = DEFAULT_TARGET_LEVEL;
+ LimiterLevel = DEFAULT_LIMITER_LEVEL;
+ Only48kHz = 0;
+}
diff --git a/vdr_setup.h b/vdr_setup.h
new file mode 100644
index 0000000..a77cf75
--- /dev/null
+++ b/vdr_setup.h
@@ -0,0 +1,76 @@
+/*!
+ * \file vdr_setup.h
+ * \brief A setup class for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_setup.h,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___SETUP_MG_H
+#define ___SETUP_MG_H
+
+// #include <osd.h>
+#include <menuitems.h>
+
+#define MAX_STRING_LEN 128
+
+#define DEFAULT_TARGET_LEVEL 25
+#define MAX_TARGET_LEVEL 50
+#define DEFAULT_LIMITER_LEVEL 70
+#define MIN_LIMITER_LEVEL 25
+
+/*!
+ * \brief storage for setup data
+ */
+class mgSetup
+{
+public:
+ int InitLoopMode;
+ int InitShuffleMode;
+ int AudioMode;
+ int DisplayMode;
+ int BackgrMode;
+ int MenuMode;
+ int TargetLevel;
+ int LimiterLevel;
+ int Only48kHz;
+
+ char *DbHost;
+ char *DbName;
+ char *DbUser;
+ char *DbPass;
+ int DbPort;
+ bool GdCompatibility;
+ char *ToplevelDir;
+
+ char PathPrefix[MAX_STRING_LEN];
+
+ public:
+ mgSetup(void);
+};
+
+extern mgSetup the_setup;
+
+/*!
+ * \brief allow user to modify setup on OSD
+ */
+class mgMenuSetup : public cMenuSetupPage
+{
+private:
+ mgSetup m_data;
+protected:
+ virtual void Store();
+public:
+ mgMenuSetup();
+};
+
+
+#endif //___SETUP_MP3_H
diff --git a/vdr_sound.c b/vdr_sound.c
new file mode 100644
index 0000000..b06061c
--- /dev/null
+++ b/vdr_sound.c
@@ -0,0 +1,609 @@
+/*!
+ * \file vdr_setup.c
+ * \brief Sound manipulation classes for a VDR media plugin (muggle)
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_sound.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001,2002 Stefan Huelswitt <huels@iname.com>
+ */
+
+// --- cResample ------------------------------------------------------------
+
+// The resample code has been adapted from the madplay project
+// (resample.c) found in the libmad distribution
+
+class cResample
+{
+private:
+ mad_fixed_t ratio;
+ mad_fixed_t step;
+ mad_fixed_t last;
+ mad_fixed_t resampled[MAX_NSAMPLES];
+public:
+ bool SetInputRate(unsigned int oldrate, unsigned int newrate);
+ unsigned int ResampleBlock(unsigned int nsamples, const mad_fixed_t *old);
+ const mad_fixed_t *Resampled(void) { return resampled; }
+ };
+
+bool cResample::SetInputRate(unsigned int oldrate, unsigned int newrate)
+{
+ if(oldrate<8000 || oldrate>newrate*6) { // out of range
+ esyslog("WARNING: samplerate %d out of range 8000-%d\n",oldrate,newrate*6);
+ return 0;
+ }
+ ratio=mad_f_tofixed((double)oldrate/(double)newrate);
+ step=0; last=0;
+#ifdef DEBUG
+ static mad_fixed_t oldratio=0;
+ if(oldratio!=ratio) {
+ printf("mad: new resample ratio %f (from %d kHz to %d kHz)\n",mad_f_todouble(ratio),oldrate,newrate);
+ oldratio=ratio;
+ }
+#endif
+ return ratio!=MAD_F_ONE;
+}
+
+unsigned int cResample::ResampleBlock(unsigned int nsamples, const mad_fixed_t *old)
+{
+ // This resampling algorithm is based on a linear interpolation, which is
+ // not at all the best sounding but is relatively fast and efficient.
+ //
+ // A better algorithm would be one that implements a bandlimited
+ // interpolation.
+
+ mad_fixed_t *nsam=resampled;
+ const mad_fixed_t *end=old+nsamples;
+ const mad_fixed_t *begin=nsam;
+
+ if(step < 0) {
+ step = mad_f_fracpart(-step);
+
+ while (step < MAD_F_ONE) {
+ *nsam++ = step ? last+mad_f_mul(*old-last,step) : last;
+ step += ratio;
+ if(((step + 0x00000080L) & 0x0fffff00L) == 0)
+ step = (step + 0x00000080L) & ~0x0fffffffL;
+ }
+ step -= MAD_F_ONE;
+ }
+
+ while (end - old > 1 + mad_f_intpart(step)) {
+ old += mad_f_intpart(step);
+ step = mad_f_fracpart(step);
+ *nsam++ = step ? *old + mad_f_mul(old[1] - old[0], step) : *old;
+ step += ratio;
+ if (((step + 0x00000080L) & 0x0fffff00L) == 0)
+ step = (step + 0x00000080L) & ~0x0fffffffL;
+ }
+
+ if (end - old == 1 + mad_f_intpart(step)) {
+ last = end[-1];
+ step = -step;
+ }
+ else step -= mad_f_fromint(end - old);
+
+ return nsam-begin;
+}
+
+// --- cLevel ----------------------------------------------------------------
+
+// The normalize algorithm and parts of the code has been adapted from the
+// Normalize 0.7 project. (C) 1999-2002, Chris Vaill <cvaill@cs.columbia.edu>
+
+// A little background on how normalize computes the volume
+// of a wav file, in case you want to know just how your
+// files are being munged:
+//
+// The volumes calculated are RMS amplitudes, which corre­
+// spond (roughly) to perceived volume. Taking the RMS ampli­
+// tude of an entire file would not give us quite the measure
+// we want, though, because a quiet song punctuated by short
+// loud parts would average out to a quiet song, and the
+// adjustment we would compute would make the loud parts
+// excessively loud.
+//
+// What we want is to consider the maximum volume of the
+// file, and normalize according to that. We break up the
+// signal into 100 chunks per second, and get the signal
+// power of each chunk, in order to get an estimation of
+// "instantaneous power" over time. This "instantaneous
+// power" signal varies too much to get a good measure of the
+// original signal's maximum sustained power, so we run a
+// smoothing algorithm over the power signal (specifically, a
+// mean filter with a window width of 100 elements). The max­
+// imum point of the smoothed power signal turns out to be a
+// good measure of the maximum sustained power of the file.
+// We can then take the square root of the power to get maxi­
+// mum sustained RMS amplitude.
+
+class cLevel {
+private:
+ double maxpow;
+ mad_fixed_t peak;
+ struct Power {
+ // smooth
+ int npow, wpow;
+ double powsum, pows[POW_WIN];
+ // sum
+ unsigned int nsum;
+ double sum;
+ } power[2];
+ //
+ inline void AddPower(struct Power *p, double pow);
+public:
+ void Init(void);
+ void GetPower(struct mad_pcm *pcm);
+ double GetLevel(void);
+ double GetPeak(void);
+ };
+
+void cLevel::Init(void)
+{
+ for(int l=0 ; l<2 ; l++) {
+ struct Power *p=&power[l];
+ p->sum=p->powsum=0.0; p->wpow=p->npow=p->nsum=0;
+ for(int i=POW_WIN-1 ; i>=0 ; i--) p->pows[i]=0.0;
+ }
+ maxpow=0.0; peak=0;
+}
+
+void cLevel::GetPower(struct mad_pcm *pcm)
+{
+ for(int i=0 ; i<pcm->channels ; i++) {
+ struct Power *p=&power[i];
+ mad_fixed_t *data=pcm->samples[i];
+ for(int n=pcm->length ; n>0 ; n--) {
+ if(*data < -peak) peak = -*data;
+ if(*data > peak) peak = *data;
+ double s=mad_f_todouble(*data++);
+ p->sum+=(s*s);
+ if(++(p->nsum)>=pcm->samplerate/100) {
+ AddPower(p,p->sum/(double)p->nsum);
+ p->sum=0.0; p->nsum=0;
+ }
+ }
+ }
+}
+
+void cLevel::AddPower(struct Power *p, double pow)
+{
+ p->powsum+=pow;
+ if(p->npow>=POW_WIN) {
+ if(p->powsum>maxpow) maxpow=p->powsum;
+ p->powsum-=p->pows[p->wpow];
+ }
+ else p->npow++;
+ p->pows[p->wpow]=pow;
+ p->wpow=(p->wpow+1) % POW_WIN;
+}
+
+double cLevel::GetLevel(void)
+{
+ if(maxpow<EPSILON) {
+ // Either this whole file has zero power, or was too short to ever
+ // fill the smoothing buffer. In the latter case, we need to just
+ // get maxpow from whatever data we did collect.
+
+ if(power[0].powsum>maxpow) maxpow=power[0].powsum;
+ if(power[1].powsum>maxpow) maxpow=power[1].powsum;
+ }
+ double level=sqrt(maxpow/(double)POW_WIN); // adjust for the smoothing window size and root
+ printf("norm: new volumen level=%f peak=%f\n",level,mad_f_todouble(peak));
+ return level;
+}
+
+double cLevel::GetPeak(void)
+{
+ return mad_f_todouble(peak);
+}
+
+// --- cNormalize ------------------------------------------------------------
+
+class cNormalize {
+private:
+ mad_fixed_t gain;
+ double d_limlvl, one_limlvl;
+ mad_fixed_t limlvl;
+ bool dogain, dolimit;
+#ifdef DEBUG
+ // stats
+ unsigned long limited, clipped, total;
+ mad_fixed_t peak;
+#endif
+ // limiter
+#ifdef USE_FAST_LIMITER
+ mad_fixed_t *table, tablestart;
+ int tablesize;
+ inline mad_fixed_t FastLimiter(mad_fixed_t x);
+#endif
+ inline mad_fixed_t Limiter(mad_fixed_t x);
+public:
+ cNormalize(void);
+ ~cNormalize();
+ void Init(double Level, double Peak);
+ void Stats(void);
+ void AddGain(struct mad_pcm *pcm);
+ };
+
+cNormalize::cNormalize(void)
+{
+ d_limlvl = (double)the_setup.LimiterLevel / 100.0;
+ one_limlvl = 1 - d_limlvl;
+ limlvl = mad_f_tofixed(d_limlvl);
+ printf( "norm: lim_lev=%f lim_acc=%d\n", d_limlvl, LIM_ACC );
+
+#ifdef USE_FAST_LIMITER
+ mad_fixed_t start=limlvl & ~(F_LIM_JMP-1);
+ tablestart=start;
+ tablesize=(unsigned int)(F_LIM_MAX-start)/F_LIM_JMP + 2;
+ table=new mad_fixed_t[tablesize];
+ if(table) {
+ printf("norm: table size=%d start=%08x jump=%08x\n",tablesize,start,F_LIM_JMP);
+ for(int i=0 ; i<tablesize ; i++) {
+ table[i]=Limiter(start);
+ start+=F_LIM_JMP;
+ }
+ tablesize--; // avoid a -1 in FastLimiter()
+
+ // do a quick accuracy check, just to be sure that FastLimiter() is working
+ // as expected :-)
+#ifdef ACC_DUMP
+ FILE *out=fopen("/tmp/limiter","w");
+#endif
+ mad_fixed_t maxdiff=0;
+ for(mad_fixed_t x=F_LIM_MAX ; x>=limlvl ; x-=mad_f_tofixed(1e-4)) {
+ mad_fixed_t diff=mad_f_abs(Limiter(x)-FastLimiter(x));
+ if(diff>maxdiff) maxdiff=diff;
+#ifdef ACC_DUMP
+ fprintf(out,"%0.10f\t%0.10f\t%0.10f\t%0.10f\t%0.10f\n",
+ mad_f_todouble(x),mad_f_todouble(Limiter(x)),mad_f_todouble(FastLimiter(x)),mad_f_todouble(diff),mad_f_todouble(maxdiff));
+ if(ferror(out)) break;
+#endif
+ }
+#ifdef ACC_DUMP
+ fclose(out);
+#endif
+ printf("norm: accuracy %.12f\n",mad_f_todouble(maxdiff));
+ if(mad_f_todouble(maxdiff)>1e-6)
+ {
+ esyslog("ERROR: accuracy check failed, normalizer disabled");
+ delete table; table=0;
+ }
+ }
+ else esyslog("ERROR: no memory for lookup table, normalizer disabled");
+#endif // USE_FAST_LIMITER
+}
+
+cNormalize::~cNormalize()
+{
+#ifdef USE_FAST_LIMITER
+ delete table;
+#endif
+}
+
+void cNormalize::Init(double Level, double Peak)
+{
+ double Target=(double)the_setup.TargetLevel/100.0;
+ double dgain=Target/Level;
+ if(dgain>MAX_GAIN) dgain=MAX_GAIN;
+ gain=mad_f_tofixed(dgain);
+ // Check if we actually need to apply a gain
+ dogain=(Target>0.0 && fabs(1-dgain)>MIN_GAIN);
+#ifdef USE_FAST_LIMITER
+ if(!table) dogain=false;
+#endif
+ // Check if we actually need to do limiting:
+ // we have to if limiter is enabled, if gain>1 and if the peaks will clip.
+ dolimit=(d_limlvl<1.0 && dgain>1.0 && Peak*dgain>1.0);
+#ifdef DEBUG
+ printf("norm: gain=%f dogain=%d dolimit=%d (target=%f level=%f peak=%f)\n",dgain,dogain,dolimit,Target,Level,Peak);
+ limited=clipped=total=0; peak=0;
+#endif
+}
+
+void cNormalize::Stats(void)
+{
+#ifdef DEBUG
+ if(total)
+ printf("norm: stats tot=%ld lim=%ld/%.3f%% clip=%ld/%.3f%% peak=%.3f\n",
+ total,limited,(double)limited/total*100.0,clipped,(double)clipped/total*100.0,mad_f_todouble(peak));
+#endif
+}
+
+mad_fixed_t cNormalize::Limiter(mad_fixed_t x)
+{
+// Limiter function:
+//
+// / x (for x <= lev)
+// x' = |
+// \ tanh((x - lev) / (1-lev)) * (1-lev) + lev (for x > lev)
+//
+// call only with x>=0. For negative samples, preserve sign outside this function
+//
+// With limiter level = 0, this is equivalent to a tanh() function;
+// with limiter level = 1, this is equivalent to clipping.
+
+ if(x>limlvl) {
+#ifdef DEBUG
+ if(x>MAD_F_ONE) clipped++;
+ limited++;
+#endif
+ x=mad_f_tofixed(tanh((mad_f_todouble(x)-d_limlvl) / one_limlvl) * one_limlvl + d_limlvl);
+ }
+ return x;
+}
+
+#ifdef USE_FAST_LIMITER
+mad_fixed_t cNormalize::FastLimiter(mad_fixed_t x)
+{
+// The fast algorithm is based on a linear interpolation between the
+// the values in the lookup table. Relays heavly on libmads fixed point format.
+
+ if(x>limlvl) {
+ int i=(unsigned int)(x-tablestart)/F_LIM_JMP;
+#ifdef DEBUG
+ if(x>MAD_F_ONE) clipped++;
+ limited++;
+ if(i>=tablesize) printf("norm: overflow x=%f x-ts=%f i=%d tsize=%d\n",
+ mad_f_todouble(x),mad_f_todouble(x-tablestart),i,tablesize);
+#endif
+ mad_fixed_t r=x & (F_LIM_JMP-1);
+ x=MAD_F_ONE;
+ if(i<tablesize) {
+ mad_fixed_t *ptr=&table[i];
+ x=*ptr;
+ mad_fixed_t d=*(ptr+1)-x;
+ //x+=mad_f_mul(d,r)<<LIM_ACC; // this is not accurate as mad_f_mul() does >>MAD_F_FRACBITS
+ // which is senseless in the case of following <<LIM_ACC.
+ x+=((long long)d*(long long)r)>>LIM_SHIFT; // better, don't know if works on all machines
+ }
+ }
+ return x;
+}
+#endif
+
+#ifdef USE_FAST_LIMITER
+#define LIMITER_FUNC FastLimiter
+#else
+#define LIMITER_FUNC Limiter
+#endif
+
+void cNormalize::AddGain(struct mad_pcm *pcm)
+{
+ if(dogain) {
+ for(int i=0 ; i<pcm->channels ; i++) {
+ mad_fixed_t *data=pcm->samples[i];
+#ifdef DEBUG
+ total+=pcm->length;
+#endif
+ if(dolimit) {
+ for(int n=pcm->length ; n>0 ; n--) {
+ mad_fixed_t s=mad_f_mul(*data,gain);
+ if(s<0) {
+ s=-s;
+#ifdef DEBUG
+ if(s>peak) peak=s;
+#endif
+ s=LIMITER_FUNC(s);
+ s=-s;
+ }
+ else {
+#ifdef DEBUG
+ if(s>peak) peak=s;
+#endif
+ s=LIMITER_FUNC(s);
+ }
+ *data++=s;
+ }
+ }
+ else {
+ for(int n=pcm->length ; n>0 ; n--) {
+ mad_fixed_t s=mad_f_mul(*data,gain);
+#ifdef DEBUG
+ if(s>peak) peak=s;
+ else if(-s>peak) peak=-s;
+#endif
+ if(s>MAD_F_ONE) s=MAD_F_ONE; // do clipping
+ if(s<-MAD_F_ONE) s=-MAD_F_ONE;
+ *data++=s;
+ }
+ }
+ }
+ }
+}
+
+// --- cScale ----------------------------------------------------------------
+
+// The dither code has been adapted from the madplay project
+// (audio.c) found in the libmad distribution
+
+enum eAudioMode { amRound, amDither };
+
+class cScale {
+private:
+ enum { MIN=-MAD_F_ONE, MAX=MAD_F_ONE - 1 };
+#ifdef DEBUG
+ // audio stats
+ unsigned long clipped_samples;
+ mad_fixed_t peak_clipping;
+ mad_fixed_t peak_sample;
+#endif
+ // dither
+ struct dither {
+ mad_fixed_t error[3];
+ mad_fixed_t random;
+ } leftD, rightD;
+ //
+ inline mad_fixed_t Clip(mad_fixed_t sample, bool stats=true);
+ inline signed long LinearRound(mad_fixed_t sample);
+ inline unsigned long Prng(unsigned long state);
+ inline signed long LinearDither(mad_fixed_t sample, struct dither *dither);
+public:
+ void Init(void);
+ void Stats(void);
+ unsigned int ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode);
+ };
+
+void cScale::Init(void)
+{
+#ifdef DEBUG
+ clipped_samples=0; peak_clipping=peak_sample=0;
+#endif
+ memset(&leftD,0,sizeof(leftD));
+ memset(&rightD,0,sizeof(rightD));
+}
+
+void cScale::Stats(void)
+{
+#ifdef DEBUG
+ printf("mp3: scale stats clipped=%ld peak_clip=%f peak=%f\n",
+ clipped_samples,mad_f_todouble(peak_clipping),mad_f_todouble(peak_sample));
+#endif
+}
+
+// gather signal statistics while clipping
+mad_fixed_t cScale::Clip(mad_fixed_t sample, bool stats)
+{
+#ifndef DEBUG
+ if (sample > MAX) sample = MAX;
+ if (sample < MIN) sample = MIN;
+#else
+ if(!stats) {
+ if (sample > MAX) sample = MAX;
+ if (sample < MIN) sample = MIN;
+ }
+ else {
+ if (sample >= peak_sample) {
+ if (sample > MAX) {
+ ++clipped_samples;
+ if (sample - MAX > peak_clipping)
+ peak_clipping = sample - MAX;
+ sample = MAX;
+ }
+ peak_sample = sample;
+ }
+ else if (sample < -peak_sample) {
+ if (sample < MIN) {
+ ++clipped_samples;
+ if (MIN - sample > peak_clipping)
+ peak_clipping = MIN - sample;
+ sample = MIN;
+ }
+ peak_sample = -sample;
+ }
+ }
+#endif
+ return sample;
+}
+
+// generic linear sample quantize routine
+signed long cScale::LinearRound(mad_fixed_t sample)
+{
+ // round
+ sample += (1L << (MAD_F_FRACBITS - OUT_BITS));
+ // clip
+ sample=Clip(sample);
+ // quantize and scale
+ return sample >> (MAD_F_FRACBITS + 1 - OUT_BITS);
+}
+
+// 32-bit pseudo-random number generator
+unsigned long cScale::Prng(unsigned long state)
+{
+ return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+}
+
+// generic linear sample quantize and dither routine
+signed long cScale::LinearDither(mad_fixed_t sample, struct dither *dither)
+{
+ unsigned int scalebits;
+ mad_fixed_t output, mask, random;
+
+ // noise shape
+ sample += dither->error[0] - dither->error[1] + dither->error[2];
+ dither->error[2] = dither->error[1];
+ dither->error[1] = dither->error[0] / 2;
+ // bias
+ output = sample + (1L << (MAD_F_FRACBITS + 1 - OUT_BITS - 1));
+ scalebits = MAD_F_FRACBITS + 1 - OUT_BITS;
+ mask = (1L << scalebits) - 1;
+ // dither
+ random = Prng(dither->random);
+ output += (random & mask) - (dither->random & mask);
+ dither->random = random;
+ // clip
+ output=Clip(output);
+ sample=Clip(sample,false);
+ // quantize
+ output &= ~mask;
+ // error feedback
+ dither->error[0] = sample - output;
+ // scale
+ return output >> scalebits;
+}
+
+// write a block of signed 16-bit big-endian PCM samples
+unsigned int cScale::ScaleBlock(unsigned char *data, unsigned int size, unsigned int &nsamples, const mad_fixed_t * &left, const mad_fixed_t * &right, eAudioMode mode)
+{
+ signed int sample;
+ unsigned int len, res;
+
+ len=size/OUT_FACT; res=size;
+ if(len>nsamples) { len=nsamples; res=len*OUT_FACT; }
+ nsamples-=len;
+
+ if(right) { // stereo
+ switch (mode) {
+ case amRound:
+ while (len--) {
+ sample = LinearRound(*left++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ sample = LinearRound(*right++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ case amDither:
+ while (len--) {
+ sample = LinearDither(*left++,&leftD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ sample = LinearDither(*right++,&rightD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ }
+ }
+ else { // mono, duplicate left channel
+ switch (mode) {
+ case amRound:
+ while (len--) {
+ sample = LinearRound(*left++);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ case amDither:
+ while (len--) {
+ sample = LinearDither(*left++,&leftD);
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ *data++ = sample >> 8;
+ *data++ = sample >> 0;
+ }
+ break;
+ }
+ }
+ return res;
+}
diff --git a/vdr_stream.c b/vdr_stream.c
new file mode 100644
index 0000000..d82877e
--- /dev/null
+++ b/vdr_stream.c
@@ -0,0 +1,332 @@
+/*!
+ * \file vdr_stream.c
+ * \brief Implementation of media stream classes
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_stream.c,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001-2003 Stefan Huelswitt <huels@iname.com>
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/statfs.h>
+#include <iostream>
+
+#include <interface.h>
+
+#include "mg_tools.h"
+
+// #include "setup-mp3.h"
+#include "vdr_stream.h"
+#include "vdr_network.h"
+#include "vdr_config.h"
+// #include "i18n.h"
+// #include "version.h"
+
+#define tr(x) x
+
+#ifdef USE_MMAP
+#include <sys/mman.h>
+#endif
+
+#define DEFAULT_PORT 80 // default port for streaming (HTTP)
+
+using namespace std;
+
+// --- mgStream -----------------------------------------------------------------
+
+mgStream::mgStream( string filename )
+ : m_filename( filename )
+{
+ m_fd = -1;
+ m_ismmap = false;
+ m_buffer = 0;
+}
+
+mgStream::~mgStream()
+{
+ close();
+}
+
+bool mgStream::open(bool log)
+{
+ if( m_fd >= 0 )
+ {
+ return seek();
+ }
+
+ // just check, whether file exists?
+ if( fileinfo( log ) )
+ {
+ printf( "mgStream::open: fileinfo == true\n");
+
+ if( ( m_fd = ::open( m_filename.c_str(), O_RDONLY ) ) >=0 )
+ {
+ printf( "mgStream::open: file opened\n" );
+
+ m_buffpos = m_readpos = 0;
+ m_fill = 0;
+
+ printf( "mgStream::open: buffpos, readpos, fill set\n" );
+
+ /*
+#ifdef USE_MMAP
+ if( m_filesize <= MAX_MMAP_SIZE )
+ {
+ m_buffer = (unsigned char*)mmap( 0, m_filesize, PROT_READ,
+ MAP_SHARED, m_fd, 0 );
+ if( m_buffer != MAP_FAILED )
+ {
+ m_ismmap = true;
+ return true;
+ }
+ else
+ {
+ dsyslog("mmap() failed for %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+#endif
+ */
+ printf( "mgStream::open: allocating buffer: %d\n", MP3FILE_BUFSIZE );
+ m_buffer = new unsigned char[MP3FILE_BUFSIZE];
+ printf( "mgStream::open: buffer allocated\n" );
+
+ if( m_buffer )
+ {
+ printf( "mgStream::open: buffer allocated, returning true\n" );
+
+ return true;
+ }
+ else
+ {
+ esyslog("ERROR: not enough memory for buffer: %s", m_filename.c_str() );
+ }
+ }
+ else
+ {
+ if( log )
+ {
+ esyslog("ERROR: failed to open file %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+ }
+
+ close();
+ printf( "mgStream::open: returning false\n" );
+ return false;
+}
+
+void mgStream::close(void)
+{
+#ifdef USE_MMAP
+ if( m_ismmap )
+ {
+ munmap( m_buffer, m_filesize );
+ m_buffer = 0;
+ m_ismmap = false;
+ }
+ else
+ {
+#endif
+ delete m_buffer;
+ m_buffer = 0;
+#ifdef USE_MMAP
+ }
+#endif
+ if( m_fd >= 0 )
+ {
+ ::close( m_fd );
+ m_fd = -1;
+ }
+}
+
+bool mgStream::seek(unsigned long long pos)
+{
+ printf( "mgStream::seek\n" );
+ if( m_fd >= 0 && pos >= 0 && pos <= m_filesize )
+ {
+ printf( "mgStream::seek valid file and position detected\n" );
+
+ m_buffpos = 0;
+ m_fill = 0;
+
+ if( m_ismmap )
+ {
+ m_readpos = pos;
+
+ printf( "mgStream::seek: returning true\n" );
+ return true;
+ }
+ else
+ {
+ if( ( m_readpos = lseek64( m_fd, pos, SEEK_SET ) ) >=0 )
+ {
+ if( m_readpos != pos )
+ {
+ dsyslog( "seek mismatch in %s, wanted %lld, got %lld", m_filename.c_str(), pos, m_readpos );
+ }
+ printf( "mgStream::seek: returning true\n" );
+ return true;
+ }
+ else
+ {
+ esyslog( "ERROR: seeking failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) );
+ }
+ }
+ }
+ else
+ {
+ printf( "mp3: bad seek call fd=%d pos=%lld name=%s\n", m_fd, pos, m_filename.c_str() );
+ }
+
+ printf( "mgStream::seek: returning false\n" );
+ return false;
+}
+
+bool mgStream::stream(unsigned char * &data,
+ unsigned long &len,
+ const unsigned char *rest)
+{
+ if( m_fd >= 0 )
+ {
+ if( m_readpos < m_filesize )
+ {
+ if( m_ismmap )
+ {
+ if( rest && m_fill )
+ {
+ m_readpos = (rest - m_buffer); // take care of remaining data
+ }
+ m_fill = m_filesize - m_readpos;
+ data = m_buffer + m_readpos;
+ len = m_fill;
+ m_buffpos = m_readpos;
+ m_readpos += m_fill;
+
+ return true;
+ }
+ else
+ {
+ if( rest && m_fill )
+ { // copy remaining data to start of buffer
+ m_fill -= ( rest - m_buffer); // remaing bytes
+ memmove( m_buffer, rest, m_fill );
+ }
+ else
+ {
+ m_fill = 0;
+ }
+
+ int r;
+ do
+ {
+ r = read( m_fd, m_buffer + m_fill,
+ MP3FILE_BUFSIZE - m_fill );
+ } while( r == -1 && errno == EINTR );
+
+ if( r >= 0 )
+ {
+ m_buffpos = m_readpos - m_fill;
+ m_readpos += r;
+ m_fill += r;
+ data = m_buffer;
+ len = m_fill;
+
+ return true;
+ }
+ else
+ {
+ esyslog("ERROR: read failed in %s: %d,%s", m_filename.c_str(), errno, strerror(errno) );
+ }
+ }
+ }
+ else
+ {
+ len = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool mgStream::removable()
+{
+ // we do not handle removable media at this time
+ return false;
+}
+
+bool mgStream::fileinfo( bool log )
+{
+ struct stat64 ds;
+
+ if( !stat64( m_filename.c_str(), &ds ) )
+ {
+ printf( "mgStream::fileinfo: stat64 == 0\n" );
+
+ if( S_ISREG( ds.st_mode ) )
+ {
+ m_fsID = "";
+ m_fsType = 0;
+
+ struct statfs64 sfs;
+
+ if( !statfs64( m_filename.c_str(), &sfs) )
+ {
+ if( removable() )
+ {
+ char *tmpbuf;
+ asprintf( &tmpbuf, "%llx:%llx", sfs.f_blocks, sfs.f_files );
+ m_fsID = tmpbuf;
+ free( tmpbuf );
+ }
+ m_fsType = sfs.f_type;
+ }
+ else
+ {
+ if( errno != ENOSYS && log )
+ {
+ esyslog("ERROR: can't statfs %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+ }
+
+ m_filesize = ds.st_size;
+ m_ctime = ds.st_ctime;
+
+#ifdef CDFS_MAGIC
+ if( m_fsType == CDFS_MAGIC )
+ {
+ m_ctime=0; // CDFS returns mount time as ctime
+ }
+#endif
+ // infodone tells that info has been read, like a cache flag
+ // InfoDone();
+ return true;
+ }
+ else
+ {
+ if(log)
+ {
+ esyslog("ERROR: %s is not a regular file", m_filename.c_str() );
+ }
+ }
+ }
+ else
+ {
+ if(log)
+ {
+ esyslog("ERROR: can't stat %s: %s", m_filename.c_str(), strerror(errno) );
+ }
+
+ printf( "mgStream::fileinfo: stat64 != 0 for %s\n", m_filename.c_str() );
+ }
+
+ return false;
+}
diff --git a/vdr_stream.h b/vdr_stream.h
new file mode 100644
index 0000000..68f7ea7
--- /dev/null
+++ b/vdr_stream.h
@@ -0,0 +1,57 @@
+/*!
+ * \file vdr_stream.h
+ * \brief Definitions of media streams
+ *
+ * \version $Revision: 1.2 $
+ * \date $Date: 2004/05/28 15:29:19 $
+ * \author Ralf Klueber, Lars von Wedel, Andreas Kellner
+ * \author Responsible author: $Author: lvw $
+ *
+ * $Id: vdr_stream.h,v 1.2 2004/05/28 15:29:19 lvw Exp $
+ *
+ * Adapted from
+ * MP3/MPlayer plugin to VDR (C++)
+ * (C) 2001-2003 Stefan Huelswitt <huels@iname.com>
+ */
+
+#ifndef ___STREAM_H
+#define ___STREAM_H
+
+#include <string>
+
+#include "vdr_decoder.h"
+
+class cNet;
+
+// ----------------------------------------------------------------
+
+class mgStream // : public mgFileInfo
+{
+private:
+ int m_fd;
+ bool m_ismmap;
+
+ // from cFileInfo
+ std::string m_filename, m_fsID;
+ unsigned long long m_filesize;
+ time_t m_ctime;
+ long m_fsType;
+
+ bool fileinfo( bool log );
+ bool removable();
+
+protected:
+ unsigned char *m_buffer;
+ unsigned long long m_readpos, m_buffpos;
+ unsigned long m_fill;
+public:
+ mgStream( std::string filename );
+ virtual ~mgStream();
+ virtual bool open(bool log = true);
+ virtual void close();
+ virtual bool stream( unsigned char *&data, unsigned long &len, const unsigned char *rest=NULL );
+ virtual bool seek( unsigned long long pos = 0 );
+ virtual unsigned long long bufferPos() { return m_buffpos; }
+};
+
+#endif //___STREAM_H