mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-15 22:38:09 +00:00
a2aba2f18e
svn-id: r38592
317 lines
6.9 KiB
C++
317 lines
6.9 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* 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 General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU 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.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
/* Polled player, mostly for PCM-based thingies (which _can_ poll, after all) */
|
|
|
|
#include "sci/include/sfx_player.h"
|
|
#include "sci/sfx/softseq.h"
|
|
#include "sci/sfx/mixer.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
static song_iterator_t *play_it;
|
|
static int play_paused = 0;
|
|
static sfx_softseq_t *seq;
|
|
static int volume = 100;
|
|
static sfx_timestamp_t new_timestamp;
|
|
static int new_song = 0;
|
|
|
|
/* The time counter is used to determine how close to the end of a tick we are.
|
|
** For each frame played, it is decreased by 60. */
|
|
#define TIME_INC 60
|
|
static int time_counter = 0;
|
|
|
|
static void
|
|
pp_tell_synth(int buf_nr, byte *buf) {
|
|
seq->handle_command(seq, buf[0], buf_nr - 1, buf + 1);
|
|
}
|
|
|
|
|
|
/*----------------------*/
|
|
/* Mixer implementation */
|
|
/*----------------------*/
|
|
int
|
|
ppf_poll(sfx_pcm_feed_t *self, byte *dest, int size) {
|
|
int written = 0;
|
|
byte buf[4];
|
|
int buf_nr;
|
|
|
|
if (!play_it)
|
|
return 0;
|
|
|
|
if (play_paused)
|
|
return 0;
|
|
|
|
while (written < size) {
|
|
int can_play;
|
|
int do_play;
|
|
|
|
while (time_counter <= TIME_INC) {
|
|
int next_stat = songit_next(&play_it,
|
|
&(buf[0]), &buf_nr,
|
|
IT_READER_MASK_ALL
|
|
| IT_READER_MAY_FREE
|
|
| IT_READER_MAY_CLEAN);
|
|
|
|
switch (next_stat) {
|
|
case SI_PCM:
|
|
sfx_play_iterator_pcm(play_it, 0);
|
|
break;
|
|
|
|
case SI_FINISHED:
|
|
songit_free(play_it);
|
|
play_it = NULL;
|
|
return written; /* We're done... */
|
|
|
|
case SI_IGNORE:
|
|
case SI_LOOP:
|
|
case SI_RELATIVE_CUE:
|
|
case SI_ABSOLUTE_CUE:
|
|
break; /* Boooring... .*/
|
|
|
|
case 0: /* MIDI command */
|
|
|
|
seq->handle_command(seq, buf[0], buf_nr - 1, buf + 1);
|
|
break;
|
|
|
|
default:
|
|
time_counter += next_stat * seq->pcm_conf.rate;
|
|
}
|
|
}
|
|
|
|
can_play = time_counter / TIME_INC;
|
|
do_play = (can_play > (size - written)) ? (size - written) : can_play;
|
|
|
|
time_counter -= do_play * TIME_INC;
|
|
|
|
seq->poll(seq, dest + written * self->frame_size, do_play);
|
|
written += do_play;
|
|
}
|
|
|
|
return size; /* Apparently, we wrote all that was requested */
|
|
}
|
|
|
|
void
|
|
ppf_destroy(sfx_pcm_feed_t *self) {
|
|
/* no-op */
|
|
}
|
|
|
|
int
|
|
ppf_get_timestamp(sfx_pcm_feed_t *self, sfx_timestamp_t *timestamp) {
|
|
if (!new_song)
|
|
return PCM_FEED_IDLE;
|
|
|
|
/* Otherwise, we have a timestamp: */
|
|
|
|
*timestamp = new_timestamp;
|
|
new_song = 0;
|
|
return PCM_FEED_TIMESTAMP;
|
|
}
|
|
|
|
extern sfx_player_t sfx_player_polled;
|
|
static
|
|
sfx_pcm_feed_t pcmfeed = {
|
|
ppf_poll,
|
|
ppf_destroy,
|
|
ppf_get_timestamp,
|
|
NULL,
|
|
{0, 0, 0},
|
|
"polled-player-feed",
|
|
0,
|
|
0 /* filled in by the mixer */
|
|
};
|
|
|
|
/*=======================*/
|
|
/* Player implementation */
|
|
/*=======================*/
|
|
|
|
|
|
/*--------------------*/
|
|
/* API implementation */
|
|
/*--------------------*/
|
|
|
|
static void
|
|
pp_timer_callback(void) {
|
|
/* Hey, we're polled anyway ;-) */
|
|
}
|
|
|
|
static int
|
|
pp_set_option(char *name, char *value) {
|
|
return SFX_ERROR;
|
|
}
|
|
|
|
static int
|
|
pp_init(ResourceManager *resmgr, int expected_latency) {
|
|
resource_t *res = NULL, *res2 = NULL;
|
|
|
|
if (!mixer)
|
|
return SFX_ERROR;
|
|
|
|
/* FIXME Temporary hack to detect Amiga games. */
|
|
if (!Common::File::exists("bank.001"))
|
|
seq = sfx_find_softseq(NULL);
|
|
else
|
|
seq = sfx_find_softseq("amiga");
|
|
|
|
if (!seq) {
|
|
sciprintf("[sfx:seq:polled] Initialisation failed: Could not find software sequencer\n");
|
|
return SFX_ERROR;
|
|
}
|
|
|
|
if (seq->patch_nr != SFX_SEQ_PATCHFILE_NONE) {
|
|
res = scir_find_resource(resmgr, sci_patch, seq->patch_nr, 0);
|
|
}
|
|
|
|
if (seq->patch2_nr != SFX_SEQ_PATCHFILE_NONE) {
|
|
res2 = scir_find_resource(resmgr, sci_patch, seq->patch2_nr, 0);
|
|
}
|
|
|
|
if (seq->init(seq,
|
|
(res) ? res->data : NULL,
|
|
(res) ? res->size : 0,
|
|
(res2) ? res2->data : NULL,
|
|
(res2) ? res2->size : 0)) {
|
|
sciprintf("[sfx:seq:polled] Initialisation failed: Sequencer '%s', v%s failed to initialise\n",
|
|
seq->name, seq->version);
|
|
return SFX_ERROR;
|
|
}
|
|
|
|
pcmfeed.conf = seq->pcm_conf;
|
|
|
|
seq->set_volume(seq, volume);
|
|
mixer->subscribe(mixer, &pcmfeed);
|
|
|
|
sfx_player_polled.polyphony = seq->polyphony;
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_add_iterator(song_iterator_t *it, GTimeVal start_time) {
|
|
song_iterator_t *old = play_it;
|
|
|
|
SIMSG_SEND(it, SIMSG_SET_PLAYMASK(seq->playmask));
|
|
SIMSG_SEND(it, SIMSG_SET_RHYTHM(seq->play_rhythm));
|
|
|
|
if (play_it == NULL)
|
|
seq->allstop(seq);
|
|
|
|
play_it = sfx_iterator_combine(play_it, it);
|
|
|
|
seq->set_volume(seq, volume);
|
|
|
|
/* The check must happen HERE, and not at the beginning of the
|
|
function, to avoid a race condition with the mixer. */
|
|
if (old == NULL) {
|
|
new_timestamp = sfx_new_timestamp(start_time.tv_sec,
|
|
start_time.tv_usec,
|
|
seq->pcm_conf.rate);
|
|
/* ASAP otherwise */
|
|
time_counter = 0;
|
|
new_song = 1;
|
|
}
|
|
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_fade_out(void) {
|
|
fprintf(stderr, __FILE__": Attempt to fade out- not implemented yet\n");
|
|
return SFX_ERROR;
|
|
}
|
|
|
|
static int
|
|
pp_stop(void) {
|
|
song_iterator_t *it = play_it;
|
|
|
|
play_it = NULL;
|
|
fprintf(stderr, "[play] Now stopping it %p\n", (void *)it);
|
|
if (it)
|
|
songit_free(it);
|
|
|
|
seq->allstop(seq);
|
|
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_send_iterator_message(song_iterator_message_t msg) {
|
|
if (!play_it)
|
|
return SFX_ERROR;
|
|
|
|
songit_handle_message(&play_it, msg);
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_pause(void) {
|
|
play_paused = 1;
|
|
seq->set_volume(seq, 0);
|
|
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_resume(void) {
|
|
if (!play_it) {
|
|
play_paused = 0;
|
|
return SFX_OK; /* Nothing to resume */
|
|
}
|
|
|
|
if (play_paused)
|
|
new_song = 1; /* Fake starting a new song, re-using the old
|
|
** time stamp (now long in the past) to indicate
|
|
** resuming ASAP */
|
|
|
|
play_paused = 0;
|
|
seq->set_volume(seq, volume);
|
|
return SFX_OK;
|
|
}
|
|
|
|
static int
|
|
pp_exit(void) {
|
|
seq->exit(seq);
|
|
songit_free(play_it);
|
|
play_it = NULL;
|
|
|
|
return SFX_OK;
|
|
}
|
|
|
|
sfx_player_t sfx_player_polled = {
|
|
"polled",
|
|
"0.1",
|
|
&pp_set_option,
|
|
&pp_init,
|
|
&pp_add_iterator,
|
|
&pp_fade_out,
|
|
&pp_stop,
|
|
&pp_send_iterator_message,
|
|
&pp_pause,
|
|
&pp_resume,
|
|
&pp_exit,
|
|
&pp_timer_callback,
|
|
&pp_tell_synth,
|
|
0 /* polyphony */
|
|
};
|