/* SCCS Id: @(#)amisnd.c 3.2 2000/01/12*/ /* Copyright (c) 1992, 1993, 1995 by Gregg Wonderly */ /* NetHack may be freely redistributed. See license for details. */ /* * This file contains music playing code. * * If we were REALLY determined, we would make the sound play * asynchronously, but I'll save that one for a rainy day... */ #include "hack.h" #include "dlb.h" #undef red #undef blue #undef green #include #include #include #include #include #include #include #include #include #include #include #include #include #define AMII_AVERAGE_VOLUME 60 int amii_volume = AMII_AVERAGE_VOLUME; typedef struct VHDR { char name[4]; long len; unsigned long oneshot, repeat, samples; UWORD freq; UBYTE n_octaves, compress; LONG volume; } VHDR; typedef struct IFFHEAD { char FORM[4]; long flen; char _8SVX[4]; VHDR vhdr; char NAME[4]; long namelen; } IFFHEAD; extern struct GfxBase *GfxBase; UBYTE whichannel[] = { 1, 2, 4, 8 }; void makesound( char *, char *, int vol); void amii_speaker( struct obj *instr, char *melody, int vol ); /* A major scale in indexs to freqtab... */ int notetab[] = { 0, 2, 4, 5, 7, 9, 11, 12 }; /* Frequencies for a scale starting at one octave below 'middle C' */ long freqtab[] = { 220, /*A */ 233, /*Bb*/ 246, /*B */ 261, /*C */ 277, /*Db*/ 293, /*D */ 311, /*Eb*/ 329, /*E */ 349, /*F */ 370, /*Gb*/ 392, /*G */ 415, /*Ab*/ 440, /*A */ }; #ifdef TESTING main( argc, argv ) int argc; char **argv; { makesound( "wooden_flute", "AwBwCwDwEwFwGwawbwcwdwewfwgw", 60 ); makesound( "wooden_flute", "AhBhChDhEhFhGhahbhchdhehfhgh", 60 ); makesound( "wooden_flute", "AqBqCqDqEqFqGqaqbqcqdqeqfqgq", 60 ); makesound( "wooden_flute", "AeBeCeDeEeFeGeaebecedeeefege", 60 ); makesound( "wooden_flute", "AxBxCxDxExFxGxaxbxcxdxexfxgx", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); makesound( "wooden_flute", "AtBtCtDtEtFtGtatbtctdtetftgt", 60 ); } #else void amii_speaker( struct obj *instr, char *melody, int vol ) { int typ = instr->otyp; char * actualn = (char *)OBJ_NAME( objects[typ] ) ; /* Make volume be relative to users volume level, with 60 being the * average level that will be passed to us. */ vol = vol * amii_volume / AMII_AVERAGE_VOLUME; makesound( actualn, melody, vol ); } #endif void makesound ( char *actualn , char * melody, int vol ) { char *t; int c, cycles, dot, dlay; dlb *stream = 0; IFFHEAD iffhead; struct IOAudio *AudioIO = 0; struct MsgPort *AudioMP = 0; struct Message *AudioMSG = 0; ULONG device = -1; BYTE *waveptr = 0; LONG frequency=440, duration=1, clock, samp, samples, samcyc=1; unsigned char name [ 100 ] ; if ( flags.silent ) return; if( GfxBase->DisplayFlags & PAL ) clock = 3546895; else clock = 3579545; /* * Convert type to file name - if there's nothing to play we * shouldn't be here in the first place. */ strncpy(name, actualn,sizeof(name) ) ; for( t = strchr( name, ' ' ); t; t = strchr( name, ' ' ) ) *t = '_'; if( (stream = dlb_fopen( name, "r" )) == NULL ) { perror( name ); return; } AudioIO = (struct IOAudio *) AllocMem( sizeof( struct IOAudio ), MEMF_PUBLIC|MEMF_CLEAR ); if( AudioIO == 0 ) goto killaudio; AudioMP = CreatePort( NULL, 0 ); if( AudioMP == 0 ) goto killaudio; AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP; AudioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0; AudioIO->ioa_Request.io_Command = ADCMD_ALLOCATE; AudioIO->ioa_Request.io_Flags = ADIOF_NOWAIT; AudioIO->ioa_AllocKey = 0; AudioIO->ioa_Data = whichannel; AudioIO->ioa_Length = sizeof( whichannel ); device = OpenDevice( AUDIONAME, 0L, (struct IORequest *)AudioIO, 0L ); if( device != 0 ) goto killaudio; if( dlb_fread( (genericptr_t)&iffhead, sizeof( iffhead ), 1, stream ) != 1 ) goto killaudio; /* This is an even number of bytes long */ if( dlb_fread( name, (iffhead.namelen+1) & ~1, 1, stream ) != 1 ) goto killaudio; if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 ) goto killaudio; if( dlb_fread( (genericptr_t)&samples, 4, 1, stream ) != 1 ) goto killaudio; waveptr = AllocMem( samples, MEMF_CHIP|MEMF_PUBLIC ); if( !waveptr ) goto killaudio; if( dlb_fread( waveptr, samples, 1, stream ) != 1 ) goto killaudio; while( melody[0] && melody[1] ) { c = *melody++; duration = *melody++; dot = 0; if( *melody == '.' ) { dot = 1; ++melody; } switch( duration ) { case 'w': dlay = 3; duration = 1; cycles = 1; break; case 'h': dlay = 3; duration = 2; cycles = 1; break; case 'q': dlay = 2; duration = 4; cycles = 1; break; case 'e': dlay = 1; duration = 8; cycles = 1; break; case 'x': dlay = 0; duration = 16; cycles = 1; break; case 't': dlay = 0; duration = 32; cycles = 1; break; default: goto killaudio; /* unrecognized duration */ } /* Lower case characters are one octave above upper case */ switch( c ) { case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': c -= 'a' - 7; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': c -= 'A'; break; default: continue; } samcyc = samples; /* lowercase start at middle 'C', upper case are one octave below */ frequency = c > 7 ? freqtab[notetab[c%7]]*2 : freqtab[notetab[c]]; /* We can't actually do a dotted whole note unless we add code for a real * 8SVX sample which includes sustain sample information to tell us how * to hold the note steady... So when duration == 1, ignore 'dot'... */ if( dot && duration > 1 ) samp = ((samples / duration) * 3) / 2; else samp = samples / duration; /* Only use some of the samples based on frequency */ samp = frequency * samp / 880; /* The 22khz samples are middle C samples, so adjust the play * back frequency accordingly */ frequency = (frequency * (iffhead.vhdr.freq*2)/3) / 440L; AudioIO->ioa_Request.io_Message.mn_ReplyPort = AudioMP; AudioIO->ioa_Request.io_Command = CMD_WRITE; AudioIO->ioa_Request.io_Flags = ADIOF_PERVOL; AudioIO->ioa_Data = (BYTE *)waveptr; AudioIO->ioa_Length = samp; /* The clock rate represents the unity rate, so dividing by * the frequency gives us a period ratio... */ /*printf( "clock: %ld, freq: %ld, div: %ld\n", clock, frequency, clock/frequency );*/ AudioIO->ioa_Period = clock/frequency; AudioIO->ioa_Volume = vol; AudioIO->ioa_Cycles = cycles; BeginIO( (struct IORequest *)AudioIO ); WaitPort( AudioMP ); AudioMSG = GetMsg( AudioMP ); if( dlay ) Delay( dlay ); } killaudio: if( stream ) dlb_fclose( stream ); if( waveptr ) FreeMem( waveptr, samples ); if( device == 0 ) CloseDevice( (struct IORequest *)AudioIO ); if( AudioMP ) DeletePort( AudioMP ); if( AudioIO ) FreeMem( AudioIO, sizeof( *AudioIO ) ); }