1 /* Festalon - NSF Player
2 * Copyright (C) 2004 Xodnizel
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #ifdef INTERFACE_BMP
20 #include <bmp/plugin.h>
21 #include <bmp/util.h>
22 #else
23 #include <xmms/plugin.h>
24 #include <xmms/util.h>
25 #endif
26
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include "../driver.h"
33
34 static FESTALON *Player = NULL;
35
36 #define CMD_SEEK 0x8000
37 #define CMD_STOP 0x4000
38
39 static volatile uint32 command=0;
40 static uint32 current;
41 static volatile int playing=0;
42
43 extern InputPlugin festa_ip;
44
get_iplugin_info(void)45 InputPlugin *get_iplugin_info(void)
46 {
47 festa_ip.description = "Festalon NSF Plugin v"FESTALON_VERSION;
48 return &festa_ip;
49 }
50
init(void)51 void init(void)
52 {
53 }
about(void)54 void about(void)
55 {
56
57 }
config(void)58 void config(void)
59 {
60
61 }
62
testfile(char * fn)63 int testfile(char *fn)
64 {
65 char buf[5];
66 FILE *fp;
67
68 if(!(fp=fopen(fn,"rb"))) return(0);
69 if(fread(buf,1,5,fp)!=5) {fclose(fp);return(0);}
70 fclose(fp);
71 if(memcmp(buf,"NESM\x1a",5) && memcmp(buf, "NSFE", 4) && memcmp(buf,"HESM",4))
72 return 0;
73
74 return(1);
75 }
76
SI(void)77 static void SI(void)
78 {
79 char *tmp;
80
81 if(Player->SongNames && Player->SongNames[current])
82 asprintf(&tmp,"[%d/%d] %s - %s",(int)current+1,Player->TotalSongs,Player->GameName,Player->SongNames[current]);
83 else
84 asprintf(&tmp,"[%d/%d] %s",(int)current+1,Player->TotalSongs,Player->GameName);
85 festa_ip.set_info(tmp,Player->TotalSongs*1000,48000*2*Player->OutChannels*8,48000,Player->OutChannels);
86 free(tmp);
87 }
88 static pthread_t dethread;
FESTAD_Update(float * Buffer,int Count)89 int FESTAD_Update(float *Buffer, int Count)
90 {
91 int16 buf[Count * 2];
92 char *tmp=(char *)buf;
93 int x;
94
95 for(x=0;x<Count * Player->OutChannels;x++) buf[x]=Buffer[x]*65535 - 32767;
96
97 //printf("%d\n",festa_ip.output->written_time());
98 //festa_ip.add_vis_pcm(festa_ip.output->written_time(), FMT_S16_NE, 2, Count, buf);
99
100 while(Count>0)
101 {
102 int t=festa_ip.output->buffer_free();
103
104 t /= 2;
105 t /= Player->OutChannels;
106
107 if(t>Count)
108 festa_ip.output->write_audio(tmp,Count * 2 * Player->OutChannels);
109 else
110 {
111 if(t)
112 festa_ip.output->write_audio(tmp,t * 2 * Player->OutChannels);
113 usleep((Count-t)*125/6); // 1000*1000/48000
114 }
115 Count-=t;
116 tmp+=t*2 * Player->OutChannels;
117 }
118 if(command&CMD_STOP)
119 {
120 playing=0;
121 festa_ip.output->close_audio();
122 command=0;
123 }
124 if(command&CMD_SEEK)
125 {
126 int to = (command & 255) - 1;
127
128 if(to == -1) to = current - 1;
129 else if(to == (Player->TotalSongs-1) && (abs(to-current)<5)) to = current + 1;
130 else if(abs(to - current) == 5) to = (int)current + (to - (int)current) / 5;
131 current=FESTAI_SongControl(Player, to, 1);
132 SI();
133 festa_ip.output->flush(0);
134 }
135 command=0;
136 return(playing);
137 }
138
playloop(void * arg)139 static void *playloop(void *arg)
140 {
141 int count;
142 float *buf;
143
144 do
145 {
146 buf=FESTAI_Emulate(Player, &count);
147 } while(FESTAD_Update(buf,count));
148 pthread_exit(0);
149 }
150
play(char * fn)151 void play(char *fn)
152 {
153 int size;
154 char *buf;
155
156 if(playing)
157 return;
158
159 {
160 FILE *fp=fopen(fn,"rb");
161 fseek(fp,0,SEEK_END);
162 size=ftell(fp);
163 fseek(fp,0,SEEK_SET);
164 buf=malloc(size);
165 fread(buf,1,size,fp);
166 fclose(fp);
167 }
168
169 if(!(Player=FESTAI_Load(buf,size)))
170 {
171 free(buf);
172 return;
173 }
174 free(buf);
175
176 if(!festa_ip.output->open_audio(FMT_S16_LE, 48000, Player->OutChannels))
177 {
178 puts("Error opening audio.");
179 return;
180 }
181 FESTAI_SetSound(Player, 48000, 0);
182 FESTAI_SetVolume(Player, 100);
183 current=Player->StartingSong;
184 SI();
185 playing=1;
186 pthread_create(&dethread,0,playloop,0);
187 }
188
stop(void)189 void stop(void)
190 {
191 //puts("stop");
192 festa_ip.output->pause(0);
193 command=CMD_STOP;
194 pthread_join(dethread,0);
195 FESTAI_Close(Player);
196 Player = NULL;
197 }
198
festa_pause(short paused)199 void festa_pause(short paused)
200 {
201 festa_ip.output->pause(paused);
202 }
203
seek(int time)204 void seek(int time)
205 {
206 //puts("seek");
207 command=CMD_SEEK|time;
208 // festa_ip.output->flush(0);
209 }
210
gettime(void)211 int gettime(void)
212 {
213 // return festa_ip.output->output_time();
214 //puts("gettime");
215 return((current+1)*1000);
216 }
217
getsonginfo(char * fn,char ** title,int * length)218 void getsonginfo(char *fn, char **title, int *length)
219 {
220 FESTALON *fe;
221 int size;
222 char *buf;
223
224 FILE *fp=fopen(fn,"rb");
225 fseek(fp,0,SEEK_END);
226 size=ftell(fp);
227 fseek(fp,0,SEEK_SET);
228 buf=malloc(size);
229 fread(buf,1,size,fp);
230 fclose(fp);
231
232 fe = FESTAI_GetFileInfo(buf, size, FESTAGFI_TAGS);
233 free(buf);
234 if(fe)
235 {
236 *length=(fe->TotalSongs)*1000;
237 *title = strdup(fe->GameName?fe->GameName:fn);
238 FESTAI_FreeFileInfo(fe);
239 }
240 }
241
242 InputPlugin festa_ip =
243 {
244 0,0,"Some description",0,0,0,testfile,0,play,stop,festa_pause,
245 seek,0,gettime,0,0,0,0,0,0,0,getsonginfo,0,0
246 };
247
248