The copyright holder (Laszlo Menczel) hereby grants you the additional right to statically link the SNDCAP library to your application and distribute the resulting binary executable under a licence of your own choice.
The above clause does not free you from the obligation of providing (upon request) to the recipients of your binary executable the full source code of the SNDCAP library used for building said executable. In the documentation accompanying your binary executable you must inform the end user that the source code of the library is available upon request.
If you distribute modified versions of the SNDCAP library you must also grant end users the additional right of static linking as described above.
SNDCAP is a simple sound capture library written in C for Linux and Win9x/ME/2000/XP. It can open the default sound capture device and save the captured samples to a ring buffer from which applications can retrive the sound data. The library also contains functions for ring buffer management and for saving and loading WAV files.
This library is the result of my frustration. During the development of another library (Voice Chat System for networked games, VCS) I needed a sound capture library (written in C) which could work under Windows and Linux. After searching the net I decided to use OpenAL (it seemed to be just the thing I wanted). However, the sound capture module of OpenAL did not work under Windows as advertised (at least for me, I tried to use it in several ways but always had problems).
I realized that the waveOut-waveIn API in Windows is fairly simple, and under Linux the ALSA system can be used for sound management. So it seemed to me that writing a sound capture library should not be a very difficult job. I started to code and this library is the result.
The functionality implemented by SNDCAP is fairly simple. Don't expect professional multi-channel audio recording. SNDCAP just opens the default capture device and keeps on saving sound samples in a ring buffer. It does contain a few utility functions for the manipulation of sound in WAV format, as well as functions for the creation and management of ring buffers needed for capture.
The functions in SNDCAP always return a positive integer or a valid pointer if the operation was successful. In case of error the return value is zero or NULL. The type of error can be obtained by using the error reporting functions described in Section 2.4.
The following data structures and constants (defined in the header 'sndcap.h') are relevant to ring buffers:
typedef struct ringbuf_s { byte *data; // buffer containing data int dynamic; // 1 = dynamically allocated int incr; // size of data items int size; // number of bytes in buffer int count; // current number of data items in buffer int tail; // pointer to first data item int head; // pointer to first empty location int overflow; // overflow flag } ringbuf_t; enum { RBUF_TAIL, RBUF_HEAD }; enum { RBUF_BYTE, RBUF_WORD, RBUF_DWORD };
Creates a ring buffer which can store 'count' data chunks each 'size' bytes long. Returns a pointer to the new ring buffer structure, or NULL in case of error. The field 'dynamic' is set to non-zero to indicate that not only the data buffer, but the structure itself is also dynamically allocated. 'size' is set to the total number of bytes in the data buffer, 'incr' is set to the value of 'size', the other fields are initialized to zero.
Discards the data structures used by the dynamically allocated ringbuf 'buf'. If 'buf->dynamic' is zero, the function returns zero (error) and nothing is done. Otherwise if 'buf->data' is not NULL, the data buffer is discarded, and finally the structure itself is also discarded by calling 'free()'.
Resets the ringbuf 'buf'. This means that 'buf->data' is filled with zeros and all structure fields (except 'incr' and 'size') are set to zero.
Increments the tail or head pointer of the ring buffer 'buf' depending on the value of 'which' (may be RBUF_TAIL or RBUF_HEAD). The pointer wraps around to zero if it reaches the end of the buffer. 'buf->count' is incremented when the head pointer is incremented, and is decremented if the tail pointer is incremented. So its value is the number of data items in the buffer. Returns failure (zero) if 'buf' is invalid.
The buffer may overflow (i.e. a new item is added to a buffer already full). In this case the oldest item is overwritten and 'buf->overflow' is set to 1. Otherwise the value of 'buf->overflow' is zero. The consumer of buffer data should check this flag, and perform the necessary adjustment if it is non-zero. When the item at 'buf->tail' has been processed and removed from the buffer, the flag is set back to zero. Please note that a non-zero flag value means at least one (but possibly several) items overwritten and lost.
Returns non-zero if 'buf' is empty (no consumable data chunk), zero otherwise. Also returns non-zero if 'buf' is invalid, so that a non-existent buffer is always treated as empty and (hopefully) no attempt is made to read data from it.
Returns non-zero if 'buf' is full (no data chunk can be added w/o overwriting an old one), zero otherwise. Also returns non-zero if 'buf' is invalid, so that a non-existent buffer is always treated as full and (hopefully) no attempt is made to add data to it.
Adds the data block at address 'data' to the first available empty location (at offset 'buf->head') of ringbuffer 'buf'. The number of bytes in 'data' is assumed to be the same as the value of 'buf->incr', its type is specified in the argument 'data_type' (RBUF_BYTE, RBUF_WORD or RBUF_DWORD). The appropriate fields of the structure 'buf' (i.e. 'count', 'head' and possibly 'tail' and 'overflow') are updated
Copies the oldest available data chunk (at offset 'buf->tail') of the ringbuffer 'buf' to the address 'data'. The number of bytes copied is 'buf->incr', the type of copied data is specified in the argument 'data_type' (RBUF_BYTE, RBUF_WORD or RBUF_DWORD). The appropriate fields of 'buf' ('tail' and 'count') are updated. Returns zero (error) if 'buf' is empty.
The following constants (enumerated in the header 'sndcap.h') are relevant to sound capture:
enum { SCAP_8K = 8000, SCAP_11K = 11025, SCAP_22K = 22050, SCAP_44K = 44100 }; enum { SCAP_BYTE = 8, SCAP_WORD = 16 }; enum { SCAP_MONO = 1, SCAP_STEREO = 2 };
Initializes the sound system for capture. 'chan' is the number of sound channels (SCAP_MONO or SCAP_STEREO), 'freq' is the sampling rate (SCAP_8K, SCAP_11K, SCAP_22K or SCAP_44K), and 'bits' is the number of bits in a single sound sample (SCAP_BYTE or SCAP_WORD). 'buf' must be a properly initialized ring buffer. The minimum chunk size of this ring buffer is 800 bytes (corresponding to 0.1 seconds of sound assuming 8 kHz 8-bit mono capture). After calling this function the sound capture device is opened and initialized, but capture is not started yet.
Starts the capture of sound.
Stops the capture of sound.
This function should be called before the application exits. Stops capture (if running) and properly closes the sound capture device.
The following constants and data structures (defined in the header 'sndcap.h') are relevant to WAV data manipulation:
#define WAV_MAX_PATHLEN 255 typedef struct { void *buf; // buffer for sound data char name[WAV_MAX_PATHLEN + 1]; // name of WAV file int dynamic; // 1 = 'data' dynamically allocated int freq; // sampling rate int numbits; // size of sound samples int numchan; // number of sound channels int frames; // number of sound frames } wavdata_t; enum { SCAP_STATIC, SCAP_DYNAMIC };
Attempts to retrieve format information from the sound file 'name'. If successful, the values are stored in 'data' and 'name' is copied to 'data->name'. In case of error returns zero and the fields of 'data' are reset.
Attempts to load sound data from the file 'name'. If 'count' is non-zero, it specifies the number of bytes to load from the file, otherwise the whole file is loaded. If 'buf' is not NULL, data is copied to this address. If 'buf' is NULL, the function tries to allocate a buffer large enough to store the requested data. The address of the buffer is stored in 'data->buf'. 'buftype' specifies the type of buffer passed, it should be SCAP_DYNAMIC (1, for dynamically allocated buffers) or SCAP_STATIC (zero, for static arrays). 'data->dynamic' is set to this value. The other fields of 'data' are initialized using values obtained from the file header. Returns the number of bytes loaded, or zero in case of error.
Saves the sound data from the buffer of 'data' to a file. If 'name' is NULL, 'data->name' is used as filename, otherwise the name of the file will be 'name' (in this case 'data->name' is updated to contain the new name supplied). Sound is saved as standard uncompressed WAV in the format described by 'data'. The number of sound frames to save is specified in the argument 'count'. If 'count' is zero, 'data->frames' is used as count.
Compares the format information (number of channels, frequency and sample size) of 'a' and 'b'. Returns non-zero if they are identical, zero if not or an error occured.
Converts the sound data block described by 'src' to the format specified by 'dst'. A new buffer is allocated to store the converted data and its pointer is stored in 'dst->buf'. Any previous data in the buffer of 'dst' is lost.
Discards the data buffer of 'data' (i.e. calls 'free()' to deallocate it). The operation is carried out only if 'data->dynamic' is non-zero meaning that the buffer was allocated dynamically using 'malloc()'. If the data buffer is deleted, all structure fields are reset to zero.
Returns a pointer to a static string containing information on the format of sound described by 'data'.
The following error codes are enumerated in the header 'sndcap.h':
CAPERR_NONE no error (value is zero) // capture CAPERR_INIT capture module not initialized CAPERR_BAD_ARG bad argument CAPERR_ALLOC memory allocation error CAPERR_NULL_PTR NULL pointer argument CAPERR_BAD_BUF bad ring buffer (data block NULL or increment out of range) CAPERR_DEV_OPEN cannot open capture device CAPERR_IN_RECORD currently recording sound CAPERR_NOT_RECORD not recording sound CAPERR_START could not start sound capture CAPERR_BUF_ADD could not add capture buffer to queue CAPERR_THREAD could not create the capture thread // ring buffer RBERR_BAD_ARG bad argument RBERR_BAD_BUF buffer data block is NULL RBERR_BUF_STATIC buffer structure is static (not allocated by the library) RBERR_EMPTY buffer is empty // WAV WAVERR_FILE_NAME bad file name (empty) WAVERR_FILE_OPEN could not open/create the specified file WAVERR_FILE_READ failed reading data from the file WAVERR_FILE_WRITE failed writing data to the file WAVERR_RIFF not a RIFF file WAVERR_WAVE not a WAVE file WAVERR_FMT_CHUNK 'fmt' chunk not found WAVERR_TAG cannot load compressed WAV WAVERR_NUM_CHAN bad channel number (should be 1 or 2) WAVERR_DATA_CHUNK 'data' chunk not found WAVERR_BITS bad data size (should be 8 or 16 bits) WAVERR_FREQ bad sampling rate (should be 8000, 11025, 22050, or 44100) WAVERR_SAMPLES missing sound samples WAVERR_SAME_FORMAT same WAV format (conversion is pointless)
The following aliases are also defined:
#define RBERR_NONE CAPERR_NONE #define RBERR_ALLOC CAPERR_ALLOC #define RBERR_NULL_PTR CAPERR_NULL_PTR #define WAVERR_NONE CAPERR_NONE #define WAVERR_NULL_PTR CAPERR_NULL_PTR
Returns the code of the last error (CAPERR_NONE if the last operation succeeded).
Returns a pointer to a static string describing the last error that occured.
The present implementation can be compiled and used under Windows (Win9x, WinME, Win2000, WinXP) and Linux. Since the library contains platform specific code (for handling the sound device) it is not trivial to port to other systems. I have no intention to do this, but feel free to carry out the porting if you wish to use the library under a different OS :-) Platform specific code is restricted to the modules 'capture_win32.c' and 'capture_alsa.c', so this is the only source module which you have to re-implement. The rest of the code is standard ANSI C usable under any OS.
The source tree of the library contains subdirectories for building under Windows and Linux. For the Win32 build you must have MinGW installed (it is a port of the GCC compiler and development system to Windows). To compile the library just change to the appropriate build directory and run the make script ('mk.bat' or 'mk'). If you want to compile the library using MS Visual C++, you should create the appropriate Makefile or project file yourself. I don't use MS compilers unless absolutely necessary (it happens very rarely).
Precompiled versions of the library are included in the directories 'bin\linux' and 'bin\win32'. After compiling the library you can save a copy of the new version by running the script 'save(.bat)' from the build directory. The library can be installed by running the 'inst(.bat)' script from 'bin\linux' or 'bin\win32'.
Applications using the library should include the main header 'sndcap.h'. When you link the executable, the appropriate support libraries (winmm.dll and kernel32.dll for Win32, libasound.so and libm.so for Linux) must be specified. I have included a simple test program ('captest.c') which can give you an idea about how to use the library. This program just opens the capture device, records 2 seconds of sound and saves it into a WAV file.
The code in the module 'resample.c' for converting sound to different formats have been extracted from the libresample library created by Dominic Mazzoni (based on resample-1.7).