home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio Version 4.94
/
audioversion4.94knowledgemediaresourcelibraryoctober1994.iso
/
amiga
/
utils
/
exp_iv
/
play.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-05-16
|
25KB
|
642 lines
/* play.c */
/* $Author: Espie $
* $Date: 91/05/16 15:05:39 $
* $Revision: 1.46 $
* $Log: play.c,v $
* Revision 1.46 91/05/16 15:05:39 Espie
* Conditional asm, else use stub.
*
* Revision 1.45 91/05/12 22:39:01 Espie
* Corrected a stupid bug in change_speed:
* with speed 0, I was going through the OTHER cases,
* with expected results (MUCH too fast).
*
* Revision 1.44 91/05/12 19:55:47 Espie
* Split partly to commands.
* Corrected oneshot logic, added new periods handling.
* Other minor changes.
*
* Revision 1.43 91/05/12 15:59:33 Espie
* Tried to correct the oneshot bug.
*
* Revision 1.42 91/05/11 14:58:37 Espie
* Tried to correct the ``eaten note'' problem. Not done yet.
*
* Revision 1.41 91/05/09 17:37:01 Espie
* Non standard speed modes. (Old and New).
* Hopefully a temporary kludge, with better
* loader, we could magically determine what
* each speed change means...
*
* Revision 1.40 91/05/08 15:52:12 Espie
* Apparent problems with volume latch, nothing changed,
* problems were not coming from here.
*
* Revision 1.39 91/05/07 12:13:15 Espie
* Speed or not speed ??
*
* Revision 1.38 91/05/07 02:53:30 Espie
* Corrected oneshot bug.
*
* Revision 1.37 91/05/06 15:15:35 Espie
* Changed some more stuff from public to private status.
*
* Revision 1.36 91/05/05 19:07:14 Espie
* New private fields, now we should manage all speed changes
* by ourselves.
*
* Revision 1.35 91/05/05 15:40:15 Espie
* Semi-automatic conversion. Works mostly
* (bug in run the gauntlet at measure 6).
*
* Revision 1.34 91/05/02 23:33:09 Espie
* *** empty log message ***
*
* Revision 1.33 91/05/02 01:32:31 Espie
* New automaton for the player, simpler.
*
* Revision 1.32 91/04/30 16:52:58 Espie
* Corrected speed bug: we only
* set the new speed after all channels have
* been processed.
*
* Revision 1.31 91/04/30 01:47:06 Espie
* Fixed out some minor problems: now speed 0 is recognized as an end.
* On start at a new pos, we send a ON_PATTERN message for people
* wanting to update the pattern number dumbly.
* Easier for us than for them !
*
* Revision 1.30 91/04/30 00:35:50 Espie
* Stable version III.
*
* Revision 1.29 91/04/29 15:06:49 Espie
* Moved control of start/stop actions to interrupt.c
*
* Revision 1.28 91/04/28 22:52:54 Espie
* Tranpose added.
*
* Revision 1.27 91/04/28 20:35:04 Espie
* New check for speed (finespeed adjust).
*
* Revision 1.26 91/04/27 20:48:37 Espie
* Dual speed tempo.
*
* Revision 1.25 91/04/27 16:44:27 Espie
* Optimized again.
* Changed part of the control, now all the commands are local functions.
* Tried to optimize size of parameters, but Lattice won't let me... bug, Bug, BUG !
*
* Revision 1.24 91/04/27 04:00:52 Espie
* many new optimizations, some cleanup.
* (constant folding, separation of arpeggio in 3 commands, etc).
*
* Revision 1.23 91/04/27 00:25:00 Espie
* New timing routine (works).
* Now times itself.
* First try at constant folding for cases.
* Optimize further...
*
* Revision 1.22 91/04/26 16:34:36 Espie
* Completely new timing, far from perfect yet.
* The interrupt routine is now a 3-state automaton.
* The timing relies entirely on the wait() function, which
* waits for intervals between each call. Smallest wait should
* be reparameterized.
* Should add an ``efficiency'' count: what percentage of the CPU time
* do we use ? Since nobody is able to time us, we should time ourselves.
*
* Revision 1.21 91/04/26 01:29:16 Espie
* Refixed the vibrato command once again.
* Plays right, don't touch :-(.
*
*
* Revision 1.20 91/04/25 02:05:52 Espie
* Corrected vibrato.... should work correctly.
* Added filter control (good idea ?).
*
* Revision 1.19 91/04/24 23:40:13 Espie
* Fixed the vibrato routine. This is now the correct
* depth/speed.
*
* Revision 1.18 91/04/24 15:26:07 Espie
* Fixed up small problem, which had no chance to appear before.
* The instrument_reload command does reset everything, including
* the period now, since we can multitask with other users...
*
* Revision 1.17 91/04/23 21:24:54 Espie
* Totally revised logic for the automaton.
* It is now much easier to set up anything through the interrupt.
* While it is in stop mode, the song pointers don't have to be valid.
* The oneshot setting has been thoroughly tested, it seems necessary
* to stop the audio hardware at that point (cleanup is not fast enough).
* Added the reload_instrument stuff to try to restart from a pause.
* Also, finetunes are now implemented.
* Parameters not yet reduced to minimum size, logic between note and period
* not completely alright too.
*
* Revision 1.16 91/04/21 20:04:17 Espie
* Handles arpeggio, apparently correctly.
* Has a crude ``oversampling'' mechanism.
* Does know something about notes.
* Not perfect in its handling of finetune instruments (examples ?).
*
* Revision 1.15 91/04/21 12:11:47 Espie
* Stable version, known as bunch II.
* Also features ``right'' log description.
*
* Revision 1.14 91/04/21 11:31:56 Espie
* Try out for automatic log messages
*
* Revision 1.13 91/04/21 11:27:13 Espie
* Added automatic log messages.
*
* Revision 1.12 91/04/21 11:16:17 Espie
* Simplified player.
*
* Revision 1.11 91/04/20 18:12:59 Espie
* Improved player:
* simplified calls to audio routines.
* Caught a subtle bug in the instrument number stuff.
* Caught the volume change problems.
* Seems to work great.
*
* Revision 1.10 91/04/20 03:55:31 Espie
* Cleaned up version.
* Does play mod.shadowfire correctly.
*
* Revision 1.9 91/04/20 03:07:08 Espie
* Debugged version. Portamento is now working correctly.
* Incorrect loading of instrument has been fixed:
* while portamento, frequency should not be changed, but
* volume should be anyway. Not too easy.
* (To check, change volume always, actually).
*
* Revision 1.8 91/04/19 19:46:57 Espie
* Tone portamento working better. Successfull.
*
* Revision 1.7 91/04/19 18:35:24 Espie
* Version before an experiment.
*
* Revision 1.6 91/04/19 13:20:30 Espie
* Augmented player: broken up in small functions,
* signals task for events, recognizes many events,
* but not everything quite yet...
*
* Revision 1.5 91/04/19 02:18:35 Espie
* new player, plays about 90% of my files correctly.
* Missing arpegios, volume slide and command 4, whatever that is.
*
* Revision 1.4 91/04/18 20:23:34 Espie
* No assembly stub necessary under SAS C.
*
* Revision 1.3 91/04/18 02:26:14 Espie
* bunch I.
*
* Revision 1.2 91/04/18 02:19:12 Espie
* ``Working'' simple-minded player.
*
* Revision 1.1 91/04/18 01:34:53 Espie
* Initial revision
*
*/
#include <exec/types.h>
#include <hardware/cia.h>
#include <proto/exec.h>
#include <dos/dos.h>
#include <stdlib.h>
#include <custom/cleanup.h>
#include "song.h"
#include "player.h"
#include "public_play.h"
#include "int_play.h"
#include "proto.h"
#include "periods.h"
#include "lproto.h"
/***
*
* The micro-timing package
*
***/
/* get current timer value
*/
UWORD gettimer(struct play *play)
{
return *PRIVATE.latchlo | (*PRIVATE.latchhi<<8);
}
/* relatch with the correct time for an interval
* between the start of this interrupt and the next
* interrupt of value
*/
void relatch(struct play *play, int value)
{
PRIVATE.current = PRIVATE.latched - gettimer(play);
value -= PRIVATE.current;
*PRIVATE.latchlo = value & 255;
*PRIVATE.latchhi = value>>8;
/* IMPORTANT: force load of latched value ! */
*PRIVATE.control |= CIACRAF_LOAD;
PRIVATE.latched = value;
/* time ourselves, while we're at it */
PUBLIC.sleep+= PRIVATE.latched;
PUBLIC.cpu += PRIVATE.current;
}
void rebuild_timers(struct play *play)
{
PRIVATE.maintimer = PUBLIC.timebase/PRIVATE.finespeed;
PRIVATE.effecttimer = PUBLIC.effectbase/PRIVATE.finespeed;
PRIVATE.smalltimer = 600;
}
#ifdef LATTICE
void __interrupt __asm do_play(register __a1 struct play *play)
#else
void C_do_play(struct play *play)
#endif
{
(*PRIVATE.state)(play);
}
void init_player(struct play *play)
{
int track;
PRIVATE.state = wait_play;
reset_player(play);
rebuild_timers(play);
PRIVATE.latched = PRIVATE.maintimer;
for (track = 0; track < NUMBER_TRACKS; track++)
{
PRIVATE.track[track]->pursue = do_nothing;
PRIVATE.track[track]->channel = track;
}
PRIVATE.setup[0] = setup_arpeggio;
PRIVATE.setup[1] = setup_porta_up;
PRIVATE.setup[2] = setup_porta_down;
PRIVATE.setup[3] = setup_portamento;
PRIVATE.setup[4] = setup_vibrato;
PRIVATE.setup[5] = ignore;
PRIVATE.setup[6] = ignore;
PRIVATE.setup[7] = ignore;
PRIVATE.setup[8] = ignore;
PRIVATE.setup[9] = ignore;
PRIVATE.setup[10]= setup_volume_slide;
PRIVATE.setup[11]= do_fastskip;
PRIVATE.setup[12]= do_change_volume;
PRIVATE.setup[13]= do_skip;
PRIVATE.setup[14]= do_change_filter;
PRIVATE.setup[15]= do_change_speed;
init_audio_hard(&play->private);
}
void wait_play(struct play *play)
{
if (PUBLIC.command)
PRIVATE.state = normal_play;
return;
}
void normal_play(struct play *play)
{
PRIVATE.replay = FALSE;
PRIVATE.volume = PUBLIC.volume;
play_next(play);
/* setup for the new replay routine
*/
if (PRIVATE.tempo_change)
{
rebuild_timers(play);
PRIVATE.tempo_change = FALSE;
}
if (PRIVATE.replay)
{
PRIVATE.state = latch_samples;
PRIVATE.spent = PRIVATE.latched - gettimer(play);
relatch(play, PRIVATE.smalltimer+PRIVATE.spent);
}
else
relatch(play, PRIVATE.effecttimer);
}
void latch_samples(struct play *play)
{
/* dma was off, turn it back on */
turn_on_dma(&play->private);
PRIVATE.state = install_replay;
relatch(play, PRIVATE.smalltimer);
}
void install_replay(struct play *play)
{
int track;
/* setup the replay section */
for (track = 0; track < NUMBER_TRACKS; track++)
if (PRIVATE.track[track]->newnote)
set_replay(&play->private, PRIVATE.track[track]->instr,
PRIVATE.track[track]->channel);
PRIVATE.state = normal_play;
relatch(play, PRIVATE.maintimer - 2 * PRIVATE.smalltimer
- PRIVATE.spent);
}
/* this function should be called every time a volume is to be set,
* because the master volume changes according to the user's whim.
*/
int scaled_volume(int master, struct automaton *cst)
{
return (cst->volume * master) /256;
}
void new_volume(struct priv_play *private, struct automaton *cst)
{
change_volume(private, cst->channel,
scaled_volume(private->volume, cst));
}
void new_period(struct priv_play *private, struct automaton *cst)
{
change_period(private, cst->channel, cst->period);
}
/* what to do when play has just ended */
void ended_play(struct play *play)
{
PRIVATE.has_ended = TRUE;
}
void send_out(struct play *play)
{
PUBLIC.position = PRIVATE.position;
PUBLIC.pattern = PRIVATE.pattern;
PUBLIC.speed = PRIVATE.speed;
PUBLIC.finespeed = PRIVATE.finespeed;
}
void install_filter(struct priv_play *private)
{
if (private->filter)
filter_on();
else
filter_off();
}
void reset_player(struct play *play)
{
int track;
struct automaton *cst;
PRIVATE.filter = FALSE;
install_filter(&play->private);
reset_audio();
for (track = 0; track < NUMBER_TRACKS; track++)
{
cst = PRIVATE.track[track];
cst->instr = PUBLIC.sample[0];
cst->rate = 0;
cst->depth = 0;
cst->speed = 0;
}
/* dummy sample */
PRIVATE.counter = 0;
send_out(play);
/* turns out to be simpler for the display */
send(play, ON_PATTERN);
PRIVATE.speed = 6;
PRIVATE.finespeed = 100;
rebuild_timers(play);
}
void play_next(struct play *play)
{
struct automaton *cst;
int track;
if (PUBLIC.resume)
{
clear_mask(&play->private);
install_filter(&play->private);
for (track = 0; track < NUMBER_TRACKS; track++)
{
cst = PRIVATE.track[track];
cst->p_table =
PRIVATE.period_table[cst->instr->finetune]
+PUBLIC.transpose;
new_period(&play->private, cst);
new_volume(&play->private, cst);
set_replay(&play->private,cst->instr, cst->channel);
}
turn_on_dma(&play->private);
}
if (++PRIVATE.counter < PRIVATE.speed)
{
/* continue_notes */
for (track = 0; track < NUMBER_TRACKS; track++)
(*PRIVATE.track[track]->pursue)(&play->private,
PRIVATE.track[track]);
}
else
{
if (PRIVATE.has_ended)
{
send(play, ON_END);
PRIVATE.has_ended = FALSE;
if (PUBLIC.oneshot)
{
PUBLIC.command = STOP;
PUBLIC.oneshot = FALSE;
}
}
if (PUBLIC.command)
{
switch(PUBLIC.command)
{
case STOP:
PRIVATE.state = wait_play;
reset_player(play);
break;
case NEWPOS:
PRIVATE.pattern = PUBLIC.pattern;
PRIVATE.position = PUBLIC.position;
reset_player(play);
clear_mask(&play->private);
break;
default:
break;
}
PUBLIC.command = NONE;
send(play, ON_COMMAND);
return;
}
PRIVATE.block = PUBLIC.info->pblocks[PRIVATE.pattern];
PRIVATE.counter = 0;
clear_mask(&play->private);
play_notes(play);
}
}
UWORD compute_period(struct priv_play *private, struct automaton *cst)
{
if (cst->note > FINE_PERIOD)
return private->period_table[NUMBER_TUNING - 1]
[cst->note - FINE_PERIOD];
else
{
return cst->p_table[cst->note];
}
}
void play_notes(struct play *play)
{
struct automaton *cst;
int track;
struct priv_play *private;
private = &play->private;
private->fastskip = -1;
private->skip = -1;
private->newspeed = -1;
for (track = 0; track < NUMBER_TRACKS; track++)
{
cst = private->track[track];
private->e = &private->block->e[track][PRIVATE.position];
/* We DO reload the volume each time we change the sample */
if (private->e->sample_number != 0)
{
cst->instr = PUBLIC.sample[private->e->sample_number];
cst->volume = cst->instr->volume;
cst->p_table = private->period_table[cst->instr->finetune]
+PUBLIC.transpose;
if (cst->instr->finetune)
send(play, ON_BLIP);
new_volume(private, cst);
}
/* default next command, unless
* there is something more interesting to do
*/
cst->pursue = do_nothing;
/* there is a new note unless we don't have any period,
* or this is the SPECIAL PORTAMENTO command
*/
if (cst->newnote = (private->e->note != NO_NOTE &&
private->setup[private->e->effect] != setup_portamento))
{
cst->note = private->e->note;
cst->period = compute_period(private, cst);
set_note(private, cst->instr, cst->channel,
cst->period);
cst->offset = 0;
private->replay = TRUE;
}
(*private->setup[private->e->effect])(private, cst);
}
if (private->newspeed != -1)
change_speed(play);
if (private->skip!=-1)
{
private->position = private->skip;
private->pattern++;
if (private->pattern >= PUBLIC.info->length)
{
private->pattern = 0;
ended_play(play);
}
send(play, ON_PATTERN);
}
else
{
if (private->fastskip != -1)
{
private->position = 0;
if (private->fastskip < private->pattern)
ended_play(play);
private->pattern = private->fastskip;
send(play, ON_PATTERN);
}
else
advance_position(play);
}
PUBLIC.pattern = private->pattern;
PUBLIC.position = private->position;
}
#define STD 0
#define OLD 1
#define NEW 2
void change_speed(struct play *play)
{
if (PRIVATE.newspeed == 0)
{
switch(PUBLIC.mode)
{
case NEW:
PRIVATE.speed = 6;
PRIVATE.finespeed = 100;
rebuild_timers(play);
PRIVATE.fastskip = 0;
break;
case STD:
ended_play(play);
break;
case OLD:
break;
}
}
else
{
if (PRIVATE.newspeed >= 32 && PUBLIC.mode != OLD)
{
PRIVATE.finespeed = PRIVATE.newspeed-31;
if (PUBLIC.mode == STD)
PRIVATE.speed = 6;
rebuild_timers(play);
}
else
{
PRIVATE.speed = PRIVATE.newspeed;
if (PRIVATE.finespeed != 100 && PUBLIC.mode != NEW)
{
PRIVATE.finespeed = 100;
rebuild_timers(play);
}
}
}
send_out(play);
send(play, ON_SPEED_CHANGE);
}
void send(struct play *play, ULONG event)
{
if (PUBLIC.on_signal & event && PUBLIC.task && PUBLIC.signal != 0)
{
Signal(PUBLIC.task, PUBLIC.signal);
}
PUBLIC.signaled |= event;
}
void advance_position(struct play *play)
{
PRIVATE.position++;
if (PRIVATE.position < BLOCK_LENGTH)
return;
PRIVATE.position = 0;
PRIVATE.pattern++;
send(play, ON_PATTERN);
if (PRIVATE.pattern >= PUBLIC.info->length)
{
PRIVATE.pattern = 0;
ended_play(play);
}
}