home *** CD-ROM | disk | FTP | other *** search
- /* 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);
- }
- }
-