[wrap]
/*
CD-DA "Red Book" audio sound hardware handler
Relies on the actual CD logic and reading in cdrom.c.
*/
#include "sndintrf.h"
#include "streams.h"
#include "cpuintrf.h"
#include "cdrom.h"
#include "cdda.h"
typedef struct _cdda_info cdda_info;
struct _cdda_info
{
sound_stream * stream;
cdrom_file * disc;
INT8 audio_playing, audio_pause, audio_ended_normally;
UINT32 audio_lba, audio_length;
UINT8 * audio_cache;
UINT32 audio_samples;
UINT32 audio_bptr;
};
#define MAX_SECTORS ( 4 )
static void get_audio_data(cdda_info *info, stream_sample_t *bufL, stream_sample_t *bufR, UINT32 samples_wanted);
/*-------------------------------------------------
cdda_update - stream update callback
-------------------------------------------------*/
static STREAM_UPDATE( cdda_update )
{
cdda_info *info = param;
get_audio_data(info, &outputs[0][0], &outputs[1][0], samples);
}
/*-------------------------------------------------
SND_START( cdda ) - audio start callback
-------------------------------------------------*/
static SND_START( cdda )
{
const struct CDDAinterface *intf;
cdda_info *info;
info = auto_malloc(sizeof(*info));
memset(info, 0, sizeof(*info));
/* allocate an audio cache */
info->audio_cache = auto_malloc( CD_MAX_SECTOR_DATA * MAX_SECTORS );
intf = config;
info->stream = stream_create(device, 0, 2, 44100, info, cdda_update);
state_save_register_device_item( device, 0, info->audio_playing );
state_save_register_device_item( device, 0, info->audio_pause );
state_save_register_device_item( device, 0, info->audio_ended_normally );
state_save_register_device_item( device, 0, info->audio_lba );
state_save_register_device_item( device, 0, info->audio_length );
state_save_register_device_item_pointer( device, 0, info->audio_cache, CD_MAX_SECTOR_DATA * MAX_SECTORS );
state_save_register_device_item( device, 0, info->audio_samples );
state_save_register_device_item( device, 0, info->audio_bptr );
return info;
}
/*-------------------------------------------------
cdda_set_cdrom - set the CD-ROM file for the
given CDDA stream
-------------------------------------------------*/
void cdda_set_cdrom(int num, void *file)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
info->disc = (cdrom_file *)file;
}
/*-------------------------------------------------
cdda_num_from_cdrom - find the CDDA stream
that references the given CD-ROM file
-------------------------------------------------*/
int cdda_num_from_cdrom(void *file)
{
int index = 0;
do
{
cdda_info *info;
if (!sndti_exists(SOUND_CDDA, index))
return -1;
info = sndti_token(SOUND_CDDA, index);
if (info == NULL)
return -1;
if (info->disc == file)
return index;
index++;
} while (1);
}
/*-------------------------------------------------
cdda_start_audio - begin playback of a Red
Book audio track
-------------------------------------------------*/
void cdda_start_audio(int num, UINT32 startlba, UINT32 numblocks)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
stream_update(info->stream);
info->audio_playing = TRUE;
info->audio_pause = FALSE;
info->audio_ended_normally = FALSE;
info->audio_lba = startlba;
info->audio_length = numblocks;
}
/*-------------------------------------------------
cdda_stop_audio - stop playback of a Red Book
audio track
-------------------------------------------------*/
void cdda_stop_audio(int num)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
stream_update(info->stream);
info->audio_playing = FALSE;
info->audio_ended_normally = TRUE;
}
/*-------------------------------------------------
cdda_pause_audio - pause/unpause playback of
a Red Book audio track
-------------------------------------------------*/
void cdda_pause_audio(int num, int pause)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
stream_update(info->stream);
info->audio_pause = pause;
}
/*-------------------------------------------------
cdda_get_audio_lba - returns the current LBA
(physical sector) during Red Book playback
-------------------------------------------------*/
UINT32 cdda_get_audio_lba(int num)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
stream_update(info->stream);
return info->audio_lba;
}
/*-------------------------------------------------
cdda_audio_active - returns Red Book audio
playback status
-------------------------------------------------*/
int cdda_audio_active(int num)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
stream_update(info->stream);
return info->audio_playing;
}
/*-------------------------------------------------
cdda_audio_paused - returns if Red Book
playback is paused
-------------------------------------------------*/
int cdda_audio_paused(int num)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
return info->audio_pause;
}
/*-------------------------------------------------
cdda_audio_ended - returns if a Red Book
track reached it's natural end
-------------------------------------------------*/
int cdda_audio_ended(int num)
{
cdda_info *info = sndti_token(SOUND_CDDA, num);
return info->audio_ended_normally;
}
/*-------------------------------------------------
get_audio_data - reads Red Book data off
the disc if playback is in progress and
converts it to 2 16-bit 44.1 kHz streams
-------------------------------------------------*/
static void get_audio_data(cdda_info *info, stream_sample_t *bufL, stream_sample_t *bufR, UINT32 samples_wanted)
{
int i, sectoread, remaining;
INT16 *audio_cache = (INT16 *) info->audio_cache;
/* if no file, audio not playing, audio paused, or out of disc data,
just zero fill */
if (!info->disc || !info->audio_playing || info->audio_pause || (!info->audio_length && !info->audio_samples))
{
if( info->disc && info->audio_playing && !info->audio_pause && !info->audio_length )
{
info->audio_playing = FALSE;
info->audio_ended_normally = TRUE;
}
memset(bufL, 0, sizeof(stream_sample_t)*samples_wanted);
memset(bufR, 0, sizeof(stream_sample_t)*samples_wanted);
return;
}
/* if we've got enough samples, just feed 'em out */
if (samples_wanted <= info->audio_samples)
{
for (i = 0; i < samples_wanted; i++)
{
*bufL++ = audio_cache[ info->audio_bptr++ ];
*bufR++ = audio_cache[ info->audio_bptr++ ];
}
info->audio_samples -= samples_wanted;
return;
}
/* we don't have enough, so first feed what we've got */
for (i = 0; i < info->audio_samples; i++)
{
*bufL++ = audio_cache[ info->audio_bptr++ ];
*bufR++ = audio_cache[ info->audio_bptr++ ];
}
/* remember how much left for later */
remaining = samples_wanted - info->audio_samples;
/* reset the buffer and get what we can from the disc */
info->audio_samples = 0;
if (info->audio_length >= MAX_SECTORS)
{
sectoread = MAX_SECTORS;
}
else
{
sectoread = info->audio_length;
}
for (i = 0; i < sectoread; i++)
{
cdrom_read_data(info->disc, info->audio_lba, &info->audio_cache[CD_MAX_SECTOR_DATA*i], CD_TRACK_AUDIO);
info->audio_lba++;
}
info->audio_samples = (CD_MAX_SECTOR_DATA*sectoread)/4;
info->audio_length -= sectoread;
/* CD-DA data on the disc is big-endian, flip if we're not */
#ifdef LSB_FIRST
for( i = 0; i < info->audio_samples * 2; i++ )
{
audio_cache[ i ] = BIG_ENDIANIZE_INT16( audio_cache[ i ] );
}
#endif
/* reset feedout ptr */
info->audio_bptr = 0;
/* we've got data, feed it out by calling ourselves recursively */
get_audio_data(info, bufL, bufR, remaining);
}
/**************************************************************************
* Generic get_info
**************************************************************************/
static SND_SET_INFO( cdda )
{
switch (state)
{
/* no parameters to set */
}
}
SND_GET_INFO( cdda )
{
switch (state)
{
/* --- the following bits of info are returned as 64-bit signed integers --- */
/* --- the following bits of info are returned as pointers to data or functions --- */
case SNDINFO_PTR_SET_INFO: info->set_info = SND_SET_INFO_NAME( cdda ); break;
case SNDINFO_PTR_START: info->start = SND_START_NAME( cdda ); break;
case SNDINFO_PTR_STOP: /* nothing */ break;
case SNDINFO_PTR_RESET: /* nothing */ break;
/* --- the following bits of info are returned as NULL-terminated strings --- */
case SNDINFO_STR_NAME: strcpy(info->s, "CD/DA"); break;
case SNDINFO_STR_CORE_FAMILY: strcpy(info->s, "CD Audio"); break;
case SNDINFO_STR_CORE_VERSION: strcpy(info->s, "1.0"); break;
case SNDINFO_STR_CORE_FILE: strcpy(info->s, __FILE__); break;
case SNDINFO_STR_CORE_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
}
}