/* snd.c -- low-level sound I/O * * Roger Dannenberg * 21 Jun 1997 * * based on sndheader.c: * * Jim Zelenka, CMU/ITC, 9 Jun 1992 (rewritten from my old sources) * Roger Dannenberg, CMU, Mar 1993 (extensive changes and additions) * * and on sndread.c & sndwrite.c: * * Roger Dannenberg */ /* Standard includes */ #include #include #include "snd.h" #include "sndfileio.h" #include "sndheader.h" static int snd_initialized = FALSE; typedef struct { char *interf; char *device; snd_fns_type dictionary; } descriptor_node, *descriptor_type; static int descriptor_index = 0; #define snd_descriptor_max 32 static descriptor_node descriptors[snd_descriptor_max]; /* snd_add_device -- describe interface/device pair to library * * This is called at intialization time, once for each * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) * The strings are retained but NOT COPIED, so do not destroy them! */ void snd_add_device(char *interf, char *device, snd_fns_type dictionary) { if (descriptor_index >= snd_descriptor_max) { snd_fail("snd_add_device: snd_descriptor_max exceeded"); } descriptors[descriptor_index].interf = interf; descriptors[descriptor_index].device = device; descriptors[descriptor_index].dictionary = dictionary; descriptor_index++; } int snd_device(int n, char *interf, char *device) { if (!snd_initialized) { snd_init(); snd_initialized = TRUE; } if (n >= 0 && n < descriptor_index) { strcpy(interf, descriptors[n].interf); strcpy(device, descriptors[n].device); return SND_SUCCESS; } return !SND_SUCCESS; } /* failure_fn -- "noop" function pointer */ /**/ static int failure_fn(snd_type snd) { return !SND_SUCCESS; } /* success_fn -- "noop" function pointer */ /**/ static int success_fn(snd_type snd) { return SND_SUCCESS; } static long file_poll(snd_type snd) { return MAX_FILE_BUF_LEN; } static long mem_poll(snd_type snd) { if (snd->write_flag == SND_READ) { return (snd->u.mem.buffer_len - snd->u.mem.buffer_pos); } else { return (snd->u.mem.buffer_max - snd->u.mem.buffer_len); } } static long none_poll(snd_type snd) { return 0; } #if !defined(WIN32) && !defined(IRIX) && !defined(SGI) static void _swab(char *to, char *from, long length) { short *to16 = (short *) to; short *from16 = (short *) from; int i = 1; while (i < length) { short data = *from16++; *to16++ = (data << 8) | ((data >> 8) & 0xFF); i += 2; } } #endif static void change_byte_order(snd_type snd, void *buffer, long length) { int bytes = (snd->format.bits + 7) >> 3; switch (bytes) { case 1: break; case 2: _swab((char *) buffer, (char *) buffer, length); break; case 4: { long *buff = (long *) buffer; while (length > 0) { long data = *buff; *buff++ = ((data >> 24) & 0xFF) | ((data >> 8) & 0xFF00) | ((data << 8) & 0xFF0000) | (data << 24); length -= 4; } break; } default: { char msg[100]; sprintf(msg, "Cannot handle %d-byte samples", bytes); snd_fail(msg); break; } } } static long file_read(snd_type snd, void *buffer, long length) { long togo, cnt; togo = snd->u.file.end_offset - snd->u.file.current_offset; if (length > togo) length = togo; cnt = snd_file_read(snd->u.file.file, (char *) buffer, length); snd->u.file.current_offset += cnt; if (snd->u.file.swap) change_byte_order(snd, buffer, cnt); return cnt; } static long mem_read(snd_type snd, void *buffer, long length) { /* if there are fewer than length bytes, reduce length: */ if ((snd->u.mem.buffer_len - snd->u.mem.buffer_pos) < length) { length = snd->u.mem.buffer_len - snd->u.mem.buffer_pos; } memcpy(buffer, snd->u.mem.buffer + snd->u.mem.buffer_pos, length); snd->u.mem.buffer_pos += length; return length; } static long none_read(snd_type snd, void *buffer, long length) { return 0; } static long file_write(snd_type snd, void *buffer, long length) { long cnt; if (snd->u.file.swap) change_byte_order(snd, buffer, length); cnt = snd_file_write(snd->u.file.file, (char *) buffer, length); snd->u.file.end_offset += cnt; /* keep track of how much data */ return cnt; } static long mem_write(snd_type snd, void *buffer, long length) { /* if there are fewer than length bytes, reduce length: */ if ((snd->u.mem.buffer_max - snd->u.mem.buffer_len) < length) { length = snd->u.mem.buffer_max - snd->u.mem.buffer_len; } memcpy(snd->u.mem.buffer + snd->u.mem.buffer_len, buffer, length); snd->u.mem.buffer_len += length; return length; } static long none_write(snd_type snd, void *buffer, long length) { return 0; } static int mem_open(snd_type snd, long *flags) { return SND_SUCCESS; } static int none_open(snd_type snd, long *flags) { return !SND_SUCCESS; } static int file_close(snd_type snd) { if (snd->write_flag == SND_WRITE) { write_sndheader_finish(snd); } return snd_file_close(snd->u.file.file); } #define mem_close success_fn #define none_close failure_fn #define file_reset success_fn static int mem_reset(snd_type snd) { snd->u.mem.buffer_pos = 0; if (snd->write_flag != SND_READ) { snd->u.mem.buffer_len = 0; } return SND_SUCCESS; } #define none_reset failure_fn #define file_flush success_fn #define mem_flush success_fn #define none_flush failure_fn char *snd_mode_string[] = { "ADPCM", "PCM", "ULAW", "ALAW", "Float", "UPCM", "Unknown" }; char *snd_mode_to_string(long mode) { if (mode < 0 || mode >= SND_NUM_MODES) return "InvalidData"; else return snd_mode_string[mode]; } static snd_fns_node mem_dictionary = { mem_poll, mem_read, mem_write, mem_open, mem_close, mem_reset, mem_flush }; static snd_fns_node file_dictionary = { file_poll, file_read, file_write, snd_open_file, file_close, file_reset, file_flush }; snd_fns_node snd_none_dictionary = { none_poll, none_read, none_write, none_open, none_close, none_reset, none_flush }; long snd_poll(snd_type snd) { return (*snd->dictionary->poll)(snd); } /* snd_read -- read up to length bytes from source into buffer */ /* * returns number of bytes actually read */ long snd_read(snd_type snd, void *buffer, long length) { int bpf = snd_bytes_per_frame(snd); return ((*snd->dictionary->read)(snd, buffer, length * bpf)) / bpf; } long snd_write(snd_type snd, void *buffer, long length) { int bpf = snd_bytes_per_frame(snd); return (*snd->dictionary->write)(snd, buffer, length * bpf) / bpf; } int snd_open(snd_type snd, long *flags) { if (!snd_initialized) { snd_init(); snd_initialized = TRUE; } *flags = 0; if (snd->device == SND_DEVICE_FILE) { snd->dictionary = &file_dictionary; } else if (snd->device == SND_DEVICE_AUDIO) { int i; for (i = 0; i < descriptor_index; i++) { /* take the first descriptor that matches both interface and device, where empty string matches anything: */ if ((snd->u.audio.interfacename[0] == 0 || strcmp(snd->u.audio.interfacename, descriptors[i].interf) == 0) && /*&& commented out by dmazzoni - don't want to compare dev && uncommented by rbd - please see previous comment/specification */ (snd->u.audio.devicename[0] == 0 || strcmp(snd->u.audio.devicename, descriptors[i].device) == 0)) { snd->dictionary = descriptors[i].dictionary; break; } } if (i >= descriptor_index) { return !SND_SUCCESS; } } else if (snd->device == SND_DEVICE_MEM) { snd->dictionary = &mem_dictionary; } return (*snd->dictionary->open)(snd, flags); } int snd_close(snd_type snd) { return (*snd->dictionary->close)(snd); } int snd_reset(snd_type snd) { return (*snd->dictionary->reset)(snd); } /* snd_flush -- allows client to finish audio output before closing */ /**/ int snd_flush(snd_type snd) { if (snd->write_flag != SND_READ) { return (*snd->dictionary->flush)(snd); } return SND_SUCCESS; }