demux_nsf.c (11885B)
1 /* 2 * Copyright Copyright (C) 2000-2002 the xine project 3 * 4 * This file is part of xine, a free video player. 5 * 6 * xine is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * xine is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 * NSF File "Demuxer" by Mike Melanson (melanson@pcisys.net) 21 * This is really just a loader for NES Music File Format (extension NSF) 22 * which loads an entire NSF file and passes it over to the NSF audio 23 * decoder. 24 * 25 * After the file is sent over, the demuxer controls the playback by 26 * sending empty buffers with incrementing pts values. 27 * 28 * For more information regarding the NSF format, visit: 29 * http://www.tripoint.org/kevtris/nes/nsfspec.txt 30 * 31 * $Id: demux_nsf.c,v 1.1 2003/04/03 14:51:25 grothoff Exp $ 32 */ 33 34 #ifdef HAVE_CONFIG_H 35 #include "config.h" 36 #endif 37 38 #include <stdio.h> 39 #include <fcntl.h> 40 #include <unistd.h> 41 #include <string.h> 42 #include <stdlib.h> 43 44 #include "xine_internal.h" 45 #include "xineutils.h" 46 #include "compat.h" 47 #include "demux.h" 48 #include "bswap.h" 49 50 #define NSF_HEADER_SIZE 0x80 51 #define NSF_SAMPLERATE 44100 52 #define NSF_BITS 8 53 #define NSF_CHANNELS 1 54 #define NSF_REFRESH_RATE 60 55 #define NSF_PTS_INC (90000 / NSF_REFRESH_RATE) 56 57 typedef struct 58 { 59 60 demux_plugin_t demux_plugin; 61 62 xine_stream_t *stream; 63 64 config_values_t *config; 65 66 fifo_buffer_t *video_fifo; 67 fifo_buffer_t *audio_fifo; 68 69 input_plugin_t *input; 70 71 pthread_t thread; 72 int thread_running; 73 pthread_mutex_t mutex; 74 int send_end_buffers; 75 76 int status; 77 78 char *title; 79 char *artist; 80 char *copyright; 81 int total_songs; 82 int current_song; 83 int new_song; /* indicates song change */ 84 off_t filesize; 85 86 int64_t current_pts; 87 int file_sent; 88 89 char last_mrl[1024]; 90 91 } demux_nsf_t; 92 93 typedef struct 94 { 95 96 demux_class_t demux_class; 97 98 /* class-wide, global variables here */ 99 100 xine_t *xine; 101 config_values_t *config; 102 } demux_nsf_class_t; 103 104 105 /* returns 1 if the NSF file was opened successfully, 0 otherwise */ 106 static int 107 open_nsf_file (demux_nsf_t * this) 108 { 109 110 unsigned char header[NSF_HEADER_SIZE]; 111 112 this->input->seek (this->input, 0, SEEK_SET); 113 if (this->input->read (this->input, header, NSF_HEADER_SIZE) != 114 NSF_HEADER_SIZE) 115 return 0; 116 117 /* check for the signature */ 118 if ((header[0] != 'N') || 119 (header[1] != 'E') || 120 (header[2] != 'S') || (header[3] != 'M') || (header[4] != 0x1A)) 121 return 0; 122 123 this->total_songs = header[6]; 124 this->current_song = header[7]; 125 this->title = strdup (&header[0x0E]); 126 this->artist = strdup (&header[0x2E]); 127 this->copyright = strdup (&header[0x4E]); 128 129 this->filesize = this->input->get_length (this->input); 130 131 return 1; 132 } 133 134 static int 135 demux_nsf_send_chunk (demux_plugin_t * this_gen) 136 { 137 138 demux_nsf_t *this = (demux_nsf_t *) this_gen; 139 buf_element_t *buf; 140 int bytes_read; 141 char title[100]; 142 143 /* send chunks of the file to the decoder until file is completely 144 * loaded; then send control buffers */ 145 if (!this->file_sent) 146 { 147 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); 148 buf->type = BUF_AUDIO_NSF; 149 bytes_read = 150 this->input->read (this->input, buf->content, buf->max_size); 151 152 if (bytes_read == 0) 153 { 154 /* the file has been completely loaded, free the buffer and start 155 * sending control buffers */ 156 buf->free_buffer (buf); 157 this->file_sent = 1; 158 159 } 160 else 161 { 162 163 /* keep loading the file */ 164 if (bytes_read < buf->max_size) 165 buf->size = bytes_read; 166 else 167 buf->size = buf->max_size; 168 169 buf->extra_info->input_pos = 0; 170 buf->extra_info->input_length = 0; 171 buf->extra_info->input_time = 0; 172 buf->pts = 0; 173 174 this->audio_fifo->put (this->audio_fifo, buf); 175 } 176 } 177 178 /* this is not an 'else' because control might fall through from above */ 179 if (this->file_sent) 180 { 181 /* send a control buffer */ 182 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); 183 184 if (this->new_song) 185 { 186 187 buf->decoder_info[1] = this->current_song; 188 this->new_song = 0; 189 sprintf (title, "%s, song %d/%d", 190 this->title, this->current_song, this->total_songs); 191 if (this->stream->meta_info[XINE_META_INFO_TITLE]) 192 free (this->stream->meta_info[XINE_META_INFO_TITLE]); 193 this->stream->meta_info[XINE_META_INFO_TITLE] = strdup (title); 194 xine_demux_control_newpts (this->stream, this->current_pts, 0); 195 196 } 197 else 198 buf->decoder_info[1] = 0; 199 200 buf->type = BUF_AUDIO_NSF; 201 buf->extra_info->input_pos = this->current_song - 1; 202 buf->extra_info->input_length = this->total_songs; 203 buf->extra_info->input_time = this->current_pts / 90; 204 buf->pts = this->current_pts; 205 buf->size = 0; 206 this->audio_fifo->put (this->audio_fifo, buf); 207 208 this->current_pts += NSF_PTS_INC; 209 } 210 211 return this->status; 212 } 213 214 static void 215 demux_nsf_send_headers (demux_plugin_t * this_gen) 216 { 217 218 demux_nsf_t *this = (demux_nsf_t *) this_gen; 219 buf_element_t *buf; 220 char copyright[100]; 221 222 this->video_fifo = this->stream->video_fifo; 223 this->audio_fifo = this->stream->audio_fifo; 224 225 this->status = DEMUX_OK; 226 227 /* load stream information */ 228 this->stream->stream_info[XINE_STREAM_INFO_HAS_VIDEO] = 0; 229 this->stream->stream_info[XINE_STREAM_INFO_HAS_AUDIO] = 1; 230 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_CHANNELS] = NSF_CHANNELS; 231 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_SAMPLERATE] = 232 NSF_SAMPLERATE; 233 this->stream->stream_info[XINE_STREAM_INFO_AUDIO_BITS] = NSF_BITS; 234 235 this->stream->meta_info[XINE_META_INFO_TITLE] = strdup (this->title); 236 this->stream->meta_info[XINE_META_INFO_ARTIST] = strdup (this->artist); 237 sprintf (copyright, "Copyright (C) %s", this->copyright); 238 this->stream->meta_info[XINE_META_INFO_COMMENT] = strdup (copyright); 239 240 /* send start buffers */ 241 xine_demux_control_start (this->stream); 242 243 /* send init info to the audio decoder */ 244 if (this->audio_fifo) 245 { 246 buf = this->audio_fifo->buffer_pool_alloc (this->audio_fifo); 247 buf->type = BUF_AUDIO_NSF; 248 buf->decoder_flags = BUF_FLAG_HEADER; 249 buf->decoder_info[0] = 5; 250 buf->decoder_info[1] = NSF_SAMPLERATE; 251 buf->decoder_info[2] = NSF_BITS; 252 buf->decoder_info[3] = NSF_CHANNELS; 253 254 /* send the NSF filesize in the body, big endian format */ 255 buf->content[0] = (this->filesize >> 24) & 0xFF; 256 buf->content[1] = (this->filesize >> 16) & 0xFF; 257 buf->content[2] = (this->filesize >> 8) & 0xFF; 258 buf->content[3] = (this->filesize >> 0) & 0xFF; 259 /* send the requested song */ 260 buf->content[4] = this->current_song + 5; 261 262 this->audio_fifo->put (this->audio_fifo, buf); 263 } 264 } 265 266 static int 267 demux_nsf_seek (demux_plugin_t * this_gen, off_t start_pos, int start_time) 268 { 269 270 demux_nsf_t *this = (demux_nsf_t *) this_gen; 271 272 /* if thread is not running, initialize demuxer */ 273 if (!this->stream->demux_thread_running) 274 { 275 276 /* send new pts */ 277 xine_demux_control_newpts (this->stream, 0, 0); 278 279 this->status = DEMUX_OK; 280 281 /* reposition stream at the start for loading */ 282 this->input->seek (this->input, 0, SEEK_SET); 283 284 this->file_sent = 0; 285 this->current_pts = 0; 286 this->new_song = 1; 287 } 288 else 289 { 290 this->current_song = start_pos + 1; 291 this->new_song = 1; 292 this->current_pts = 0; 293 xine_demux_flush_engine (this->stream); 294 } 295 296 return this->status; 297 } 298 299 static void 300 demux_nsf_dispose (demux_plugin_t * this_gen) 301 { 302 demux_nsf_t *this = (demux_nsf_t *) this_gen; 303 304 free (this->title); 305 free (this->artist); 306 free (this->copyright); 307 free (this); 308 } 309 310 static int 311 demux_nsf_get_status (demux_plugin_t * this_gen) 312 { 313 demux_nsf_t *this = (demux_nsf_t *) this_gen; 314 315 return this->status; 316 } 317 318 /* return the approximate length in miliseconds */ 319 static int 320 demux_nsf_get_stream_length (demux_plugin_t * this_gen) 321 { 322 323 return 0; 324 } 325 326 static uint32_t 327 demux_nsf_get_capabilities (demux_plugin_t * this_gen) 328 { 329 return DEMUX_CAP_NOCAP; 330 } 331 332 static int 333 demux_nsf_get_optional_data (demux_plugin_t * this_gen, 334 void *data, int data_type) 335 { 336 return DEMUX_OPTIONAL_UNSUPPORTED; 337 } 338 339 static demux_plugin_t * 340 open_plugin (demux_class_t * class_gen, xine_stream_t * stream, 341 input_plugin_t * input_gen) 342 { 343 344 input_plugin_t *input = (input_plugin_t *) input_gen; 345 demux_nsf_t *this; 346 347 if (!(input->get_capabilities (input) & INPUT_CAP_SEEKABLE)) 348 { 349 if (stream->xine->verbosity >= XINE_VERBOSITY_DEBUG) 350 printf (_("demux_nsf.c: input not seekable, can not handle!\n")); 351 return NULL; 352 } 353 354 this = xine_xmalloc (sizeof (demux_nsf_t)); 355 this->stream = stream; 356 this->input = input; 357 358 this->demux_plugin.send_headers = demux_nsf_send_headers; 359 this->demux_plugin.send_chunk = demux_nsf_send_chunk; 360 this->demux_plugin.seek = demux_nsf_seek; 361 this->demux_plugin.dispose = demux_nsf_dispose; 362 this->demux_plugin.get_status = demux_nsf_get_status; 363 this->demux_plugin.get_stream_length = demux_nsf_get_stream_length; 364 this->demux_plugin.get_video_frame = NULL; 365 this->demux_plugin.got_video_frame_cb = NULL; 366 this->demux_plugin.get_capabilities = demux_nsf_get_capabilities; 367 this->demux_plugin.get_optional_data = demux_nsf_get_optional_data; 368 this->demux_plugin.demux_class = class_gen; 369 370 this->status = DEMUX_FINISHED; 371 372 switch (stream->content_detection_method) 373 { 374 375 case METHOD_BY_CONTENT: 376 case METHOD_EXPLICIT: 377 378 if (!open_nsf_file (this)) 379 { 380 free (this); 381 return NULL; 382 } 383 384 break; 385 386 case METHOD_BY_EXTENSION: 387 { 388 char *ending, *mrl; 389 390 mrl = input->get_mrl (input); 391 392 ending = strrchr (mrl, '.'); 393 394 if (!ending) 395 { 396 free (this); 397 return NULL; 398 } 399 400 if (strncasecmp (ending, ".nsf", 4)) 401 { 402 free (this); 403 return NULL; 404 } 405 406 if (!open_nsf_file (this)) 407 { 408 free (this); 409 return NULL; 410 } 411 412 } 413 414 break; 415 416 default: 417 free (this); 418 return NULL; 419 } 420 421 strncpy (this->last_mrl, input->get_mrl (input), 1024); 422 423 return &this->demux_plugin; 424 } 425 426 static char * 427 get_description (demux_class_t * this_gen) 428 { 429 return "NES Music file demux plugin"; 430 } 431 432 static char * 433 get_identifier (demux_class_t * this_gen) 434 { 435 return "NSF"; 436 } 437 438 static char * 439 get_extensions (demux_class_t * this_gen) 440 { 441 return "nsf"; 442 } 443 444 static char * 445 get_mimetypes (demux_class_t * this_gen) 446 { 447 return NULL; 448 } 449 450 static void 451 class_dispose (demux_class_t * this_gen) 452 { 453 454 demux_nsf_class_t *this = (demux_nsf_class_t *) this_gen; 455 456 free (this); 457 } 458 459 void * 460 demux_nsf_init_plugin (xine_t * xine, void *data) 461 { 462 463 demux_nsf_class_t *this; 464 465 this = xine_xmalloc (sizeof (demux_nsf_class_t)); 466 this->config = xine->config; 467 this->xine = xine; 468 469 this->demux_class.open_plugin = open_plugin; 470 this->demux_class.get_description = get_description; 471 this->demux_class.get_identifier = get_identifier; 472 this->demux_class.get_mimetypes = get_mimetypes; 473 this->demux_class.get_extensions = get_extensions; 474 this->demux_class.dispose = class_dispose; 475 476 return this; 477 }