[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / cores / paplayer / TimidityCodec.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "TimidityCodec.h"
22 #include "URL.h"
23 #include "../DllLoader/LibraryLoader.h"
24 #include "../DllLoader/SoLoader.h"
25 #include "../DllLoader/DllLoader.h"
26 #include "Util.h"
27 #include "utils/log.h"
28 #include "filesystem/SpecialProtocol.h"
29 #ifdef _WIN32
30 #include "../DllLoader/Win32DllLoader.h"
31 #endif
32
33 static const char * DEFAULT_SOUNDFONT_FILE = "special://masterprofile/timidity/soundfont.sf2";
34
35 TimidityCodec::TimidityCodec()
36 {
37   m_CodecName = "mid";
38   m_mid = 0;
39   m_iTrack = -1;
40   m_iDataPos = -1;
41   m_loader = NULL;
42   memset(&m_dll, 0, sizeof(m_dll));
43 }
44
45 TimidityCodec::~TimidityCodec()
46 {
47   DeInit();
48 }
49
50 bool TimidityCodec::Init(const CStdString &strFile, unsigned int filecache)
51 {
52   // We do not need to init/load Timidity more than once
53   //
54   // Note the above comment makes no sense as TimidityCodec is not a singleton.
55   // In fact, MID_CODEC can ONLY be opened and used by one instance (lot's of statics).
56   // So to work around this problem with MID_CODEC, we need to make sure that
57   // each instance of TimidityCodec has it's own instance of MID_CODEC. Do this by
58   // coping DLL_PATH_MID_CODEC into special://temp and using a unique name. Then
59   // loading this unique named MID_CODEC as the library.
60   // This forces the shared lib loader to load a per-instance copy of MID_CODEC.
61   if ( !m_loader )
62   {
63 #ifdef _LINUX
64     m_loader_name = CUtil::GetNextFilename("special://temp/libtimidity-%03d.so", 999);
65     XFILE::CFile::Cache(DLL_PATH_MID_CODEC, m_loader_name);
66
67     m_loader = new SoLoader(m_loader_name.c_str());
68 #else
69     m_loader_name = CUtil::GetNextFilename("special://temp/libtimidity-%03d.dll", 999);
70     XFILE::CFile::Cache(DLL_PATH_MID_CODEC, m_loader_name);
71
72     m_loader = new Win32DllLoader(m_loader_name);
73 #endif
74     if (!m_loader)
75     {
76       XFILE::CFile::Delete(m_loader_name);
77       return false;
78     }
79
80     if (!m_loader->Load())
81     {
82       delete m_loader;
83       m_loader = NULL;
84       XFILE::CFile::Delete(m_loader_name);
85       return false;
86     }
87
88     m_loader->ResolveExport("DLL_Init",(void**)&m_dll.Init);
89     m_loader->ResolveExport("DLL_LoadMID",(void**)&m_dll.LoadMID);
90     m_loader->ResolveExport("DLL_FreeMID",(void**)&m_dll.FreeMID);
91     m_loader->ResolveExport("DLL_FillBuffer",(void**)&m_dll.FillBuffer);
92     m_loader->ResolveExport("DLL_GetLength",(void**)&m_dll.GetLength);
93     m_loader->ResolveExport("DLL_Cleanup",(void**)&m_dll.Cleanup);
94     m_loader->ResolveExport("DLL_ErrorMsg",(void**)&m_dll.ErrorMsg);
95     m_loader->ResolveExport("DLL_Seek",(void**)&m_dll.Seek);
96
97     if ( m_dll.Init( DEFAULT_SOUNDFONT_FILE ) == 0 )
98     {
99       CLog::Log(LOGERROR,"TimidityCodec: cannot init codec: %s", m_dll.ErrorMsg() );
100       CLog::Log(LOGERROR,"Failed to initialize MIDI codec. Please make sure you configured MIDI playback according to http://wiki.xbmc.org/?title=HOW-TO:_Setup_XBMC_for_karaoke" );
101       return false;
102     }
103   }
104
105   // Free the song if already loaded
106   if ( m_mid )
107     m_dll.FreeMID( m_mid );
108
109   CStdString file = strFile;
110   CURL url(strFile);
111   if (!url.IsLocal())
112   {
113     CStdString file = CUtil::GetNextFilename("special://temp/midi%03d.mid",999);
114     XFILE::CFile::Cache(strFile,file);
115     url.Parse(file);
116   }
117
118   m_mid = m_dll.LoadMID(CSpecialProtocol::TranslatePath(url.Get()).c_str());
119   if (!m_mid)
120   {
121     CLog::Log(LOGERROR,"TimidityCodec: error opening file %s: %s",strFile.c_str(), m_dll.ErrorMsg());
122     return false;
123   }
124
125   m_Channels = 2;
126   m_SampleRate = 48000;
127   m_BitsPerSample = 16;
128   m_DataFormat = AE_FMT_S16NE;
129   m_TotalTime = (int64_t)m_dll.GetLength(m_mid);
130
131   return true;
132 }
133
134 void TimidityCodec::DeInit()
135 {
136   if ( m_mid )
137     m_dll.FreeMID(m_mid);
138
139   if ( m_loader )
140   {
141     m_dll.Cleanup();
142     delete m_loader;
143     XFILE::CFile::Delete(m_loader_name);
144   }
145
146   m_mid = 0;
147   m_loader = 0;
148 }
149
150 int64_t TimidityCodec::Seek(int64_t iSeekTime)
151 {
152   int64_t result = (int64_t)m_dll.Seek(m_mid,(unsigned long)iSeekTime);
153   m_iDataPos = result/1000*48000*4;
154
155   return result;
156 }
157
158 int TimidityCodec::ReadPCM(BYTE *pBuffer, int size, int *actualsize)
159 {
160   if (m_iDataPos == -1)
161   {
162     m_iDataPos = 0;
163   }
164
165   if (m_iDataPos >= m_TotalTime/1000*48000*4)
166     return READ_EOF;
167
168   if ((*actualsize=m_dll.FillBuffer(m_mid,(char*)pBuffer,size))> 0)
169   {
170     m_iDataPos += *actualsize;
171     return READ_SUCCESS;
172   }
173
174   return READ_ERROR;
175 }
176
177 bool TimidityCodec::CanInit()
178 {
179   return XFILE::CFile::Exists("special://xbmc/system/players/paplayer/timidity/timidity.cfg")
180       || XFILE::CFile::Exists( DEFAULT_SOUNDFONT_FILE );
181 }
182
183 bool TimidityCodec::IsSupportedFormat(const CStdString& strExt)
184 {
185   if (strExt == "mid" || strExt == "kar")
186     return true;
187
188   return false;
189 }
190