Only in SDL-1.2.4/: SDL.spec
diff -rc --exclude=Makefile SDL-1.2.4/include/SDL_audio.h SDL-1.2.4-record/include/SDL_audio.h
*** SDL-1.2.4/include/SDL_audio.h	Thu Apr 11 07:35:13 2002
--- SDL-1.2.4-record/include/SDL_audio.h	Thu Apr 18 14:48:44 2002
***************
*** 73,78 ****
--- 73,82 ----
  #define AUDIO_U16	AUDIO_U16LSB
  #define AUDIO_S16	AUDIO_S16LSB
  
+ /* Audio format flags abused to indicate audio mode */
+ #define AUDIO_INPUT	0x100	/* Recording */
+ #define AUDIO_OUTPUT	0x200	/* Playback */
+ 
  /* Native audio byte ordering */
  #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  #define AUDIO_U16SYS	AUDIO_U16LSB
***************
*** 198,203 ****
--- 202,225 ----
  /* Compatibility convenience function -- loads a WAV from a file */
  #define SDL_LoadWAV(file, spec, audio_buf, audio_len) \
  	SDL_LoadWAV_RW(SDL_RWFromFile(file, "rb"),1, spec,audio_buf,audio_len)
+ 
+ /*
+  * This function saves a WAVE to the data destination, automatically
+  * freeing that destination if 'freedst' is non-zero.  For example, to
+  * save a WAVE file, you could do:
+  *	SDL_SaveWAV_RW(SDL_RWFromFile("sample.wav", "rb"), 1, ...);
+  *
+  * This function returns 0 if successful. You need to free the audio
+  * buffer with SDL_FreeWAV() when you are done with it.
+  *
+  * This function returns -1 and sets the SDL error message if anything
+  * goes wrong. Currently only standard PCM WAV files are supported.
+  */
+ extern DECLSPEC int SDLCALL SDL_SaveWAV_RW(SDL_RWops *dst, int freedst, const SDL_AudioSpec *spec, const Uint8 *audio_buf, Uint32 audio_len);
+ 
+ /* Compatibility convenience function -- saves a WAV to a file */
+ #define SDL_SaveWAV(file, spec, audio_buf, audio_len) \
+ 	SDL_SaveWAV_RW(SDL_RWFromFile(file, "wb"),1, spec,audio_buf,audio_len)
  
  /*
   * This function frees data previously allocated with SDL_LoadWAV_RW()
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/SDL_audio.c SDL-1.2.4-record/src/audio/SDL_audio.c
*** SDL-1.2.4/src/audio/SDL_audio.c	Sat Mar 30 11:48:56 2002
--- SDL-1.2.4-record/src/audio/SDL_audio.c	Tue Apr 16 16:26:13 2002
***************
*** 105,111 ****
  	Uint8 *stream;
  	int    stream_len;
  	void  *udata;
! 	void (*fill)(void *userdata,Uint8 *stream, int len);
  	int    silence;
  #ifdef ENABLE_AHI
  	int started = 0;
--- 105,111 ----
  	Uint8 *stream;
  	int    stream_len;
  	void  *udata;
! 	void (*callback)(void *userdata,Uint8 *stream, int len);
  	int    silence;
  #ifdef ENABLE_AHI
  	int started = 0;
***************
*** 130,136 ****
  	audio->threadid = SDL_ThreadID();
  
  	/* Set up the mixing function */
! 	fill  = audio->spec.callback;
  	udata = audio->spec.userdata;
  
  #ifdef ENABLE_AHI
--- 130,136 ----
  	audio->threadid = SDL_ThreadID();
  
  	/* Set up the mixing function */
! 	callback = audio->spec.callback;
  	udata = audio->spec.userdata;
  
  #ifdef ENABLE_AHI
***************
*** 162,217 ****
  
  	/* Loop, filling the audio buffers */
  	while ( audio->enabled ) {
! 
! 		/* Wait for new current buffer to finish playing */
! 		if ( stream == audio->fake_stream ) {
! 			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
! 		} else {
  #ifdef ENABLE_AHI
! 			if ( started > 1 )
  #endif
! 			audio->WaitAudio(audio);
  		}
  
! 		/* Fill the current buffer with sound */
! 		if ( audio->convert.needed ) {
! 			if ( audio->convert.buf ) {
! 				stream = audio->convert.buf;
  			} else {
! 				continue;
  			}
! 		} else {
! 			stream = audio->GetAudioBuf(audio);
! 			if ( stream == NULL ) {
! 				stream = audio->fake_stream;
  			}
  		}
- 		memset(stream, silence, stream_len);
  
! 		if ( ! audio->paused ) {
  			SDL_mutexP(audio->mixer_lock);
! 			(*fill)(udata, stream, stream_len);
  			SDL_mutexV(audio->mixer_lock);
  		}
  
! 		/* Convert the audio if necessary */
! 		if ( audio->convert.needed ) {
! 			SDL_ConvertAudio(&audio->convert);
! 			stream = audio->GetAudioBuf(audio);
! 			if ( stream == NULL ) {
! 				stream = audio->fake_stream;
! 			}
! 			memcpy(stream, audio->convert.buf,
! 			               audio->convert.len_cvt);
  		}
  
! 		/* Ready current buffer for play and change current buffer */
! 		if ( stream != audio->fake_stream ) {
! 			audio->PlayAudio(audio);
  #ifdef ENABLE_AHI
  /* AmigaOS don't have to wait the first time audio is played! */
! 			started++;
  #endif
  		}
  	}
  	/* Wait for the audio to drain.. */
--- 162,294 ----
  
  	/* Loop, filling the audio buffers */
  	while ( audio->enabled ) {
! 		/* If the device is opened for recording */
! 		if (audio->input_mode) {
! 			/* Wait for current buffer to finish recording */
! 			if ( stream == audio->fake_stream ) {
! 				SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
! 			} else {
  #ifdef ENABLE_AHI
! 				if ( started > 1 )
  #endif
! 				audio->WaitAudioRecord(audio);
! 			}
! 
! 			/* Select the current record buffer */
! 			if ( audio->convert_record.needed ) {
! 				if ( audio->convert_record.buf ) {
! 					stream = audio->convert_record.buf;
! 				} else {
! 					continue;
! 				}
! 			} else {
! 				stream = audio->GetAudioRecordBuf(audio);
! 	 			if ( stream == NULL ) {
! 					stream = audio->fake_stream;
! 				}
! 			}
! 
! 			/* Record audio data from sound device */
! 			if ( stream != audio->fake_stream ) {
! 				audio->RecordAudio(audio);
! 			}
! 
! 			/* Convert the audio if necessary */
! 			if ( audio->convert_record.needed ) {
! 				SDL_ConvertAudio(&audio->convert_record);
! 				stream = audio->GetAudioRecordBuf(audio);
! 				if ( stream == NULL ) {
! 					stream = audio->fake_stream;
! 				}
! 				memcpy(stream, audio->convert_record.buf,
! 			               audio->convert_record.len_cvt);
! 			}
  		}
  
! 		/* If the device is opened for playback */
! 		if (audio->output_mode) {
! 			/* Wait for current buffer to finish playing */
! 			if ( stream == audio->fake_stream ) {
! 				SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
  			} else {
! #ifdef ENABLE_AHI
! 				if ( started > 1 )
! #endif
! 				audio->WaitAudio(audio);
  			}
! 		}
! 
! 
! 		/* If the device is NOT opened for recording */
! 		if (!audio->input_mode) {
! 			/* Select the current playback buffer and clear it */
! 			if ( audio->convert.needed ) {
! 				if ( audio->convert.buf ) {
! 					stream = audio->convert.buf;
! 				} else {
! 					continue;
! 				}
! 			} else {
! 				stream = audio->GetAudioBuf(audio);
! 				if ( stream == NULL ) {
! 					stream = audio->fake_stream;
! 				}
  			}
+ 			memset(stream, silence, stream_len);
  		}
  
! 		/* Send recorded audio data and receive playback audio data */
! 		if ( audio->paused ) {
! 			memset(stream, silence, stream_len);
! 		} else {
  			SDL_mutexP(audio->mixer_lock);
! 			(*callback)(udata, stream, stream_len);
  			SDL_mutexV(audio->mixer_lock);
  		}
  
! 		/* If the device is opened for recording and playback */
! 		if (audio->input_mode && audio->output_mode) {
! 			/* Copy callback buffer into proper playback buffer */
! 			if ( audio->convert.needed ) {
! 				if ( audio->convert.buf ) {
! 					memcpy(audio->convert.buf, stream,
! 						audio->convert.len);
! 					stream = audio->convert.buf;
! 				} else {
! 					continue;
! 				}
! 			} else {
! 				memcpy(audio->GetAudioBuf(audio), stream,
! 					stream_len);
! 				stream = audio->GetAudioBuf(audio);
! 				if ( stream == NULL ) {
! 					stream = audio->fake_stream;
! 				}
! 			}
  		}
  
! 
! 		/* If the device is opened for playback */
!                 if (audio->output_mode) {
! 			/* Convert the audio if necessary */
! 			if ( audio->convert.needed ) {
! 				SDL_ConvertAudio(&audio->convert);
! 				stream = audio->GetAudioBuf(audio);
! 				if ( stream == NULL ) {
! 					stream = audio->fake_stream;
! 				}
! 				memcpy(stream, audio->convert.buf,
! 				               audio->convert.len_cvt);
! 			}
! 
! 			/* Play audio data with sound device */
! 			if ( stream != audio->fake_stream ) {
! 				audio->PlayAudio(audio);
  #ifdef ENABLE_AHI
  /* AmigaOS don't have to wait the first time audio is played! */
! 				started++;
  #endif
+ 			}
  		}
  	}
  	/* Wait for the audio to drain.. */
***************
*** 346,351 ****
--- 423,429 ----
  int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
  {
  	SDL_AudioDevice *audio;
+ 	Uint16 format_abuse_flags;
  
  	/* Start up the audio driver, if necessary */
  	if ( ! current_audio ) {
***************
*** 361,366 ****
--- 439,461 ----
  		return(-1);
  	}
  
+ 	/* Determine the playback/recording mode */
+ 	if (desired->format & AUDIO_INPUT && desired->format & AUDIO_OUTPUT) {
+ 		audio->input_mode = 1;
+ 		audio->output_mode = 1;
+ 	} else if (desired->format & AUDIO_INPUT) {
+ 		audio->input_mode = 1;
+ 		audio->output_mode = 0;
+ 	} else {
+ 		desired->format |= AUDIO_OUTPUT;
+ 		audio->input_mode = 0;
+ 		audio->output_mode = 1;
+ 	}
+ 
+ 	/* Clear abused format bits so original SDL code doesn't get confused */
+ 	format_abuse_flags = desired->format & (AUDIO_INPUT | AUDIO_OUTPUT);
+ 	desired->format &= ~(AUDIO_INPUT | AUDIO_OUTPUT);
+ 
  	/* Verify some parameters */
  	if ( desired->callback == NULL ) {
  		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
***************
*** 393,405 ****
--- 488,503 ----
  	/* Open the audio subsystem */
  	memcpy(&audio->spec, desired, sizeof(audio->spec));
  	audio->convert.needed = 0;
+ 	audio->convert_record.needed = 0;
  	audio->enabled = 1;
  	audio->paused  = 1;
  
  #ifndef ENABLE_AHI
  
  /* AmigaOS opens audio inside the main loop */
+ 	audio->spec.format |= format_abuse_flags;
  	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
+ 	audio->spec.format &= ~(AUDIO_INPUT | AUDIO_OUTPUT);
  
  	if ( ! audio->opened ) {
  		SDL_CloseAudio();
***************
*** 448,454 ****
  		if ( obtained != NULL ) {
  			memcpy(obtained, &audio->spec, sizeof(audio->spec));
  		} else {
! 			/* Build an audio conversion block */
  			if ( SDL_BuildAudioCVT(&audio->convert,
  				desired->format, desired->channels,
  						desired->freq,
--- 546,552 ----
  		if ( obtained != NULL ) {
  			memcpy(obtained, &audio->spec, sizeof(audio->spec));
  		} else {
! 			/* Build an audio conversion block for playback */
  			if ( SDL_BuildAudioCVT(&audio->convert,
  				desired->format, desired->channels,
  						desired->freq,
***************
*** 467,472 ****
--- 565,592 ----
  					return(-1);
  				}
  			}
+ 
+ 			/* Build an audio conversion block for recording */
+ 			if ( SDL_BuildAudioCVT(&audio->convert_record,
+ 				audio->spec.format, audio->spec.channels,
+ 						audio->spec.freq,
+ 				desired->format, desired->channels,
+ 						desired->freq) < 0 ) {
+ 				SDL_CloseAudio();
+ 				return(-1);
+ 			}
+ 			if ( audio->convert_record.needed ) {
+ 				audio->convert_record.len = audio->spec.size;
+ 				audio->convert_record.buf =
+ 				    (Uint8 *)SDL_AllocAudioMem(
+ 				    audio->convert_record.len *
+ 				    audio->convert_record.len_mult);
+ 				if ( audio->convert_record.buf == NULL ) {
+ 					SDL_CloseAudio();
+ 					SDL_OutOfMemory();
+ 					return(-1);
+ 				}
+ 			}
  		}
  	}
  
***************
*** 563,569 ****
  		}
  		if ( audio->convert.needed ) {
  			SDL_FreeAudioMem(audio->convert.buf);
! 
  		}
  #ifndef ENABLE_AHI
  		if ( audio->opened ) {
--- 683,691 ----
  		}
  		if ( audio->convert.needed ) {
  			SDL_FreeAudioMem(audio->convert.buf);
! 		}
! 		if ( audio->convert_record.needed ) {
! 			SDL_FreeAudioMem(audio->convert_record.buf);
  		}
  #ifndef ENABLE_AHI
  		if ( audio->opened ) {
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/SDL_sysaudio.h SDL-1.2.4-record/src/audio/SDL_sysaudio.h
*** SDL-1.2.4/src/audio/SDL_sysaudio.h	Sat Mar 30 11:48:56 2002
--- SDL-1.2.4-record/src/audio/SDL_sysaudio.h	Tue Apr 16 16:26:13 2002
***************
*** 53,60 ****
--- 53,63 ----
  	int  (*OpenAudio)(_THIS, SDL_AudioSpec *spec);
  	void (*ThreadInit)(_THIS);	/* Called by audio thread at start */
  	void (*WaitAudio)(_THIS);
+ 	void (*WaitAudioRecord)(_THIS);
  	void (*PlayAudio)(_THIS);
+ 	void (*RecordAudio)(_THIS);
  	Uint8 *(*GetAudioBuf)(_THIS);
+ 	Uint8 *(*GetAudioRecordBuf)(_THIS);
  	void (*WaitDone)(_THIS);
  	void (*CloseAudio)(_THIS);
  
***************
*** 71,76 ****
--- 74,80 ----
  
  	/* An audio conversion block for audio format emulation */
  	SDL_AudioCVT convert;
+ 	SDL_AudioCVT convert_record;
  
  	/* Current state flags */
  	int enabled;
***************
*** 94,99 ****
--- 98,107 ----
  	/* * * */
  	/* The function used to dispose of this structure */
  	void (*free)(_THIS);
+ 
+ 	/* Audio recording/playback mode flags */
+ 	int input_mode;
+ 	int output_mode;
  };
  #undef _THIS
  
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/SDL_wave.c SDL-1.2.4-record/src/audio/SDL_wave.c
*** SDL-1.2.4/src/audio/SDL_wave.c	Wed Mar  6 03:23:02 2002
--- SDL-1.2.4-record/src/audio/SDL_wave.c	Thu Apr 18 14:49:48 2002
***************
*** 567,572 ****
--- 567,681 ----
  	return(spec);
  }
  
+ int SDL_SaveWAV_RW (SDL_RWops *dst, int freedst, const SDL_AudioSpec *spec,
+ 	const Uint8 *audio_buf, Uint32 audio_len)
+ {
+ 	int was_error;
+ 	Chunk chunk;
+ 	Uint16 channel;
+ 	Uint8 *buf_pos;
+ 	Uint16 bytespersample;
+ 
+ 	/* FMT chunk */
+ 	WaveFMT *format = NULL;
+ 
+ 	/* Make sure we are passed a valid data destination */
+ 	was_error = 0;
+ 	if ( dst == NULL ) {
+ 		was_error = 1;
+ 		goto done;
+ 	}
+ 		
+ 	/* Write the magic header */
+ 	if ( SDL_WriteLE32(dst, RIFF) == -1 ) {
+ 		SDL_SetError("Could not write WAV header");
+ 		was_error = 1;
+ 		goto done;
+ 	}
+ 	SDL_WriteLE32(dst, HEADER_SIZE + audio_len);
+ 	SDL_WriteLE32(dst, WAVE);
+ 
+ 	/* Write the audio data format chunk */
+ 	chunk.magic = FMT;
+ 	chunk.length = FMT_DATA_SIZE;
+ 	chunk.data = (Uint8 *)malloc(chunk.length);
+ 	if ( chunk.data == NULL ) {
+ 		SDL_Error(SDL_ENOMEM);
+ 		return(-1);
+ 	}
+ 	format = (WaveFMT *)chunk.data;
+ 	format->encoding = PCM_CODE;
+ 	format->channels = (Uint16)spec->channels;
+ 	format->frequency = (Uint32)spec->freq;
+ 	switch (spec->format) {
+ 		case AUDIO_U8: case AUDIO_S8:
+ 			format->bitspersample = 8;
+ 			break;
+ 		default:
+ 			format->bitspersample = 16;
+ 			break;
+ 	};
+ 	bytespersample = format->bitspersample >> 3;
+ 	format->byterate = format->frequency * format->channels *
+ 			bytespersample;
+ 	format->blockalign = bytespersample * format->channels;
+ 	SDL_WriteLE32(dst, chunk.magic);
+ 	SDL_WriteLE32(dst, chunk.length);
+ 	SDL_WriteLE16(dst, format->encoding);
+ 	SDL_WriteLE16(dst, format->channels);
+ 	SDL_WriteLE32(dst, format->frequency);
+ 	SDL_WriteLE32(dst, format->byterate);
+ 	SDL_WriteLE16(dst, format->blockalign);
+ 	SDL_WriteLE16(dst, format->bitspersample);
+ 
+ 	/* Write the audio data chunk */
+ 	SDL_WriteLE32(dst, DATA);
+ 	SDL_WriteLE32(dst, audio_len);
+ 
+ 	buf_pos = audio_buf;
+ 	while (buf_pos < audio_buf + audio_len) {
+ 		for (channel=0; channel < format->channels; ++channel) {
+ 			switch (spec->format) {
+ 				case AUDIO_U8:
+ 				case AUDIO_S8:
+ 					SDL_SetError("8-bit WAV writing unsupported");
+ 					was_error = 1;
+ 					goto done;
+ 					break;
+ 				case AUDIO_U16LSB:
+ 					SDL_WriteLE16(dst, SDL_SwapLE16(
+ 						*(Sint16*)buf_pos - TO_SINT16));
+ 					break;
+ 				case AUDIO_U16MSB:
+ 					SDL_WriteLE16(dst, SDL_SwapBE16(
+ 						*(Sint16*)buf_pos - TO_SINT16));
+ 					break;
+ 				case AUDIO_S16LSB:
+ 					SDL_WriteLE16(dst, SDL_SwapLE16(
+ 						*(Sint16*)buf_pos));
+ 					break;
+ 				case AUDIO_S16MSB:
+ 					SDL_WriteLE16(dst, SDL_SwapBE16(
+ 						*(Sint16*)buf_pos));
+ 					break;
+ 			}
+ 			buf_pos += bytespersample;
+ 		}
+ 	}
+ 
+ done:
+ 	if ( format != NULL ) {
+ 		free(format);
+ 	}
+ 	if ( freedst && dst ) {
+ 		SDL_RWclose(dst);
+ 	}
+ 	if ( was_error ) {
+ 		return(-1);
+ 	}
+ 	return(0);
+ }
+ 
  /* Since the WAV memory is allocated in the shared library, it must also
     be freed here.  (Necessary under Win32, VC++)
   */
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/SDL_wave.h SDL-1.2.4-record/src/audio/SDL_wave.h
*** SDL-1.2.4/src/audio/SDL_wave.h	Wed Mar  6 03:23:02 2002
--- SDL-1.2.4-record/src/audio/SDL_wave.h	Wed Apr 17 18:03:27 2002
***************
*** 41,46 ****
--- 41,49 ----
  #define IMA_ADPCM_CODE	0x0011
  #define WAVE_MONO	1
  #define WAVE_STEREO	2
+ #define FMT_DATA_SIZE	16			/* size of format chunk data */
+ #define HEADER_SIZE	28			/* header from "WAVE" onwards */
+ #define TO_SINT16	0x8000			/* converts Uint16 to Sint16 */
  
  /* Normally, these three chunks come consecutively in a WAVE file */
  typedef struct WaveFMT {
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/dsp/SDL_dspaudio.c SDL-1.2.4-record/src/audio/dsp/SDL_dspaudio.c
*** SDL-1.2.4/src/audio/dsp/SDL_dspaudio.c	Wed Mar  6 03:23:02 2002
--- SDL-1.2.4-record/src/audio/dsp/SDL_dspaudio.c	Tue Apr 16 16:26:13 2002
***************
*** 56,70 ****
  /* The tag name used by DSP audio */
  #define DSP_DRIVER_NAME         "dsp"
  
! /* Open the audio device for playback, and don't block if busy */
  /*#define USE_BLOCKING_WRITES*/
! #define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
  
  /* Audio driver functions */
  static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
  static void DSP_WaitAudio(_THIS);
  static void DSP_PlayAudio(_THIS);
  static Uint8 *DSP_GetAudioBuf(_THIS);
  static void DSP_CloseAudio(_THIS);
  
  /* Audio driver bootstrap functions */
--- 56,73 ----
  /* The tag name used by DSP audio */
  #define DSP_DRIVER_NAME         "dsp"
  
! /* Don't block the audio device if busy */
  /*#define USE_BLOCKING_WRITES*/
! #define OPEN_FLAGS	(O_NONBLOCK)
  
  /* Audio driver functions */
  static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
  static void DSP_WaitAudio(_THIS);
+ static void DSP_WaitAudioRecord(_THIS);
  static void DSP_PlayAudio(_THIS);
+ static void DSP_RecordAudio(_THIS);
  static Uint8 *DSP_GetAudioBuf(_THIS);
+ static Uint8 *DSP_GetAudioRecordBuf(_THIS);
  static void DSP_CloseAudio(_THIS);
  
  /* Audio driver bootstrap functions */
***************
*** 75,81 ****
  	int available;
  
  	available = 0;
! 	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
  	if ( fd >= 0 ) {
  		available = 1;
  		close(fd);
--- 78,84 ----
  	int available;
  
  	available = 0;
! 	fd = SDL_OpenAudioPath(NULL, 0, O_WRONLY | OPEN_FLAGS, 0);
  	if ( fd >= 0 ) {
  		available = 1;
  		close(fd);
***************
*** 113,120 ****
--- 116,126 ----
  	/* Set the function pointers */
  	this->OpenAudio = DSP_OpenAudio;
  	this->WaitAudio = DSP_WaitAudio;
+ 	this->WaitAudioRecord = DSP_WaitAudioRecord;
  	this->PlayAudio = DSP_PlayAudio;
+ 	this->RecordAudio = DSP_RecordAudio;
  	this->GetAudioBuf = DSP_GetAudioBuf;
+ 	this->GetAudioRecordBuf = DSP_GetAudioRecordBuf;
  	this->CloseAudio = DSP_CloseAudio;
  
  	this->free = Audio_DeleteDevice;
***************
*** 166,172 ****
  #endif
  		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
  			const char *message =
! 			"Audio timeout - buggy audio driver? (disabled)";
  			/* In general we should never print to the screen,
  			   but in this case we have no other way of letting
  			   the user know what happened.
--- 172,178 ----
  #endif
  		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
  			const char *message =
! 			"Audio output timeout - buggy audio driver? (disabled)";
  			/* In general we should never print to the screen,
  			   but in this case we have no other way of letting
  			   the user know what happened.
***************
*** 186,191 ****
--- 192,287 ----
  #endif /* !USE_BLOCKING_WRITES */
  }
  
+ /* This function waits until it is possible to read a full sound buffer */
+ static void DSP_WaitAudioRecord(_THIS)
+ {
+ 	/* Check to see if the thread-parent process is still alive */
+ 	{ static int cnt = 0;
+ 		/* Note that this only works with thread implementations 
+ 		   that use a different process id for each thread.
+ 		*/
+ 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
+ 			if ( kill(parent, 0) < 0 ) {
+ 				this->enabled = 0;
+ 			}
+ 		}
+ 	}
+ 
+ #ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
+ 	/* See if we need to use timed audio synchronization */
+ 	if ( frame_ticks ) {
+ 		/* Use timer for general audio synchronization */
+ 		Sint32 ticks;
+ 
+ 		ticks = ((Sint32)(next_record_frame - SDL_GetTicks()))-FUDGE_TICKS;
+ 		if ( ticks > 0 ) {
+ 			SDL_Delay(ticks);
+ 		}
+ 	} else {
+ 		/* Use select() for audio synchronization */
+ 		fd_set fdset;
+ 		struct timeval timeout;
+ 
+ 		FD_ZERO(&fdset);
+ 		FD_SET(audio_fd, &fdset);
+ 		timeout.tv_sec = 10;
+ 		timeout.tv_usec = 0;
+ #ifdef DEBUG_AUDIO
+ 		fprintf(stderr, "Waiting for audio to get ready\n");
+ #endif
+ 		if ( select(audio_fd+1, &fdset, NULL, NULL, &timeout) <= 0 ) {
+ 			const char *message =
+ 			"Audio input timeout - buggy audio driver? (disabled)";
+ 			/* In general we should never print to the screen,
+ 			   but in this case we have no other way of letting
+ 			   the user know what happened.
+ 			*/
+ 			fprintf(stderr, "SDL: %s\n", message);
+ 			this->enabled = 0;
+ 			/* Don't try to close - may hang */
+ 			audio_fd = -1;
+ #ifdef DEBUG_AUDIO
+ 			fprintf(stderr, "Done disabling audio\n");
+ #endif
+ 		}
+ #ifdef DEBUG_AUDIO
+ 		fprintf(stderr, "Ready!\n");
+ #endif
+ 	}
+ #endif /* !USE_BLOCKING_WRITES */
+ }
+ 
+ static void DSP_RecordAudio(_THIS)
+ {
+ 	int read_size, p=0;
+ 
+ 	/* Read the audio data, checking for EAGAIN on broken audio drivers */
+ 	do {
+ 		read_size = read(audio_fd, &mixbuf_record[p], mixlen_record-p);
+ 		if (read_size>0)
+ 		   p += read_size;
+ 		if (read_size == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
+ 		{
+ 		   /* Non recoverable error has occurred. It should be reported!!! */
+ 		   perror("audio");
+ 		   break;
+ 		}
+ 
+ 		if ( p < read_size || ((read_size < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
+ 			SDL_Delay(1);	/* Let a little CPU time go by */
+ 		}
+ 	} while ( p < read_size );
+ 
+ 	/* If timer synchronization is enabled, set the next write frame */
+ 	if ( frame_ticks ) {
+ 		next_record_frame += frame_ticks;
+ 	}
+ 
+ #ifdef DEBUG_AUDIO
+ 	fprintf(stderr, "Read %d bytes of audio data\n", read_size);
+ #endif
+ }
+ 
  static void DSP_PlayAudio(_THIS)
  {
  	int written, p=0;
***************
*** 226,237 ****
--- 322,342 ----
  	return(mixbuf);
  }
  
+ static Uint8 *DSP_GetAudioRecordBuf(_THIS)
+ {
+ 	return(mixbuf_record);
+ }
+ 
  static void DSP_CloseAudio(_THIS)
  {
  	if ( mixbuf != NULL ) {
  		SDL_FreeAudioMem(mixbuf);
  		mixbuf = NULL;
  	}
+ 	if ( mixbuf_record != NULL ) {
+ 		SDL_FreeAudioMem(mixbuf_record);
+ 		mixbuf_record = NULL;
+ 	}
  	if ( audio_fd >= 0 ) {
  		int value;
  		ioctl(audio_fd, SNDCTL_DSP_RESET, &value);
***************
*** 245,254 ****
  {
  	int frag_spec;
  	int value;
  
  	/* Close and then reopen the audio device */
  	close(audio_fd);
! 	audio_fd = open(audiodev, O_WRONLY, 0);
  	if ( audio_fd < 0 ) {
  		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  		return(-1);
--- 350,370 ----
  {
  	int frag_spec;
  	int value;
+ 	int open_flags = OPEN_FLAGS;
+ 
+ 	/* Determine the proper file open flags */
+ 	if (spec->format & AUDIO_INPUT && spec->format & AUDIO_OUTPUT) {
+ 		open_flags |= O_RDWR;
+ 	} else if (spec->format & AUDIO_INPUT) {
+ 		open_flags |= O_RDONLY;
+ 	} else if (spec->format & AUDIO_OUTPUT) {
+ 		open_flags |= O_WRONLY;
+ 	}
+ 	spec->format &= ~(AUDIO_INPUT | AUDIO_OUTPUT);
  
  	/* Close and then reopen the audio device */
  	close(audio_fd);
! 	audio_fd = open(audiodev, open_flags, 0);
  	if ( audio_fd < 0 ) {
  		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  		return(-1);
***************
*** 276,285 ****
  #ifdef DEBUG_AUDIO
  	{ audio_buf_info info;
  	  ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
! 	  fprintf(stderr, "fragments = %d\n", info.fragments);
! 	  fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
! 	  fprintf(stderr, "fragsize = %d\n", info.fragsize);
! 	  fprintf(stderr, "bytes = %d\n", info.bytes);
  	}
  #endif
  
--- 392,408 ----
  #ifdef DEBUG_AUDIO
  	{ audio_buf_info info;
  	  ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
! 	  fprintf(stderr, "playback fragments = %d\n", info.fragments);
! 	  fprintf(stderr, "playback fragstotal = %d\n", info.fragstotal);
! 	  fprintf(stderr, "playback fragsize = %d\n", info.fragsize);
! 	  fprintf(stderr, "playback bytes = %d\n", info.bytes);
! 	}
! 	{ audio_buf_info info;
! 	  ioctl(audio_fd, SNDCTL_DSP_GETISPACE, &info);
! 	  fprintf(stderr, "record fragments = %d\n", info.fragments);
! 	  fprintf(stderr, "record fragstotal = %d\n", info.fragstotal);
! 	  fprintf(stderr, "record fragsize = %d\n", info.fragsize);
! 	  fprintf(stderr, "record bytes = %d\n", info.bytes);
  	}
  #endif
  
***************
*** 325,341 ****
  	int format;
  	int value;
  	Uint16 test_format;
  
  	/* Reset the timer synchronization flag */
  	frame_ticks = 0.0;
  
  	/* Open the audio device */
! 	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
  	if ( audio_fd < 0 ) {
  		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  		return(-1);
  	}
  	mixbuf = NULL;
  
  #ifdef USE_BLOCKING_WRITES
  	/* Make the file descriptor use blocking writes with fcntl() */
--- 448,477 ----
  	int format;
  	int value;
  	Uint16 test_format;
+ 	Uint16 format_abuse_flags;
+ 	int open_flags = OPEN_FLAGS;
  
  	/* Reset the timer synchronization flag */
  	frame_ticks = 0.0;
  
+ 	if (spec->format & AUDIO_INPUT && spec->format & AUDIO_OUTPUT) {
+ 		open_flags |= O_RDWR;
+ 	} else if (spec->format & AUDIO_INPUT) {
+ 		open_flags |= O_RDONLY;
+ 	} else if (spec->format & AUDIO_OUTPUT) {
+ 		open_flags |= O_WRONLY;
+ 	}
+ 	format_abuse_flags = spec->format & (AUDIO_INPUT | AUDIO_OUTPUT);
+ 	spec->format &= ~(AUDIO_INPUT | AUDIO_OUTPUT);
+ 
  	/* Open the audio device */
! 	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), open_flags, 0);
  	if ( audio_fd < 0 ) {
  		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
  		return(-1);
  	}
  	mixbuf = NULL;
+ 	mixbuf_record = NULL;
  
  #ifdef USE_BLOCKING_WRITES
  	/* Make the file descriptor use blocking writes with fcntl() */
***************
*** 431,448 ****
  	   after setting the format, we must re-open the audio device
  	   once we know what format and channels are supported
  	 */
  	if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
  		/* Error is set by DSP_ReopenAudio() */
  		return(-1);
  	}
  
! 	/* Allocate mixing buffer */
! 	mixlen = spec->size;
! 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
! 	if ( mixbuf == NULL ) {
! 		return(-1);
  	}
! 	memset(mixbuf, spec->silence, spec->size);
  
  #ifndef USE_BLOCKING_WRITES
  	/* Check to see if we need to use select() workaround */
--- 567,597 ----
  	   after setting the format, we must re-open the audio device
  	   once we know what format and channels are supported
  	 */
+ 	spec->format |= format_abuse_flags;
  	if ( DSP_ReopenAudio(this, audiodev, format, spec) < 0 ) {
  		/* Error is set by DSP_ReopenAudio() */
  		return(-1);
  	}
  
! 	/* Allocate mixing buffers */
! 	spec->format |= format_abuse_flags;
! 	if (spec->format & AUDIO_OUTPUT) {
! 		mixlen = spec->size;
! 		mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
! 		if ( mixbuf == NULL ) {
! 			return(-1);
! 		}
! 		memset(mixbuf, spec->silence, spec->size);
  	}
! 	if (spec->format & AUDIO_INPUT) {
! 		mixlen_record = spec->size;
! 		mixbuf_record = (Uint8 *)SDL_AllocAudioMem(mixlen_record);
! 		if ( mixbuf_record == NULL ) {
! 			return(-1);
! 		}
! 		memset(mixbuf_record, spec->silence, spec->size);
!         }
! 	spec->format &= ~(AUDIO_INPUT | AUDIO_OUTPUT);
  
  #ifndef USE_BLOCKING_WRITES
  	/* Check to see if we need to use select() workaround */
***************
*** 451,456 ****
--- 600,606 ----
  		if ( workaround ) {
  			frame_ticks = (float)(spec->samples*1000)/spec->freq;
  			next_frame = SDL_GetTicks()+frame_ticks;
+ 			next_record_frame = next_frame;
  		}
  	}
  #endif /* !USE_BLOCKING_WRITES */
diff -rc --exclude=Makefile SDL-1.2.4/src/audio/dsp/SDL_dspaudio.h SDL-1.2.4-record/src/audio/dsp/SDL_dspaudio.h
*** SDL-1.2.4/src/audio/dsp/SDL_dspaudio.h	Wed Mar  6 03:23:02 2002
--- SDL-1.2.4-record/src/audio/dsp/SDL_dspaudio.h	Tue Apr 16 16:26:13 2002
***************
*** 40,52 ****
  	/* The parent process id, to detect when application quits */
  	pid_t parent;
  
! 	/* Raw mixing buffer */
  	Uint8 *mixbuf;
  	int    mixlen;
  
  	/* Support for audio timing using a timer, in addition to select() */
  	float frame_ticks;
  	float next_frame;
  };
  #define FUDGE_TICKS	10	/* The scheduler overhead ticks per frame */
  
--- 40,57 ----
  	/* The parent process id, to detect when application quits */
  	pid_t parent;
  
! 	/* Raw mixing buffer for playback */
  	Uint8 *mixbuf;
  	int    mixlen;
  
+ 	/* Raw mixing buffer for recording */
+ 	Uint8 *mixbuf_record;
+ 	int    mixlen_record;
+ 
  	/* Support for audio timing using a timer, in addition to select() */
  	float frame_ticks;
  	float next_frame;
+ 	float next_record_frame;
  };
  #define FUDGE_TICKS	10	/* The scheduler overhead ticks per frame */
  
***************
*** 55,61 ****
--- 60,69 ----
  #define parent			(this->hidden->parent)
  #define mixbuf			(this->hidden->mixbuf)
  #define mixlen			(this->hidden->mixlen)
+ #define mixbuf_record		(this->hidden->mixbuf_record)
+ #define mixlen_record		(this->hidden->mixlen_record)
  #define frame_ticks		(this->hidden->frame_ticks)
  #define next_frame		(this->hidden->next_frame)
+ #define next_record_frame	(this->hidden->next_record_frame)
  
  #endif /* _SDL_dspaudio_h */
