home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************/
- /* */
- /* Amiga C Encyclopedia (ACE) V3.0 Amiga C Club (ACC) */
- /* ------------------------------- ------------------ */
- /* */
- /* Book: ACM Devices Amiga C Club */
- /* Chapter: AudioDevice Tulevagen 22 */
- /* File: Example6.c 181 41 LIDINGO */
- /* Author: Anders Bjerin SWEDEN */
- /* Date: 92-04-24 */
- /* Version: 1.00 */
- /* */
- /* Copyright 1992, Anders Bjerin - Amiga C Club (ACC) */
- /* */
- /* Registered members may use this program freely in their */
- /* own commercial/noncommercial programs/articles. */
- /* */
- /***********************************************************/
-
- /* This program will play some notes (A to G#) with help */
- /* of the Audio Device. It will use as many audio channels */
- /* as possible, and we are modifying the hardware registers */
- /* directly instead of using the special Audio Device */
- /* commands. */
- /* */
- /* You are allowed to use the hardware registers directly */
- /* if you make sure that no other task can steel them from */
- /* you before you have cleared all necessary registers. */
-
-
-
- #include <exec/types.h> /* STRPTR */
- #include <exec/memory.h> /* MEMF_CHIP */
- #include <devices/audio.h> /* Audio Device */
- #include <hardware/custom.h> /* struct Custom */
- #include <hardware/dmabits.h> /* DMAF_SETCLR */
-
-
-
- /* The audio channels: (Sadly these constants */
- /* have not been defined in any header file.) */
-
- /* Values: */
- #define LEFT0B 0
- #define RIGHT0B 1
- #define RIGHT1B 2
- #define LEFT1B 3
-
- /* Bit fields: */
- #define LEFT0F (1<<LEFT0B)
- #define RIGHT0F (1<<RIGHT0B)
- #define RIGHT1F (1<<RIGHT1B)
- #define LEFT1F (1<<LEFT1B)
-
- /* Sound priorities: */
- #define SOUND_UNSTOPPABLE 127
- #define SOUND_EMERGENCIES 95
- #define SOUND_ATTENTION 85
- #define SOUND_SPEECH 75
- #define SOUND_INFORMATION 60
- #define SOUND_MUSIC 0
- #define SOUND_EFFECT -35
- #define SOUND_BACKGROUND -90
- #define SOUND_SILENCE -128
-
- /* The clock constant: */
- #define NTSC_CLOCK 3579545 /* American Amigas - 60Hz */
- #define PAL_CLOCK 3546895 /* European Amigas - 50Hz */
-
- /* Some common notes (their frequencies are */
- /* defined later on in this program): */
- #define NOTE_A 0
- #define NOTE_Ax 1
- #define NOTE_B 2
- #define NOTE_C 3
- #define NOTE_Cx 4
- #define NOTE_D 5
- #define NOTE_Dx 6
- #define NOTE_E 7
- #define NOTE_F 8
- #define NOTE_Fx 9
- #define NOTE_G 10
- #define NOTE_Gx 11
-
- /* An octave consists of 12 notes: */
- #define OCTAVE 12
-
- /* Define min/max-volumes: */
- #define MAXVOLUME 64
- #define MINVOLUME 0
-
- /* Our square waveform data consists of two samples: */
- /* (Waveform data must alwyas be an even number of */
- /* byte long.) */
- #define SQUARE_DATA_LENGTH 2
-
- /* Timer units (50 units / second): */
- #define SECONDS *50
-
-
-
- /* Declare a pointer to our reply port: */
- struct MsgPort *replymp = NULL;
-
- /* Declare a pointer to our audio request block: */
- struct IOAudio *audio_req = NULL;
-
- /* Declare a pointer to our lock: */
- struct IOAudio *audio_lock = NULL;
-
-
-
- /* Our list of preffered channel combinations: */
- /* (We want as many channels as possible. The */
- /* way I write it may look a bit strange, but */
- /* it is actually easier to understand this */
- /* code, than if we had removed all spaces.) */
-
- UBYTE desired_channels[]=
- {
- LEFT0F | RIGHT0F | RIGHT1F | LEFT1F,
- LEFT0F | RIGHT0F | RIGHT1F ,
- LEFT0F | RIGHT0F | LEFT1F,
- LEFT0F | RIGHT0F ,
- LEFT0F | RIGHT1F | LEFT1F,
- LEFT0F | RIGHT1F ,
- LEFT0F | LEFT1F,
- LEFT0F ,
- RIGHT0F | RIGHT1F | LEFT1F,
- RIGHT0F | RIGHT1F ,
- RIGHT0F | LEFT1F,
- RIGHT0F ,
- RIGHT1F | LEFT1F,
- RIGHT1F ,
- LEFT1F,
- };
-
-
-
- /* Declare a pointer to some soundwave data: */
- BYTE *square_wave = NULL;
-
-
-
- /* The notes (defined above) frequencies. These frequencies */
- /* represent notes which are one octave higher than the middle */
- /* octave on a piano. To change octave, simply double/half these */
- /* values. Ex, A=880, one octave lower A=440, one octave higher */
- /* A=1760. */
- /* */
- /* Instead of changing the frequencies you can of course double */
- /* or half the amount of samled waveform data. If you double the */
- /* amount of sampled waveformdata you will move down one octave */
- /* and vice versa. In this example when we caluculate the period */
- /* value we use the length of the vaweform as one parameter. */
- /* Therefore, if you change the length of the waveform the same */
- /* frequencies will be used. */
-
- UWORD note_frequency[ OCTAVE ]=
- {
- 880.0, /* A */
- 932.3, /* A# */
- 987.8, /* B */
- 1046.5, /* C */
- 1108.7, /* C# */
- 1174.7, /* D */
- 1244.5, /* D# */
- 1318.5, /* E */
- 1396.9, /* F */
- 1480.0, /* F# */
- 1568.0, /* G */
- 1661.2 /* G# */
- };
-
-
-
- /* These structure are defined in the headerfile "hardware/custom.h" */
- /* and are automatically connected to the hardware registers. We do */
- /* therefore not need to initialize them. */
-
- extern UWORD far dmacon; /* DMA control */
- extern struct AudChannel far aud[]; /* Audio channels. */
-
- /* Since the hardware data most certainly will not be within reach */
- /* for normal (small) pointers, they must both be declared as far. */
-
-
-
- /* Declare our functions: */
- void main();
- void clean_up( STRPTR text );
-
-
-
- void main()
- {
- /* Error messages: */
- BYTE error;
-
- /* The channel we have received: */
- UBYTE channels;
-
- /* Current note: */
- int note;
-
- /* Pointer to the audio register: */
- /* (If we have successfuly reserved */
- /* the sound channel we initialize the */
- /* corresponding pointer, else it */
- /* remains NULL.) */
- struct AudChannel *left0_audio_register = NULL;
- struct AudChannel *left1_audio_register = NULL;
- struct AudChannel *right0_audio_register = NULL;
- struct AudChannel *right1_audio_register = NULL;
-
-
-
- /* Get a reply port: (No name, priority 0) */
- replymp = (struct MsgPort *)
- CreatePort( NULL, 0 );
- if( !replymp )
- clean_up( "Could not create the reply port!" );
-
-
-
- /* Allocate and preinitialize an audio request block: */
- audio_req = (struct IOAudio *)
- CreateExtIO( replymp, sizeof( struct IOAudio ) );
- if( !audio_req )
- clean_up( "Not enough memory for the IOAudio structure!" );
-
-
-
- /* Allocate memory for the lock: */
- audio_lock = (struct IOAudio *)
- CreateExtIO( replymp, sizeof( struct IOAudio ) );
- if( !audio_lock )
- clean_up( "Not enough memory for the Lock!" );
-
-
-
- /* Set sound priority: */
- audio_req->ioa_Request.io_Message.mn_Node.ln_Pri = SOUND_EFFECT;
-
- /* Give the audio structure our replyport: */
- audio_req->ioa_Request.io_Message.mn_ReplyPort = replymp;
-
- /* Tell the Audio Device which channels we preffere: */
- audio_req->ioa_Data = desired_channels;
-
- /* The size of our list of desired channels: */
- audio_req->ioa_Length = sizeof( desired_channels );
-
-
-
- /* Open the Audio Device and at the same time try to */
- /* reserve the channel(s): */
- error = OpenDevice( AUDIONAME, 0, audio_req, 0 );
- if( error )
- {
- /* Clear the "io_Device" flag since we have not opened the device: */
- audio_req->ioa_Request.io_Device = NULL;
-
- /* Quit: */
- clean_up( "Could not open the Audio Device!" );
- }
-
-
-
- /* Check which channel we received: */
- channels = (UBYTE) audio_req->ioa_Request.io_Unit;
-
- if( channels & LEFT0F )
- {
- printf( "First left sound channel.\n" );
- left0_audio_register = &aud[ LEFT0B ];
- }
-
- if( channels & RIGHT0F )
- {
- printf( "First right sound chanel.\n" );
- right0_audio_register = &aud[ RIGHT0B ];
- }
-
- if( channels & RIGHT1F )
- {
- printf( "Second right sound channel.\n" );
- right1_audio_register = &aud[ RIGHT1B ];
- }
-
- if( channels & LEFT1F )
- {
- printf( "Second left sound channel.\n" );
- left1_audio_register = &aud[ LEFT1B ];
- }
-
-
-
- /* Directly after we have received a channel we lock it: */
-
- /* We want to lock some channels: */
- audio_lock->ioa_Request.io_Command = ADCMD_LOCK;
-
- /* The lock should use our reply port: */
- audio_lock->ioa_Request.io_Message.mn_ReplyPort = replymp;
-
- /* Use the same audio device: */
- audio_lock->ioa_Request.io_Device = audio_req->ioa_Request.io_Device;
-
- /* Lock all our channels: */
- audio_lock->ioa_Request.io_Unit = audio_req->ioa_Request.io_Unit;
-
- /* Allocate: */
- audio_lock->ioa_AllocKey = audio_req->ioa_AllocKey;
-
- /* Set the lock: (SendIO() is OK to use) */
- SendIO( audio_lock );
-
- /* Check if the channel has been stolen: */
- if( CheckIO( audio_lock ) )
- clean_up( "Our channel was stolen!" );
-
-
-
- /* Allocate some memory where we can store the waveform we */
- /* want to use. Note that it must be Chip memory, and placed */
- /* on a word boundary! */
- square_wave = (BYTE *) AllocMem( SQUARE_DATA_LENGTH, MEMF_CHIP );
- if( !square_wave )
- clean_up( "Could not allocate enough memory for the square wave!" );
-
- /* Initialize the waveform: (This is the smallest */
- /* waveform you can use, and undouptly the easiest.) */
- square_wave[ 0 ] = 127;
- square_wave[ 1 ] = -127;
-
-
-
-
- /* We will now initialize all hardware registers of the */
- /* sound channels we successfully reserved. */
-
- /* First left sound channel: */
- if( left0_audio_register )
- {
- /* Pointer to the waveform: */
- left0_audio_register->ac_ptr = (UWORD *) square_wave;
-
- /* Set the lenght of the waveform */
- left0_audio_register->ac_len = SQUARE_DATA_LENGTH;
-
- /* Set the period value: */
- left0_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ 0 ] / SQUARE_DATA_LENGTH;
-
- /* Maximum volume: */
- left0_audio_register->ac_vol = MAXVOLUME;
- }
-
-
-
- /* First right sound channel: */
- if( right0_audio_register )
- {
- /* Pointer to the waveform: */
- right0_audio_register->ac_ptr = (UWORD *) square_wave;
-
- /* Set the lenght of the waveform */
- right0_audio_register->ac_len = SQUARE_DATA_LENGTH;
-
- /* Set the period value: */
- right0_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ 0 ] / SQUARE_DATA_LENGTH;
-
- /* Maximum volume: */
- right0_audio_register->ac_vol = MAXVOLUME;
- }
-
-
-
- /* Second right sound channel: */
- if( right1_audio_register )
- {
- /* Pointer to the waveform: */
- right1_audio_register->ac_ptr = (UWORD *) square_wave;
-
- /* Set the lenght of the waveform */
- right1_audio_register->ac_len = SQUARE_DATA_LENGTH;
-
- /* Set the period value: */
- right1_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ 0 ] / SQUARE_DATA_LENGTH;
-
- /* Maximum volume: */
- right1_audio_register->ac_vol = MAXVOLUME;
- }
-
-
-
- /* Second left sound channel: */
- if( left1_audio_register )
- {
- /* Pointer to the waveform: */
- left1_audio_register->ac_ptr = (UWORD *) square_wave;
-
- /* Set the lenght of the waveform */
- left1_audio_register->ac_len = SQUARE_DATA_LENGTH;
-
- /* Set the period value: */
- left1_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ 0 ] / SQUARE_DATA_LENGTH;
-
- /* Maximum volume: */
- left1_audio_register->ac_vol = MAXVOLUME;
- }
-
-
-
- /* Now all values have been set as desired, now tell the */
- /* hardware that we want to have some music!: */
-
- dmacon = DMAF_SETCLR | channels;
-
-
- /* Tell the user to be prepared: */
- printf( "Here comes some notes: (A to G#)\n" );
-
- /* Take a small pause: */
- Delay( 1 SECONDS );
-
- /* Play one octave: */
- for( note=1; note < OCTAVE; note++ )
- {
- /* Check if the channel has been stolen: */
- if( CheckIO( audio_lock ) )
- clean_up( "Someone wants one (or more) of our sound channels!" );
-
- /* Change the period value: */
- left0_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ note ] / SQUARE_DATA_LENGTH;
-
- right0_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ note ] / SQUARE_DATA_LENGTH;
-
- right1_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ note ] / SQUARE_DATA_LENGTH;
-
- left1_audio_register->ac_per =
- PAL_CLOCK / note_frequency[ note ] / SQUARE_DATA_LENGTH;
-
- /* Remember that you may only write to these hardware */
- /* registers and not read them. If you try to read these */
- /* registers the values may be corrupted, and the sound */
- /* may be destroyed. When you are using this type of */
- /* hardware registers you should therefore never try to */
- /* use commands like +=, -=, *= etc... */
-
-
-
- /* Print a mark: */
- printf( "*" );
-
- /* Take a small pause: */
- Delay( 1 SECONDS );
- }
-
-
- /* Clean up and quit: */
- clean_up( "The End!" );
- }
-
-
-
- /* Close and return everything that has been */
- /* opened and allocated before we quit: */
-
- void clean_up( STRPTR text )
- {
- /* If we have an audio request block, and it does */
- /* not contain any errors, we must free its sound */
- /* channels: */
- if( audio_req && !(audio_req->ioa_Request.io_Error) )
- {
- /* Free the channel(s): */
- audio_req->ioa_Request.io_Command = ADCMD_FREE;
-
- /* We are allowed to use the function DoIO() for */
- /* this request since it will not change any */
- /* values that are vital for us: */
- DoIO( audio_req );
-
- /* The lock is automatically unlocked when we */
- /* free the audio channel. */
- }
-
- /* Empty the reply port: */
- while( GetMsg( replymp ) )
- printf( "Collected a message at the reply port.\n" );
-
- /* If we have a request block and the "io_Device" field */
- /* is not zero, we know that the device has successfully */
- /* been opened and must now be closed: */
- if( audio_req && audio_req->ioa_Request.io_Device )
- CloseDevice( audio_req );
-
- /* Remove the replyport: */
- if( replymp )
- DeletePort( replymp);
-
- /* Dealocate the IOAudio structure: */
- if( audio_req )
- DeleteExtIO( audio_req, sizeof( struct IOAudio ) );
-
- /* Dealocate the lock: */
- if( audio_lock )
- DeleteExtIO( audio_lock, sizeof( struct IOAudio ) );
-
- /* Dealocate the square waveform: */
- if( square_wave )
- FreeMem( square_wave, SQUARE_DATA_LENGTH );
-
- /* Print the last message: */
- printf( "%s\n", text );
-
- /* Quit: */
- exit( 0 );
- }
-
-
-