/* * Routines for sampling from Soundblaster 8-bit soundcards * These routines require the SB functions and DMA functions written * by Heath I. Hunnicutt. The required functions are included here. * Copyright (C) 1997 Philip VanBaren & Emil Laurentiu * Last modified: Wednesday, 06 August 1997 */ #include "freq.h" #ifdef SC_SB8 #include #include #include #include #include #include "sb.h" #include "sbio.h" #include "extern.h" /* Function prototypes */ unsigned int sbpro_get_line_level( void ); unsigned int sbpro_get_cd_level( void ); unsigned int sbpro_get_mic_level( void ); int atox( char *ptr ); void far interrupt SBHandler( void ); void init_sb8( char **environ ); void reset_sb8( void ); void halt_sb8( void ); void cleanup_sb8( void ); void recordblock_sb8( void far * buffer ); void set_mixer_sb8( int mix, int level ); unsigned int sbpro_get_line_level( void ) { unsigned int val; outportb( sb_addr + 4, 0x2E ); val = inportb( sb_addr + 5 ); return ( ( ( val & 0x0F ) + ( val / 16 ) ) * 100 / 32 ); } unsigned int sbpro_get_cd_level( void ) { unsigned int val; outportb( sb_addr + 4, 0x28 ); val = inportb( sb_addr + 5 ); return ( ( ( val & 0x0F ) + ( val / 16 ) ) * 100 / 32 ); } unsigned int sbpro_get_mic_level( void ) { unsigned int val; outportb( sb_addr + 4, 0x0A ); val = inportb( sb_addr + 5 ); return ( ( val & 0x07 ) * 100 / 8 ); } int atox( char *ptr ) { // Convert ascii hex values to integer int v = 0; while ( ( ( *ptr >= '0' ) && ( *ptr <= '9' ) ) || ( ( ( *ptr | 0x20 ) >= 'a' ) && ( ( *ptr | 0x20 ) <= 'f' ) ) ) { v = v * 16; if ( *ptr <= '9' ) v = v + *ptr - '0'; else v = v + ( *ptr | 0x20 ) - 'a' + 10; ptr++; } return v; } #define is_blaster(var) ((((var)[0]=='B')||((var)[0]=='b')) && \ (((var)[1]=='L')||((var)[1]=='l')) && \ (((var)[2]=='A')||((var)[2]=='a')) && \ (((var)[3]=='S')||((var)[3]=='s')) && \ (((var)[4]=='T')||((var)[4]=='t')) && \ (((var)[5]=='E')||((var)[5]=='e')) && \ (((var)[6]=='R')||((var)[6]=='r'))) void far interrupt( *OldIRQ ) ( ); /* Interrupt handler for DMA complete IRQ from Soundblaster */ void far interrupt SBHandler( ) { flag[record_buffer] = 1; if ( ++record_buffer >= BUFFERS ) record_buffer = 0; inportb( DSP_DATA_AVAIL ); outportb( 0x20, 0x20 ); } void init_sb8( char **environ ) { int i; unsigned char tm, im; long timeout; /* Scan the environment variables for BLASTER=Axxx Ix Dx */ for ( i = 0; environ[i] != NULL; i++ ) { if ( is_blaster( environ[i] ) ) { int j; DOUT( "SB8: Found BLASTER environment variable." ); for ( j = 8; environ[i][j] != 0; j++ ) { if ( ( environ[i][j] == 'A' ) || ( environ[i][j] == 'a' ) ) sb_addr = atox( &environ[i][j + 1] ); if ( ( environ[i][j] == 'D' ) || ( environ[i][j] == 'd' ) ) sb_dma = atoi( &environ[i][j + 1] ); if ( ( environ[i][j] == 'I' ) || ( environ[i][j] == 'i' ) ) sb_irq = atoi( &environ[i][j + 1] ); // Skip to the next parameter while ( ( environ[i][j] != ' ' ) && ( environ[i][j + 1] != 0 ) ) j++; } break; } } #ifdef DEBUG_OUTPUT { char message[100]; sprintf( message, "SB8: Address=0x%03x, DMA=%d, IRQ=%d", sb_addr, sb_dma, sb_irq ); DOUT( message ); } #endif if ( ( sb_dma < 0 ) || ( sb_dma > 3 ) ) { puts( "Only SB DMA channels up to 3 are supported." ); exit( 1 ); } if ( ( sb_irq < 1 ) || ( sb_irq > 7 ) ) { puts( "Only SB IRQs up to 7 are supported." ); exit( 1 ); } reset_soundcard = reset_sb8; halt_soundcard = halt_sb8; cleanup_soundcard = cleanup_sb8; recordblock = recordblock_sb8; sample_size = 8; mixers = 0; set_SB_address( sb_addr ); #ifndef DEBUG_MODE if ( !dsp_reset( ) ) { closegraph( ); printf( "Soundblaster not found at 0x%04x\n", sb_addr ); exit( 1 ); } dsp_voice( 0 ); /* Check if we can do mixers, and enable them, if so */ timeout = 1000000L; /* Wait for bit 7 to be cleared, or for a timeout */ while ( ( inportb( sb_addr + 0x0E ) & 0x80 ) && ( --timeout ) ); outportb( sb_addr + 0x0C, 0xE1 ); /* Get DSP version number */ timeout = 1000000L; /* Wait for bit 7 to be set, or for a timeout */ while ( ( !( inportb( sb_addr + 0x0E ) & 0x80 ) ) && ( --timeout ) ); i = inportb( sb_addr + 0x0A );/* Get the major version number */ while ( ( !( inportb( sb_addr + 0x0E ) & 0x80 ) ) && ( --timeout ) ); inportb( sb_addr + 0x0A ); /* Get the major version number */ if ( i > 2 ) { DOUT( "SB8: Found an SBPro or greater, mixers are available" ); mixers = 1; set_mixer = set_mixer_sb8; mic_level = sbpro_get_mic_level( ); ext_level = sbpro_get_line_level( ); int_level = sbpro_get_cd_level( ); DOUT( "SB8: Set Master volume & FM volume to maximum" ); set_master_level( 0x0f ); set_fm_level( 0x0f ); } /* * Add the SB DMA interrupt handler in the interrupt chain */ disable( ); OldIRQ = getvect( 0x08 + sb_irq ); setvect( 0x08 + sb_irq, SBHandler ); im = inportb( 0x21 ); tm = ~( 1 << sb_irq ); outportb( 0x21, im & tm ); enable( ); #endif } void reset_sb8( void ) { /* * Initialize Soundblaster stuff */ int i, dsp_tc; #ifndef DEBUG_MODE /* Round sampling rate to a valid value for the SB card */ i = floor( 1000000.0 / SampleRate + 0.5 ); if ( i < 1 ) i = 1; SampleRate = floor( 1000000.0 / ( double ) i + 0.5 ); dsp_tc = 256 - i; DOUT( "SB8: Setting sampling rate" ); dsp_time( dsp_tc ); /* * Initialize the SB DMA channel */ DOUT( "SB8: Resetting the DMA channel" ); if ( dma_reset( sb_dma ) ) { closegraph( ); puts( "Error resetting SB DMA channel." ); puts( dma_errlist[dma_errno] ); cleanup_sb8( ); exit( 1 ); } // Reset the buffer pointers queue_buffer = 0; // Pointer to next buffer to be queued record_buffer = 0; // Pointer to next buffer to be filled process_buffer = 0; // Pointer to next buffer to be FFTed for ( i = 0; i < BUFFERS; i++ ) flag[i] = 0; /* * This function starts the DMA process. */ DOUT( "SB8: Starting the DMA recording process" ); recordblock_sb8( buffer[queue_buffer] ); if ( ++queue_buffer >= BUFFERS ) queue_buffer = 0; #endif } void halt_sb8( void ) { #ifndef DEBUG_MODE /* Shut down the DMA transfers */ DOUT( "SB8: Stopping the DMA transfer" ); disable( ); dma_reset( sb_dma ); dsp_reset( ); enable( ); #endif } void cleanup_sb8( void ) { // Clean up the soundcard routines unsigned char im, tm; #ifndef DEBUG_MODE DOUT( "SB8: Cleaning up the soundcard setup" ); disable( ); dma_reset( sb_dma ); dsp_reset( ); Sb_FM_Reset( ); /* Restore old IRQ vector */ setvect( 0x08 + sb_irq, OldIRQ ); im = inportb( 0x21 ); tm = 1 << sb_irq; outportb( 0x21, im | tm ); enable( ); #endif } void recordblock_sb8( void far * buffer ) { #ifndef DEBUG_MODE /* * Start recording a new buffer. For now we don't have queueing * capabilities for the SB */ if ( dma_setup( sb_dma, buffer, fftlen, 0 ) ) { closegraph( ); printf( "Error in dma_setup(): %d\n", dma_errno ); puts( dma_errlist[dma_errno] ); cleanup_sb8( ); exit( 1 ); } if ( dma_errno != 0 ) { closegraph( ); puts( "DMA error" ); puts( dma_errlist[dma_errno] ); cleanup_sb8( ); exit( 1 ); } dsp_dma_prepare( 0, fftlen ); #endif } void set_mixer_sb8( int mix, int level ) { #ifndef DEBUG_MODE /* * Set a mixer level on the PAS16 card */ level = level * 16 / 100; if ( level > 15 ) level = 15; if ( mix == MIXER_EXT ) { DOUT( "SB8: Setting the line mixer level" ); level = level + level * 16; outportb( sb_addr + 4, 0x2e ); outportb( sb_addr + 5, level ); outportb( sb_addr + 4, 0x0C ); /* Select the line input */ outportb( sb_addr + 5, 0x27 ); } else if ( mix == MIXER_INT ) { DOUT( "SB8: Setting the CD mixer level" ); level = level + level * 16; outportb( sb_addr + 4, 0x28 ); outportb( sb_addr + 5, level ); outportb( sb_addr + 4, 0x0C ); /* Select the CD input */ outportb( sb_addr + 5, 0x23 ); } else if ( mix == MIXER_MIC ) { DOUT( "SB8: Setting the microphone mixer level" ); outportb( sb_addr + 4, 0x0a ); outportb( sb_addr + 5, level / 2 ); outportb( sb_addr + 4, 0x0C ); /* Select the mic input */ outportb( sb_addr + 5, 0x21 ); } #endif } #endif