summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/imagedev/cassette.cpp
blob: 313ef6b42a21237a398a823092c399fac9f8f728 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11

                                                    



                                                                      
                                                    



                                                                      
                             
                     
                      
 
                             

                                                        






                                                                    
                                                                                                                           
                                                                                                 
                                               
                            





                                  
                       
                                            
                               
                                       
                            
 

 




                                                   
 
 
 







                                                    


                                                                                           
 




                                      



                                                                      
                                         
 
                                                                  
                             
                                                                      





                             
                                    
 
                                                                
 
                          
         
                                                                                                    
 
                                                         
                                     
                                                                                                                   


                                   
                                         
                         
                                                                                                        
                                                                
                                                             

                                                          
                                                                                                                            

                                                              




                                                                                                                            


                              
                                          
         
                                   

 
                                                                                   
 

                                 
                            


                                                                 
                                 
         

                                    




         
                                     
 


                            

                         

                                                      
                                                                                      





                            
                                                
 
                                                                                         
         
                         



                                         
                                                       



         
                                            
 
                                     
 
                          
                                                                                                          




                        
                                          
 

                                 
                                             


                                                                    


















                                                    

 
                                                         
 

                      
                 
 
                              









                               
                                       









                                             
                          







                                                                      
                                          
 
                                  
                             
                                  
                    

 




                                                                                           
                                       
 

                           

                              
                                                
                         
 
                                                    

                                       
                                                                                                                                                                    







                                      
                                                     
                                                                                                                                   
                                          
                                                        

                                                       
                                                                
                                                                  
                                              
                                                                                                     
                                        
                                                       

                                 
                                                                                                                                      


                                                    
                                                







                                                                
                                                             

                                

                                                                
 



                                                         



                               



















                                                          




                               
                                         
 
                                                               

                                                                 

                                    
                                   
                             

                                                                
                                                              


 

                                                   
                                      
                                                   
 
                                                 
 





                                                                            
         

















                                                                                                 
                                                                                                        
                                                                                   

                                                                     








                                                                       
                 
                                       
                         



                                                                                                                          


                         
                      
 
// license:BSD-3-Clause
// copyright-holders:Nathan Woods, Miodrag Milanovic
/*********************************************************************

    cassette.c

    Interface to the cassette image abstraction code

*********************************************************************/

#include "emu.h"
#include "formats/imageutl.h"
#include "cassette.h"
#include "ui/uimain.h"

#define VERBOSE             0
#define LOG(x) do { if (VERBOSE) logerror x; } while (0)

// device type definition
const device_type CASSETTE = &device_creator<cassette_image_device>;

//-------------------------------------------------
//  cassette_image_device - constructor
//-------------------------------------------------

cassette_image_device::cassette_image_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: device_t(mconfig, CASSETTE, "Cassette", tag, owner, clock, "cassette_image", __FILE__),
	device_image_interface(mconfig, *this),
	m_cassette(nullptr),
	m_state(CASSETTE_STOPPED),
	m_position(0),
	m_position_time(0),
	m_value(0),
	m_channel(0),
	m_speed(0),
	m_direction(0),
	m_formats(cassette_default_formats),
	m_create_opts(nullptr),
	m_default_state(CASSETTE_PLAY),
	m_interface(nullptr)
{
}

//-------------------------------------------------
//  cassette_image_device - destructor
//-------------------------------------------------

cassette_image_device::~cassette_image_device()
{
}

//-------------------------------------------------
//  device_config_complete - perform any
//  operations now that the configuration is
//  complete
//-------------------------------------------------

void cassette_image_device::device_config_complete()
{
	m_extension_list[0] = '\0';
	for (int i = 0; m_formats[i]; i++ )
		image_specify_extension( m_extension_list, 256, m_formats[i]->extensions );

	// set brief and instance name
	update_names();
}


/*********************************************************************
    cassette IO
*********************************************************************/

bool cassette_image_device::is_motor_on()
{
	if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_STOPPED)
		return FALSE;
	if ((m_state & CASSETTE_MASK_MOTOR) != CASSETTE_MOTOR_ENABLED)
		return FALSE;
	return TRUE;
}



void cassette_image_device::update()
{
	double cur_time = device().machine().time().as_double();

	if (is_motor_on())
	{
		double new_position = m_position + (cur_time - m_position_time)*m_speed*m_direction;

		switch(m_state & CASSETTE_MASK_UISTATE) {
		case CASSETTE_RECORD:
			cassette_put_sample(m_cassette, m_channel, m_position, new_position - m_position, m_value);
			break;

		case CASSETTE_PLAY:
			if ( m_cassette )
			{
				cassette_get_sample(m_cassette, m_channel, new_position, 0.0, &m_value);
				/* See if reached end of tape */
				double length = get_length();
				if (new_position > length)
				{
					m_state = (cassette_state)(( m_state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED);
					new_position = length;
				}
				else if (new_position < 0)
				{
					m_state = (cassette_state)(( m_state & ~CASSETTE_MASK_UISTATE ) | CASSETTE_STOPPED);
					new_position = 0;
				}
			}
			break;
		}
		m_position = new_position;
	}
	m_position_time = cur_time;
}

void cassette_image_device::change_state(cassette_state state, cassette_state mask)
{
	cassette_state new_state;

	new_state = m_state;
	new_state = (cassette_state)(new_state & ~mask);
	new_state = (cassette_state)(new_state | (state & mask));

	if (new_state != m_state)
	{
		update();
		m_state = new_state;
	}
}



double cassette_image_device::input()
{
	INT32 sample;
	double double_value;

	update();
	sample = m_value;
	double_value = sample / ((double) 0x7FFFFFFF);

	LOG(("cassette_input(): time_index=%g value=%g\n", m_position, double_value));

	return double_value;
}



void cassette_image_device::output(double value)
{
	if (((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD) && (m_value != value))
	{
		update();

		value = MIN(value, 1.0);
		value = MAX(value, -1.0);

		m_value = (INT32) (value * 0x7FFFFFFF);
	}
}


double cassette_image_device::get_position()
{
	double position = m_position;

	if (is_motor_on())
		position += (device().machine().time().as_double() - m_position_time)*m_speed*m_direction;
	return position;
}



double cassette_image_device::get_length()
{
	struct CassetteInfo info;

	cassette_get_info(m_cassette, &info);
	return ((double) info.sample_count) / info.sample_frequency;
}

void cassette_image_device::set_channel(int channel)
{
	m_channel = channel;
}

void cassette_image_device::set_speed(double speed)
{
	m_speed = speed;
}

void cassette_image_device::go_forward()
{
	m_direction = 1;
}

void cassette_image_device::go_reverse()
{
	m_direction = -1;
}


void cassette_image_device::seek(double time, int origin)
{
	double length;

	update();

	length = get_length();

	switch(origin) {
	case SEEK_SET:
		break;

	case SEEK_END:
		time += length;
		break;

	case SEEK_CUR:
		time += get_position();
		break;
	}

	/* clip position into legal bounds */
	if (time < 0)
		time = 0;
	else
	if (time > length)
		time = length;

	m_position = time;
}



/*********************************************************************
    cassette device init/load/unload/specify
*********************************************************************/

void cassette_image_device::device_start()
{
	/* set to default state */
	m_cassette = nullptr;
	m_state = m_default_state;
	m_value = 0;
}

bool cassette_image_device::call_create(int format_type, option_resolution *format_options)
{
	return call_load();
}

bool cassette_image_device::call_load()
{
	casserr_t err;
	int cassette_flags;
	const char *extension;
	int is_writable;
	device_image_interface *image = nullptr;
	interface(image);

	if ((has_been_created()) || (length() == 0))
	{
		/* creating an image */
		err = cassette_create((void *)image, &image_ioprocs, &wavfile_format, m_create_opts, CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT, &m_cassette);
		if (err)
			goto error;
	}
	else
	{
		/* opening an image */
		do
		{
			is_writable = !is_readonly();
			cassette_flags = is_writable ? (CASSETTE_FLAG_READWRITE|CASSETTE_FLAG_SAVEONEXIT) : CASSETTE_FLAG_READONLY;
			std::string fname;
			if (software_entry()==nullptr) {
				extension = filetype();
			} else {
				fname = m_mame_file->filename();
				int loc = fname.find_last_of('.');
				if (loc!=-1) {
					extension = fname.substr(loc + 1,fname.length()-loc).c_str();
				} else {
					extension = "";
				}
			}
			err = cassette_open_choices((void *)image, &image_ioprocs, extension, m_formats, cassette_flags, &m_cassette);

			/* this is kind of a hack */
			if (err && is_writable)
				make_readonly();
		}
		while(err && is_writable);

		if (err)
			goto error;
	}

	/* set to default state, but only change the UI state */
	change_state(m_default_state, CASSETTE_MASK_UISTATE);

	/* reset the position */
	m_position = 0.0;
	m_position_time = device().machine().time().as_double();

	/* default channel to 0, speed multiplier to 1 */
	m_channel = 0;
	m_speed = 1;
	m_direction = 1;

	return IMAGE_INIT_PASS;

error:
	image_error_t imgerr = IMAGE_ERROR_UNSPECIFIED;
	switch(err)
	{
		case CASSETTE_ERROR_INTERNAL:
			imgerr = IMAGE_ERROR_INTERNAL;
			break;
		case CASSETTE_ERROR_UNSUPPORTED:
			imgerr = IMAGE_ERROR_UNSUPPORTED;
			break;
		case CASSETTE_ERROR_OUTOFMEMORY:
			imgerr = IMAGE_ERROR_OUTOFMEMORY;
			break;
		case CASSETTE_ERROR_INVALIDIMAGE:
			imgerr = IMAGE_ERROR_INVALIDIMAGE;
			break;
		default:
			imgerr = IMAGE_ERROR_UNSPECIFIED;
			break;
	}
	image->seterror(imgerr, "" );
	return IMAGE_INIT_FAIL;
}



void cassette_image_device::call_unload()
{
	/* if we are recording, write the value to the image */
	if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_RECORD)
		update();

	/* close out the cassette */
	cassette_close(m_cassette);
	m_cassette = nullptr;

	/* set to default state, but only change the UI state */
	change_state(CASSETTE_STOPPED, CASSETTE_MASK_UISTATE);
}


//-------------------------------------------------
//  display a small tape animation, with the
//  current position in the tape image
//-------------------------------------------------

std::string cassette_image_device::call_display()
{
	const int ANIMATION_FPS = 1;

	std::string result;

	// only show the image when a cassette is loaded and the motor is on
	if (exists() && is_motor_on())
	{
		int n;
		double position, length;
		cassette_state uistate;
		static const char *shapes[] = { u8"\u2500", u8"\u2572", u8"\u2502", u8"\u2571" };

		// figure out where we are in the cassette
		position = get_position();
		length = get_length();
		uistate = (cassette_state)(get_state() & CASSETTE_MASK_UISTATE);

		// choose which frame of the animation we are at
		n = ((int)position / ANIMATION_FPS) % ARRAY_LENGTH(shapes);

		// play or record
		const char *status_icon = (uistate == CASSETTE_PLAY)
			? u8"\u25BA"
			: u8"\u25CF";

		// Since you can have anything in a BDF file, we will use crude ascii characters instead
		result = string_format("%s %s %02d:%02d (%04d) [%02d:%02d (%04d)]",
			shapes[n],                  // animation
			status_icon,                // play or record
			((int)position / 60),
			((int)position % 60),
			(int)position,
			((int)length / 60),
			((int)length % 60),
			(int)length);

		// make sure tape stops at end when playing
		if ((m_state & CASSETTE_MASK_UISTATE) == CASSETTE_PLAY)
		{
			if (m_cassette)
			{
				if (get_position() > get_length())
				{
					m_state = (cassette_state)((m_state & ~CASSETTE_MASK_UISTATE) | CASSETTE_STOPPED);
				}
			}
		}
	}
	return result;
}