/* ** alarming_clock -- by Brian Neal 6/24/89 ** ** A simple clock program whoose alarm packs a punch! Be sure to ** crank up the stereo for this one. Illustrates the use of the ** timer and digitized sound playback. ** Compiled with Manx Aztec C v3.6 ** cc -n alarming_clock ** ln -g alarming_clock isup -lc */ #include #include #include #include #include #include /* useful macros */ #define CHAR_WIDTH (font -> tf_XSize) #define CHAR_HEIGHT (font -> tf_YSize) #define WIN_WIDTH 200 /* window constants */ #define WIN_HEIGHT 60 #define BUTT_WIDTH 50 /* gadget constants */ #define BUTT_HEIGHT 14 #define TEST 1 /* GadgetIDs */ #define ALARM 2 #define UP 3 #define DOWN 4 #define HRS_TEN 5 #define HRS_ONE 6 #define MIN_TEN 7 #define MIN_ONE 8 #define ARROW_WIDTH 16 /* arrow dimensions */ #define ARROW_HEIGHT 8 #define MAPRIGHT 0x00000006 /* masks for channel allocation */ #define MAPLEFT 0x00000009 #define SCREAM_PERIOD 400 #define SCREAM_LENGTH 12536 /* length (in bytes) of data file */ /* used CLI's List to get this */ #define POW_PERIOD 475 #define POW_LENGTH 23632 #define CLOCK_LEFT ((WIN_WIDTH - (5 * CHAR_WIDTH)) / 2) #define CLOCK_TOP 12 #define ALARM_LEFT CLOCK_LEFT #define ALARM_TOP 24 extern BOOL open_libraries(); extern struct Window *make_window(); extern void init_itext(), *copy_chip(); void shut_down(), init_sounds(), shut_down_audio(), display_time(), shut_down_timer(), send_time_request(), init_arrow(), init_image(), set_up_display(), sound_alarm(), display_alarm(), turn_off_alarm(), init_digit(), adjust_alarm(); UBYTE init_audio(); BOOL read_data(), init_timer(), set_up_window(), time_to_sound(), set_up_gadgets(); struct IntuiText test_text, alarm_text, clock_face, alarm_face; struct Gadget arrow[2]; struct Gadget digit[4]; struct Image arrow_image[2]; #define ARROW_BYTES 16 USHORT up_image[] = { 0x0000, 0x0180, 0x03c0, 0x07e0, 0x0ff0, 0x1ff8, 0x7ffe, 0x0000 }; USHORT down_image[] = { 0x0000, 0x7ffe, 0x1ff8, 0x0ff0, 0x07e0, 0x03c0, 0x0180, 0x0000 }; SHORT points[] = { -1, -1, BUTT_WIDTH, -1, BUTT_WIDTH, BUTT_HEIGHT, -1, BUTT_HEIGHT, -1, -1 }; struct Border butt_border = { 0, 0, /* LeftEdge, TopEdge */ 3, 0, /* FrontPen, BackPen */ JAM1, /* DrawMode */ 5, /* Count */ &points[0], /* XY */ NULL }; /* NextBorder */ struct Gadget test_button = { NULL, /* NextGadget */ 20, -(BUTT_HEIGHT + 5), /* LeftEdge, TopEdge */ BUTT_WIDTH, BUTT_HEIGHT, /* Width, Height */ GADGHCOMP | GRELBOTTOM, /* Flags */ GADGIMMEDIATE | RELVERIFY, /* Activation */ BOOLGADGET, /* GadgetType */ (APTR) &butt_border, /* GadgetRender */ NULL, /* SelectRender */ &test_text, /* GadgetText */ 0L, /* MutualExclude */ NULL, /* SpecialInfo */ TEST, /* GadgetID */ NULL }; /* UserData */ struct Gadget alarm_button = { &test_button, -(BUTT_WIDTH + 20), -(BUTT_HEIGHT + 5), BUTT_WIDTH, BUTT_HEIGHT, GADGHCOMP | GRELRIGHT | GRELBOTTOM, GADGIMMEDIATE | TOGGLESELECT, BOOLGADGET, (APTR) &butt_border, NULL, &alarm_text, 0L, NULL, ALARM, NULL }; /* We are going to use topaz.font 80 */ struct TextAttr our_font = { (STRPTR) "topaz.font", /* ta_Name */ 8, /* ta_YSize */ FS_NORMAL, /* ta_Style */ FPB_ROMFONT }; /* ta_Flags */ struct my_time /* hours in range of 0..24, minutes in 0..59 */ { UBYTE hours, minutes; }; struct IntuitionBase *IntuitionBase = NULL; struct GfxBase *GfxBase = NULL; struct TextFont *font = NULL; void main() { struct Window *win = NULL; struct IntuiMessage *msg; struct IOAudio scream, pow; UBYTE channel_mask; UBYTE *scream_data = NULL, *pow_data = NULL; struct timerequest time_req; struct my_time current, alarm; struct Gadget *gad; ULONG class; USHORT active_digit; BOOL alarm_set, gad_images = FALSE; if (!(open_libraries(33L, 33L))) shut_down(10, win, scream_data, pow_data, gad_images); if (!(font = (struct TextFont *) OpenFont(&our_font))) shut_down(20, win, scream_data, pow_data, gad_images); if (!(read_data(&scream_data, &pow_data))) shut_down(30, win, scream_data, pow_data, gad_images); if (!(gad_images = set_up_gadgets())) shut_down(40, win, scream_data, pow_data, gad_images); if (!set_up_window(&win)) shut_down(50, win, scream_data, pow_data, gad_images); if ((channel_mask = init_audio(&scream)) == 0) shut_down(60, win, scream_data, pow_data, gad_images); init_sounds(&scream, &pow, channel_mask, scream_data, pow_data); if (!(init_timer(&time_req))) { shut_down_audio(&scream, channel_mask); shut_down(70, win, scream_data, pow_data, gad_images); } set_up_display(win, &clock_face, &alarm_face); /* Give us a boost in priority */ SetTaskPri(FindTask(NULL), 20L); alarm_set = FALSE; active_digit = MIN_ONE; alarm.hours = 0; alarm.minutes = 0; display_time(win, &clock_face, ¤t); display_alarm(win, &alarm_face, alarm); send_time_request(&time_req); FOREVER { /* * if our time request is back, update clock and restart the * timer. If alarm is set, and it is that magic time, sound * the alarm. */ if (GetMsg(time_req.tr_node.io_Message.mn_ReplyPort)) { display_time(win, &clock_face, ¤t); send_time_request(&time_req); if ((time_to_sound(current, alarm)) && alarm_set) sound_alarm(win, &scream, &pow); } /* Wait on timer and input from user via window */ Wait(1L << win -> UserPort -> mp_SigBit | 1L << time_req.tr_node.io_Message.mn_ReplyPort -> mp_SigBit); while (msg = (struct IntuiMessage *) GetMsg(win -> UserPort)) { class = msg -> Class; gad = (struct Gadget *) msg -> IAddress; ReplyMsg(msg); switch (class) { case CLOSEWINDOW: shut_down_timer(&time_req); shut_down_audio(&scream, channel_mask); shut_down(0, win, scream_data, pow_data, gad_images); break; case GADGETUP: /* has no meaning; set in gadgets */ break; /* only to let user see GADGHCOMP */ case GADGETDOWN: switch(gad -> GadgetID) { case ALARM: if (alarm_set) { turn_off_alarm(&alarm_button, win); alarm_set = FALSE; } else alarm_set = TRUE; break; case TEST: sound_alarm(win, &scream, &pow); break; case UP: case DOWN: adjust_alarm(&alarm, active_digit, gad -> GadgetID); display_alarm(win, &alarm_face, alarm); break; case HRS_TEN: case HRS_ONE: case MIN_TEN: case MIN_ONE: active_digit = gad -> GadgetID; break; } break; default: break; } /* switch */ } /* while */ } /* for(;;) */ } /* main */ /* ** shut_down() ** Frees up everything but audio and timer related activiy. **/ void shut_down(code, win, scream, pow, gad_images) WORD code; struct Window *win; UBYTE *scream, *pow; BOOL gad_images; { struct IntuiMessage *msg; if (gad_images) { FreeMem(arrow_image[0].ImageData, (ULONG) ARROW_BYTES); FreeMem(arrow_image[1].ImageData, (ULONG) ARROW_BYTES); } if (pow) FreeMem(pow, (ULONG) POW_LENGTH); if (scream) FreeMem(scream, (ULONG) SCREAM_LENGTH); if (win) { while(msg = (struct IntuiMessage *) GetMsg(win -> UserPort)) ReplyMsg(msg); CloseWindow(win); } if (font) CloseFont(font); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); Exit((LONG) code); } /* ** shut_down_audio() ** Cleans up after using audio.device. */ void shut_down_audio(scream, channels_used) struct IOAudio *scream; UBYTE channels_used; { scream -> ioa_Request.io_Unit = (struct Unit *) channels_used; CloseDevice(scream); DeletePort(scream -> ioa_Request.io_Message.mn_ReplyPort); /* remember, pow and scream shared the same reply port */ } /* ** init_audio() ** Opens audio device and allocates a stereo pair of channels. ** Returns a mask of channels allocated, or 0 if error. */ UBYTE init_audio(iob) struct IOAudio *iob; { UBYTE channel_map[4]; LONG error; ULONG mask; channel_map[0] = 0x03; /* channels 0 and 1 */ channel_map[1] = 0x05; /* channels 0 and 2 */ channel_map[2] = 0x0a; /* channels 3 and 1 */ channel_map[3] = 0x0c; /* channels 3 and 2 */ /* Get a reply port so the audio device can communicate with us */ if ((iob -> ioa_Request.io_Message.mn_ReplyPort = CreatePort("ac", 0L)) == NULL) return (0); /* We are now going to be pigs and set our priority to the max. If * successful, we hold exclusive access to our stereo pair of channels. * Why don't we just set our priority lower, allocate channels, and then * lock them? Because we don't intend to give up our access until * we quit. */ iob -> ioa_Request.io_Message.mn_Node.ln_Pri = ADALLOC_MAXPREC; iob -> ioa_Data = &channel_map[0]; iob -> ioa_Length = 4L; error = OpenDevice(AUDIONAME, 0L, iob, 0L); if (error == 0) { mask = (ULONG) iob -> ioa_Request.io_Unit; return ((UBYTE) mask); } else { DeletePort(iob -> ioa_Request.io_Message.mn_ReplyPort); return (0); } } /* ** init_sounds() ** Initializes our IOAudio request blocks for sounding ** once. Attaches waveform data. */ void init_sounds(scream, pow, channel_mask, scream_data, pow_data) struct IOAudio *scream, *pow; UBYTE channel_mask; UBYTE *scream_data, *pow_data; { scream -> ioa_Request.io_Unit = (struct Unit *) ((ULONG) channel_mask & MAPRIGHT); scream -> ioa_Request.io_Command = CMD_WRITE; scream -> ioa_Request.io_Flags = IOF_QUICK | ADIOF_PERVOL; scream -> ioa_Data = scream_data; scream -> ioa_Cycles = 1; scream -> ioa_Period = SCREAM_PERIOD; scream -> ioa_Length = (ULONG) SCREAM_LENGTH; scream -> ioa_Volume = 64; /* copy keys, ports, and above stuff */ *pow = *scream; /* Now, more specifically... */ pow -> ioa_Request.io_Unit = (struct Unit *) ((ULONG) channel_mask & MAPLEFT); pow -> ioa_Data = pow_data; pow -> ioa_Period = POW_PERIOD; pow -> ioa_Length = (ULONG) POW_LENGTH; } /* ** read_data() ** Read in digitized sound files, placing them into ** Chip memory. Note that the files are IFF, but treated as ** raw data. Hopefully, won't be able to hear the 'pop' as the ** header information is played. NOTE: Would be simple ** to skip over the header. A job for a future revision. ** Call AmigaDos directly, bypassing all that "ffunction" stuff. ** shut_down() is responsible for freeing up the allocated Chip ** memory, so don't have to worry about that here. */ BOOL read_data(screamptr, powptr) UBYTE **screamptr, **powptr; { struct FileHandle *scream_file, *pow_file; LONG scream_actual, pow_actual; *screamptr = AllocMem((ULONG) SCREAM_LENGTH, MEMF_CHIP); *powptr = AllocMem((ULONG) POW_LENGTH, MEMF_CHIP); pow_file = Open("explosion.sound", MODE_OLDFILE); scream_file = Open("scream.sound", MODE_OLDFILE); if ((!scream_file) || (!pow_file) || (!(*screamptr)) || (!(*powptr))) { if (scream_file) Close(scream_file); if (pow_file) Close(pow_file); return (FALSE); } scream_actual = Read(scream_file, *screamptr, (LONG) SCREAM_LENGTH); pow_actual = Read(pow_file, *powptr, (LONG) POW_LENGTH); Close(scream_file); Close(pow_file); if ((scream_actual != SCREAM_LENGTH) || (pow_actual != POW_LENGTH)) return (FALSE); else return (TRUE); } /* ** init_timer() ** Initializes our time request and opens the timer ** device. */ BOOL init_timer(time_req) struct timerequest *time_req; { /* obtain reply port so timer can signal us */ if (!(time_req -> tr_node.io_Message.mn_ReplyPort = CreatePort("ac_timer", 0L))) return (FALSE); if (OpenDevice(TIMERNAME, UNIT_VBLANK, time_req, 0L) != NULL) { DeletePort(time_req -> tr_node.io_Message.mn_ReplyPort); return (FALSE); } else { /* set up our time request so the timer will time time for us */ time_req -> tr_node.io_Command = TR_ADDREQUEST; time_req -> tr_node.io_Flags = IOF_QUICK; return (TRUE); } } /* ** display_time() ** Outputs the current time to our window. */ void display_time(win, clock_face, current) struct Window *win; struct IntuiText *clock_face; struct my_time *current; { struct DateStamp now; SHORT hours, minutes; static char time_buffer[7]; DateStamp(&now); /* get system time from AmigaDos */ hours = now.ds_Minute / 60; minutes = now.ds_Minute % 60; /* format our time display in the time_buffer */ sprintf(&time_buffer[0], "%02d:%02d", hours, minutes); /* attach it to our IntuiText structure, and display it */ clock_face -> IText = (UBYTE *) &time_buffer[0]; PrintIText(win -> RPort, clock_face, 0L, 0L); current -> hours = hours; current -> minutes = minutes; } /* ** display_alarm() ** Outputs the current setting of the alarm to our window. */ void display_alarm(win, alarm_face, alarm) struct Window *win; struct IntuiText *alarm_face; struct my_time alarm; { static char alarm_buffer[7]; sprintf(&alarm_buffer[0], "%02d:%02d", alarm.hours, alarm.minutes); alarm_face -> IText = (UBYTE *) &alarm_buffer[0]; PrintIText(win -> RPort, alarm_face, 0L, 0L); } /* ** shut_down_timer() ** Releases access to the timer device. */ void shut_down_timer(time_req) struct timerequest *time_req; { AbortIO(time_req); CloseDevice(time_req); DeletePort(time_req -> tr_node.io_Message.mn_ReplyPort); } /* ** send_time_request() ** Sends a message to the timer, telling it to signal us ** in five seconds. Thus our clock can never be off from the system ** clock by more than five seconds. ** */ void send_time_request(time_req) struct timerequest *time_req; { time_req -> tr_time.tv_secs = 5L; time_req -> tr_time.tv_micro = 0L; BeginIO(time_req); } /* ** init_arrow() ** Initializes the alarm's set buttons. */ void init_arrow(arrow, id) struct Gadget *arrow; COUNT id; { arrow -> NextGadget = (id == 0) ? &arrow[1] : &alarm_button; arrow -> LeftEdge = (id == 0) ? (ALARM_LEFT - (ARROW_WIDTH + 6)) : (ALARM_LEFT + (CHAR_WIDTH * 5) + 6); arrow -> TopEdge = ALARM_TOP; arrow -> Width = ARROW_WIDTH; arrow -> Height = ARROW_HEIGHT; arrow -> Flags = GADGHCOMP | GADGIMAGE; arrow -> Activation = GADGIMMEDIATE | RELVERIFY; arrow -> GadgetType = BOOLGADGET; arrow -> GadgetRender = (APTR) &arrow_image[id]; arrow -> SelectRender = NULL; arrow -> GadgetText = NULL; arrow -> MutualExclude = 0L; arrow -> SpecialInfo = NULL; arrow -> GadgetID = UP + id; arrow -> UserData = NULL; } /* ** init_image() ** Initializes the alarm set button's images. */ void init_image(arrow, id) struct Image *arrow; COUNT id; { arrow -> LeftEdge = 0; arrow -> TopEdge = 0; arrow -> Width = ARROW_WIDTH; arrow -> Height = ARROW_HEIGHT; arrow -> Depth = 1; arrow -> ImageData = (id == 0) ? &up_image[0] : &down_image[0]; arrow -> PlanePick = 2; arrow -> PlaneOnOff = 0; arrow -> NextImage = NULL; } /* ** init_digit() ** Initializes the alarm's digit gadgets. */ void init_digit(d, id) struct Gadget *d; UCOUNT id; { d -> NextGadget = (id < 3) ? &digit[id + 1] : &arrow[0]; if (id < 2) d -> LeftEdge = ALARM_LEFT + (id * CHAR_WIDTH); else d -> LeftEdge = ALARM_LEFT + CHAR_WIDTH + (id * CHAR_WIDTH); d -> TopEdge = ALARM_TOP; d -> Width = CHAR_WIDTH; d -> Height = CHAR_HEIGHT; d -> Flags = GADGHCOMP; d -> Activation = GADGIMMEDIATE | RELVERIFY; d -> GadgetType = BOOLGADGET; d -> GadgetRender = NULL; d -> SelectRender = NULL; d -> GadgetText = NULL; d -> MutualExclude = 0L; d -> SpecialInfo = NULL; d -> GadgetID = HRS_TEN + id; d -> UserData = NULL; } /* ** set_up_window() ** Hides the details of opening the window from ** main(). */ BOOL set_up_window(win) struct Window **win; { if (!(*win = make_window(100, 60, WIN_WIDTH, WIN_HEIGHT, -1, -1, CLOSEWINDOW | GADGETUP | GADGETDOWN, WINDOWCLOSE | ACTIVATE | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0))) return (FALSE); SetFont((*win) -> RPort, font); SetWindowTitles(*win, (UBYTE *) "Alarming Clock", NULL); AddGList(*win, &digit[0], -1L, -1L, NULL); RefreshGList(&digit[0], *win, NULL, -1L); return (TRUE); } /* ** set_up_gadgets() ** Hides the details of initializing all of our gadgets ** from main(). Our gadget list looks like: ** digits -> arrows -> alarm_button -> test_button */ BOOL set_up_gadgets() { COUNT i; init_itext(&test_text, 1, 9, 3, "TEST"); init_itext(&alarm_text, 1, 5, 3, "ALARM"); for (i = 0; i < 4; ++i) init_digit(&digit[i], i); for (i = 0; i < 2; ++i) { init_arrow(&arrow[i], i); init_image(&arrow_image[i], i); } arrow_image[0].ImageData = copy_chip(&up_image[0], ARROW_BYTES); arrow_image[1].ImageData = copy_chip(&down_image[0], ARROW_BYTES); if ((arrow_image[0].ImageData) && (arrow_image[1].ImageData)) return (TRUE); else { if (arrow_image[0].ImageData) FreeMem(arrow_image[0].ImageData, (ULONG) ARROW_BYTES); if (arrow_image[1].ImageData) FreeMem(arrow_image[1].ImageData, (ULONG) ARROW_BYTES); return (FALSE); } } /* ** set_up_display() ** Hides the details of "drawing" our clock face from ** main(). */ void set_up_display(win, clock_face, alarm_face) struct Window *win; struct IntuiText *clock_face, *alarm_face; { init_itext(clock_face, 1, CLOCK_LEFT, CLOCK_TOP, ""); init_itext(alarm_face, 3, ALARM_LEFT, ALARM_TOP, ""); clock_face -> DrawMode = JAM2; /* use both pens, so old junk */ alarm_face -> DrawMode = JAM2; /* gets erased when written on */ } /* ** sound_alarm() ** Beeps screen, and brings window to the front. ** Sends off a scream request, waits for it to finish, ** then fires off the pow request. Waits for it to finish, then ** returns. */ void sound_alarm(win, scream, pow) struct Window *win; struct IOAudio *scream, *pow; { DisplayBeep(NULL); /* Beep everyone's screen, impolite */ WindowToFront(win); /* but hey, someone needs reminding */ BeginIO(scream); WaitIO(scream); BeginIO(pow); WaitIO(pow); } /* ** time_to_sound() ** Returns true is current time == time alarm is supposed ** to go off. */ BOOL time_to_sound(current, alarm) struct my_time current, alarm; { return ((current.hours == alarm.hours) && (current.minutes == alarm.minutes)); } /* ** turn_off_alarm() ** Shuts off the alarm by deselecting the alarm gadget. ** Note that you can't simply change the gadget's flags and refresh, ** you have to remove it, change it's flags, add it, then refresh. */ void turn_off_alarm(alarm_button, win) struct Gadget *alarm_button; struct Window *win; { RemoveGadget(win, alarm_button); alarm_button -> Flags &= ~SELECTED; AddGadget(win, alarm_button, -1L); RefreshGList(alarm_button, win, NULL, 1L); } /* ** adjust_alarm() ** When user clicks on arrows, we come here and adjust ** our alarm display. The variable active_digit keeps track of the ** digit being changed. This is a big mess!! */ void adjust_alarm(alarm, active_digit, direction) struct my_time *alarm; USHORT active_digit, direction; { switch(active_digit) { case HRS_TEN: if (direction == UP) { if (alarm -> hours >= 14) alarm -> hours = 0; else alarm -> hours += 10; } else { if (alarm -> hours < 9) alarm -> hours = 23; else alarm -> hours -= 10; } break; case HRS_ONE: if (direction == UP) alarm -> hours = (alarm -> hours + 1) % 24; else { if (alarm -> hours == 0) alarm -> hours = 23; else --alarm -> hours; } break; case MIN_TEN: if (direction == UP) { if (alarm -> minutes > 49) alarm -> minutes = 0; else alarm -> minutes += 10; } else { if (alarm -> minutes < 10) alarm -> minutes = 59; else alarm -> minutes -= 10; } break; case MIN_ONE: if (direction == UP) alarm -> minutes = (alarm -> minutes + 1) % 60; else { if (alarm -> minutes == 0) alarm -> minutes = 59; else --alarm -> minutes; } break; } }