/** * @file sid.cpp * @brief Sidplay demux module for VLC media player */ /***************************************************************************** * Copyright © 2010 Rémi Denis-Courmont * Copyright © 2010 Alan Fischer * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * NOTA BENE: this module requires the linking against a library which is * known to require licensing under the GNU General Public License version 2 * (or later). Therefore, the result of compiling this module will normally * be subject to the terms of that later license. *****************************************************************************/ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include static int Open (vlc_object_t *); static void Close (vlc_object_t *); vlc_module_begin () set_shortname ("sid") set_description ( N_("C64 sid demuxer") ) set_category (CAT_INPUT) set_subcategory (SUBCAT_INPUT_DEMUX) set_capability ("demux", 100) set_callbacks (Open, Close) vlc_module_end () struct demux_sys_t { sidplay2 *player; sid2_config_t config; sid2_info_t info; SidTune *tune; SidTuneInfo tuneInfo; int bytes_per_frame; int block_size; es_out_id_t *es; date_t pts; }; static int Demux (demux_t *); static int Control (demux_t *, int, va_list); static int Open (vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; demux_sys_t *sys = NULL; es_format_t fmt; bool result = false; SidTune *tune = NULL; sidplay2 *player = NULL; ReSIDBuilder *builder = NULL; int64_t size = stream_Size (demux->s); if (size < 4 || size > LONG_MAX) /* We need to load the whole file for sidplay */ return VLC_EGENERIC; const uint8_t *peek; if (vlc_stream_Peek (demux->s, &peek, 4) < 4) return VLC_EGENERIC; /* sidplay2 can read PSID and the newer RSID formats */ if(memcmp(peek,"PSID",4)!=0 && memcmp(peek,"RSID",4)!=0) return VLC_EGENERIC; uint8_t *data = (uint8_t*) malloc(size); if (unlikely (data==NULL)) goto error; if (vlc_stream_Read (demux->s,data,size) < size) { free (data); goto error; } tune = new (std::nothrow) SidTune(0); if (unlikely (tune==NULL)) { free (data); goto error; } result = tune->read (data, size); free (data); if (!result) goto error; player = new (std::nothrow) sidplay2(); if (unlikely(player==NULL)) goto error; sys = (demux_sys_t*) calloc (1, sizeof(demux_sys_t)); if (unlikely(sys==NULL)) goto error; sys->player = player; sys->tune = tune; tune->getInfo (sys->tuneInfo); sys->info = player->info(); sys->config = player->config(); builder = new (std::nothrow) ReSIDBuilder ("ReSID"); if (unlikely(builder==NULL)) goto error; builder->create (sys->info.maxsids); builder->sampling (sys->config.frequency); sys->config.sidEmulation = builder; sys->config.precision = 16; sys->config.playback = (sys->info.channels == 2 ? sid2_stereo : sid2_mono); player->config (sys->config); sys->bytes_per_frame = sys->info.channels * sys->config.precision / 8; sys->block_size = sys->config.frequency / 10 * sys->bytes_per_frame; es_format_Init (&fmt, AUDIO_ES, VLC_CODEC_S16N); fmt.audio.i_channels = sys->info.channels; fmt.audio.i_bitspersample = sys->config.precision; fmt.audio.i_rate = sys->config.frequency; fmt.audio.i_bytes_per_frame = sys->bytes_per_frame; fmt.audio.i_frame_length = fmt.audio.i_bytes_per_frame; fmt.audio.i_blockalign = fmt.audio.i_bytes_per_frame; fmt.i_bitrate = fmt.audio.i_rate * fmt.audio.i_bytes_per_frame; sys->es = es_out_Add (demux->out, &fmt); date_Init (&sys->pts, fmt.audio.i_rate, 1); date_Set (&sys->pts, 0); sys->tune->selectSong (0); result = (sys->player->load (sys->tune) >=0 ); sys->player->fastForward (100); if (!result) goto error; /* Callbacks */ demux->pf_demux = Demux; demux->pf_control = Control; demux->p_sys = sys; return VLC_SUCCESS; error: msg_Err (demux, "An error occurred during sid demuxing" ); delete player; delete builder; delete tune; free (sys); return VLC_EGENERIC; } static void Close (vlc_object_t *obj) { demux_t *demux = (demux_t *)obj; demux_sys_t *sys = demux->p_sys; delete sys->player; delete sys->config.sidEmulation; delete sys->tune; free (sys); } static int Demux (demux_t *demux) { demux_sys_t *sys = demux->p_sys; block_t *block = block_Alloc( sys->block_size); if (unlikely(block==NULL)) return 0; if (!sys->tune->getStatus()) { block_Release (block); return 0; } int i_read = sys->player->play ((void*)block->p_buffer, block->i_buffer); if (i_read <= 0) { block_Release (block); return 0; } block->i_buffer = i_read; block->i_pts = block->i_dts = VLC_TICK_0 + date_Get (&sys->pts); es_out_SetPCR (demux->out, block->i_pts); es_out_Send (demux->out, sys->es, block); date_Increment (&sys->pts, i_read / sys->bytes_per_frame); return 1; } static int Control (demux_t *demux, int query, va_list args) { demux_sys_t *sys = demux->p_sys; switch (query) { case DEMUX_GET_TIME : { int64_t *v = va_arg (args, int64_t*); *v = sys->player->time() * sys->player->timebase() * (CLOCK_FREQ / 100); return VLC_SUCCESS; } case DEMUX_GET_META : { vlc_meta_t *p_meta = va_arg (args, vlc_meta_t *); /* These are specified in the sid tune class as 0 = Title, 1 = Artist, 2 = Copyright/Publisher */ vlc_meta_SetTitle( p_meta, sys->tuneInfo.infoString[0] ); vlc_meta_SetArtist( p_meta, sys->tuneInfo.infoString[1] ); vlc_meta_SetCopyright( p_meta, sys->tuneInfo.infoString[2] ); return VLC_SUCCESS; } case DEMUX_GET_TITLE_INFO : if ( sys->tuneInfo.songs > 1 ) { input_title_t ***ppp_title = va_arg (args, input_title_t ***); int *pi_int = va_arg( args, int* ); *pi_int = sys->tuneInfo.songs; *ppp_title = (input_title_t**) vlc_alloc( sys->tuneInfo.songs, sizeof (input_title_t*)); for( int i = 0; i < sys->tuneInfo.songs; i++ ) { (*ppp_title)[i] = vlc_input_title_New(); } return VLC_SUCCESS; } return VLC_EGENERIC; case DEMUX_SET_TITLE : { int i_idx = va_arg (args, int); sys->tune->selectSong (i_idx+1); bool result = (sys->player->load (sys->tune) >=0 ); if (!result) return VLC_EGENERIC; demux->info.i_title = i_idx; demux->info.i_update = INPUT_UPDATE_TITLE; msg_Dbg( demux, "set song %i", i_idx); return VLC_SUCCESS; } } return VLC_EGENERIC; }