/***************************************************************************** * * VT100-like Beep Sound Example 22 February 1986 * * Copyright (c) 1986 Sam Dicker, Commodore/Amiga Inc. * This is public domain software. Permission is granted to copy and use * this software as long as this notice is included. * * This module makes a beep sound like the CTRL-G on a VT100 terminal. * * Calling VTBeep() starts the sound which stops when it times out. * Calling it again, while it is playing, only restarts the timer. * Call KillVTBeep() to kill any beeps in progress before exiting your * main program. * * VTBeep() and KillVTBeep() are re-entrant but cannot be used in interrupt * code. * * The audio in this example is very crude. It is more of an example of * using the timer device and multi-tasking. I sincerely hope, if it appears * in any products, that it is only used as an authentic 'cheap terminal' * sound. A little more code can produce much better (less obnoxious) sound. * * VT100 is a trademark of Digital Equipment Corporation. * *****************************************************************************/ #include "exec/types.h" #include "exec/errors.h" #include "exec/memory.h" #include "devices/timer.h" #include "devices/audio.h" #include "libraries/dos.h" #define BEEPNAME "VTBeep" #define BEEPSIZE 10 #define BEEPFREQ 1000 #define COLORCLOCK 3579545 #define SIGB_BEEP 31 #define SIGF_BEEP (1 << 31) #define SIGF_PORT (1 << replyPort->mp_SigBit) /* channel allocation map (try left channel; if unavailable try right) */ UBYTE allocationMap[] = { 1, 8, 2, 4 }; /* make beep sound */ VTBeep() { struct Task *beepTCB; VOID beepTask(); /* prevent beep child task, if it already exists, from going away before * it is signaled */ Forbid(); /* find the task by name */ beepTCB = (struct Task *)FindTask(BEEPNAME); /* check if the task exists */ if (beepTCB == NULL) /* it doesn't exist, so create it */ beepTCB = (struct Task *)CreateTask(BEEPNAME, 25, beepTask, 500); else /* it already exist so signal it so restart it's timer */ Signal(beepTCB, SIGF_BEEP); Permit(); /* return success */ return(beepTCB != NULL); } /* kill any beep sounds in progress. This is necessary before exiting the * main program; otherwise, if a beep is playing, when the beep times out * and the child task wakes up its code segment may be gone */ KillVTBeep() { struct Task *beepTCB; do { /* prevent beep child task, if it already exists, from going away * before it is signaled */ Forbid(); /* find the task by name */ beepTCB = (struct Task *)FindTask(BEEPNAME); /* check if the task exists */ if (beepTCB != NULL) { /* it already exist so signal it so go away */ Signal(beepTCB, SIGBREAKF_CTRL_C); /* give it a chance to wake up, if it is lower priority */ Delay(10); } Permit(); /* if it existed, kill it again */ } while (beepTCB != NULL); } /* beep sound child task */ VOID beepTask() { struct MsgPort *replyPort; struct timerequest *timerIOB; struct IOAudio *audioIOB; UBYTE *beepWave; ULONG signals; /* allocate signal used to re-start beep */ if (AllocSignal(SIGB_BEEP) == SIGB_BEEP) { /* create reply port for timer and sound I/O block */ replyPort = (struct MsgPort *)CreatePort(NULL); if (replyPort != NULL) { /* create timer I/O block */ timerIOB = (struct timerequest *) CreateExtIO(replyPort, sizeof(struct timerequest)); if (timerIOB != NULL) { /* open timer device */ if (OpenDevice(TIMERNAME, UNIT_VBLANK, timerIOB, 0) == 0) { timerIOB->tr_node.io_Command = TR_ADDREQUEST; /* create beep waveform */ beepWave = (UBYTE *)AllocMem(BEEPSIZE, MEMF_CHIP | MEMF_CLEAR); if (beepWave != 0) { beepWave[0] = 100; /* create audio I/O block */ audioIOB = (struct IOAudio *) CreateExtIO(replyPort, sizeof(struct IOAudio)); if (audioIOB != NULL) { /* setup audio I/O block to allocate a channel * when the audio device is opened (see Volume 1 of * the Amiga ROM Kernel Manual - Audio Device * chapter and Volume 2 of the Amiga ROM Kernel * Manual - Device Summaries appendix for details) */ audioIOB->ioa_Request.io_Message.mn_Node.ln_Pri = 85; audioIOB->ioa_Data = allocationMap; audioIOB->ioa_Length = sizeof(allocationMap); /* open the audio device */ if (OpenDevice(AUDIONAME, 0, audioIOB, 0) == 0) { /* setup the audio I/O block to play sound */ audioIOB->ioa_Request.io_Command = CMD_WRITE; audioIOB->ioa_Request.io_Flags = ADIOF_PERVOL; audioIOB->ioa_Data = beepWave; audioIOB->ioa_Length = BEEPSIZE; audioIOB->ioa_Period = COLORCLOCK / (BEEPSIZE * BEEPFREQ); audioIOB->ioa_Volume = 64; /* start the sound */ BeginIO(audioIOB); /* from this point on the task in cannot be pre-empted. This prevents the parent task from signaling it to restart the timer while it is cleaning up */ Forbid(); do { /* start the timer */ timerIOB->tr_time.tv_secs = 0; timerIOB->tr_time.tv_micro = 250000; SendIO(timerIOB); /* wait for: . the timer to time out, . the audio I/O block to return (indicating an error had occurred), . a signal to restart to timer, or . a signal to quit */ do signals = Wait(SIGBREAKF_CTRL_C | SIGF_BEEP | SIGF_PORT); while ((signals & SIGF_PORT) != 0 && CheckIO(timerIOB) == 0 && CheckIO(audioIOB) == 0); /* if the timer is still going, kill it */ if (CheckIO(timerIOB) == 0) { AbortIO(timerIOB); WaitIO(timerIOB); } /* restart the timer if signaled */ } while ((signals & SIGF_BEEP) != 0); /* clean up */ /* closing the audio device kills the sound * and frees the channels */ CloseDevice(audioIOB); } DeleteExtIO(audioIOB); } FreeMem(beepWave, BEEPSIZE); } CloseDevice(timerIOB); } DeleteExtIO(timerIOB); } DeletePort(replyPort); } } }