/************************************************************************* laserdsc.h Core laserdisc player implementation. **************************************************************************** Copyright Aaron Giles All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name 'MAME' nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *************************************************************************/ #pragma once #ifndef __LASERDSC_H__ #define __LASERDSC_H__ #include "vbiparse.h" #include "avhuff.h" //************************************************************************** // CONSTANTS //************************************************************************** // laserdisc field codes enum laserdisc_field_code { LASERDISC_CODE_WHITE_FLAG = 11, // boolean white flag LASERDISC_CODE_LINE16 = 16, // 24-bit line 16 code LASERDISC_CODE_LINE17 = 17, // 24-bit line 17 code LASERDISC_CODE_LINE18 = 18, // 24-bit line 18 code LASERDISC_CODE_LINE1718 = 1718 // 24-bit best of line 17/18 code }; // special frame and chapter numbers from VBI conversion #define FRAME_NOT_PRESENT -2 // no frame number information present #define FRAME_LEAD_IN -1 // lead-in code detected #define FRAME_LEAD_OUT 99999 // lead-out code detected #define CHAPTER_NOT_PRESENT -2 // no chapter number information present #define CHAPTER_LEAD_IN -1 // lead-in code detected #define CHAPTER_LEAD_OUT 100 // lead-out code detected // generic head movement speeds; use player-specific information where appropriate #define GENERIC_SLOW_SPEED (5) // 1/5 normal speed #define GENERIC_FAST_SPEED (3) // 3x normal speed #define GENERIC_SCAN_SPEED (50) // 50x normal speed #define GENERIC_SEARCH_SPEED (5000) // 5000x normal speed // generic timings; use player-specific information where appropriate #define GENERIC_EJECT_TIME (attotime::from_seconds(5)) #define GENERIC_SPINUP_TIME (attotime::from_seconds(2)) #define GENERIC_LOAD_TIME (attotime::from_seconds(5)) //************************************************************************** // DEVICE CONFIGURATION MACROS //************************************************************************** #define MCFG_LASERDISC_GET_DISC(_func) \ laserdisc_device::static_set_get_disc(*device, _func); \ #define MCFG_LASERDISC_AUDIO(_func) \ laserdisc_device::static_set_audio(*device, _func); \ #define MCFG_LASERDISC_OVERLAY_STATIC(_width, _height, _func) \ laserdisc_device::static_set_overlay(*device, _width, _height, screen_update_delegate_smart(&screen_update_##_func, "screen_update_" #_func)); \ #define MCFG_LASERDISC_OVERLAY_DRIVER(_width, _height, _class, _method) \ laserdisc_device::static_set_overlay(*device, _width, _height, screen_update_delegate_smart(&_class::_method, #_class "::" #_method, NULL)); \ #define MCFG_LASERDISC_OVERLAY_DEVICE(_width, _height, _device, _class, _method) \ laserdisc_device::static_set_overlay(*device, _width, _height, screen_update_delegate_smart(&_class::_method, #_class "::" #_method, _device)); \ #define MCFG_LASERDISC_OVERLAY_CLIP(_minx, _maxx, _miny, _maxy) \ laserdisc_device::static_set_overlay_clip(*device, _minx, _maxx, _miny, _maxy); \ #define MCFG_LASERDISC_OVERLAY_POSITION(_posx, _posy) \ laserdisc_device::static_set_overlay_position(*device, _posx, _posy); \ #define MCFG_LASERDISC_OVERLAY_SCALE(_scalex, _scaley) \ laserdisc_device::static_set_overlay_scale(*device, _scalex, _scaley); \ // use these to add laserdisc screens with proper video update parameters #define MCFG_LASERDISC_SCREEN_ADD_NTSC(_tag, _ldtag) \ MCFG_DEVICE_MODIFY(_ldtag) \ laserdisc_device::static_set_screen(*device, _tag); \ MCFG_VIDEO_ATTRIBUTES(VIDEO_SELF_RENDER) \ MCFG_SCREEN_ADD(_tag, RASTER) \ MCFG_SCREEN_RAW_PARAMS(XTAL_14_31818MHz*2, 910, 0, 704, 525, 44, 524) \ MCFG_SCREEN_UPDATE_DEVICE(_ldtag, laserdisc_device, screen_update) \ // not correct yet; fix me... #define MCFG_LASERDISC_SCREEN_ADD_PAL(_tag, _ldtag) \ MCFG_DEVICE_MODIFY(_ldtag) \ laserdisc_device::static_set_screen(*device, _tag); \ MCFG_VIDEO_ATTRIBUTES(VIDEO_SELF_RENDER) \ MCFG_SCREEN_ADD(_tag, RASTER) \ MCFG_SCREEN_RAW_PARAMS(XTAL_14_31818MHz, 910, 0, 704, 525.0/2, 0, 480/2) \ MCFG_SCREEN_UPDATE_DEVICE(_ldtag, laserdisc_device, screen_update) \ //************************************************************************** // MACROS //************************************************************************** #define SCANNING_PARAM(speed,duration) (((speed) << 8) | ((duration) & 0xff)) //************************************************************************** // TYPE DEFINITIONS //************************************************************************** // forward declarations class laserdisc_device; // delegates typedef delegate laserdisc_get_disc_delegate; typedef delegate laserdisc_audio_delegate; // ======================> laserdisc_overlay_config // overlay configuration struct laserdisc_overlay_config { float m_overposx; float m_overposy; float m_overscalex; float m_overscaley; }; // ======================> laserdisc_device // base laserdisc class class laserdisc_device : public device_t, public device_sound_interface, public laserdisc_overlay_config { protected: // construction/destruction laserdisc_device(const machine_config &mconfig, device_type type, const char *name, const char *shortname, const char *tag, device_t *owner, UINT32 clock); virtual ~laserdisc_device(); public: // reset line control // core control and status bool video_active() { return (!m_videosquelch && current_frame().m_numfields >= 2); } bitmap_yuy16 &get_video() { return (!video_active()) ? m_emptyframe : current_frame().m_visbitmap; } UINT32 get_field_code(laserdisc_field_code code, bool zero_if_squelched); // video interface void video_enable(bool enable) { m_videoenable = enable; } void overlay_enable(bool enable) { m_overenable = enable; } // video update callback UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); // configuration bool overlay_configured() const { return (m_overwidth > 0 && m_overheight > 0 && (!m_overupdate_ind16.isnull() || !m_overupdate_rgb32.isnull())); } void get_overlay_config(laserdisc_overlay_config &config) { config = static_cast(*this); } void set_overlay_config(const laserdisc_overlay_config &config) { static_cast(*this) = config; } // static configuration helpers static void static_set_screen(device_t &device, const char *screen); static void static_set_get_disc(device_t &device, laserdisc_get_disc_delegate callback); static void static_set_audio(device_t &device, laserdisc_audio_delegate callback); static void static_set_overlay(device_t &device, UINT32 width, UINT32 height, screen_update_ind16_delegate update); static void static_set_overlay(device_t &device, UINT32 width, UINT32 height, screen_update_rgb32_delegate update); static void static_set_overlay_clip(device_t &device, INT32 minx, INT32 maxx, INT32 miny, INT32 maxy); static void static_set_overlay_position(device_t &device, float posx, float posy); static void static_set_overlay_scale(device_t &device, float scalex, float scaley); protected: // timer IDs enum { TID_VBI_FETCH, TID_FIRST_PLAYER_TIMER }; // common laserdisc states enum player_state { LDSTATE_NONE, // unspecified state LDSTATE_EJECTING, // in the process of ejecting LDSTATE_EJECTED, // fully ejected LDSTATE_PARKED, // head parked in lead-in LDSTATE_LOADING, // loading from ejected state LDSTATE_SPINUP, // spinning up LDSTATE_PAUSING, // looking for a frame boundary to pause LDSTATE_PAUSED, // found a frame boundary; now paused // parameter specifies the fieldnum of the first frame LDSTATE_PLAYING, // playing forward normally, with audio // parameter specifies the target frame, or 0 if none LDSTATE_PLAYING_SLOW_REVERSE, // playing slow in the reverse direction, with no audio // parameter specifies the number of times to repeat each track LDSTATE_PLAYING_SLOW_FORWARD, // playing slow in the forward direction, with no audio // parameter specifies the number of times to repeat each track LDSTATE_PLAYING_FAST_REVERSE, // playing fast in the reverse direction, with no audio // parameter specifies the number of frames to skip backwards after each frame LDSTATE_PLAYING_FAST_FORWARD, // playing fast in the forward direction, with no audio // parameter specifies the number of frames to skip forwards after each frame LDSTATE_STEPPING_REVERSE, // single frame stepping in the reverse direction LDSTATE_STEPPING_FORWARD, // single frame stepping in the forward direction LDSTATE_SCANNING, // scanning in the forward or reverse direction // parameter(0:7) controls how many vsyncs until revert to savestate // parameter(8:31) specifies the speed LDSTATE_SEEKING, // seeking to a specific frame // parameter specifies the target frame LDSTATE_OTHER // other states start here }; // slider position enum slider_position { SLIDER_MINIMUM, // at the minimum value SLIDER_VIRTUAL_LEADIN, // within the virtual lead-in area SLIDER_CHD, // within the boundaries of the CHD SLIDER_OUTSIDE_CHD, // outside of the CHD area but before the virtual lead-out area SLIDER_VIRTUAL_LEADOUT, // within the virtual lead-out area SLIDER_MAXIMUM // at the maximum value }; // information about the current player state struct player_state_info { player_state m_state; // current state INT32 m_substate; // internal sub-state; starts at 0 on any state change INT32 m_param; // parameter for current state attotime m_endtime; // minimum ending time for current state }; // subclass overrides virtual void player_vsync(const vbi_metadata &vbi, int fieldnum, attotime curtime) = 0; virtual INT32 player_update(const vbi_metadata &vbi, int fieldnum, attotime curtime) = 0; virtual void player_overlay(bitmap_yuy16 &bitmap) = 0; // device-level overrides virtual void device_start(); virtual void device_stop(); virtual void device_reset(); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); // device_sound_interface overrides virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples); // subclass helpers screen_device &screen() { assert(m_screen != NULL); return *m_screen; } void set_audio_squelch(bool squelchleft, bool squelchright) { m_stream->update(); m_audiosquelch = (squelchleft ? 1 : 0) | (squelchright ? 2 : 0); } void set_video_squelch(bool squelch) { m_videosquelch = squelch; } void set_slider_speed(INT32 tracks_per_vsync); void advance_slider(INT32 numtracks); slider_position get_slider_position(); INT32 generic_update(const vbi_metadata &vbi, int fieldnum, attotime curtime, player_state_info &curstate); // general helpers bool is_start_of_frame(const vbi_metadata &vbi); int frame_from_metadata(const vbi_metadata &metadata); int chapter_from_metadata(const vbi_metadata &metadata); player_state_info m_player_state; // active state player_state_info m_saved_state; // saved state during temporary operations private: // internal type definitions struct frame_data { bitmap_yuy16 m_bitmap; // cached bitmap bitmap_yuy16 m_visbitmap; // wrapper around bitmap with only visible lines UINT8 m_numfields; // number of fields in this frame INT32 m_lastfield; // last absolute field number }; // internal helpers void init_disc(); void init_video(); void init_audio(); void add_and_clamp_track(INT32 delta) { m_curtrack += delta; m_curtrack = MAX(m_curtrack, 1); m_curtrack = MIN(m_curtrack, m_maxtrack - 1); } void fillbitmap_yuy16(bitmap_yuy16 &bitmap, UINT8 yval, UINT8 cr, UINT8 cb); void update_slider_pos(); void vblank_state_changed(screen_device &screen, bool vblank_state); frame_data ¤t_frame(); void read_track_data(); static void *read_async_static(void *param, int threadid); void process_track_data(); void config_load(int config_type, xml_data_node *parentnode); void config_save(int config_type, xml_data_node *parentnode); // configuration laserdisc_get_disc_delegate m_getdisc_callback; laserdisc_audio_delegate m_audio_callback; // audio streaming callback const char * m_screen_name; // name of the screen device laserdisc_overlay_config m_orig_config; // original overlay configuration UINT32 m_overwidth; // overlay screen width UINT32 m_overheight; // overlay screen height rectangle m_overclip; // overlay visarea screen_update_ind16_delegate m_overupdate_ind16; // overlay update delegate screen_update_rgb32_delegate m_overupdate_rgb32; // overlay update delegate // disc parameters chd_file * m_disc; // handle to the disc itself dynamic_buffer m_vbidata; // pointer to precomputed VBI data int m_width; // width of video int m_height; // height of video UINT32 m_fps_times_1million; // frame rate of video int m_samplerate; // audio samplerate int m_readresult; // result of the most recent read UINT32 m_chdtracks; // number of tracks in the CHD avhuff_decompress_config m_avhuff_config; // decompression configuration // async operations osd_work_queue * m_work_queue; // work queue UINT32 m_queued_hunknum; // queued hunk // core states UINT8 m_audiosquelch; // audio squelch state: bit 0 = audio 1, bit 1 = audio 2 UINT8 m_videosquelch; // video squelch state: bit 0 = on/off UINT8 m_fieldnum; // field number (0 or 1) INT32 m_curtrack; // current track at this end of this vsync UINT32 m_maxtrack; // maximum track number attoseconds_t m_attospertrack; // attoseconds per track, or 0 if not moving attotime m_sliderupdate; // time of last slider update // video data screen_device * m_screen; // pointer to the screen device frame_data m_frame[3]; // circular list of frames UINT8 m_videoindex; // index of the current video buffer bitmap_yuy16 m_emptyframe; // blank frame // audio data sound_stream * m_stream; INT16 * m_audiobuffer[2]; // buffer for audio samples UINT32 m_audiobufsize; // size of buffer UINT32 m_audiobufin; // input index UINT32 m_audiobufout; // output index UINT32 m_audiocursamples; // current samples this track UINT32 m_audiomaxsamples; // maximum samples per track // metadata vbi_metadata m_metadata[2]; // metadata parsed from the stream, for each field // video updating bool m_videoenable; // is video enabled? render_texture * m_videotex; // texture for the video palette_t * m_videopalette; // palette for the video // overlays bool m_overenable; // is the overlay enabled? screen_bitmap m_overbitmap[2]; // overlay bitmaps int m_overindex; // index of the overlay bitmap render_texture * m_overtex; // texture for the overlay }; // iterator - interface iterator works for subclasses too typedef device_interface_iterator laserdisc_device_iterator; //************************************************************************** // INLINE FUNCTIONS //************************************************************************** //------------------------------------------------- // is_start_of_frame - return true if this is // the start of a frame //------------------------------------------------- inline bool laserdisc_device::is_start_of_frame(const vbi_metadata &vbi) { // is it not known if the white flag or the presence of a frame code // determines the start of frame; the former seems to be the "official" // way, but the latter seems to be the practical implementation return (vbi.white || (vbi.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE); } //------------------------------------------------- // frame_from_metadata - return the frame number // encoded in the metadata, if present, or // FRAME_NOT_PRESENT //------------------------------------------------- inline int laserdisc_device::frame_from_metadata(const vbi_metadata &metadata) { if ((metadata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) return VBI_CAV_PICTURE(metadata.line1718); else if (metadata.line1718 == VBI_CODE_LEADIN) return FRAME_LEAD_IN; else if (metadata.line1718 == VBI_CODE_LEADOUT) return FRAME_LEAD_OUT; return FRAME_NOT_PRESENT; } //------------------------------------------------- // chapter_from_metadata - return the chapter // number encoded in the metadata, if present, // or CHAPTER_NOT_PRESENT //------------------------------------------------- inline int laserdisc_device::chapter_from_metadata(const vbi_metadata &metadata) { if ((metadata.line1718 & VBI_MASK_CHAPTER) == VBI_CODE_CHAPTER) return VBI_CHAPTER(metadata.line1718); else if (metadata.line1718 == VBI_CODE_LEADIN) return CHAPTER_LEAD_IN; else if (metadata.line1718 == VBI_CODE_LEADOUT) return CHAPTER_LEAD_OUT; return CHAPTER_NOT_PRESENT; } #endif