1 /*
2 XMMS-SID - SIDPlay input plugin for X MultiMedia System (XMMS)
3
4 libSIDPlay v2 support
5
6 Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
7 (C) Copyright 1999-2009 Tecnic Software productions (TNSP)
8
9 Ported to sidplayfp:
10 (C) Copyright 2013 Cristian Morales Vega and Hans de Goede
11
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 2 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License along
23 with this program; if not, write to the Free Software Foundation, Inc.,
24 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 */
26
27 #include "xs_config.h"
28 #include "xs_sidplay2.h"
29
30 #include <pthread.h>
31 #include <string.h>
32
33 #include <sidplayfp/sidplayfp.h>
34 #include <sidplayfp/SidDatabase.h>
35 #include <sidplayfp/SidInfo.h>
36 #include <sidplayfp/SidTune.h>
37 #include <sidplayfp/SidTuneInfo.h>
38 #include <sidplayfp/builders/residfp.h>
39
40 #include <libaudcore/runtime.h>
41 #include <libaudcore/vfs.h>
42
43 struct SidState {
44 sidplayfp *currEng;
45 sidbuilder *currBuilder;
46 SidTune *currTune;
47
48 SidDatabase database;
49 bool database_loaded = false;
50 pthread_mutex_t database_mutex = PTHREAD_MUTEX_INITIALIZER;
51 };
52
53 static SidState state;
54
55
56 /* Check if we can play the given file
57 */
xs_sidplayfp_probe(const void * buf,int64_t bufSize)58 bool xs_sidplayfp_probe(const void *buf, int64_t bufSize)
59 {
60 if (bufSize < 4)
61 return false;
62
63 return !memcmp(buf, "PSID", 4) || !memcmp(buf, "RSID", 4);
64 }
65
66
67 /* Initialize SIDPlayFP
68 */
xs_sidplayfp_init()69 bool xs_sidplayfp_init()
70 {
71 /* Initialize the engine */
72 state.currEng = new sidplayfp;
73
74 /* Get current configuration */
75 SidConfig config = state.currEng->config();
76
77 /* Configure channels and stuff */
78 switch (xs_cfg.audioChannels)
79 {
80 case XS_CHN_STEREO:
81 config.playback = SidConfig::STEREO;
82 break;
83
84 case XS_CHN_MONO:
85 config.playback = SidConfig::MONO;
86 break;
87 }
88
89 /* Audio parameters sanity checking and setup */
90 config.frequency = xs_cfg.audioFrequency;
91
92 /* Initialize builder object */
93 state.currBuilder = new ReSIDfpBuilder("ReSIDfp builder");
94
95 /* Builder object created, initialize it */
96 state.currBuilder->create(state.currEng->info().maxsids());
97 if (!state.currBuilder->getStatus()) {
98 AUDERR("reSID->create() failed.\n");
99 return false;
100 }
101
102 state.currBuilder->filter(xs_cfg.emulateFilters);
103 if (!state.currBuilder->getStatus()) {
104 AUDERR("reSID->filter(%d) failed.\n", xs_cfg.emulateFilters);
105 return false;
106 }
107
108 config.sidEmulation = state.currBuilder;
109
110 /* Clockspeed settings */
111 switch (xs_cfg.clockSpeed) {
112 case XS_CLOCK_NTSC:
113 config.defaultC64Model = SidConfig::NTSC;
114 break;
115
116 default:
117 AUDERR("[SIDPlayFP] Invalid clockSpeed=%d, falling back to PAL.\n",
118 xs_cfg.clockSpeed);
119
120 case XS_CLOCK_PAL:
121 config.defaultC64Model = SidConfig::PAL;
122 xs_cfg.clockSpeed = XS_CLOCK_PAL;
123 break;
124 }
125
126 config.forceC64Model = xs_cfg.forceSpeed;
127
128 /* Configure rest of the emulation */
129 if (xs_cfg.mos8580)
130 config.defaultSidModel = SidConfig::MOS8580;
131 else
132 config.defaultSidModel = SidConfig::MOS6581;
133
134 config.forceSidModel = xs_cfg.forceModel;
135
136 /* Now set the emulator configuration */
137 if (!state.currEng->config(config)) {
138 AUDERR("[SIDPlayFP] Emulator engine configuration failed!\n");
139 return false;
140 }
141
142 /* Load ROMs */
143 VFSFile kernal_file("file://" SIDDATADIR "sidplayfp/kernal", "r");
144 VFSFile basic_file("file://" SIDDATADIR "sidplayfp/basic", "r");
145 VFSFile chargen_file("file://" SIDDATADIR "sidplayfp/chargen", "r");
146
147 if (kernal_file && basic_file && chargen_file)
148 {
149 Index<char> kernal = kernal_file.read_all();
150 Index<char> basic = basic_file.read_all();
151 Index<char> chargen = chargen_file.read_all();
152
153 if (kernal.len() == 8192 && basic.len() == 8192 && chargen.len() == 4096)
154 state.currEng->setRoms((uint8_t*)kernal.begin(), (uint8_t*)basic.begin(), (uint8_t*)chargen.begin());
155 }
156
157 /* Load song length database */
158 state.database_loaded = state.database.open(SIDDATADIR "sidplayfp/Songlengths.txt");
159
160 /* Create the sidtune */
161 state.currTune = new SidTune(0);
162
163 return true;
164 }
165
166
167 /* Close SIDPlayFP engine
168 */
xs_sidplayfp_close()169 void xs_sidplayfp_close()
170 {
171 /* Free internals */
172 if (state.currBuilder) {
173 delete state.currBuilder;
174 state.currBuilder = nullptr;
175 }
176
177 if (state.currEng) {
178 delete state.currEng;
179 state.currEng = nullptr;
180 }
181
182 if (state.currTune) {
183 delete state.currTune;
184 state.currTune = nullptr;
185 }
186
187 if (state.database_loaded)
188 state.database.close();
189 }
190
191
192 /* Initialize current song and sub-tune
193 */
xs_sidplayfp_initsong(int subtune)194 bool xs_sidplayfp_initsong(int subtune)
195 {
196 if (!state.currTune->selectSong(subtune)) {
197 AUDERR("[SIDPlayFP] currTune->selectSong() failed\n");
198 return false;
199 }
200
201 if (!state.currEng->load(state.currTune)) {
202 AUDERR("[SIDPlayFP] currEng->load() failed\n");
203 return false;
204 }
205
206 return true;
207 }
208
209
210 /* Emulate and render audio data to given buffer
211 */
xs_sidplayfp_fillbuffer(char * audioBuffer,unsigned audioBufSize)212 unsigned xs_sidplayfp_fillbuffer(char * audioBuffer, unsigned audioBufSize)
213 {
214 return state.currEng->play((short *)audioBuffer, audioBufSize / 2) * 2;
215 }
216
217
218 /* Load a given SID-tune file
219 */
xs_sidplayfp_load(const void * buf,int64_t bufSize)220 bool xs_sidplayfp_load(const void *buf, int64_t bufSize)
221 {
222 /* Try to get the tune */
223 state.currTune->read((const uint8_t*)buf, bufSize);
224
225 return state.currTune->getStatus();
226 }
227
228
xs_sidplayfp_getinfo(xs_tuneinfo_t & ti,const void * buf,int64_t bufSize)229 bool xs_sidplayfp_getinfo(xs_tuneinfo_t &ti, const void *buf, int64_t bufSize)
230 {
231 /* Check if the tune exists and is readable */
232 SidTune myTune((const uint8_t*)buf, bufSize);
233
234 if (!myTune.getStatus())
235 return false;
236
237 /* Get general tune information */
238 const SidTuneInfo *myInfo = myTune.getInfo();
239
240 /* Allocate tuneinfo structure and set information */
241 ti.sidName = String (myInfo->infoString(0));
242 ti.sidComposer = String (myInfo->infoString(1));
243 ti.sidCopyright = String (myInfo->infoString(2));
244
245 ti.nsubTunes = myInfo->songs();
246 ti.startTune = myInfo->startSong();
247
248 ti.sidFormat = String (myInfo->formatString());
249
250 /* Fill in subtune information */
251 ti.subTunes.insert(0, ti.nsubTunes);
252
253 if (state.database_loaded)
254 {
255 pthread_mutex_lock(&state.database_mutex);
256
257 for (int i = 0; i < ti.nsubTunes; i++)
258 {
259 myTune.selectSong(i + 1);
260 ti.subTunes[i].tuneLength = state.database.length(myTune);
261 }
262
263 pthread_mutex_unlock(&state.database_mutex);
264 }
265
266 return true;
267 }
268