diff options
Diffstat (limited to 'lib')
37 files changed, 12326 insertions, 0 deletions
diff --git a/lib/.config b/lib/.config new file mode 100644 index 0000000..1beec7c --- /dev/null +++ b/lib/.config @@ -0,0 +1 @@ +mode=user diff --git a/lib/libOSSlib/.config b/lib/libOSSlib/.config new file mode 100644 index 0000000..f34695c --- /dev/null +++ b/lib/libOSSlib/.config @@ -0,0 +1,2 @@ +mode=shlib +targetos=Linux diff --git a/lib/libOSSlib/.nativemake b/lib/libOSSlib/.nativemake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/libOSSlib/.nativemake diff --git a/lib/libOSSlib/Makefile b/lib/libOSSlib/Makefile new file mode 100644 index 0000000..98029f1 --- /dev/null +++ b/lib/libOSSlib/Makefile @@ -0,0 +1,29 @@ +OBJS=libmain.o midiparser.o play_event.o +INSTALLLIB=/lib +LDFLAGS=-shared -fPIC +CFLAGS=-O -fPIC +#LDFLAGS=-Bdynamic +AROPTS=rc +AR=ar + +all: + sh ./compile.sh $(INSTALLLIB) "$(CC)" "$(CFLAGS)" "$(MAKE)" + +install: all + cp libOSSlib.* $(INSTALLLIB) + cp drums.o3 std.o3 /etc + +static: libOSSlib.a + cp libOSSlib.a $(INSTALLLIB) + +libOSSlib.so: $(OBJS) + ${CC} $(LDFLAGS) -o libOSSlib.so $(OBJS) + +dep: + +clean: + rm -f *.o *.so x y z core *.a + +libOSSlib.a: $(OBJS) + $(AR) $(AROPTS) libOSSlib.a $(OBJS) + #-ranlib libOSSlib.a diff --git a/lib/libOSSlib/Readme b/lib/libOSSlib/Readme new file mode 100644 index 0000000..b975b78 --- /dev/null +++ b/lib/libOSSlib/Readme @@ -0,0 +1,120 @@ +About OSSlib +============ + +This version of OSSlib which supports patch caching for cards like GUS, OPL3 +and SoftOSS. This part of the library is only used by some older synthesizer +chips that are no longer available. Also the SoftOSS virtual synth is +compatible with it. + +Another part is the midiparser library. It's a simple callback based +system that translates a stream of MIDI bytes (for example from MIDI +keyboard) to events. + +This library is compatible with all OSS implementations including the freeware +implementations. + +Copying conditions +------------------ + +This library can be freely used in ANY applications that is compatible +with the OSS API. No parts of the code can be borroved to other purposes +without written permission by 4Front Technologies. + +Installing OSSlib +----------------- + +Execute make. If your operating system is recognized you will get +libOSSlib.so or libOSSlib.a depending on operating system. +If make complains about unknown operating system you should use +make libOSSlib.so or make libOSSlib.a to build it. + +Finally copy libOSSlib.* to /usr/lib + +Using OSSlib in programs +------------------------ + +OSSlib is intended to be used in programs using /dev/music but +it also works with /dev/sequencer too. This version of the library +contains only some basic patch caching functions to be used with +OPL3 and GUS (and SoftOSS as well). Support for other cards is still +missing. + +There are steps to be done when using OSSlib in a program: + +1) Change Makefile to use -DOSSLIB and -lOSSlib + +Version 3.8 of <sys/soundcard.h> has been designed to be compatible +with OSSlib when this OSSLIB is defined. You can also use +#ifdef OSSLIB in programs to cover the minor differences between +the OSSlib and non OSSlib versions. + +2) If the program defines segbuf_dump() routine, put it inside +#ifndef OSSLIB/#endif pair. This routine is included in OSSlib +and having the same routine inside the program conflicts with it. + +3) Add the following call to the program after /dev/music is opened. + +#ifdef OSSLIB + OSS_init(seqfd, 1024); +#endif + +In the above seqfd is the file descriptor of /dev/music and 1024 +is the size of the local buffer used for output events (1024 is +a good value). + +4) Add patch caching calls to the program. Patch caching should +be called as early as possible. It's recommended to load all +_required_ patches before starting the actual playback since +loading large patches from disk may introduce significant delays +to playback. + +There are two macros (in soundcard.h) to be used when loading +instruments: + +SEQ_LOAD_GMINSTR(dev#, instr#) loads an instrument (melodic) patch. +The first parameter is the synthesizer/device number (0 to N) and +the second parameter is the General MIDI instrument number (0 to 127). +Note that the numbering starts from #0 while many GM instrument +tables start from #1. + +SEQ_LOAD_GMDRUM(dev#, drum#) is similar than the above but it loads +an General MIDI percussive instrument. + +5) After the above steps your program is ready to start playing. + +Examples for using OSSlib and for playing MIDI data can be found +in ../mplay/* and ../gustest/midithru.c + +GUS support in OSSlib +--------------------- + +You will need the full GM patch set distributed with the Ultrasound +drivers for DOS. The default location where the library tries to +find the patches is /dos/ultrasnd/midi but you can change it by +editing guslib.c (the second line). Alternative patch sets +(such as midia) are available from the net. Another source is the MIDIA +library. + +Since non PnP GUS has only 1M of +RAM (at most) it's not possible to load the full patch set. When playing +lot of MIDI files in sequence it's possible that the menory becomes +full if the program doesn't reset the samples between files. + +OPL3 support in OSSlib +---------------------- + +OSSlib supports patch loading to OPL3 FM synth cards (older OPL2 cards +are not supported). The patch set is included in the std.o3 and drums.o3 +files distributed with this package. These files should be copied to /etc +(will be done by "make install"). These patch files are the same ones +included in Adagio/glib package by Greg Lee. They are also distributed +with playmidi. + +Using OSSlib with SoftOSS +------------------------- + +For instructions about using OSSLib with SoftOSS see the instructions +for GUS (above). + + +For more info about using OSSlib contact hannu@opensound.com diff --git a/lib/libOSSlib/compile.sh b/lib/libOSSlib/compile.sh new file mode 100644 index 0000000..0a03b1b --- /dev/null +++ b/lib/libOSSlib/compile.sh @@ -0,0 +1,81 @@ +#!/bin/sh +MAKE=$4 + +case `uname` in + +"Linux") + $MAKE libOSSlib.so + ;; + +"FreeBSD") + $MAKE libOSSlib.so + ;; + +"OSF1") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"NetBSD") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"OpenBSD") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"BSD/OS") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"AIX") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"HP-UX") + $MAKE libOSSlib.a CC=$2 CFLAGS=$3 + ;; + +"LynxOS") + $MAKE AROPTS=rcs libOSSlib.a CFLAGS="" + ;; + +"UNIX_SV") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"UnixWare") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"OpenUNIX") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"SCO_SV") + $MAKE libOSSlib.a INSTALLLIB="$1" CFLAGS="" + ;; + +"SunOS") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"PowerMAX_OS") + $MAKE libOSSlib.a CFLAGS="" + ;; +"DragonFly") + $MAKE libOSSlib.a CFLAGS="" + ;; + +"BeOS"|"Haiku") + $MAKE libOSSlib.a CFLAGS="" + ;; + +*) + echo Can\'t recognize your operating system '('`uname`')'. + echo;echo + echo use $MAKE libOSSlib.so or $MAKE libOSSlib.a to build OSSlib + exit 0 + +esac + +exit 0 diff --git a/lib/libOSSlib/gmidi.h b/lib/libOSSlib/gmidi.h new file mode 100644 index 0000000..bc196a9 --- /dev/null +++ b/lib/libOSSlib/gmidi.h @@ -0,0 +1,259 @@ +char patch_names[][9] = { + /* 0 */ "acpiano", + /* 1 */ "britepno", + /* 2 */ "synpiano", + /* 3 */ "honky", + /* 4 */ "epiano1", + /* 5 */ "epiano2", + /* 6 */ "hrpschrd", + /* 7 */ "clavinet", + /* 8 */ "celeste", + /* 9 */ "glocken", + /* 10 */ "musicbox", + /* 11 */ "vibes", + /* 12 */ "marimba", + /* 13 */ "xylophon", + /* 14 */ "tubebell", + /* 15 */ "santur", + /* 16 */ "homeorg", + /* 17 */ "percorg", + /* 18 */ "rockorg", + /* 19 */ "church", + /* 20 */ "reedorg", + /* 21 */ "accordn", + /* 22 */ "harmonca", + /* 23 */ "concrtna", + /* 24 */ "nyguitar", + /* 25 */ "acguitar", + /* 26 */ "jazzgtr", + /* 27 */ "cleangtr", + /* 28 */ "mutegtr", + /* 29 */ "odguitar", + /* 30 */ "distgtr", + /* 31 */ "gtrharm", + /* 32 */ "acbass", + /* 33 */ "fngrbass", + /* 34 */ "pickbass", + /* 35 */ "fretless", + /* 36 */ "slapbas1", + /* 37 */ "slapbas2", + /* 38 */ "synbass1", + /* 39 */ "synbass2", + /* 40 */ "violin", + /* 41 */ "viola", + /* 42 */ "cello", + /* 43 */ "contraba", + /* 44 */ "marcato", + /* 45 */ "pizzcato", + /* 46 */ "harp", + /* 47 */ "timpani", + /* 48 */ "marcato", + /* 49 */ "slowstr", + /* 50 */ "synstr1", + /* 51 */ "synstr2", + /* 52 */ "choir", + /* 53 */ "doo", + /* 54 */ "voices", + /* 55 */ "orchhit", + /* 56 */ "trumpet", + /* 57 */ "trombone", + /* 58 */ "tuba", + /* 59 */ "mutetrum", + /* 60 */ "frenchrn", + /* 61 */ "hitbrass", + /* 62 */ "synbras1", + /* 63 */ "synbras2", + /* 64 */ "sprnosax", + /* 65 */ "altosax", + /* 66 */ "tenorsax", + /* 67 */ "barisax", + /* 68 */ "oboe", + /* 69 */ "englhorn", + /* 70 */ "bassoon", + /* 71 */ "clarinet", + /* 72 */ "piccolo", + /* 73 */ "flute", + /* 74 */ "recorder", + /* 75 */ "woodflut", + /* 76 */ "bottle", + /* 77 */ "shakazul", + /* 78 */ "whistle", + /* 79 */ "ocarina", + /* 80 */ "sqrwave", + /* 81 */ "sawwave", + /* 82 */ "calliope", + /* 83 */ "chiflead", + /* 84 */ "voxlead", + /* 85 */ "voxlead", + /* 86 */ "lead5th", + /* 87 */ "basslead", + /* 88 */ "fantasia", + /* 89 */ "warmpad", + /* 90 */ "polysyn", + /* 91 */ "ghostie", + /* 92 */ "bowglass", + /* 93 */ "metalpad", + /* 94 */ "halopad", + /* 95 */ "sweeper", + /* 96 */ "aurora", + /* 97 */ "soundtrk", + /* 98 */ "crystal", + /* 99 */ "atmosphr", + /* 100 */ "freshair", + /* 101 */ "unicorn", + /* 102 */ "sweeper", + /* 103 */ "startrak", + /* 104 */ "sitar", + /* 105 */ "banjo", + /* 106 */ "shamisen", + /* 107 */ "koto", + /* 108 */ "kalimba", + /* 109 */ "bagpipes", + /* 110 */ "fiddle", + /* 111 */ "shannai", + /* 112 */ "carillon", + /* 113 */ "agogo", + /* 114 */ "steeldrm", + /* 115 */ "woodblk", + /* 116 */ "taiko", + /* 117 */ "toms", + /* 118 */ "syntom", + /* 119 */ "revcym", + /* 120 */ "fx-fret", + /* 121 */ "fx-blow", + /* 122 */ "seashore", + /* 123 */ "jungle", + /* 124 */ "telephon", + /* 125 */ "helicptr", + /* 126 */ "applause", + /* 127 */ "pistol", + + "", /* 128 = drum 0 */ + "", /* 129 = drum 1 */ + "", /* 130 = drum 2 */ + "", /* 131 = drum 3 */ + "", /* 132 = drum 4 */ + "", /* 133 = drum 5 */ + "", /* 134 = drum 6 */ + "", /* 135 = drum 7 */ + "", /* 136 = drum 8 */ + "", /* 137 = drum 9 */ + "", /* 138 = drum 10 */ + "", /* 139 = drum 11 */ + "", /* 140 = drum 12 */ + "", /* 141 = drum 13 */ + "", /* 142 = drum 14 */ + "", /* 143 = drum 15 */ + "", /* 144 = drum 16 */ + "", /* 145 = drum 17 */ + "", /* 146 = drum 18 */ + "", /* 147 = drum 19 */ + "", /* 148 = drum 20 */ + "", /* 149 = drum 21 */ + "", /* 150 = drum 22 */ + "", /* 151 = drum 23 */ + "", /* 152 = drum 24 */ + "", /* 153 = drum 25 */ + "", /* 154 = drum 26 */ + "highq", /* 155 = drum 27 */ + "slap", /* 156 = drum 28 */ + "scratch1", /* 157 = drum 29 */ + "scratch2", /* 158 = drum 30 */ + "sticks", /* 159 = drum 31 */ + "sqrclick", /* 160 = drum 32 */ + "metclick", /* 161 = drum 33 */ + "metbell", /* 162 = drum 34 */ + "kick1", /* 163 = drum 35 */ + "kick2", /* 164 = drum 36 */ + "stickrim", /* 165 = drum 37 */ + "snare1", /* 166 = drum 38 */ + "claps", /* 167 = drum 39 */ + "snare2", /* 168 = drum 40 */ + "tomlo2", /* 169 = drum 41 */ + "hihatcl", /* 170 = drum 42 */ + "tomlo1", /* 171 = drum 43 */ + "hihatpd", /* 172 = drum 44 */ + "tommid2", /* 173 = drum 45 */ + "hihatop", /* 174 = drum 46 */ + "tommid1", /* 175 = drum 47 */ + "tomhi2", /* 176 = drum 48 */ + "cymcrsh1", /* 177 = drum 49 */ + "tomhi1", /* 178 = drum 50 */ + "cymride1", /* 179 = drum 51 */ + "cymchina", /* 180 = drum 52 */ + "cymbell", /* 181 = drum 53 */ + "tamborin", /* 182 = drum 54 */ + "cymsplsh", /* 183 = drum 55 */ + "cowbell", /* 184 = drum 56 */ + "cymcrsh2", /* 185 = drum 57 */ + "vibslap", /* 186 = drum 58 */ + "cymride2", /* 187 = drum 59 */ + "bongohi", /* 188 = drum 60 */ + "bongolo", /* 189 = drum 61 */ + "congahi1", /* 190 = drum 62 */ + "congahi2", /* 191 = drum 63 */ + "congalo", /* 192 = drum 64 */ + "timbaleh", /* 193 = drum 65 */ + "timbalel", /* 194 = drum 66 */ + "agogohi", /* 195 = drum 67 */ + "agogolo", /* 196 = drum 68 */ + "cabasa", /* 197 = drum 69 */ + "maracas", /* 198 = drum 70 */ + "whistle1", /* 199 = drum 71 */ + "whistle2", /* 200 = drum 72 */ + "guiro1", /* 201 = drum 73 */ + "guiro2", /* 202 = drum 74 */ + "clave", /* 203 = drum 75 */ + "woodblk1", /* 204 = drum 76 */ + "woodblk2", /* 205 = drum 77 */ + "cuica1", /* 206 = drum 78 */ + "cuica2", /* 207 = drum 79 */ + "triangl1", /* 208 = drum 80 */ + "triangl2", /* 209 = drum 81 */ + "shaker", /* 210 = drum 82 */ + "jingles", /* 211 = drum 83 */ + "belltree", /* 212 = drum 84 */ + "castinet", /* 213 = drum 85 */ + "surdo1", /* 214 = drum 86 */ + "surdo2", /* 215 = drum 87 */ + "", /* 216 = drum 88 */ + "", /* 217 = drum 89 */ + "", /* 218 = drum 90 */ + "", /* 219 = drum 91 */ + "", /* 220 = drum 92 */ + "", /* 221 = drum 93 */ + "", /* 222 = drum 94 */ + "", /* 223 = drum 95 */ + "", /* 224 = drum 96 */ + "", /* 225 = drum 97 */ + "", /* 226 = drum 98 */ + "", /* 227 = drum 99 */ + "", /* 228 = drum 100 */ + "", /* 229 = drum 101 */ + "", /* 230 = drum 102 */ + "", /* 231 = drum 103 */ + "", /* 232 = drum 104 */ + "", /* 233 = drum 105 */ + "", /* 234 = drum 106 */ + "", /* 235 = drum 107 */ + "", /* 236 = drum 108 */ + "", /* 237 = drum 109 */ + "", /* 238 = drum 110 */ + "", /* 239 = drum 111 */ + "", /* 240 = drum 112 */ + "", /* 241 = drum 113 */ + "", /* 242 = drum 114 */ + "", /* 243 = drum 115 */ + "", /* 244 = drum 116 */ + "", /* 245 = drum 117 */ + "", /* 246 = drum 118 */ + "", /* 247 = drum 119 */ + "", /* 248 = drum 120 */ + "", /* 249 = drum 121 */ + "", /* 250 = drum 122 */ + "", /* 251 = drum 123 */ + "", /* 252 = drum 124 */ + "", /* 253 = drum 125 */ + "", /* 254 = drum 126 */ + "" /* 255 = drum 127 */ +}; diff --git a/lib/libOSSlib/libmain.c b/lib/libOSSlib/libmain.c new file mode 100644 index 0000000..d295874 --- /dev/null +++ b/lib/libOSSlib/libmain.c @@ -0,0 +1,200 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +#define OSSLIB +#include "../../include/soundcard.h" + +int __seqfd = -1; +static int initialized = 0; + +extern void _dump_midi (void); /* From play_event.c */ + +unsigned char *_seqbuf; +int _seqbuflen = 0; +int _seqbufptr = 0; + +int synth_types[64] = { 0 }; + +#define ST_NONE 0 +#define ST_GUS 1 +#define ST_OPL 2 + +int nrsynths = 0; + +extern void play_event (unsigned char *ev); + +void +seqbuf_dump (void) +{ + int l = _seqbufptr; + + unsigned char *ev = _seqbuf; + + while (l >= 8) + { + play_event (ev); + l -= 8; + ev += 8; + } + + _seqbufptr = 0; + _dump_midi (); +} + +static int +oss_init_lib (void) +{ + if (_seqbuflen < 32 || _seqbuflen > 2048) + _seqbuflen = 2048; + + if ((_seqbuf = malloc (_seqbuflen)) == NULL) + return 3; + + initialized = 1; + return 0; /* OK */ +} + +int +OSS_init (int userfd, int buflen) +{ + if (_seqbuflen || _seqbuflen || __seqfd != -1 || initialized) + { + fprintf (stderr, "libOSS: OSS_init called too late\n"); + return 1; + } + + __seqfd = userfd; + + if (buflen < 32 || buflen > 2048) + { + fprintf (stderr, "libOSS: OSS_init called with invalid buflen\n"); + return 2; + } + + _seqbuflen = buflen; + + return oss_init_lib (); +} + +static void +sanity_check (int fd, unsigned char *buf, int buflen) +{ + if (__seqfd != fd) + if (__seqfd == -1) + __seqfd = fd; + else + { + fprintf (stderr, "OSSlib: seqfd is inconsistent\n"); + } + + if (buf != _seqbuf) + { + fprintf (stderr, "OSSlib: _seqbuf is inconsistent\n"); + } + + if (buflen != _seqbuflen) + { + fprintf (stderr, "OSSlib: _seqbuf is inconsistent\n"); + } + + if (!initialized) + if (oss_init_lib () != 0) + { + fprintf (stderr, "OSSlib: Library initialization failed\n"); + exit (-1); + } +} + +void +OSS_seqbuf_dump (int fd, unsigned char *buf, int buflen) +{ + seqbuf_dump (); +} + +void +OSS_seq_advbuf (int len, int fd, unsigned char *buf, int buflen) +{ + sanity_check (fd, buf, buflen); + _seqbufptr += len; +} + +void +OSS_seq_needbuf (int len, int fd, unsigned char *buf, int buflen) +{ + sanity_check (fd, buf, buflen); + + if ((_seqbufptr + len) > _seqbuflen) + { + seqbuf_dump (); + } +} + +void +OSS_patch_caching (int dev, int chn, int patch, int fd, unsigned char *buf, + int buflen) +{ +} + +void +OSS_drum_caching (int dev, int chn, int patch, int fd, unsigned char *buf, + int buflen) +{ +} + +void +OSS_write_patch (int fd, unsigned char *buf, int len) +{ + sanity_check (fd, _seqbuf, _seqbuflen); + + if (write (fd, buf, len) == -1) + { + perror ("OSS_write_patch"); + exit (-1); + } +} + +int +OSS_write_patch2 (int fd, unsigned char *buf, int len) +{ + sanity_check (fd, _seqbuf, _seqbuflen); + + return write (fd, buf, len); +} + +/* + * audio routines + */ + +int +osslib_open (const char *path, int flags, int dummy) +{ + return open (path, flags, dummy); +} + +void +osslib_close (int fd) +{ + close (fd); +} + +int +osslib_write (int fd, const void *buf, int count) +{ + return write (fd, buf, count); +} + +int +osslib_read (int fd, void *buf, int count) +{ + return read (fd, buf, count); +} + +int +osslib_ioctl (int fd, unsigned int request, void *arg) +{ + return ioctl (fd, request, arg); +} diff --git a/lib/libOSSlib/midiparser.c b/lib/libOSSlib/midiparser.c new file mode 100644 index 0000000..742e8f5 --- /dev/null +++ b/lib/libOSSlib/midiparser.c @@ -0,0 +1,401 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "../../include/soundcard.h" +#include "midiparser.h" + +struct _mtc_state +{ + int prev_ix; + int state; + int offset; + oss_mtc_data_t mtc, mtc0; +}; + +typedef struct midi_input_info +{ /* MIDI input scanner variables */ +#define MI_MAX 64 + int m_busy; + unsigned char m_buf[MI_MAX]; + unsigned char m_prev_status; /* For running status */ + int m_ptr; +#define MST_INIT 0 +#define MST_DATA 1 +#define MST_SYSEX 2 + int m_state; + int m_left; + int m_f1_flag; +} midi_input_info_t; + +struct midiparser_common +{ + midi_input_info_t inc; + midiparser_callback_t callback; + midiparser_mtc_callback_t mtc_callback; + void *client_context; + struct _mtc_state mtc_state; +}; + +#define DBG(x) {} + +#define CALLBACK(cat, msg, ch, p1, p2, p3) \ + { \ + unsigned char arr[3];arr[0]=p1;arr[1]=p2;arr[2]=p3; \ + synth->callback(synth->client_context, cat, msg, ch, arr, 3); \ + } +#define CALLBACK_A(cat, msg, ch, parms, len) \ + synth->callback(synth->client_context, cat, msg, ch, parms, len) + +void +do_system_msg (midiparser_common_p synth, unsigned char *msg, int mlen) +{ + DBG (printf ("System msg %02x, %02x (%d)\n", msg[0], msg[1], mlen)); + CALLBACK_A (CAT_REALTIME, *msg, 0, msg, mlen); + return; +} + +static void +do_sysex_msg (midiparser_common_p synth, unsigned char *msg, int mlen) +{ + CALLBACK_A (CAT_SYSEX, 0, 0, msg, mlen); + return; +} + +static void +do_realtime_msg (midiparser_common_p synth, unsigned char data) +{ +} + +void +do_midi_msg (midiparser_common_p synth, unsigned char *msg, int mlen) +{ + switch (msg[0] & 0xf0) + { + case 0x90: + if (msg[2] != 0) + { + CALLBACK (CAT_VOICE, 0x90, msg[0] & 0x0f, msg[1], msg[2], 0); + break; + } + msg[2] = 64; + + case 0x80: + CALLBACK (CAT_VOICE, 0x80, msg[0] & 0x0f, msg[1], msg[2], 0); + break; + + case 0xA0: + CALLBACK (CAT_VOICE, 0xA0, msg[0] & 0x0f, msg[1], msg[2], 0); + break; + + case 0xB0: + CALLBACK (CAT_CHN, 0xB0, msg[0] & 0x0f, msg[1], msg[2], 0); + break; + + case 0xC0: + CALLBACK (CAT_CHN, 0xC0, msg[0] & 0x0f, msg[1], 0, 0); + break; + + case 0xD0: + CALLBACK (CAT_CHN, 0xD0, msg[0] & 0x0f, msg[1], 0, 0); + break; + + case 0xE0: + CALLBACK (CAT_VOICE, 0xE0, msg[0] & 0x0f, msg[1], msg[2], 0); + break; + + case 0xf0: /* System common messages */ + do_system_msg (synth, msg, mlen); + break; + + default: + ; + } +} + +static void +send_mtc (midiparser_common_p synth, struct _mtc_state *st) +{ + oss_mtc_data_t mtc; + + memcpy (&mtc, &st->mtc, sizeof (mtc)); + + mtc.qframes += st->offset; + mtc.frames += mtc.qframes / 4; + mtc.qframes %= 4; + + if (mtc.time_code_type == 0) + mtc.time_code_type = 30; + mtc.seconds += mtc.frames / mtc.time_code_type; /* TODO: Handle drop frames */ + mtc.frames %= mtc.time_code_type; + + mtc.minutes += mtc.seconds / 60; + mtc.seconds %= 60; + + mtc.hours += mtc.minutes / 60; + mtc.minutes %= 60; + +#if 0 + printf ("%2d:%02d:%02d %02d.%d %2dFPS\n", + mtc.hours, mtc.minutes, + mtc.seconds, mtc.frames, mtc.qframes, mtc.time_code_type); +#endif + + synth->mtc_callback (synth->client_context, &mtc); +} + +void +mtc_message (midiparser_common_p synth, struct _mtc_state *st, + unsigned char b) +{ + static char frame_types[4] = { 24, 25, 29, 30 }; + int ix, data; + int previx; + + ix = b >> 4; + data = b & 0x0f; + + previx = (st->prev_ix + 1) % 8; + + if (ix == previx) + st->mtc0.direction = st->mtc.direction = MTC_DIR_FORWARD; + else if (ix == st->prev_ix) + st->mtc0.direction = st->mtc.direction = MTC_DIR_STOPPED; + else + st->mtc0.direction = st->mtc.direction = MTC_DIR_BACKWARD; + st->prev_ix = ix; + + if (st->state == 0) + { + if (ix != 0) /* Not the beginning of the sequence yet */ + return; + st->state = 1; + st->offset = -1; + } + + switch (ix) + { + case 0: /* Frame count LS nibble */ + st->mtc0.qframes = 0; + st->mtc0.frames = data; + break; + + case 1: /* Frame count MS nibble */ + st->mtc0.frames |= data << 4; + break; + + case 2: /* Seconds count LS nibble */ + st->mtc0.seconds = data; + break; + + case 3: /* Seconds count MS nibble */ + st->mtc0.seconds |= data << 4; + break; + + case 4: /* Minutes count LS nibble */ + st->mtc0.minutes = data; + break; + + case 5: /* Minutes count MS nibble */ + st->mtc0.minutes |= data << 4; + break; + + case 6: /* Hours count LS nibble */ + st->mtc0.hours = data; + break; + + case 7: /* Hours count MS nibble */ + st->mtc0.hours |= data << 4; + st->mtc0.time_code_type = frame_types[(st->mtc0.hours >> 5) & 0x03]; + st->mtc0.hours &= 0x1f; + memcpy (&st->mtc, &st->mtc0, sizeof (st->mtc)); + break; + } + + if (ix == 7) + st->offset = 7; + else + st->offset++; + send_mtc (synth, st); +} + +static void +handle_midi_input (midiparser_common_p synth, midi_input_info_t * inc, + unsigned char data) +{ + extern int seq_actsense_enable; + extern int seq_rt_enable; + + static unsigned char len_tab[] = /* # of data bytes following a status + */ + { + 2, /* 8x */ + 2, /* 9x */ + 2, /* Ax */ + 2, /* Bx */ + 1, /* Cx */ + 1, /* Dx */ + 2, /* Ex */ + 0 /* Fx */ + }; + /* printf("%02x (%d) ", data, inc->m_state); */ + + if (data == 0xfe) /* Active sensing */ + { + return; + } + + if (data >= 0xf8) /* Real time message */ + { + do_realtime_msg (synth, data); + CALLBACK (CAT_REALTIME, data, 0, 0, 0, 0); + return; + } + + if (data == 0xf1) /* MTC quarter frame (1st byte) */ + { + inc->m_f1_flag = 1; + return; + } + + if (inc->m_f1_flag) /* MTC quarter frame (2nd byte) */ + { + inc->m_f1_flag = 0; + + if (synth->mtc_callback != NULL) + { + mtc_message (synth, &synth->mtc_state, data); + return; + } + CALLBACK (CAT_MTC, 0xf1, 0, data, 0, 0); + return; + } + + switch (inc->m_state) + { + case MST_INIT: + if (data & 0x80) /* MIDI status byte */ + { + if ((data & 0xf0) == 0xf0) /* Common message */ + { + switch (data) + { + case 0xf0: /* Sysex */ + inc->m_state = MST_SYSEX; + inc->m_ptr = 1; + inc->m_left = MI_MAX; + inc->m_buf[0] = data; + break; /* Sysex */ + + case 0xf1: /* MTC quarter frame */ + case 0xf3: /* Song select */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 1; + inc->m_buf[0] = data; + break; + + case 0xf2: /* Song position pointer */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = 2; + inc->m_buf[0] = data; + break; + + default: /* Other common messages */ + inc->m_buf[0] = data; + inc->m_ptr = 1; + do_midi_msg (synth, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + inc->m_left = 0; + } + } + else + { + /* Channel messages */ + inc->m_state = MST_DATA; + inc->m_ptr = 1; + inc->m_left = len_tab[(data >> 4) - 8]; + inc->m_buf[0] = inc->m_prev_status = data; + } + } + else /* Running status */ if (inc->m_prev_status & 0x80) /* Ignore if no previous status (yet) */ + { + inc->m_state = MST_DATA; + inc->m_ptr = 2; + inc->m_left = len_tab[(inc->m_prev_status >> 4) - 8] - 1; + inc->m_buf[0] = inc->m_prev_status; + inc->m_buf[1] = data; + } + break; /* MST_INIT */ + + case MST_DATA: + inc->m_buf[inc->m_ptr++] = data; + if (--inc->m_left <= 0) + { + inc->m_state = MST_INIT; + do_midi_msg (synth, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + break; /* MST_DATA */ + + case MST_SYSEX: + inc->m_buf[inc->m_ptr++] = data; + if (data == 0xf7) /* Sysex end */ + { + do_sysex_msg (synth, inc->m_buf, inc->m_ptr); + inc->m_state = MST_INIT; + inc->m_left = 0; + inc->m_ptr = 0; + + } + else if (inc->m_ptr >= MI_MAX) /* Overflow protection */ + { + do_sysex_msg (synth, inc->m_buf, inc->m_ptr); + inc->m_ptr = 0; + } + + break; /* MST_SYSEX */ + + default: + inc->m_state = MST_INIT; + } +} + +midiparser_common_p +midiparser_create (midiparser_callback_t callback, void *context) +{ + midiparser_common_p synth; + + if ((synth = malloc (sizeof (*synth))) == NULL) + return NULL; + + memset (synth, 0, sizeof (*synth)); + + synth->callback = callback; + synth->client_context = context; + synth->mtc_state.prev_ix = -1; + + return synth; +} + +void +midiparser_mtc_callback (midiparser_common_p common, + midiparser_mtc_callback_t callback) +{ + common->mtc_callback = callback; +} + +void +midiparser_input (midiparser_common_p synth, unsigned char data) +{ + handle_midi_input (synth, &synth->inc, data); +} + +void +midiparser_input_buf (midiparser_common_p synth, unsigned char *data, int len) +{ + int i; + + for (i = 0; i < len; i++) + handle_midi_input (synth, &synth->inc, data[i]); +} diff --git a/lib/libOSSlib/midiparser.h b/lib/libOSSlib/midiparser.h new file mode 100644 index 0000000..20aac3a --- /dev/null +++ b/lib/libOSSlib/midiparser.h @@ -0,0 +1 @@ +#include "../../kernel/framework/include/midiparser.h" diff --git a/lib/libOSSlib/play_event.c b/lib/libOSSlib/play_event.c new file mode 100644 index 0000000..62687ef --- /dev/null +++ b/lib/libOSSlib/play_event.c @@ -0,0 +1,273 @@ +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include "../../include/soundcard.h" +#include <sys/time.h> + +#undef DEBUG + +extern int __seqfd; + +static midi_packet_t midi_packet; + +static unsigned char *midibuf = &midi_packet.payload[0]; +static int mp = 0; + +oss_midi_time_t current_tick = 0; + +static int timer_started = 0; + +void +OSS_set_timebase (int tb) +{ +/* TODO? */ +} + +#if 1 +#define _write write +#else +static size_t +_write (int fildes, void *b, size_t nbyte) +{ + midi_packet_header_t *hdr = b; + unsigned char *buf; + int l = nbyte; + + + buf = (unsigned char *) b + sizeof (*hdr); + + if (hdr->magic == MIDI_HDR_MAGIC) /* Timed mode write */ + { + l -= sizeof (*hdr); + + switch (hdr->event_type) + { + case MIDI_EV_WRITE: + printf ("%9lu: MIDI_EV_WRITE: ", hdr->time); + break; + case MIDI_EV_TEMPO: + printf ("%9lu: MIDI_EV_TEMPO: ", hdr->time); + break; + case MIDI_EV_ECHO: + printf ("%9lu: MIDI_EV_ECHO: ", hdr->time); + break; + case MIDI_EV_START: + printf ("%9lu: MIDI_EV_START: ", hdr->time); + break; + case MIDI_EV_STOP: + printf ("%9lu: MIDI_EV_STOP: ", hdr->time); + break; + default: + printf ("%9lu: MIDI_EV_%d: ", hdr->time, hdr->event_type); + break; + } + + if (hdr->options & MIDI_OPT_TIMED) + printf ("TIMED "); + + if (l > 0) + { + int i; + + printf ("%d bytes (", l); + + for (i = 0; i < l; i++) + printf ("%02x ", buf[i]); + printf (")"); + } + + printf ("\n"); + } + + return write (fildes, b, nbyte); +} +#endif + +static void +start_timer (void) +{ +#ifndef DEBUG + midi_packet_header_t hdr; + + hdr.magic = MIDI_HDR_MAGIC; + hdr.event_type = MIDI_EV_START; + hdr.options = MIDI_OPT_NONE; + hdr.time = 0; + hdr.parm = 0; + if (_write (__seqfd, &hdr, sizeof (hdr)) != sizeof (hdr)) + { + perror ("Write start timer"); + exit (-1); + } + + timer_started = 1; + printf ("Timer started\n"); +#endif +} + +void +_dump_midi (void) +{ + if (mp > 0) + { + if (!timer_started) + start_timer (); + midi_packet.hdr.magic = MIDI_HDR_MAGIC; + midi_packet.hdr.options = MIDI_OPT_TIMED; + midi_packet.hdr.event_type = MIDI_EV_WRITE; + midi_packet.hdr.parm = 0; + midi_packet.hdr.time = current_tick; + +#ifdef DEBUG + if (write (1, &midi_packet.payload[0], mp) == -1) +#else + if (_write (__seqfd, &midi_packet, sizeof (midi_packet_header_t) + mp) + == -1) +#endif + { + perror ("MIDI write"); + exit (-1); + } + mp = 0; + } +} + +static void +oss_do_timing (unsigned char *ev) +{ + unsigned int parm = *(unsigned int *) &ev[4]; + midi_packet_header_t hdr; + + oss_midi_time_t tick; + + _dump_midi (); + + switch (ev[1]) + { + case TMR_TEMPO: +#ifndef DEBUG + if (!timer_started) + start_timer (); + hdr.magic = MIDI_HDR_MAGIC; + hdr.event_type = MIDI_EV_TEMPO; + hdr.options = MIDI_OPT_TIMED; + hdr.time = current_tick; + hdr.parm = parm; + if (_write (__seqfd, &hdr, sizeof (hdr)) != sizeof (hdr)) + { + perror ("Write tempo"); + exit (-1); + } +#endif + break; + + case TMR_WAIT_REL: + tick = current_tick + parm; + + current_tick = tick; + break; + + case TMR_WAIT_ABS: + tick = parm; + current_tick = tick; + break; + } + +} + +static void +out_midi3 (unsigned char a, unsigned char b, unsigned char c) +{ + if (mp > 950) + _dump_midi (); + midibuf[mp++] = a; + midibuf[mp++] = b; + midibuf[mp++] = c; +/* printf("Out %02x %02x %02x\n", a, b, c); */ +} + +static void +out_midi2 (unsigned char a, unsigned char b) +{ + if (mp > 950) + _dump_midi (); + midibuf[mp++] = a; + midibuf[mp++] = b; +/* printf("Out %02x %02x\n", a, b); */ +} + +void +play_event (unsigned char *ev) +{ + int i; + + switch (ev[0]) + { + case EV_TIMING: + oss_do_timing (ev); + return; + break; + + case EV_SEQ_LOCAL: + break; + + case EV_CHN_COMMON: + switch (ev[2]) + { + case MIDI_PGM_CHANGE: + /* printf("PGM change %02x %d\n", ev[2]|ev[3], ev[4]); */ + out_midi2 (ev[2] | ev[3], ev[4]); + break; + + case MIDI_CHN_PRESSURE: + out_midi2 (ev[2] | ev[3], ev[4]); + break; + + case MIDI_CTL_CHANGE: + out_midi3 (ev[2] | ev[3], ev[4], *(short *) &ev[6]); + break; + + default: + out_midi3 (ev[2] | ev[3], ev[4], *(short *) &ev[6]); + } + return; + break; + + case EV_CHN_VOICE: + out_midi3 (ev[2] | ev[3], ev[4], ev[5]); + return; + break; + + case EV_SYSEX: + { + int i, l; + l = 8; + for (i = 2; i < 8; i++) + if (ev[i] == 0xff) + { + l = i; + break; + } + if (mp > 950) + _dump_midi (); + for (i = 2; i < l; i++) + midibuf[mp++] = ev[i]; + } + return; + break; + + case EV_SYSTEM: + printf ("EV_SYSTEM: "); + break; + + default: + printf ("Unknown event %d: ", ev[0]); + + } + + for (i = 0; i < 8; i++) + printf ("%02x ", ev[i]); + printf ("\n"); +} diff --git a/lib/libossmix/.config b/lib/libossmix/.config new file mode 100644 index 0000000..3d02eca --- /dev/null +++ b/lib/libossmix/.config @@ -0,0 +1,3 @@ +mode=shlib +forgetos=BeOS +forgetos=VxWorks diff --git a/lib/libossmix/libossmix_cache.c b/lib/libossmix/libossmix_cache.c new file mode 100644 index 0000000..a15fbf4 --- /dev/null +++ b/lib/libossmix/libossmix_cache.c @@ -0,0 +1,210 @@ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +/* + * Settings cache for libossmix + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <soundcard.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <errno.h> +#include <netdb.h> + +#define OSSMIX_REMOTE + +#include "libossmix.h" +#include "libossmix_impl.h" + +static local_mixer_t *mixers[MAX_TMP_MIXER] = { NULL }; + +void +mixc_add_node (int mixernum, int node, oss_mixext * ext) +{ + local_mixer_t *lmixer; + oss_mixext *lnode; + + if (mixers[mixernum] == NULL) + { + int i; + + mixers[mixernum] = lmixer = malloc (sizeof (*lmixer)); + if (lmixer == NULL) + { + fprintf (stderr, "mixc_add_node: Out of memory\n"); + exit (EXIT_FAILURE); + } + + memset (lmixer, 0, sizeof (*lmixer)); + for (i = 0; i < MAX_TMP_NODES; i++) + lmixer->values[i] = -1; // Invalid + } + else + lmixer = mixers[mixernum]; + + if (ext->ctrl >= lmixer->nrext) + lmixer->nrext = ext->ctrl + 1; + + if (node >= MAX_TMP_NODES) + { + fprintf (stderr, "mixc_add_node: Node number too large %d\n", node); + exit (EXIT_FAILURE); + } + + lnode = lmixer->nodes[node]; + + if (lnode == NULL) + { + lmixer->nodes[node] = lnode = malloc (sizeof (*lnode)); + + if (lnode == NULL) + { + fprintf (stderr, "mixc_get_node: Out of memory\n"); + exit (EXIT_FAILURE); + } + } + + memcpy (lnode, ext, sizeof (*ext)); + +} + +oss_mixext * +mixc_get_node (int mixernum, int node) +{ + local_mixer_t *lmixer; + oss_mixext *lnode; + + if (mixers[mixernum] == NULL) + { + return NULL; + } + lmixer = mixers[mixernum]; + + if (node >= MAX_TMP_NODES) + { + fprintf (stderr, "mixc_get_node: Node number too large %d\n", node); + exit (EXIT_FAILURE); + } + + lnode = lmixer->nodes[node]; + + return lnode; +} + +void +mixc_clear_changeflags(int mixernum) +{ + local_mixer_t *lmixer; + + if (mixers[mixernum] == NULL) + { + return; + } + lmixer = mixers[mixernum]; + + memset(lmixer->changemask, 0, sizeof(lmixer->changemask)); +} + +void +mixc_set_value (int mixernum, int node, int value) +{ + local_mixer_t *lmixer; + + if (mixers[mixernum] == NULL) + { + return; + } + lmixer = mixers[mixernum]; + + if (node >= MAX_TMP_NODES) + { + fprintf (stderr, "mixc_set_value: Node number too large %d\n", node); + exit (EXIT_FAILURE); + } + + if (lmixer->values[node] != value) + lmixer->changemask[node / 8] |= (1 << (node % 8)); + + lmixer->values[node] = value; +} + +int +mixc_get_value (int mixernum, int node) +{ + local_mixer_t *lmixer; + + if (mixers[mixernum] == NULL) + { + return -1; + } + lmixer = mixers[mixernum]; + + if (node >= MAX_TMP_NODES) + { + fprintf (stderr, "mixc_get_value: Node number too large %d\n", node); + exit (EXIT_FAILURE); + } + lmixer->changemask[node / 8] &= ~(1 << (node % 8)); + + return lmixer->values[node]; +} + +int +mixc_get_all_values (int mixernum, value_packet_t value_packet, int changecheck) +{ + int i, n = 0; + oss_mixext *lnode; + local_mixer_t *lmixer; + + if (mixers[mixernum] == NULL) + { + fprintf (stderr, "mixc_get_all_values: Mixer %d doesn't exist\n", + mixernum); + return 0; + } + lmixer = mixers[mixernum]; + + for (i = 0; i < lmixer->nrext; i++) + { + lnode = lmixer->nodes[i]; + + if (lnode == NULL) + { + fprintf (stderr, "mixc_get_all_values: Mixer %d, node %d == NULL\n", + mixernum, i); + continue; + } + + if (changecheck) // Not changed since the last time + if (!(lmixer->changemask[i / 8] & (1 << (i % 8)) )) + continue; + + if (lnode->type != MIXT_DEVROOT && lnode->type != MIXT_GROUP + && lnode->type != MIXT_MARKER) + { + value_packet[n].node = i; + value_packet[n].value = lmixer->values[i]; + + lmixer->changemask[i / 8] &= ~(1 << (i % 8)); + +//printf("Send %d = %08x\n", i, lmixer->values[i]); + n++; + } + } + + return n; +} diff --git a/lib/libossmix/libossmix_impl.h b/lib/libossmix/libossmix_impl.h new file mode 100644 index 0000000..3848166 --- /dev/null +++ b/lib/libossmix/libossmix_impl.h @@ -0,0 +1,36 @@ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ + +extern int mixlib_trace; + +typedef struct +{ + int (*connect) (const char *hostname, int port); + int (*get_fd) (ossmix_select_poll_t * cb); + void (*disconnect) (void); + void (*enable_events) (void); + int (*get_nmixers) (void); + int (*get_mixerinfo) (int mixernum, oss_mixerinfo * mi); + int (*open_mixer) (int mixernum); + void (*close_mixer) (int mixernum); + int (*get_nrext) (int mixernum); + int (*get_nodeinfo) (int mixernum, int node, oss_mixext * ext); + int (*get_enuminfo) (int mixernum, int node, oss_mixer_enuminfo * ei); + int (*get_description) (int mixernum, int node, oss_mixer_enuminfo * desc); + int (*get_value) (int mixernum, int ctl, int timestamp); + void (*set_value) (int mixernum, int ctl, int timestamp, int value); + void (*timertick)(void); +} ossmix_driver_t; + +extern ossmix_driver_t ossmix_local_driver, ossmix_tcp_driver; +extern void _client_event (int cmd, int p1, int p2, int p3, int p4, int p5); +extern int _ossmix_refresh_mixer(int mixernum, int prev_nmixers); diff --git a/lib/libossmix/libossmix_local.c b/lib/libossmix/libossmix_local.c new file mode 100644 index 0000000..22d39f7 --- /dev/null +++ b/lib/libossmix/libossmix_local.c @@ -0,0 +1,332 @@ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +/* + * Local driver for libossmix + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> + +#include <soundcard.h> + +#define OSSMIX_REMOTE + +#include "libossmix.h" +#include "libossmix_impl.h" + +static int global_fd = -1; +static int num_mixers=0; + +static int mixer_fd[MAX_TMP_MIXER]; + +static int +local_connect (const char *hostname, int port) +{ + char *devmixer; + int i; + + if (mixlib_trace > 0) + fprintf (stderr, "Entered local_connect()\n"); + + for (i = 0; i < MAX_TMP_MIXER; i++) + mixer_fd[i] = -1; + + if ((devmixer = getenv ("OSS_MIXERDEV")) == NULL) + devmixer = "/dev/mixer"; + +/* + * Open the mixer device + */ + if ((global_fd = open (devmixer, O_RDWR, 0)) == -1) + { + perror (devmixer); + return -1; + } + + return 0; +} + +static void +local_disconnect (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "Entered local_disconnect()\n"); + + if (global_fd >= 0) + close (global_fd); + + global_fd = -1; +} + +static void +local_enable_events (void) +{ +} + +static int +local_get_fd (ossmix_select_poll_t * cb) +{ + *cb = NULL; + return -1; /* No poll handling required */ +} + +static int +local_get_nmixers (void) +{ + oss_sysinfo si; + + if (ioctl (global_fd, SNDCTL_SYSINFO, &si) == -1) + { + perror ("SNDCTL_SYSINFO"); + return -1; + } + + return num_mixers = si.nummixers; +} + +static int +local_get_mixerinfo (int mixernum, oss_mixerinfo * mi) +{ + mi->dev = mixernum; + + if (ioctl (global_fd, SNDCTL_MIXERINFO, mi) == -1) + { + perror ("SNDCTL_MIXERINFO"); + return -1; + } + + return 0; +} + +static int +local_open_mixer (int mixernum) +{ + oss_mixerinfo mi; + + if (mixer_fd[mixernum] > -1) + return 0; + + if (ossmix_get_mixerinfo (mixernum, &mi) < 0) + return -1; + +//fprintf(stderr, "local_open_mixer(%d: %s)\n", mixernum, mi.devnode); + + if ((mixer_fd[mixernum] = open (mi.devnode, O_RDWR, 0)) == -1) + { + perror (mi.devnode); + return -1; + } + + return 0; +} + +static void +local_close_mixer (int mixernum) +{ +//fprintf(stderr, "local_close_mixer(%d)\n", mixernum); + + if (mixer_fd[mixernum] == -1) + return; + + close (mixer_fd[mixernum]); + mixer_fd[mixernum] = -1; +} + +static int +local_get_nrext (int mixernum) +{ + int n = -1; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_NREXT, &n) == -1) + { + perror ("SNDCTL_MIX_NREXT"); + return -1; + } + + return n; +} + +static int +local_get_nodeinfo (int mixernum, int node, oss_mixext * ext) +{ + ext->dev = mixernum; + ext->ctrl = node; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_EXTINFO, ext) == -1) + { + perror ("SNDCTL_MIX_EXTINFO"); + return -1; + } + + mixc_add_node (mixernum, node, ext); + return 0; +} + +static int +local_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei) +{ + ei->dev = mixernum; + ei->ctrl = node; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_ENUMINFO, ei) == -1) + { + perror ("SNDCTL_MIX_ENUMINFO"); + return -1; + } + + return 0; +} + +static int +local_get_description (int mixernum, int node, oss_mixer_enuminfo * desc) +{ + desc->dev = mixernum; + desc->ctrl = node; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_DESCRIPTION, desc) == -1) + { + perror ("SNDCTL_MIX_DESCRIPTION"); + return -1; + } + + return 0; +} + +static int +local_get_value (int mixernum, int ctl, int timestamp) +{ + oss_mixer_value val; + + val.dev = mixernum; + val.ctrl = ctl; + val.timestamp = timestamp; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_READ, &val) == -1) + { + perror ("SNDCTL_MIX_READ"); + return -1; + } + + mixc_set_value (mixernum, ctl, val.value); + return val.value; +} + +static int +private_get_value (int mixernum, int ctl, int timestamp) +{ + oss_mixer_value val; + + val.dev = mixernum; + val.ctrl = ctl; + val.timestamp = timestamp; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_READ, &val) == -1) + { + perror ("SNDCTL_MIX_READ"); + return -1; + } + + return val.value; +} + +static void +local_set_value (int mixernum, int ctl, int timestamp, int value) +{ + oss_mixer_value val; + + val.dev = mixernum; + val.ctrl = ctl; + val.timestamp = timestamp; + val.value = value; + + if (ioctl (mixer_fd[mixernum], SNDCTL_MIX_WRITE, &val) == -1) + { + perror ("SNDCTL_MIX_WRITE"); + } + mixc_set_value (mixernum, ctl, val.value); +} + +static void +update_values (int mixernum) +{ + oss_mixext *ext; + int i; + int nrext; + int value, prev_value; + + nrext = ossmix_get_nrext (mixernum); + + for (i = 0; i < nrext; i++) + { + if ((ext = mixc_get_node (mixernum, i)) == NULL) + { + continue; + } + + if (ext->type == MIXT_DEVROOT || ext->type == MIXT_GROUP + || ext->type == MIXT_MARKER) + continue; + + prev_value = mixc_get_value (mixernum, i); + + if ((value = private_get_value (mixernum, i, ext->timestamp)) < 0) + continue; + // TODO check for EIDRM + + if (value != prev_value) + { + mixc_set_value (mixernum, i, value); + _client_event (OSSMIX_EVENT_VALUE, mixernum, i, value, 0, 0); + } + + } +} + +static void +local_timertick(void) +{ + int mixernum, n; + + for (mixernum=0;mixernum<num_mixers;mixernum++) + if (mixer_fd[mixernum] >= 0) /* Open */ + { + update_values (mixernum); + } + + n=ossmix_get_nmixers(); + if (n>num_mixers) + { + num_mixers=n; + _client_event (OSSMIX_EVENT_NEWMIXER, n, 0, 0, 0, 0); + } +} + +ossmix_driver_t ossmix_local_driver = { + local_connect, + local_get_fd, + local_disconnect, + local_enable_events, + local_get_nmixers, + local_get_mixerinfo, + local_open_mixer, + local_close_mixer, + local_get_nrext, + local_get_nodeinfo, + local_get_enuminfo, + local_get_description, + local_get_value, + local_set_value, + local_timertick +}; diff --git a/lib/libossmix/libossmix_main.c b/lib/libossmix/libossmix_main.c new file mode 100644 index 0000000..a2b3597 --- /dev/null +++ b/lib/libossmix/libossmix_main.c @@ -0,0 +1,238 @@ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +/* + * Main module for libossmix + */ +#include <stdio.h> +#include <soundcard.h> + +#include "libossmix.h" +#include "libossmix_impl.h" + +static ossmix_driver_t *mixer_driver = NULL; +int mixlib_trace = 0; +static int num_mixers = 0; + +ossmix_callback_t event_callback = NULL; + +int +ossmix_init (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "ossmix_init() called\n"); + return 0; +} + +void +ossmix_close (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "ossmix_close() called\n"); +} + +void +_client_event (int event, int p1, int p2, int p3, int p4, int p5) +{ + /* + * To be called only by the internals of ossmixlib + */ + + if (event_callback != NULL) + { + ossmix_callback_parm_t parm; + + parm.event = event; + parm.p1 = p1; + parm.p2 = p2; + parm.p3 = p3; + parm.p4 = p4; + parm.p5 = p5; + event_callback (&parm); + } +} + +int +ossmix_connect (const char *hostname, int port) +{ + if (mixlib_trace > 0) + fprintf (stderr, "ossmix_connect(%s, %d)) called\n", hostname, port); + + if (hostname == NULL) + mixer_driver = &ossmix_local_driver; + else + mixer_driver = &ossmix_tcp_driver; + + event_callback = NULL; + + return mixer_driver->connect (hostname, port); +} + +int +ossmix_get_fd (ossmix_select_poll_t * cb) +{ + return mixer_driver->get_fd (cb); +} + +void +ossmix_set_callback (ossmix_callback_t cb) +{ + event_callback = cb; + mixer_driver->enable_events (); +} + +void +ossmix_disconnect (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "ossmix_disconnect() called\n"); + + event_callback = NULL; + + mixer_driver->disconnect (); +} + +int +ossmix_get_nmixers (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "ossmix_get_nmixes() called\n"); + return (num_mixers = mixer_driver->get_nmixers ()); +} + +int +ossmix_get_mixerinfo (int mixernum, oss_mixerinfo * mi) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_get_mixerinfo: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + + return mixer_driver->get_mixerinfo (mixernum, mi); +} + +int +ossmix_open_mixer (int mixernum) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_open_mixer: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->open_mixer (mixernum); +} + +void +ossmix_close_mixer (int mixernum) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_close_mixer: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return; + } + + mixer_driver->close_mixer (mixernum); +} + +int +ossmix_get_nrext (int mixernum) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_get_nrext: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->get_nrext (mixernum); +} + +int +ossmix_get_nodeinfo (int mixernum, int node, oss_mixext * ext) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_get_nodeinfo: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->get_nodeinfo (mixernum, node, ext); +} + +int +ossmix_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_get_enuminfo: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->get_enuminfo (mixernum, node, ei); +} + +int +ossmix_get_description (int mixernum, int node, oss_mixer_enuminfo * desc) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, + "ossmix_get_description: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->get_description (mixernum, node, desc); +} + +int +ossmix_get_value (int mixernum, int node, int timestamp) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_get_value: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return -1; + } + return mixer_driver->get_value (mixernum, node, timestamp); +} + +void +ossmix_set_value (int mixernum, int node, int timestamp, int value) +{ + if (mixernum >= num_mixers) + { + fprintf (stderr, "ossmix_set_value: Bad mixer number (%d >= %d)\n", + mixernum, num_mixers); + return; + } + + mixer_driver->set_value (mixernum, node, timestamp, value); +} + +void +ossmix_timertick(void) +{ + mixer_driver->timertick(); +} + +/* + * Internal use functions (not to be used by applications) + */ +int +_ossmix_refresh_mixer(int mixernum, int prev_nmixers) +{ +printf("_ossmix_refresh_mixer(%d, %d) called\n", mixernum, prev_nmixers); + + return prev_nmixers; // TODO +} diff --git a/lib/libossmix/libossmix_tcp.c b/lib/libossmix/libossmix_tcp.c new file mode 100644 index 0000000..a5da6f6 --- /dev/null +++ b/lib/libossmix/libossmix_tcp.c @@ -0,0 +1,813 @@ +/* + * + * This file is part of Open Sound System. + * + * Copyright (C) 4Front Technologies 1996-2008. + * + * This this source file is released under GPL v2 license (no other versions). + * See the COPYING file included in the main directory of this source + * distribution for the license terms and conditions. + * + */ +/* + * TCP driver for libossmix + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <soundcard.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <fcntl.h> +#include <errno.h> +#include <netdb.h> + +#define OSSMIX_REMOTE + +#include "libossmix.h" +#include "libossmix_impl.h" + +static int initialized = 0; +static void tcp_disconnect (void); +static int sockfd = -1; +static int do_byteswap = 0; + +static void poll_callback (void); +static void handle_packet (ossmix_commad_packet_t * msg, char *payload, + int payload_size); + +static int +read_all (int sock, void *b, int count) +{ + unsigned char *buf = b; + int l = 0; + + while (l < count) + { + int c, n = count - l; + + if ((c = read (sock, buf + l, n)) <= 0) + return c; + + l += c; + } + + return l; +} + +static inline int +bswap32 (int x) +{ + + int y = 0; + unsigned char *a = ((unsigned char *) &x) + 3; + unsigned char *b = (unsigned char *) &y; + + *b++ = *a--; + *b++ = *a--; + *b++ = *a--; + *b++ = *a--; + + return y; +} + +#define BSWAP32(x) x=bswap32(x) + +static void +byteswap_msg (ossmix_commad_packet_t * msg) +{ + BSWAP32 (msg->cmd); + BSWAP32 (msg->p1); + BSWAP32 (msg->p2); + BSWAP32 (msg->p3); + BSWAP32 (msg->p4); + BSWAP32 (msg->p5); + BSWAP32 (msg->ack_rq); + BSWAP32 (msg->unsolicited); + BSWAP32 (msg->payload_size); +} + +typedef void (*bswap_func_t) (void *data, int len); + +static void +bswap_int_array (void *data, int len) +{ + int *arr = (int *) data; + int i; + + if (len % sizeof (int) != 0) + { + fprintf (stderr, "bswap_int_array: Bad size %d\n", len); + exit (EXIT_FAILURE); + } + + for (i = 0; i < len / sizeof (int); i++) + BSWAP32 (arr[i]); +} + +static void +bswap_mixerinfo (void *data, int len) +{ + oss_mixerinfo *mi = (oss_mixerinfo *) data; + + if (len != sizeof (*mi)) + { + fprintf (stderr, "bswap_mixerinfo: Bad size (%d/%lu)\n", len, + (unsigned long)sizeof (*mi)); + exit (EXIT_FAILURE); + } + + BSWAP32 (mi->dev); + BSWAP32 (mi->modify_counter); + BSWAP32 (mi->card_number); + BSWAP32 (mi->port_number); + BSWAP32 (mi->magic); + BSWAP32 (mi->enabled); + BSWAP32 (mi->caps); + BSWAP32 (mi->flags); + BSWAP32 (mi->nrext); + BSWAP32 (mi->priority); + BSWAP32 (mi->legacy_device); +} + +static void +bswap_nodeinfo (void *data, int len) +{ + oss_mixext *ei = (oss_mixext *) data; + + if (len != sizeof (*ei)) + { + fprintf (stderr, "bswap_nodeinfo: Bad size (%d/%lu)\n", len, + (unsigned long)sizeof (*ei)); + exit (EXIT_FAILURE); + } + + BSWAP32 (ei->dev); + BSWAP32 (ei->ctrl); + BSWAP32 (ei->type); + BSWAP32 (ei->maxvalue); + BSWAP32 (ei->minvalue); + BSWAP32 (ei->flags); + BSWAP32 (ei->parent); + BSWAP32 (ei->dummy); + BSWAP32 (ei->timestamp); + BSWAP32 (ei->control_no); + BSWAP32 (ei->desc); + BSWAP32 (ei->update_counter); + BSWAP32 (ei->rgbcolor); +} + +static void +bswap_nodeinfo_array (void *data, int len) +{ + oss_mixext *ei = (oss_mixext *) data; + int i, n; + + n = len / sizeof (oss_mixext); + + if (len != n * sizeof (oss_mixext)) + { + fprintf (stderr, "bswap_enuminfo_array: Bad size (%d/%d*%lu)\n", len, n, + (unsigned long)sizeof (*ei)); + exit (EXIT_FAILURE); + } + + for (i = 0; i < n; i++) + bswap_nodeinfo ((void *) &ei[i], sizeof (oss_mixext)); + +} + + +static void +bswap_enuminfo (void *data, int len) +{ + oss_mixer_enuminfo *ei = (oss_mixer_enuminfo *) data; + + int i; + + if (len != sizeof (*ei)) + { + fprintf (stderr, "bswap_enuminfo: Bad size (%d/%lu)\n", len, + (unsigned long)sizeof (*ei)); + exit (EXIT_FAILURE); + } + + BSWAP32 (ei->dev); + BSWAP32 (ei->ctrl); + BSWAP32 (ei->nvalues); + BSWAP32 (ei->version); + + for (i = 0; i < OSS_ENUM_MAXVALUE; i++) + BSWAP32 (ei->strindex[i]); +} + +static int +get_response (void) +{ + ossmix_commad_packet_t msg; + char payload[4096]; + int l; + + if (sockfd == -1) + return -1; + + while (1) + { + payload[0] = 0; + + if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg)) + { + if (l == 0) /* Connection closed */ + return -1; + + perror ("get response"); + return -1; + } + + if (do_byteswap) + byteswap_msg (&msg); + + if (msg.payload_size > 0) + { + if ((l = + read_all (sockfd, payload, + msg.payload_size)) != msg.payload_size) + { + perror ("Get response payload"); + fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size); + return -1; + } + + payload[l] = 0; + } + + if (msg.cmd == OSSMIX_CMD_ERROR) + { + fprintf (stderr, "Remote error: %s\n", payload); + } + + /* + * Return if this was not an async notification message sent by the + * server. + */ + if (!msg.unsolicited) + return msg.cmd; + + handle_packet (&msg, payload, msg.payload_size); + } +} + +static int +check_welcome (void) +{ + ossmix_commad_packet_t msg; + int l; + + if (sockfd == -1) + return 0; + + if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg)) + { + if (l == 0) /* Connection closed */ + return 0; + + perror ("get response"); + return 0; + } + + if (msg.cmd != OSSMIX_CMD_HALOO) + { + fprintf (stderr, "Bad welcome from the remote server\n"); + return 0; + } + + do_byteswap = 0; + + if (msg.p1 != OSSMIX_P1_MAGIC) + { + byteswap_msg (&msg); + + if (msg.p1 != OSSMIX_P1_MAGIC) + { + fprintf (stderr, "Unrecognized endianess\n"); + return 0; + } + + fprintf (stderr, "Using alien endianess\n"); + do_byteswap = 1; + } + + return 1; +} + +static int +wait_payload (void *payload, int len, bswap_func_t swapper, int *truelen) +{ + ossmix_commad_packet_t msg; + int l; + + if (sockfd == -1) + return -1; + + while (1) + { + if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg)) + { + if (l == 0) /* Connection closed */ + return -1; + + perror ("get response"); + return -1; + } + + if (do_byteswap) + byteswap_msg (&msg); + + if (msg.payload_size > 0) + { + if ((l = + read_all (sockfd, payload, + msg.payload_size)) != msg.payload_size) + { + perror ("Get error message"); + fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size); + return -1; + } + } + + if (msg.cmd == OSSMIX_CMD_ERROR) + { + fprintf (stderr, "Remote error: %s\n", (char *)payload); + } + + if (!msg.unsolicited) + if (truelen == NULL) + if (msg.payload_size != len) + { + fprintf (stderr, "Payload size mismatch (%d/%d)\n", + msg.payload_size, len); + return -1; + } + + if (truelen != NULL) + *truelen = msg.payload_size; + + /* + * Return if this was not an async notification message sent by the + * server. + */ + if (!msg.unsolicited) + return msg.cmd; + + handle_packet (&msg, payload, msg.payload_size); + } +} + +int +send_request (int cmd, int p1, int p2, int p3, int p4, int p5) +{ + ossmix_commad_packet_t msg; + + memset (&msg, 0, sizeof (msg)); + + msg.cmd = cmd; + msg.p1 = p1; + msg.p2 = p2; + msg.p3 = p3; + msg.p4 = p4; + msg.p5 = p5; + msg.ack_rq = 1; + + if (do_byteswap) + byteswap_msg (&msg); + + if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg)) + { + fprintf (stderr, "Write to socket failed\n"); + } + return get_response (); +} + +void +send_request_noreply (int cmd, int p1, int p2, int p3, int p4, int p5) +{ + ossmix_commad_packet_t msg; + + memset (&msg, 0, sizeof (msg)); + + msg.cmd = cmd; + msg.p1 = p1; + msg.p2 = p2; + msg.p3 = p3; + msg.p4 = p4; + msg.p5 = p5; + msg.ack_rq = 0; + + if (do_byteswap) + byteswap_msg (&msg); + + if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg)) + { + fprintf (stderr, "Write to socket failed\n"); + } + //send(sockfd, &msg, sizeof(msg), 0); +} + +int +send_request_long (int cmd, int p1, int p2, int p3, int p4, int p5, + const char *payload) +{ + ossmix_commad_packet_t msg; + + memset (&msg, 0, sizeof (msg)); + + msg.cmd = cmd; + msg.p1 = p1; + msg.p2 = p2; + msg.p3 = p3; + msg.p4 = p4; + msg.p5 = p5; + msg.ack_rq = 1; + msg.payload_size = strlen (payload); + + if (do_byteswap) + byteswap_msg (&msg); + + if (write (sockfd, &msg, sizeof (msg)) != sizeof (msg)) + { + fprintf (stderr, "Write to socket failed\n"); + } + if (write (sockfd, payload, msg.payload_size) != msg.payload_size) + { + fprintf (stderr, "Write to socket failed\n"); + } + return get_response (); +} + +static int +tcp_connect (const char *remotehost, int port) +{ + struct sockaddr_in sa; + struct hostent *he; + + if (mixlib_trace > 0) + fprintf (stderr, "Entered tcp_connect(%s, %d)\n", remotehost, port); + + if (port == 0) + port = 7777; + + if (initialized) + { + fprintf (stderr, "Panic: ossmixlib already initialized\n"); + exit (EXIT_FAILURE); + } + + initialized = 1; + + /* + * Open the network connection + */ + + if ((sockfd = socket (PF_INET, SOCK_STREAM, 0)) == -1) + { + perror ("socket"); + return -1; + } + + if ((he = gethostbyname (remotehost)) == NULL) + { + herror (remotehost); + fprintf (stderr, "Cannot find the OSSMIX server \"%s\"\n", remotehost); + return -1; + } + + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + memcpy ((void *) &sa.sin_addr, *he->h_addr_list, he->h_length); + if (connect (sockfd, (void *) &sa, sizeof (sa)) == -1) + { + switch (errno) + { + case ECONNREFUSED: + fprintf (stderr, + "Remote OSSMIX server is not running (Connection refused)\n"); + break; + + default: + perror ("connect"); + } + fprintf (stderr, "Cannot connect OSSMIX server %s:%d\n", remotehost, + port); + return -1; + } +#if 0 +// For some reason this doesn't work under Linux + atexit (tcp_disconnect); +#endif + + if (!check_welcome ()) + return -1; + return send_request (OSSMIX_CMD_INIT, 0, 0, 0, 0, 0); +} + +static int +tcp_get_fd (ossmix_select_poll_t * cb) +{ + *cb = poll_callback; + + return sockfd; +} + +static void +tcp_enable_events (void) +{ + send_request_noreply (OSSMIX_CMD_START_EVENTS, 0, 0, 0, 0, 0); +} + +static void +tcp_disconnect (void) +{ + if (mixlib_trace > 0) + fprintf (stderr, "Entered tcp_disconnect()\n"); + + if (sockfd < 0) + return; + + send_request (OSSMIX_CMD_EXIT, 0, 0, 0, 0, 0); + close (sockfd); + sockfd = -1; +} + +static int +tcp_get_nmixers (void) +{ + int nmixers; + + nmixers = send_request (OSSMIX_CMD_GET_NMIXERS, 0, 0, 0, 0, 0); + + // TODO: num_mixers = nmixers; + + return nmixers; +} + +static int +tcp_get_mixerinfo (int mixernum, oss_mixerinfo * mi) +{ + send_request_noreply (OSSMIX_CMD_GET_MIXERINFO, mixernum, 0, 0, 0, 0); + return wait_payload (mi, sizeof (*mi), bswap_mixerinfo, NULL); +} + +static int +tcp_open_mixer (int mixernum) +{ + int nrext, nrext2; + oss_mixext nodes[MAX_NODES]; + value_packet_t value_packet; + + if (send_request (OSSMIX_CMD_OPEN_MIXER, mixernum, 0, 0, 0, 0) < 0) + return -1; + + if (mixernum >= MAX_TMP_MIXER) + { + fprintf (stderr, "tcp_open_mixer: Mixer number too large %d\n", + mixernum); + return -1; + } + + if ((nrext = send_request (OSSMIX_CMD_GET_NREXT, mixernum, 0, 0, 0, 0)) < 0) + return -1; + + nrext2 = nrext; // Save the value for the next step +/* + * Load all node info records + */ + + send_request_noreply (OSSMIX_CMD_GET_NODEINFO, mixernum, 0, nrext - 1, 0, + 0); + while (nrext > 0) + { + int i; + int n; + + n = nrext; + if (n > MAX_NODES) + n = MAX_NODES; + + if (wait_payload + (nodes, n * sizeof (oss_mixext), bswap_nodeinfo_array, NULL) < 0) + return -1; + + for (i = 0; i < n; i++) + { + oss_mixext *node; + + node = &nodes[i]; + + mixc_add_node (mixernum, node->ctrl, node); + } + + nrext -= n; + } + + nrext = nrext2; + +/* + * Load all values + */ + send_request_noreply (OSSMIX_CMD_GET_ALL_VALUES, mixernum, 0, 0, 0, 0); + + if (wait_payload + (value_packet, nrext * sizeof (value_record_t), bswap_int_array, + &nrext2) < 0) + return -1; + else + { + int i; + + for (i = 0; i < nrext2 / sizeof (value_record_t); i++) + { + mixc_set_value (mixernum, value_packet[i].node, + value_packet[i].value); + } + } + + return 0; +} + +static void +tcp_close_mixer (int mixernum) +{ + send_request_noreply (OSSMIX_CMD_CLOSE_MIXER, mixernum, 0, 0, 0, 0); +} + +static int +tcp_get_nrext (int mixernum) +{ + // TODO: Cache this information locally + return send_request (OSSMIX_CMD_GET_NREXT, mixernum, 0, 0, 0, 0); +} + +static int +tcp_get_nodeinfo (int mixernum, int node, oss_mixext * ext) +{ + oss_mixext *lnode; + + lnode = mixc_get_node (mixernum, node); + + if (lnode == NULL) + { + send_request_noreply (OSSMIX_CMD_GET_NODEINFO, mixernum, node, 0, 0, 0); + if (wait_payload (ext, sizeof (*ext), bswap_nodeinfo, NULL) < 0) + { + fprintf (stderr, + "tcp_get_nodeinfo: Mixer %d: Cannot load nodeinfo for %d\n", + mixernum, node); + return -1; + } + mixc_add_node (mixernum, node, lnode); + } + + memcpy (ext, lnode, sizeof (*ext)); + + return 0; +} + +static int +tcp_get_enuminfo (int mixernum, int node, oss_mixer_enuminfo * ei) +{ + send_request_noreply (OSSMIX_CMD_GET_ENUMINFO, mixernum, node, 0, 0, 0); + return wait_payload (ei, sizeof (*ei), bswap_enuminfo, NULL); +} + +static int +tcp_get_description (int mixernum, int node, oss_mixer_enuminfo * desc) +{ + send_request_noreply (OSSMIX_CMD_GET_DESCRIPTION, mixernum, node, 0, 0, 0); + return wait_payload (desc, sizeof (*desc), bswap_enuminfo, NULL); +} + +static int +tcp_get_value (int mixernum, int ctl, int timestamp) +{ + // return send_request(OSSMIX_CMD_GET_VALUE, mixernum, ctl, timestamp, 0, 0); + return mixc_get_value (mixernum, ctl); +} + +static void +tcp_set_value (int mixernum, int ctl, int timestamp, int value) +{ + send_request_noreply (OSSMIX_CMD_SET_VALUE, mixernum, ctl, timestamp, value, + 0); +} + +static void +tcp_timertick(void) +{ + // NOP +} + +ossmix_driver_t ossmix_tcp_driver = { + tcp_connect, + tcp_get_fd, + tcp_disconnect, + tcp_enable_events, + tcp_get_nmixers, + tcp_get_mixerinfo, + tcp_open_mixer, + tcp_close_mixer, + tcp_get_nrext, + tcp_get_nodeinfo, + tcp_get_enuminfo, + tcp_get_description, + tcp_get_value, + tcp_set_value, + tcp_timertick +}; + +static void +handle_values(int mixnum, int nvalues, value_record_t values[], int len) +{ + int i; + + if (nvalues*sizeof(value_record_t) > len) + { + fprintf(stderr, "Short value record (%lu, %d)\n", + (unsigned long)(nvalues*sizeof(value_record_t)), len); + exit(EXIT_FAILURE); + } + + for (i=0;i<nvalues;i++) + { + _client_event (OSSMIX_EVENT_VALUE, mixnum, values[i].node, values[i].value, 0, 0); + } + +} + +static void +handle_packet (ossmix_commad_packet_t * msg, char *payload, int payload_size) +{ +//printf("Got packet %d, p=0x%08x, %d, %d, %d, %d\n", +// msg->cmd, msg->p1, msg->p2, msg->p3, msg->p4, msg->p5); + +// _client_event (msg->cmd, msg->p1, msg->p2, msg->p3, msg->p4, msg->p5); + + switch(msg->cmd) + { + case OSSMIX_EVENT_VALUE: + handle_values(msg->p2, msg->p1, (value_record_t *)payload, payload_size); + break; + + case OSSMIX_EVENT_NEWMIXER: + //num_mixers=msg->p1; + _client_event (OSSMIX_EVENT_NEWMIXER, msg->p1, 0, 0, 0, 0); + break; + + default: + fprintf(stderr, "Unrecognized event packet %d\n", msg->cmd); + exit(EXIT_FAILURE); + } +} + +static void +poll_callback (void) +{ + ossmix_commad_packet_t msg; + char payload[4096]; + int l; + + if (sockfd == -1) + return; + + payload[0] = 0; + + if ((l = read_all (sockfd, &msg, sizeof (msg))) != sizeof (msg)) + { + if (l == 0) /* Connection closed */ + return; + + perror ("get response"); + return; + } + + if (do_byteswap) + byteswap_msg (&msg); + + if (msg.payload_size > 0) + { + if ((l = + read_all (sockfd, payload, msg.payload_size)) != msg.payload_size) + { + perror ("Get response payload"); + fprintf (stderr, "Payload size %d/%d\n", l, msg.payload_size); + return; + } + + payload[l] = 0; + } + + handle_packet (&msg, payload, msg.payload_size); +} diff --git a/lib/libsalsa/.config b/lib/libsalsa/.config new file mode 100644 index 0000000..4094239 --- /dev/null +++ b/lib/libsalsa/.config @@ -0,0 +1,2 @@ +targetos=Linux +depends=ALSA diff --git a/lib/libsalsa/.nativemake b/lib/libsalsa/.nativemake new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/libsalsa/.nativemake diff --git a/lib/libsalsa/Makefile b/lib/libsalsa/Makefile new file mode 100644 index 0000000..5f8216b --- /dev/null +++ b/lib/libsalsa/Makefile @@ -0,0 +1,60 @@ +LOBJECTS=main.lo output.lo misc.lo pcm.lo mix.lo seq.lo seqmid.lo rawmidi.lo seq_output.lo timer.lo stubs.lo seq_input.lo + +OSSLIBDIR=/usr/lib/oss + +CFLAGS=-O -I../../include -I../libOSSlib +LIBTOOL=libtool --tag=CC + +all: libsalsa.la + +*.lo: local.h + +main.lo: main.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c main.c + +pcm.lo: pcm.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c pcm.c + +misc.lo: misc.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c misc.c + +stubs.lo: stubs.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c stubs.c + +mix.lo: mix.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c mix.c + +output.lo: output.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c output.c + +seq.lo: seq.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c seq.c + +timer.lo: timer.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c timer.c + +seqmid.lo: seqmid.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c seqmid.c + +seq_input.lo: seq_input.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c seq_input.c + +seq_output.lo: seq_output.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c seq_output.c + +rawmidi.lo: rawmidi.c + $(LIBTOOL) --mode=compile ${CC} $(CFLAGS) -c rawmidi.c + +libsalsa.la: $(LOBJECTS) + $(LIBTOOL) --mode=link ${CC} -rpath /usr/lib -Wl,--version-script=Versions -version-info 2:0:0 -o libsalsa.la $(LOBJECTS) -L../libOSSlib -lOSSlib + +install: libsalsa.la + $(LIBTOOL) cp libsalsa.la /usr/lib + (cd /usr/lib;ln -sf libsalsa.so.2.0.0 libasound.so) + (cd /usr/lib;ln -sf libsalsa.so.2.0.0 libasound.so.2) + +dep: + +clean: + rm -f core core.* *.o *.a *.lo *.so *.loT *.la x y z *.s + rm -rf .libs diff --git a/lib/libsalsa/README b/lib/libsalsa/README new file mode 100644 index 0000000..e201b5c --- /dev/null +++ b/lib/libsalsa/README @@ -0,0 +1,34 @@ +SALSA - Simple ALSA emulation library for OSS +============================================= + +The sole purpose of this library is to make certain key ALSA applications +to work with OSS. This is necessary just because some Linux distributions +don't ship utilities like esd or xmss with OSS support compiled in. + +This library has been programmed using brute force methods and it's +not designed to be any programming example. We didn't make any attempt to get +all ALSA applications to work with it. Most applications support the OSS +API directly so there is no need for doing this. + +We do not have any specific plans to develop this library any further. However +some changes will be made occasionally. + +Copying +------- + +The seqmid.c, alsakernel.h, alsa-symbols.h and output.c files have been +copied more or less from stock alsa-lib and they have been copyrighted by the +ALSA team members. All parts of this package are distributed under GNU GPL +version 2.1. + +Installing and compiling the library +------------------------------------ + +The precompiled library is already shipped as a part of the OSS package +for Linux 2.6.x and later kernels so there should be no reason to recompile +it. This library is not applicable under any other operating systems or Linux +versions. + +To compile the library you can execute "make". "make install" will install +the library (libsalsa.so.2.0.0) to /usr/lib and then change the required +libasound.so.* symbolic links to point to it. diff --git a/lib/libsalsa/Versions b/lib/libsalsa/Versions new file mode 100644 index 0000000..c4d3065 --- /dev/null +++ b/lib/libsalsa/Versions @@ -0,0 +1,162 @@ +ALSA_0.9 { + global: + __snd_*; + _snd_*; + snd_*; + local: + *; +}; + +ALSA_0.9.0rc4 { + global: + + snd_pcm_hw_params_get_access; + snd_pcm_hw_params_set_access_first; + snd_pcm_hw_params_set_access_last; + + snd_pcm_hw_params_get_format; + snd_pcm_hw_params_set_format_first; + snd_pcm_hw_params_set_format_last; + + snd_pcm_hw_params_get_subformat; + snd_pcm_hw_params_set_subformat_first; + snd_pcm_hw_params_set_subformat_last; + + snd_pcm_hw_params_get_channels; + snd_pcm_hw_params_get_channels_min; + snd_pcm_hw_params_get_channels_max; + snd_pcm_hw_params_set_channels_near; + snd_pcm_hw_params_set_channels_first; + snd_pcm_hw_params_set_channels_last; + + snd_pcm_hw_params_get_rate; + snd_pcm_hw_params_get_rate_min; + snd_pcm_hw_params_get_rate_max; + snd_pcm_hw_params_set_rate_near; + snd_pcm_hw_params_set_rate_first; + snd_pcm_hw_params_set_rate_last; + + snd_pcm_hw_params_get_period_time; + snd_pcm_hw_params_get_period_time_min; + snd_pcm_hw_params_get_period_time_max; + snd_pcm_hw_params_set_period_time_near; + snd_pcm_hw_params_set_period_time_first; + snd_pcm_hw_params_set_period_time_last; + + snd_pcm_hw_params_get_period_size; + snd_pcm_hw_params_get_period_size_min; + snd_pcm_hw_params_get_period_size_max; + snd_pcm_hw_params_set_period_size_near; + snd_pcm_hw_params_set_period_size_first; + snd_pcm_hw_params_set_period_size_last; + + snd_pcm_hw_params_get_periods; + snd_pcm_hw_params_get_periods_min; + snd_pcm_hw_params_get_periods_max; + snd_pcm_hw_params_set_periods_near; + snd_pcm_hw_params_set_periods_first; + snd_pcm_hw_params_set_periods_last; + + snd_pcm_hw_params_get_buffer_time; + snd_pcm_hw_params_get_buffer_time_min; + snd_pcm_hw_params_get_buffer_time_max; + snd_pcm_hw_params_set_buffer_time_near; + snd_pcm_hw_params_set_buffer_time_first; + snd_pcm_hw_params_set_buffer_time_last; + + snd_pcm_hw_params_get_buffer_size; + snd_pcm_hw_params_get_buffer_size_min; + snd_pcm_hw_params_get_buffer_size_max; + snd_pcm_hw_params_set_buffer_size_near; + snd_pcm_hw_params_set_buffer_size_first; + snd_pcm_hw_params_set_buffer_size_last; + + snd_pcm_hw_params_get_tick_time; + snd_pcm_hw_params_get_tick_time_min; + snd_pcm_hw_params_get_tick_time_max; + snd_pcm_hw_params_set_tick_time_near; + snd_pcm_hw_params_set_tick_time_first; + snd_pcm_hw_params_set_tick_time_last; + +} ALSA_0.9; + +ALSA_0.9.0rc8 { + global: + + snd_pcm_forward; + snd_pcm_status_get_trigger_htstamp; + snd_pcm_status_get_htstamp; + +} ALSA_0.9.0rc4; + +ALSA_0.9.0 { + global: + + snd_pcm_type_name; + snd_timer_query_info; + snd_timer_query_params; + snd_timer_query_status; + snd_timer_params_set_exclusive; + snd_timer_params_get_exclusive; + snd_timer_params_set_filter; + snd_timer_params_get_filter; +} ALSA_0.9.0rc8; + +ALSA_0.9.3 { + global: + + snd_ctl_elem_info_get_dimensions; + snd_ctl_elem_info_get_dimension; +} ALSA_0.9.0; + +ALSA_0.9.5 { + global: + + alsa_lisp; +} ALSA_0.9.3; + +ALSA_0.9.6 { + global: + + snd_hctl_open_ctl; + snd_seq_port_info_get_timestamping; + snd_seq_port_info_get_timestamp_real; + snd_seq_port_info_get_timestamp_queue; + snd_seq_port_info_set_timestamping; + snd_seq_port_info_set_timestamp_real; + snd_seq_port_info_set_timestamp_queue; +} ALSA_0.9.5; + +ALSA_0.9.7 { + global: + + snd_user_file; + snd_hctl_ctl; + sndo_*; + alsa_lisp_*; +} ALSA_0.9.6; + +ALSA_0.9.8 { + global: + + snd_ctl_elem_add; + snd_ctl_elem_replace; + snd_ctl_elem_remove; + snd_hctl_poll_descriptors_revents; +} ALSA_0.9.7; + +ALSA_1.0.4 { + global: + + snd_spcm_init; + snd_spcm_init_duplex; + snd_spcm_init_get_params; +} ALSA_0.9.8; + +ALSA_1.0.5 { + global: + + snd_asoundlib_version; + snd_timer_params_set_early_event; + snd_timer_params_get_early_event; +} ALSA_1.0.4; diff --git a/lib/libsalsa/alsa-symbols.h b/lib/libsalsa/alsa-symbols.h new file mode 100644 index 0000000..692846c --- /dev/null +++ b/lib/libsalsa/alsa-symbols.h @@ -0,0 +1,66 @@ +/* + * ALSA lib - dynamic symbol versions + * Copyright (c) 2002 by Jaroslav Kysela <perex@suse.cz> + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __ALSA_SYMBOLS_H +#define __ALSA_SYMBOLS_H + +#if defined(PIC) && defined(VERSIONED_SYMBOLS) /* might be also configurable */ +#define USE_VERSIONED_SYMBOLS +#endif + +#define INTERNAL_CONCAT2_2(Pre, Post) Pre##Post +#define INTERNAL(Name) INTERNAL_CONCAT2_2(__, Name) + +#ifdef __powerpc64__ +# define symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@" #version); \ + __asm__ (".symver ." #real ",." #name "@" #version) +# define default_symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@@" #version); \ + __asm__ (".symver ." #real ",." #name "@@" #version) +#else +# define symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@" #version) +# define default_symbol_version(real, name, version) \ + __asm__ (".symver " #real "," #name "@@" #version) +#endif + +#ifdef USE_VERSIONED_SYMBOLS +#define use_symbol_version(real, name, version) \ + symbol_version(real, name, version) +#define use_default_symbol_version(real, name, version) \ + default_symbol_version(real, name, version) +#else +#define use_symbol_version(real, name, version) /* nothing */ +#ifdef __powerpc64__ +#define use_default_symbol_version(real, name, version) \ + __asm__ (".weak " #name); \ + __asm__ (".weak ." #name); \ + __asm__ (".set " #name "," #real); \ + __asm__ (".set ." #name ",." #real) +#else +#define use_default_symbol_version(real, name, version) \ + __asm__ (".weak " #name); \ + __asm__ (".set " #name "," #real) +#endif +#endif + +#endif /* __ALSA_SYMBOLS_H */ diff --git a/lib/libsalsa/alsakernel.h b/lib/libsalsa/alsakernel.h new file mode 100644 index 0000000..1e1fb52 --- /dev/null +++ b/lib/libsalsa/alsakernel.h @@ -0,0 +1,108 @@ + +enum sndrv_pcm_format +{ + SNDRV_PCM_FORMAT_S8 = 0, + SNDRV_PCM_FORMAT_U8, + SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_S24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_LE, /* low three bytes */ + SNDRV_PCM_FORMAT_U24_BE, /* low three bytes */ + SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT_LE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT_BE, /* 4-byte float, IEEE-754 32-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_LE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_FLOAT64_BE, /* 8-byte float, IEEE-754 64-bit, range -1.0 to 1.0 */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, /* IEC-958 subframe, Little Endian */ + SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, /* IEC-958 subframe, Big Endian */ + SNDRV_PCM_FORMAT_MU_LAW, + SNDRV_PCM_FORMAT_A_LAW, + SNDRV_PCM_FORMAT_IMA_ADPCM, + SNDRV_PCM_FORMAT_MPEG, + SNDRV_PCM_FORMAT_GSM, + SNDRV_PCM_FORMAT_SPECIAL = 31, + SNDRV_PCM_FORMAT_S24_3LE = 32, /* in three bytes */ + SNDRV_PCM_FORMAT_S24_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U24_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U24_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_S20_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_S20_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U20_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U20_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_S18_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_S18_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_U18_3LE, /* in three bytes */ + SNDRV_PCM_FORMAT_U18_3BE, /* in three bytes */ + SNDRV_PCM_FORMAT_LAST = SNDRV_PCM_FORMAT_U18_3BE, + +#ifdef SNDRV_LITTLE_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_LE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_LE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_LE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_LE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_LE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_LE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_LE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_LE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE, +#endif +#ifdef SNDRV_BIG_ENDIAN + SNDRV_PCM_FORMAT_S16 = SNDRV_PCM_FORMAT_S16_BE, + SNDRV_PCM_FORMAT_U16 = SNDRV_PCM_FORMAT_U16_BE, + SNDRV_PCM_FORMAT_S24 = SNDRV_PCM_FORMAT_S24_BE, + SNDRV_PCM_FORMAT_U24 = SNDRV_PCM_FORMAT_U24_BE, + SNDRV_PCM_FORMAT_S32 = SNDRV_PCM_FORMAT_S32_BE, + SNDRV_PCM_FORMAT_U32 = SNDRV_PCM_FORMAT_U32_BE, + SNDRV_PCM_FORMAT_FLOAT = SNDRV_PCM_FORMAT_FLOAT_BE, + SNDRV_PCM_FORMAT_FLOAT64 = SNDRV_PCM_FORMAT_FLOAT64_BE, + SNDRV_PCM_FORMAT_IEC958_SUBFRAME = SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE, +#endif +}; + +#define FORMAT(v) [SND_PCM_FORMAT_##v] = #v +static const char *snd_pcm_format_names[] = { + FORMAT (S8), + FORMAT (U8), + FORMAT (S16_LE), + FORMAT (S16_BE), + FORMAT (U16_LE), + FORMAT (U16_BE), + FORMAT (S24_LE), + FORMAT (S24_BE), + FORMAT (U24_LE), + FORMAT (U24_BE), + FORMAT (S32_LE), + FORMAT (S32_BE), + FORMAT (U32_LE), + FORMAT (U32_BE), + FORMAT (FLOAT_LE), + FORMAT (FLOAT_BE), + FORMAT (FLOAT64_LE), + FORMAT (FLOAT64_BE), + FORMAT (IEC958_SUBFRAME_LE), + FORMAT (IEC958_SUBFRAME_BE), + FORMAT (MU_LAW), + FORMAT (A_LAW), + FORMAT (IMA_ADPCM), + FORMAT (MPEG), + FORMAT (GSM), + FORMAT (SPECIAL), + FORMAT (S24_3LE), + FORMAT (S24_3BE), + FORMAT (U24_3LE), + FORMAT (U24_3BE), + FORMAT (S20_3LE), + FORMAT (S20_3BE), + FORMAT (U20_3LE), + FORMAT (U20_3BE), + FORMAT (S18_3LE), + FORMAT (S18_3BE), + FORMAT (U18_3LE), + FORMAT (U18_3BE), +}; diff --git a/lib/libsalsa/local.h b/lib/libsalsa/local.h new file mode 100644 index 0000000..5c87ebc --- /dev/null +++ b/lib/libsalsa/local.h @@ -0,0 +1,98 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include "alsa-symbols.h" +#include <alsa/asoundlib.h> +#include "../../include/soundcard.h" +#include "alsakernel.h" +#include <sys/poll.h> +#include "../../kernel/framework/include/midiparser.h" + +extern int alib_verbose; +extern int mixer_fd; + +extern char alib_appname[64]; + +extern oss_sysinfo sysinfo; + +#define dbg_printf if (alib_verbose>0)printf +#define dbg_printf0 if (alib_verbose>=0)printf +#define dbg_printf1 if (alib_verbose>=1)printf +#define dbg_printf2 if (alib_verbose>=2)printf +#define dbg_printf3 if (alib_verbose>=3)printf + +extern int alib_initialized; + +extern int init_alib (void); + +#define ALIB_INIT() \ +{ \ + int init_err; \ + if (!alib_initialized) \ + if ((init_err=alib_init())<0) \ + return init_err; \ +} + +struct _snd_pcm_info +{ + oss_audioinfo *ainfo; +}; + + +extern int alib_appcheck (void); + +struct _snd_seq_port_info +{ + oss_longname_t name; + int port; + int capability; + int midi_channels; + int synth_voices; + int midi_voices; + int type; +}; + +struct _snd_seq_client_info +{ + oss_longname_t name; + int client; +}; + +/* Size of the local event buffer */ +#define MAX_EVENTS 128 + +struct _snd_seq +{ + int fd; + int streams; + int nonblock; + int oss_mode; + oss_longname_t name; + midiparser_common_t *parser; + + snd_seq_event_t events[MAX_EVENTS]; + int nevents, nextevent; +}; + +struct _snd_rawmidi_info +{ + int dummy; +}; + +struct _snd_seq_queue_status +{ + int dummy; +}; + +struct _snd_seq_queue_timer +{ + int dummy; +}; + +extern int convert_event (snd_seq_t * seq, snd_seq_event_t * ev); +extern void midiparser_callback (void *context, int category, + unsigned char msg, unsigned char ch, + unsigned char *parms, int len); diff --git a/lib/libsalsa/main.c b/lib/libsalsa/main.c new file mode 100644 index 0000000..34c8c22 --- /dev/null +++ b/lib/libsalsa/main.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * Parts of the code is derived from the alsa-lib package that is + * copyrighted by Jaroslav Kysela and the other ALSA team members. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "local.h" + +int mixer_fd = -1; +int alib_initialized = 0; + +oss_sysinfo sysinfo = { 0 }; +struct _snd_ctl +{ + oss_card_info info; + oss_audioinfo ainfo; +}; + +int alib_verbose = 0; + +int +alib_init (void) +{ + char *p; + char *devmixer; + + if ((devmixer=getenv("OSS_MIXERDEV"))==NULL) + devmixer = "/dev/mixer"; + + if ((p = getenv ("ALIB_DEBUG")) != NULL) + alib_verbose = atoi (p); + + if (mixer_fd == -1) + { + if ((mixer_fd = open (devmixer, O_RDONLY, 0)) == -1) + return -errno; + } + + if (ioctl (mixer_fd, SNDCTL_SYSINFO, &sysinfo) == -1) + { + perror ("SNDCTL_SYSINFO"); + return -errno; + } + + return 0; +} + +char alib_appname[64] = "salsa"; + +int +alib_appcheck (void) +{ + /* + * Some programs are known to support OSS at the same time with ALSA. + * Prevent them from using this library accidently. + */ + + static int done = 0; + + if (done) + return 1; + + done = 1; + + typedef struct + { + char *name, *msg, *action; + } prog_t; + + static const prog_t banned_programs[] = { + {"artsd", "Please use 'artsd -a oss' instead.", NULL}, + {"kcontrol", NULL, NULL}, + {"kmid", NULL, NULL}, + {"krec", NULL, NULL}, + {"kplay", NULL, NULL}, + {"kamix", "Please use ossxmix instead", NULL}, + {"qamix", "Please use ossxmix instead", NULL}, + NULL + }; + + static const char *whitelist[] = { + "esd", + "kmix", + "gnome-volume-control", + "artsd", + "xmms", + "alsaplayer", + "aplay", + "alsamixer", + "vkeybd", + NULL + }; + + FILE *f; + char tmp[64], *p, *cmd = tmp; + int i; + static int warned = 0; + + if ((f = fopen ("/proc/self/cmdline", "r")) == NULL) + return 1; + + if (fgets (tmp, sizeof (tmp) - 1, f) == NULL) + { + fclose (f); + return 1; + } + + fclose (f); + + p = cmd; + + while (*p && (*p != ' ' && *p != '\n')) + p++; + *p = 0; + + p = cmd = tmp; + while (*p) + { + if (*p == '/') + cmd = p + 1; + p++; + } + + strcpy (alib_appname, cmd); + + for (i = 0; i < strlen (alib_appname); i++) + if (alib_appname[i] < 'a' || alib_appname[i] > 'z') + if (alib_appname[i] < 'A' || alib_appname[i] > 'Z') + if (alib_appname[i] < '0' || alib_appname[i] > '9') + alib_appname[i] = '_'; + + for (i = 0; banned_programs[i].name != NULL; i++) + if (strcmp (banned_programs[i].name, cmd) == 0) + { + if (alib_verbose != 0) + { + return 1; + } + fprintf (stderr, + "\n\n************** WARNING ***********************\n"); + fprintf (stderr, "This program (%s) should not use ALSA emulation\n", + cmd); + if (banned_programs[i].msg != NULL) + fprintf (stderr, "%s\n", banned_programs[i].msg); + fprintf (stderr, + "**************************************************\n\n"); + + if (banned_programs[i].action != NULL) + { + if (fork () == 0) + { + exit (system (banned_programs[i].action)); + } + while (wait () != -1); + exit (1); + } + return 0; + } + + if (alib_verbose == 0) + { + int ok = 0; + + for (i = 0; !ok && whitelist[i] != NULL; i++) + if (strcmp (cmd, whitelist[i]) == 0) + ok = 1; + + + if (!ok) + return 0; + } + + if (!warned) + { + fprintf (stderr, + "\n\n******************** WARNING *******************************\n"); + fprintf (stderr, + "Warning! %s uses ALSA emulation instead of the native OSS API\n", + cmd); + fprintf (stderr, + "****************************************************************\n\n"); + } + warned = 1; + return 1; +} + +typedef struct _snd_ctl_card_info +{ + oss_card_info *info; +} snd_ctl_card_info_t; + +/** + * \brief Try to determine the next card. + * \param rcard pointer to card number + * \result zero if success, otherwise a negative error code + * + * Tries to determine the next card from given card number. + * If card number is -1, then the first available card is + * returned. If the result card number is -1, no more cards + * are available. + */ +int +snd_card_next (int *rcard) +{ + ALIB_INIT (); + if (!alib_appcheck ()) + return -ENODEV; + + if (*rcard == -1) + *rcard = 0; + else + *rcard = *rcard + 1; + + if (*rcard >= sysinfo.numcards) + { + *rcard = -1; + return 0; + } + + return 0; +} + +/** + * \brief Convert card string to an integer value. + * \param string String containing card identifier + * \return zero if success, otherwise a negative error code + * + * The accepted format is an integer value in ASCII representation + * or the card identifier (the id parameter for sound-card drivers). + */ +int +snd_card_get_index (const char *string) +{ + dbg_printf ("snd_card_get_index(%s)\n", string); + return -EINVAL; +} + +/** + * \brief Get card name from a CTL card info + * \param obj CTL card info + * \return card name + */ +const char * +snd_ctl_card_info_get_name (const snd_ctl_card_info_t * obj) +{ + return obj->info->longname; +} + +/** + * \brief get size of #snd_ctl_card_info_t + * \return size in bytes + */ +size_t +snd_ctl_card_info_sizeof () +{ + return sizeof (snd_ctl_card_info_t); +} + +/** + * \brief Opens a CTL + * \param ctlp Returned CTL handle + * \param name ASCII identifier of the CTL handle + * \param mode Open mode (see #SND_CTL_NONBLOCK, #SND_CTL_ASYNC) + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_open (snd_ctl_t ** ctlp, const char *name, int mode) +{ + int num; + snd_ctl_t *ctl; + + ALIB_INIT (); + if (!alib_appcheck ()) + return -ENODEV; + + *ctlp = NULL; + + if (strcmp (name, "default") == 0) + num = 0; + else + { + if (name[0] != 'h' && name[1] != 'w' && name[2] != ':') + return -ENOENT; + + if (sscanf (name + 3, "%d", &num) != 1) + return -ENOENT; + } + + if (num < 0 || num >= sysinfo.numcards) + return -ENXIO; + + + if ((ctl = malloc (sizeof (*ctl))) == NULL) + return -ENOMEM; + + memset (ctl, 0, sizeof (*ctl)); + ctl->info.card = num; + if (ioctl (mixer_fd, SNDCTL_CARDINFO, &ctl->info) == -1) + { + perror ("SNDCTL_CARDINFO"); + fprintf (stderr, "Mixer fd was %d\n", mixer_fd); + return -errno; + } + + *ctlp = ctl; + return 0; +} + +/** + * \brief close CTL handle + * \param ctl CTL handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified CTL handle and frees all associated + * resources. + */ +int +snd_ctl_close (snd_ctl_t * ctl) +{ + free (ctl); + return 0; +} + +/** + * \brief Get card related information + * \param ctl CTL handle + * \param info Card info pointer + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_card_info (snd_ctl_t * ctl, snd_ctl_card_info_t * info) +{ + + memset (info, 0, sizeof (*info)); + info->info = &ctl->info; + + return 0; +} + +/** + * \brief Obtain the card name. + * \param card Card number + * \param name Result - card name corresponding to card number + * \result zero if success, otherwise a negative error code + */ +int +snd_card_get_name (int card, char **name) +{ + char tmp[256]; + + sprintf (tmp, "OSS%d", card); + + *name = strdup (tmp); + return 0; +} + +int +snd_card_get_longname (int card, char **name) +{ + oss_card_info ci; + + ci.card = card; + if (ioctl (mixer_fd, SNDCTL_CARDINFO, &ci) == -1) + return -errno; + + *name = strdup (ci.longname); + return 0; +} + +/** + * \brief Get next PCM device number + * \param ctl CTL handle + * \param device current device on entry and next device on return + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_pcm_next_device (snd_ctl_t * ctl, int *device) +{ + ALIB_INIT (); + + dbg_printf ("snd_ctl_pcm_next_device(%d)\n", *device); + + if (*device < 0) + *device = 0; + else + { + *device = *device + 1; + } + + while (1) + { + if (*device < 0 || *device >= sysinfo.numaudios) + { + *device = -1; + return 0; + } + + ctl->ainfo.dev = *device; + if (ioctl (mixer_fd, SNDCTL_AUDIOINFO, &ctl->ainfo) < 0) + return -errno; + + if (ctl->ainfo.card_number == ctl->info.card) + { + return 0; + } + + *device = *device + 1; + } + + *device = -1; + return 0; +} + +/** + * \brief Get info about a PCM device + * \param ctl CTL handle + * \param info PCM device id/info pointer + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_pcm_info (snd_ctl_t * ctl, snd_pcm_info_t * info) +{ + dbg_printf ("snd_ctl_pcm_info()\n"); + memset (info, 0, sizeof (*info)); + info->ainfo = &ctl->ainfo; + return 0; +} + +/** + * \brief Get card mixer name from a CTL card info + * \param obj CTL card info + * \return card mixer name + */ +const char * +snd_ctl_card_info_get_mixername (const snd_ctl_card_info_t * obj) +{ + return obj->info->longname; +} + +/** + * \brief Get info about a RawMidi device + * \param ctl CTL handle + * \param info RawMidi device id/info pointer + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_rawmidi_info (snd_ctl_t * ctl, snd_rawmidi_info_t * info) +{ + dbg_printf ("snd_ctl_rawmidi_info()\n"); + + return 0; +} + +/** + * \brief Get next RawMidi device number + * \param ctl CTL handle + * \param device current device on entry and next device on return + * \return 0 on success otherwise a negative error code + */ +int +snd_ctl_rawmidi_next_device (snd_ctl_t * ctl, int *device) +{ + dbg_printf ("snd_ctl_rawmidi_next_device()\n"); + + if (*device < 0) + *device = 0; + else + *device = -1; + + return 0; +} + +/** + * \brief Get card identifier from a CTL card info + * \param obj CTL card info + * \return card identifier + */ +const char * +snd_ctl_card_info_get_id (const snd_ctl_card_info_t * obj) +{ + dbg_printf ("snd_ctl_card_info_get_id()\n"); + + return "snd_ctl_card_info_get_id"; +} diff --git a/lib/libsalsa/misc.c b/lib/libsalsa/misc.c new file mode 100644 index 0000000..9af2408 --- /dev/null +++ b/lib/libsalsa/misc.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +snd_config_t *snd_config = NULL; + +const char * +snd_strerror (int errnum) +{ + if (errnum < 0) + errnum = -errnum; + + return strerror (errnum);; +} + +/** + * \brief Dumps the contents of a configuration node or tree. + * \param config Handle to the (root) configuration node. + * \param out Output handle. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_config_save (snd_config_t * config, snd_output_t * out) +{ + dbg_printf ("snd_config_save()\n"); + + return 0; +} + +/** + * \brief Searches for a node in a configuration tree. + * \param config Handle to the root of the configuration (sub)tree to search. + * \param key Search key: one or more node keys, separated with dots. + * \param result The function puts the handle to the node found at the address + * specified by \p result. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_config_search (snd_config_t * config, const char *key, + snd_config_t ** result) +{ + dbg_printf ("snd_config_search()\n"); + + return 0; +} + +/** + * \brief Updates #snd_config by rereading the global configuration files (if needed). + * \return A non-negative value if successful, otherwise a negative error code. * \retval 0 No action is needed. + * \retval 1 The configuration tree has been rebuilt. + * + * The global configuration files are specified in the environment variable + * \c ALSA_CONFIG_PATH. If this is not set, the default value is + * "/usr/share/alsa/alsa.conf". + * + * \warning If the configuration tree is reread, all string pointers and + * configuration node handles previously obtained from this tree become invalid. + */ +int +snd_config_update (void) +{ + dbg_printf ("snd_config_update()\n"); + + return 0; +} + +/** + * \brief Frees the global configuration tree in #snd_config. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_config_update_free_global (void) +{ + dbg_printf ("snd_config_update_free_global()\n"); + + return 0; +} + +/** + * \brief Sets the error handler. + * \param handler The pointer to the new error handler function. + * + * This function sets a new error handler, or (if \c handler is \c NULL) + * the default one which prints the error messages to \c stderr. + */ +int +snd_lib_error_set_handler (snd_lib_error_handler_t handler) +{ + dbg_printf ("snd_lib_error_set_handler()\n"); + return 0; +} diff --git a/lib/libsalsa/mix.c b/lib/libsalsa/mix.c new file mode 100644 index 0000000..5131518 --- /dev/null +++ b/lib/libsalsa/mix.c @@ -0,0 +1,1125 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * Parts of the code is derived from the alsa-lib package that is + * copyrighted by Jaroslav Kysela and the other ALSA team members. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +#define SND_VERS(a, b, c) (a<<16|b<<8|c) + +typedef struct _snd_mixer +{ + int mixdev; + int nrext; + + oss_mixerinfo info; + + snd_mixer_elem_t *elems; +} snd_mixer_t; + +#define MAX_MIXERS 32 +static snd_mixer_t *mixers[MAX_MIXERS]; +static int nmixers = 0; + +typedef struct _snd_mixer_selem_id +{ + int number; + char name[32]; +} snd_mixer_selem_id_t; + +typedef struct _snd_mixer_class +{ + int dummy; +} snd_mixer_class_t; + +typedef struct _snd_mixer_elem +{ + snd_mixer_t *mixer; + int ctrl; + oss_mixext ext; + + snd_mixer_elem_t *next; + + int low, high; +} snd_mixer_elem_t; + +/** + * \brief Opens an empty mixer + * \param mixerp Returned mixer handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_open (snd_mixer_t ** mixerp, int mode) +{ + snd_mixer_t *mixer; + dbg_printf2 ("snd_mixer_open()\n"); + + ALIB_INIT (); + if (!alib_appcheck ()) + return -ENODEV; + + if ((mixer = malloc (sizeof (*mixer))) == NULL) + return -ENOMEM; + + memset (mixer, 0, sizeof (*mixer)); + *mixerp = mixer; + + return 0; +} + +/** + * \brief Attach an HCTL to an opened mixer + * \param mixer Mixer handle + * \param name HCTL name (see #snd_hctl_open) + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_attach (snd_mixer_t * mixer, const char *name) +{ + int dev = 0; + + ALIB_INIT (); + dbg_printf2 ("snd_mixer_attach(%s)\n", name); + + + if (strcmp (name, "default") == 0) + dev = 0; + else if (name[0] == 'h' && name[1] == 'w' && name[2] == ':') + { + if (sscanf (name + 3, "%d", &dev) != 1) + return -ENOENT; + + if (dev < 0 || dev >= sysinfo.nummixers) + return -ENXIO; + } + else + return -ENOENT; + + mixer->mixdev = dev; + + mixer->info.dev = dev; + if (ioctl (mixer_fd, SNDCTL_MIXERINFO, &mixer->info) == -1) + return -errno; + + mixer->nrext = mixer->info.nrext; + + if (nmixers < MAX_MIXERS) + mixers[nmixers++] = mixer; + + return 0; +} + + +/** + * \brief Close a mixer and free all related resources + * \param mixer Mixer handle + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_close (snd_mixer_t * mixer) +{ + dbg_printf2 ("snd_mixer_close()\n"); + + free (mixer); + return 0; +} + +/** + * \brief Register mixer simple element class + * \param mixer Mixer handle + * \param options Options container (not used now) + * \param classp Pointer to returned mixer simple element class handle (or NULL) * \return 0 on success otherwise a negative error code + */ +#if 1 +int +snd_mixer_selem_register (snd_mixer_t * mixer, + struct snd_mixer_selem_regopt *arg, + snd_mixer_class_t ** classp) +#else +int +snd_mixer_selem_register (snd_mixer_t * mixer, void *arg, + snd_mixer_class_t ** classp) +#endif +{ + snd_mixer_class_t *class; + + dbg_printf2 ("snd_mixer_selem_register()\n"); + + if (classp == NULL) /* Why this call was ever made ? */ + return 0; + + if ((class = malloc (sizeof (*class))) == NULL) + return -ENOMEM; + + memset (class, 0, sizeof (*class)); + *classp = class; + + return 0; +} + +/** + * \brief Return info about playback volume control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if no control is present, 1 if it's present + */ +int +snd_mixer_selem_has_playback_volume (snd_mixer_elem_t * elem) +{ + dbg_printf2 ("snd_mixer_selem_has_playback_volume(%x)\n", elem); + fflush (stdout); + + if (elem->ext.type == MIXT_STEREOSLIDER) + return 1; + if (elem->ext.type == MIXT_MONOSLIDER) + return 1; + if (elem->ext.type == MIXT_SLIDER) + return 1; + + return 0; +} + +/** + * \brief Return info about playback volume control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if control is separated per channel, 1 if control acts on all channels together + */ +int +snd_mixer_selem_has_playback_volume_joined (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_playback_volume_joined()\n"); + + return elem->ext.type != MIXT_STEREOSLIDER; +} + +/** + * \brief Return info about capture volume control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if no control is present, 1 if it's present + */ +int +snd_mixer_selem_has_capture_volume (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_capture_volume()\n"); + + return 0; +} + + +/** + * \brief Return info about playback switch control existence of a mixer simple +element + * \param elem Mixer simple element handle + * \return 0 if no control is present, 1 if it's present + */ +int +snd_mixer_selem_has_playback_switch (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_playback_switch()\n"); + + if (elem->ext.type == MIXT_ONOFF || elem->ext.type == MIXT_MUTE) + return 1; + return 0; +} + +/** + * \brief Set value of playback volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_playback_volume (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + long value) +{ + int vol, range; + oss_mixer_value rec; + + dbg_printf2 ("snd_mixer_selem_set_playback_volume(%ld)\n", value); + + if (value < elem->low) + value = elem->low; + if (value > elem->high) + value = elem->high; + + range = elem->high - elem->low; + if (range == 0) + range = 100; + value -= elem->low; + vol = (value * elem->ext.maxvalue) / range; + + rec.dev = elem->ext.dev; + rec.ctrl = elem->ext.ctrl; + rec.ctrl = elem->ext.ctrl; + rec.timestamp = elem->ext.timestamp; + rec.value = vol | (vol << 8); + + if (ioctl (mixer_fd, SNDCTL_MIX_WRITE, &rec) == -1) + return -errno; + + return 0; +} + +/** + * \brief Set range for capture volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min minimum volume value + * \param max maximum volume value + */ +#if SND_LIB_VERSION > SND_VERS(1,0,9) +int +#else +void +#endif +snd_mixer_selem_set_capture_volume_range (snd_mixer_elem_t * elem, + long min, long max) +{ + dbg_printf ("snd_mixer_selem_set_capture_volume_range()\n"); + +#if SND_LIB_VERSION > SND_VERS(1,0,9) + return 0; +#endif +} + +/** + * \brief Set value of capture volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_capture_volume (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + long value) +{ + dbg_printf ("snd_mixer_selem_set_capture_volume()\n"); + + return 0; +} + +/** + * \brief Set range for playback volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min minimum volume value + * \param max maximum volume value + */ +#if SND_LIB_VERSION > SND_VERS(1,0,9) +int +#else +void +#endif +snd_mixer_selem_set_playback_volume_range (snd_mixer_elem_t * elem, + long min, long max) +{ + dbg_printf2 ("snd_mixer_selem_set_playback_volume_range(%s, %d, %d)\n", + elem->ext.extname, min, max); + + elem->low = min; + elem->high = max; + +#if SND_LIB_VERSION > SND_VERS(1,0,9) + return 0; +#endif +} + +/** + * \brief Return value of playback volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_get_playback_volume (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + int vol, range, left, right; + oss_mixer_value rec; + + dbg_printf2 ("snd_mixer_selem_get_playback_volume()\n"); + + rec.dev = elem->ext.dev; + rec.ctrl = elem->ext.ctrl; + rec.timestamp = elem->ext.timestamp; + + if (ioctl (mixer_fd, SNDCTL_MIX_READ, &rec) == -1) + return -errno; + + left = rec.value & 0xff; + right = (rec.value >> 8) & 0xff; + + if (left > right) + vol = left; + else + vol = right; + range = elem->high - elem->low; + + vol = (vol * range) / elem->ext.maxvalue; + vol += elem->low; + *value = vol; + + return 0; +} + +/** + * \brief Get range for playback volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min Pointer to returned minimum + * \param max Pointer to returned maximum + */ +#if SND_LIB_VERSION > SND_VERS(1,0,9) +int +#else +void +#endif +snd_mixer_selem_get_playback_volume_range (snd_mixer_elem_t * elem, + long *min, long *max) +{ + dbg_printf2 ("snd_mixer_selem_get_playback_volume_range()\n"); +#if 1 + *min = elem->low; + *max = elem->high; +#endif + +#if SND_LIB_VERSION > SND_VERS(1,0,9) + return 0; +#endif +} + +/** + * \brief get first element for a mixer + * \param mixer Mixer handle + * \return pointer to first element + */ +snd_mixer_elem_t * +snd_mixer_first_elem (snd_mixer_t * mixer) +{ + dbg_printf2 ("snd_mixer_first_elem()\n"); + + return mixer->elems; +} + +/** + * \brief get next mixer element + * \param elem mixer element + * \return pointer to next element + */ +snd_mixer_elem_t * +snd_mixer_elem_next (snd_mixer_elem_t * elem) +{ + dbg_printf2 ("snd_mixer_elem_next(%x/%d)\n", elem, elem); + + if (elem == NULL || (long) elem < 4096) + { + dbg_printf2 ("Returning NULL\n"); + return NULL; + } + + if (elem->next == NULL) + dbg_printf ("No more elemsnts\n"); + else + dbg_printf2 ("Returning %d/%s\n", elem->next->ctrl, + elem->next->ext.extname); + return elem->next; +} + +/** + * \brief Find a mixer simple element + * \param mixer Mixer handle + * \param id Mixer simple element identifier + * \return mixer simple element handle or NULL if not found + * + * Wuld somebody kindly explain me what in hell is the logic + * behind this idiotic "simple" mixer stuff. + */ +snd_mixer_elem_t * +snd_mixer_find_selem (snd_mixer_t * mixer, const snd_mixer_selem_id_t * id) +{ + int i; + + dbg_printf2 ("snd_mixer_find_selem(%d/%s)\n", id->number, id->name); + + if (*id->name == 0 && id->number == 0) + return NULL; + + for (i = 0; i < mixer->nrext; i++) + { + oss_mixext *ext = &mixer->elems[i].ext; + + if (ext->type == MIXT_GROUP || + ext->type == MIXT_DEVROOT || ext->type == MIXT_MARKER) + continue; + + if (strcasecmp (ext->extname, id->name) == 0) + { + return &mixer->elems[i]; + } + + if (ext->ctrl == id->number) + { + return &mixer->elems[i]; + } + } + + return NULL; +} + +/** + * \brief Handle pending mixer events invoking callbacks + * \param mixer Mixer handle + * \return 0 otherwise a negative error code on failure + */ +int +snd_mixer_handle_events (snd_mixer_t * mixer) +{ + dbg_printf2 ("snd_mixer_handle_events()\n"); + // NOP +} + +/** + * \brief Load a mixer elements + * \param mixer Mixer handle + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_load (snd_mixer_t * mixer) +{ + snd_mixer_elem_t *elems; + int i, n = 0; + + dbg_printf2 ("snd_mixer_load()\n"); + + if (mixer->elems != NULL) + free (mixer->elems); + + if ((elems = malloc (sizeof (*elems) * mixer->nrext)) == NULL) + return -ENOMEM; + + memset (elems, 0, sizeof (*elems) * mixer->nrext); + + for (i = 0; i < mixer->nrext; i++) + { + oss_mixext *ext = &elems[n].ext; + snd_mixer_elem_t *elem; + + elem = &elems[n]; + + ext->dev = mixer->mixdev; + ext->ctrl = i; + + if (ioctl (mixer_fd, SNDCTL_MIX_EXTINFO, ext) < 0) + { + int e = errno; + perror ("SNDCTL_MIX_EXTINFO"); + return -e; + } + + if (ext->type == MIXT_DEVROOT) + continue; + if (ext->type == MIXT_GROUP) + continue; + if (ext->type == MIXT_MARKER) + continue; + + elem->low = 0; + elem->high = ext->maxvalue; + elem->ctrl = elem->ext.ctrl; + + if (n > 0) + elems[n - 1].next = &elems[n]; + + n++; + } + + mixer->nrext = n; + mixer->elems = elems; + return 0; +} + +/** + * \brief Get name part of mixer simple element identifier + * \param elem Mixer simple element handle + * \return name part of simple element identifier + */ +const char * +snd_mixer_selem_get_name (snd_mixer_elem_t * elem) +{ + dbg_printf2 ("snd_mixer_selem_get_name()\n"); + + return elem->ext.extname; +} + +/** + * \brief Set index part of a mixer simple element identifier + * \param obj Mixer simple element identifier + * \param val index part + */ +void +snd_mixer_selem_id_set_index (snd_mixer_selem_id_t * obj, unsigned int val) +{ + dbg_printf2 ("snd_mixer_selem_id_set_index(%u)\n", val); + + obj->number = val; +} + +/** + * \brief Set name part of a mixer simple element identifier + * \param obj Mixer simple element identifier + * \param val name part + */ +void +snd_mixer_selem_id_set_name (snd_mixer_selem_id_t * obj, const char *val) +{ + dbg_printf2 ("snd_mixer_selem_id_set_name(%s)\n", val); + + strcpy (obj->name, val); +} + +/** + * \brief get size of #snd_mixer_selem_id_t + * \return size in bytes + */ +size_t +snd_mixer_selem_id_sizeof () +{ + return sizeof (snd_mixer_selem_id_t); +} + + +/** + * \brief Get info about the active state of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if not active, 1 if active + */ +int +snd_mixer_selem_is_active (snd_mixer_elem_t * elem) +{ + dbg_printf2 ("snd_mixer_selem_is_active()\n"); + return 1; +} + +/** + * \brief Return value of capture switch control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_get_capture_switch (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + int *value) +{ + dbg_printf ("snd_mixer_selem_get_capture_switch()\n"); + + *value = 0; + return 0; +} + +/** + * \brief Return value of playback switch control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_get_playback_switch (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + int *value) +{ + oss_mixer_value rec; + + dbg_printf ("snd_mixer_selem_get_playback_switch()\n"); + + *value = 0; + + rec.dev = elem->ext.dev; + rec.ctrl = elem->ext.ctrl; + rec.ctrl = elem->ext.ctrl; + rec.timestamp = elem->ext.timestamp; + + if (ioctl (mixer_fd, SNDCTL_MIX_READ, &rec) == -1) + return -errno; + + *value = !!rec.value; + return 0; +} + +/** + * \brief Get info about channels of capture stream of a mixer simple element + * \param elem Mixer simple element handle + * \param channel Mixer simple element channel identifier + * \return 0 if channel is not present, 1 if present + */ +int +snd_mixer_selem_has_capture_channel (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel) +{ + dbg_printf ("snd_mixer_selem_has_capture_channel()\n"); + + return (channel < 2); +} + +/** + * \brief Return info about capture switch control existence of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if no control is present, 1 if it's present + */ +int +snd_mixer_selem_has_capture_switch (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_capture_switch()\n"); + + return 0; +} + +/** + * \brief Return info about capture switch control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if control is separated per channel, 1 if control acts on all channels together + */ +int +snd_mixer_selem_has_capture_switch_joined (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_capture_switch_joined()\n"); + + return 1; +} + +/** + * \brief Get info about channels of capture stream of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if not mono, 1 if mono + */ +int +snd_mixer_selem_is_capture_mono (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_is_capture_mono()\n"); + return 0; +} + +/** + * \brief Get info about channels of playback stream of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if not mono, 1 if mono + */ +int +snd_mixer_selem_is_playback_mono (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_is_playback_mono()\n"); + return 0; +} + +/** + * \brief Set value of capture switch control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_capture_switch (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + int value) +{ + dbg_printf ("snd_mixer_selem_set_capture_switch()\n"); + + return 0; +} + +/** + * \brief Set value of capture switch control for all channels of a mixer simple element + * \param elem Mixer simple element handle + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_capture_switch_all (snd_mixer_elem_t * elem, int value) +{ + dbg_printf ("snd_mixer_selem_set_capture_switch_all()\n"); + + return 0; +} + +/** + * \brief Set value of playback switch control for all channels of a mixer simple element + * \param elem Mixer simple element handle + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_playback_switch_all (snd_mixer_elem_t * elem, int value) +{ + oss_mixer_value rec; + + dbg_printf ("snd_mixer_selem_set_playback_switch_all()\n"); + + rec.dev = elem->ext.dev; + rec.ctrl = elem->ext.ctrl; + rec.ctrl = elem->ext.ctrl; + rec.timestamp = elem->ext.timestamp; + rec.value = !!value; + + if (ioctl (mixer_fd, SNDCTL_MIX_WRITE, &rec) == -1) + return -errno; + + return 0; +} + +/** + * \brief Return value of capture volume control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value pointer to returned value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_get_capture_volume (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + dbg_printf ("snd_mixer_selem_get_capture_volume()\n"); + + *value = 0; + return 0; +} + +/** + * \brief Get mixer simple element identifier + * \param elem Mixer simple element handle + * \param id returned mixer simple element identifier + */ +void +snd_mixer_selem_get_id (snd_mixer_elem_t * elem, snd_mixer_selem_id_t * id) +{ + dbg_printf ("snd_mixer_selem_get_id()\n"); + // What in hell is this? + + id->number = elem->ctrl; + strcpy (id->name, elem->ext.extname); + dbg_printf ("ID=%d / %s\n", id->number, id->name); +} + +/** + * \brief Get name part of a mixer simple element identifier + * \param obj Mixer simple element identifier + * \return name part + */ +const char * +snd_mixer_selem_id_get_name (const snd_mixer_selem_id_t * obj) +{ + dbg_printf ("snd_mixer_selem_id_get_name()=%s\n", obj->name); +//return "Objname"; + return obj->name; +} + +/** + * \brief Get index part of a mixer simple element identifier + * \param obj Mixer simple element identifier + * \return index part + */ +unsigned int +snd_mixer_selem_id_get_index (const snd_mixer_selem_id_t * obj) +{ + dbg_printf ("snd_mixer_selem_id_get_index()\n"); + + return 0; +} + + +/** + * \brief Get elements count for a mixer + * \param mixer mixer handle + * \return elements count + */ +unsigned int +snd_mixer_get_count (const snd_mixer_t * obj) +{ + dbg_printf ("snd_mixer_get_count()\n"); + + return obj->nrext; +} + +/** + * \brief get poll descriptors + * \param mixer Mixer handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + */ +int +snd_mixer_poll_descriptors (snd_mixer_t * mixer, struct pollfd *pfds, + unsigned int space) +{ + dbg_printf ("snd_mixer_poll_descriptors()\n"); + + return 0; +} + +/** + * \brief get count of poll descriptors for mixer handle + * \param mixer Mixer handle + * \return count of poll descriptors + */ +int +snd_mixer_poll_descriptors_count (snd_mixer_t * mixer) +{ + dbg_printf ("snd_mixer_poll_descriptors_count()\n"); + + return 0; +} + +/** + * \brief get returned events from poll descriptors + * \param mixer Mixer handle + * \param pfds array of poll descriptors + * \param nfds count of poll descriptors + * \param revents returned events + * \return zero if success, otherwise a negative error code + */ +int +snd_mixer_poll_descriptors_revents (snd_mixer_t * mixer, struct pollfd *pfds, + unsigned int nfds, + unsigned short *revents) +{ + dbg_printf ("snd_mixer_poll_descriptors_revents()\n"); + + return 0; +} + +/** + * \brief Set callback function for a mixer + * \param mixer mixer handle + * \param callback callback function + */ +void +snd_mixer_set_callback (snd_mixer_t * obj, snd_mixer_callback_t val) +{ + dbg_printf0 ("snd_mixer_set_callback()\n"); + +} + +/** + * \brief Get range for capture volume of a mixer simple element + * \param elem Mixer simple element handle + * \param min Pointer to returned minimum + * \param max Pointer to returned maximum + */ +#if SND_LIB_VERSION > SND_VERS(1,0,9) +int +#else +void +#endif +snd_mixer_selem_get_capture_volume_range (snd_mixer_elem_t * elem, + long *min, long *max) +{ + dbg_printf ("snd_mixer_selem_get_capture_volume_range()\n"); + + *min = 0; + *max = 100; + +#if SND_LIB_VERSION > SND_VERS(1,0,9) + return 0; +#endif +} + +/** + * \brief Return true if mixer simple element is an enumerated control + * \param elem Mixer simple element handle + * \return 0 normal volume/switch control, 1 enumerated control + */ +int +snd_mixer_selem_is_enumerated (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_is_enumerated()\n"); + + return 0; +} + +/** + * \brief get the current selected enumerated item for the given mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param itemp the pointer to store the index of the enumerated item + * \return 0 if successful, otherwise a negative error code + */ +int +snd_mixer_selem_get_enum_item (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + unsigned int *itemp) +{ + dbg_printf ("snd_mixer_selem_get_enum_item()\n"); + + return 0; +} + +/** + * \brief Return the number of enumerated items of the given mixer simple element + * \param elem Mixer simple element handle + * \return the number of enumerated items, otherwise a negative error code + */ +int +snd_mixer_selem_get_enum_items (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_get_enum_items()\n"); + return 0; +} + +/** + * \brief get the enumerated item string for the given mixer simple element + * \param elem Mixer simple element handle + * \param item the index of the enumerated item to query + * \param maxlen the maximal length to be stored + * \param buf the buffer to store the name string + * \return 0 if successful, otherwise a negative error code + */ +int +snd_mixer_selem_get_enum_item_name (snd_mixer_elem_t * elem, + unsigned int item, + size_t maxlen, char *buf) +{ + dbg_printf ("snd_mixer_selem_get_enum_item_name()\n"); + strncpy (buf, "Enum", maxlen); + buf[maxlen - 1] = 0; + + return 0; +} + +/** + * \brief Return info about capture volume control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if control is separated per channel, 1 if control acts on all channels together + */ +int +snd_mixer_selem_has_capture_volume_joined (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_capture_volume_joined()\n"); + return elem->ext.type != MIXT_STEREOSLIDER; +} + +/** + * \brief Get info about channels of playback stream of a mixer simple element + * \param elem Mixer simple element handle + * \param channel Mixer simple element channel identifier + * \return 0 if channel is not present, 1 if present + */ +int +snd_mixer_selem_has_playback_channel (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel) +{ + dbg_printf ("snd_mixer_selem_has_playback_channel(%s, %d)\n", + elem->ext.extname, channel); + + if (elem->ext.type == MIXT_SLIDER && channel == 0) + return 1; + if (elem->ext.type == MIXT_MONOSLIDER && channel == 0) + return 1; + if (elem->ext.type == MIXT_STEREOSLIDER && channel < 2) + return 1; + + return 0; +} + +/** + * \brief Return info about playback switch control of a mixer simple element + * \param elem Mixer simple element handle + * \return 0 if control is separated per channel, 1 if control acts on all channels together + */ +int +snd_mixer_selem_has_playback_switch_joined (snd_mixer_elem_t * elem) +{ + dbg_printf ("snd_mixer_selem_has_playback_switch_joined()\n"); + + return 0; +} + +/** + * \brief copy one #snd_mixer_selem_id_t to another + * \param dst pointer to destination + * \param src pointer to source + */ +void +snd_mixer_selem_id_copy (snd_mixer_selem_id_t * dst, + const snd_mixer_selem_id_t * src) +{ + dbg_printf ("snd_mixer_selem_id_copy()\n"); + memcpy (dst, src, sizeof (*dst)); +} + + +/** + * \brief set the current selected enumerated item for the given mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param item the enumerated item index + * \return 0 if successful, otherwise a negative error code + */ +int +snd_mixer_selem_set_enum_item (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + unsigned int item) +{ + dbg_printf ("snd_mixer_selem_set_enum_item()\n"); + + return 0; +} + +/** + * \brief Set value of playback switch control of a mixer simple element + * \param elem Mixer simple element handle + * \param channel mixer simple element channel identifier + * \param value control value + * \return 0 on success otherwise a negative error code + */ +int +snd_mixer_selem_set_playback_switch (snd_mixer_elem_t * elem, + snd_mixer_selem_channel_id_t channel, + int value) +{ + dbg_printf ("snd_mixer_selem_set_playback_switch()\n"); + + return 0; +} + +/** + * * \brief Return true if mixer simple element has only one volume control for both playback and capture + * * \param elem Mixer simple element handle + * * \return 0 separated control, 1 common control + * */ +int +snd_mixer_selem_has_common_volume (snd_mixer_elem_t * elem) +{ + return 0; // TODO +} + +/** + * * \brief Return true if mixer simple element has only one switch control for both playback and capture + * * \param elem Mixer simple element handle + * * \return 0 separated control, 1 common control + * */ +int +snd_mixer_selem_has_common_switch (snd_mixer_elem_t * elem) +{ + return 0; // TODO +} diff --git a/lib/libsalsa/output.c b/lib/libsalsa/output.c new file mode 100644 index 0000000..0369a81 --- /dev/null +++ b/lib/libsalsa/output.c @@ -0,0 +1,411 @@ +/** + * \file output.c + * \brief Generic stdio-like output interface + * \author Abramo Bagnara <abramo@alsa-project.org> + * \date 2000 + * + * Generic stdio-like output interface + */ +/* + * Output object + * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "local.h" + +#ifndef DOC_HIDDEN +typedef struct _snd_output_ops +{ + int (*close) (snd_output_t * output); + int (*print) (snd_output_t * output, const char *format, va_list args); + int (*puts) (snd_output_t * output, const char *str); + int (*putch) (snd_output_t * output, int c); + int (*flush) (snd_output_t * output); +} snd_output_ops_t; + +struct _snd_output +{ + snd_output_type_t type; + snd_output_ops_t *ops; + void *private_data; +}; +#endif + +/** + * \brief Closes an output handle. + * \param output The output handle to be closed. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_output_close (snd_output_t * output) +{ + int err = output->ops->close (output); + free (output); + return err; +} + +/** + * \brief Writes formatted output (like \c fprintf(3)) to an output handle. + * \param output The output handle. + * \param format Format string in \c fprintf format. + * \param ... Other \c fprintf arguments. + * \return The number of characters written, or a negative error code. + */ +int +snd_output_printf (snd_output_t * output, const char *format, ...) +{ + int result; + va_list args; + va_start (args, format); + result = output->ops->print (output, format, args); + va_end (args); + return result; +} + +/** + * \brief Writes formatted output (like \c fprintf(3)) to an output handle. + * \param output The output handle. + * \param format Format string in \c fprintf format. + * \param args Other \c fprintf arguments. + * \return The number of characters written, or a negative error code. + */ +int +snd_output_vprintf (snd_output_t * output, const char *format, va_list args) +{ + return output->ops->print (output, format, args); +} + +/** + * \brief Writes a string to an output handle (like \c fputs(3)). + * \param output The output handle. + * \param str Pointer to the string. + * \return Zero if successful, otherwise a negative error code or \c EOF. + */ +int +snd_output_puts (snd_output_t * output, const char *str) +{ + return output->ops->puts (output, str); +} + +/** + * \brief Writes a character to an output handle (like \c putc(3)). + * \param output The output handle. + * \param c The character. + * \return Zero if successful, otherwise a negative error code or \c EOF. + */ +int +snd_output_putc (snd_output_t * output, int c) +{ + return output->ops->putch (output, c); +} + +/** + * \brief Flushes an output handle (like fflush(3)). + * \param output The output handle. + * \return Zero if successful, otherwise \c EOF. + * + * If the underlying destination is a stdio stream, this function calls + * \c fflush. If the underlying destination is a memory buffer, the write + * position is reset to the beginning of the buffer. \c =:-o + */ +int +snd_output_flush (snd_output_t * output) +{ + return output->ops->flush (output); +} + +#ifndef DOC_HIDDEN +typedef struct _snd_output_stdio +{ + int close; + FILE *fp; +} snd_output_stdio_t; + +static int +snd_output_stdio_close (snd_output_t * output ATTRIBUTE_UNUSED) +{ + snd_output_stdio_t *stdio = output->private_data; + if (stdio->close) + fclose (stdio->fp); + free (stdio); + return 0; +} + +static int +snd_output_stdio_print (snd_output_t * output, const char *format, + va_list args) +{ + snd_output_stdio_t *stdio = output->private_data; + return vfprintf (stdio->fp, format, args); +} + +static int +snd_output_stdio_puts (snd_output_t * output, const char *str) +{ + snd_output_stdio_t *stdio = output->private_data; + return fputs (str, stdio->fp); +} + +static int +snd_output_stdio_putc (snd_output_t * output, int c) +{ + snd_output_stdio_t *stdio = output->private_data; + return putc (c, stdio->fp); +} + +static int +snd_output_stdio_flush (snd_output_t * output) +{ + snd_output_stdio_t *stdio = output->private_data; + return fflush (stdio->fp); +} + +static snd_output_ops_t snd_output_stdio_ops = { + .close = snd_output_stdio_close, + .print = snd_output_stdio_print, + .puts = snd_output_stdio_puts, + .putch = snd_output_stdio_putc, + .flush = snd_output_stdio_flush, +}; + +#endif + +/** + * \brief Creates a new output object using an existing stdio \c FILE pointer. + * \param outputp The function puts the pointer to the new output object + * at the address specified by \p outputp. + * \param fp The \c FILE pointer to write to. Characters are written + * to the file starting at the current file position. + * \param close Close flag. Set this to 1 if #snd_output_close should close + * \p fp by calling \c fclose. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_output_stdio_attach (snd_output_t ** outputp, FILE * fp, int _close) +{ + snd_output_t *output; + snd_output_stdio_t *stdio; + assert (outputp && fp); + stdio = calloc (1, sizeof (*stdio)); + if (!stdio) + return -ENOMEM; + output = calloc (1, sizeof (*output)); + if (!output) + { + free (stdio); + return -ENOMEM; + } + stdio->fp = fp; + stdio->close = _close; + output->type = SND_OUTPUT_STDIO; + output->ops = &snd_output_stdio_ops; + output->private_data = stdio; + *outputp = output; + return 0; +} + +/** + * \brief Creates a new output object writing to a file. + * \param outputp The function puts the pointer to the new output object + * at the address specified by \p outputp. + * \param file The name of the file to open. + * \param mode The open mode, like \c fopen(3). + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_output_stdio_open (snd_output_t ** outputp, const char *file, + const char *mode) +{ + int err; + FILE *fp = fopen (file, mode); + if (!fp) + { + //SYSERR("fopen"); + return -errno; + } + err = snd_output_stdio_attach (outputp, fp, 1); + if (err < 0) + fclose (fp); + return err; +} + +#ifndef DOC_HIDDEN + +typedef struct _snd_output_buffer +{ + unsigned char *buf; + size_t alloc; + size_t size; +} snd_output_buffer_t; + +static int +snd_output_buffer_close (snd_output_t * output ATTRIBUTE_UNUSED) +{ + snd_output_buffer_t *buffer = output->private_data; + free (buffer->buf); + free (buffer); + return 0; +} + +static int +snd_output_buffer_need (snd_output_t * output, size_t size) +{ + snd_output_buffer_t *buffer = output->private_data; + size_t _free = buffer->alloc - buffer->size; + size_t alloc; + unsigned char *buf; + + if (_free >= size) + return _free; + if (buffer->alloc == 0) + alloc = 256; + else + alloc = buffer->alloc; + while (alloc < size) + alloc *= 2; + buf = realloc (buffer->buf, alloc); + if (!buf) + return -ENOMEM; + buffer->buf = buf; + buffer->alloc = alloc; + return buffer->alloc - buffer->size; +} + +static int +snd_output_buffer_print (snd_output_t * output, const char *format, + va_list args) +{ + snd_output_buffer_t *buffer = output->private_data; + size_t size = 256; + int result; + result = snd_output_buffer_need (output, size); + if (result < 0) + return result; + result = vsnprintf (buffer->buf + buffer->size, size, format, args); + assert (result >= 0); + if ((size_t) result <= size) + { + buffer->size += result; + return result; + } + size = result; + result = snd_output_buffer_need (output, size); + if (result < 0) + return result; + result = vsnprintf (buffer->buf + buffer->size, result, format, args); + assert (result == (int) size); + buffer->size += result; + return result; +} + +static int +snd_output_buffer_puts (snd_output_t * output, const char *str) +{ + snd_output_buffer_t *buffer = output->private_data; + size_t size = strlen (str); + int err; + err = snd_output_buffer_need (output, size); + if (err < 0) + return err; + memcpy (buffer->buf + buffer->size, str, size); + buffer->size += size; + return size; +} + +static int +snd_output_buffer_putc (snd_output_t * output, int c) +{ + snd_output_buffer_t *buffer = output->private_data; + int err; + err = snd_output_buffer_need (output, 1); + if (err < 0) + return err; + buffer->buf[buffer->size++] = c; + return 0; +} + +static int +snd_output_buffer_flush (snd_output_t * output ATTRIBUTE_UNUSED) +{ + snd_output_buffer_t *buffer = output->private_data; + buffer->size = 0; + return 0; +} + +static snd_output_ops_t snd_output_buffer_ops = { + .close = snd_output_buffer_close, + .print = snd_output_buffer_print, + .puts = snd_output_buffer_puts, + .putch = snd_output_buffer_putc, + .flush = snd_output_buffer_flush, +}; +#endif + +/** + * \brief Returns the address of the buffer of a #SND_OUTPUT_TYPE_BUFFER output handle. + * \param output The output handle. + * \param buf The functions puts the current address of the buffer at the + * address specified by \p buf. + * \return The current size of valid data in the buffer. + * + * The address of the buffer may become invalid when output functions or + * #snd_output_close are called. + */ +size_t +snd_output_buffer_string (snd_output_t * output, char **buf) +{ + snd_output_buffer_t *buffer = output->private_data; + *buf = buffer->buf; + return buffer->size; +} + +/** + * \brief Creates a new output object with an auto-extending memory buffer. + * \param outputp The function puts the pointer to the new output object + * at the address specified by \p outputp. + * \return Zero if successful, otherwise a negative error code. + */ +int +snd_output_buffer_open (snd_output_t ** outputp) +{ + snd_output_t *output; + snd_output_buffer_t *buffer; + assert (outputp); + buffer = calloc (1, sizeof (*buffer)); + if (!buffer) + return -ENOMEM; + output = calloc (1, sizeof (*output)); + if (!output) + { + free (buffer); + return -ENOMEM; + } + buffer->buf = NULL; + buffer->alloc = 0; + buffer->size = 0; + output->type = SND_OUTPUT_BUFFER; + output->ops = &snd_output_buffer_ops; + output->private_data = buffer; + *outputp = output; + return 0; +} diff --git a/lib/libsalsa/pcm.c b/lib/libsalsa/pcm.c new file mode 100644 index 0000000..a305e54 --- /dev/null +++ b/lib/libsalsa/pcm.c @@ -0,0 +1,3114 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * Parts of the code is derived from the alsa-lib package that is + * copyrighted by Jaroslav Kysela and the other ALSA team members. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define USE_VERSIONED_SYMBOLS +#define DO_VERSIONING +#define HAVE_ELF +#define PERIOD_SIZE 128 + +#include "local.h" + +#define PCMTYPE(v) [SND_PCM_TYPE_##v] = #v +#define STATE(v) [SND_PCM_STATE_##v] = #v +#define STREAM(v) [SND_PCM_STREAM_##v] = #v +#define READY(v) [SND_PCM_READY_##v] = #v +#define XRUN(v) [SND_PCM_XRUN_##v] = #v +#define SILENCE(v) [SND_PCM_SILENCE_##v] = #v +#define TSTAMP(v) [SND_PCM_TSTAMP_##v] = #v +#define ACCESS(v) [SND_PCM_ACCESS_##v] = #v +#define START(v) [SND_PCM_START_##v] = #v +#define HW_PARAM(v) [SND_PCM_HW_PARAM_##v] = #v +#define SW_PARAM(v) [SND_PCM_SW_PARAM_##v] = #v +#define FORMAT(v) [SND_PCM_FORMAT_##v] = #v +#define SUBFORMAT(v) [SND_PCM_SUBFORMAT_##v] = #v + +#define FORMATD(v, d) [SND_PCM_FORMAT_##v] = d +#define SUBFORMATD(v, d) [SND_PCM_SUBFORMAT_##v] = d + +static const char *snd_pcm_stream_names[] = { + STREAM (PLAYBACK), + STREAM (CAPTURE), +}; + +static const char *snd_pcm_state_names[] = { + STATE (OPEN), + STATE (SETUP), + STATE (PREPARED), + STATE (RUNNING), + STATE (XRUN), + STATE (DRAINING), + STATE (PAUSED), + STATE (SUSPENDED), + STATE (DISCONNECTED), +}; + +static const char *snd_pcm_access_names[] = { + ACCESS (MMAP_INTERLEAVED), + ACCESS (MMAP_NONINTERLEAVED), + ACCESS (MMAP_COMPLEX), + ACCESS (RW_INTERLEAVED), + ACCESS (RW_NONINTERLEAVED), +}; + +static const char *snd_pcm_format_descriptions[] = { + FORMATD (S8, "Signed 8 bit"), + FORMATD (U8, "Unsigned 8 bit"), + FORMATD (S16_LE, "Signed 16 bit Little Endian"), + FORMATD (S16_BE, "Signed 16 bit Big Endian"), + FORMATD (U16_LE, "Unsigned 16 bit Little Endian"), + FORMATD (U16_BE, "Unsigned 16 bit Big Endian"), + FORMATD (S24_LE, "Signed 24 bit Little Endian"), + FORMATD (S24_BE, "Signed 24 bit Big Endian"), + FORMATD (U24_LE, "Unsigned 24 bit Little Endian"), + FORMATD (U24_BE, "Unsigned 24 bit Big Endian"), + FORMATD (S32_LE, "Signed 32 bit Little Endian"), + FORMATD (S32_BE, "Signed 32 bit Big Endian"), + FORMATD (U32_LE, "Unsigned 32 bit Little Endian"), + FORMATD (U32_BE, "Unsigned 32 bit Big Endian"), + FORMATD (FLOAT_LE, "Float 32 bit Little Endian"), + FORMATD (FLOAT_BE, "Float 32 bit Big Endian"), + FORMATD (FLOAT64_LE, "Float 64 bit Little Endian"), + FORMATD (FLOAT64_BE, "Float 64 bit Big Endian"), + FORMATD (IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), + FORMATD (IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), + FORMATD (MU_LAW, "Mu-Law"), + FORMATD (A_LAW, "A-Law"), + FORMATD (IMA_ADPCM, "Ima-ADPCM"), + FORMATD (MPEG, "MPEG"), + FORMATD (GSM, "GSM"), + FORMATD (SPECIAL, "Special"), + FORMATD (S24_3LE, "Signed 24 bit Little Endian in 3bytes"), + FORMATD (S24_3BE, "Signed 24 bit Big Endian in 3bytes"), + FORMATD (U24_3LE, "Unsigned 24 bit Little Endian in 3bytes"), + FORMATD (U24_3BE, "Unsigned 24 bit Big Endian in 3bytes"), + FORMATD (S20_3LE, "Signed 20 bit Little Endian in 3bytes"), + FORMATD (S20_3BE, "Signed 20 bit Big Endian in 3bytes"), + FORMATD (U20_3LE, "Unsigned 20 bit Little Endian in 3bytes"), + FORMATD (U20_3BE, "Unsigned 20 bit Big Endian in 3bytes"), + FORMATD (S18_3LE, "Signed 18 bit Little Endian in 3bytes"), + FORMATD (S18_3BE, "Signed 18 bit Big Endian in 3bytes"), + FORMATD (U18_3LE, "Unsigned 18 bit Little Endian in 3bytes"), + FORMATD (U18_3BE, "Unsigned 18 bit Big Endian in 3bytes"), +}; + +struct _snd_pcm +{ + int fd; + int state; + oss_audioinfo info; + int stream, oss_mode; + int frame_size, sample_size, period_size; + int channels; + int periods; + snd_pcm_channel_area_t *area; +}; + +struct _snd_pcm_format_mask +{ + unsigned int mask; +}; + +struct _snd_pcm_status +{ + int dummy; +}; + +struct _snd_pcm_sw_params +{ + int dummy; +}; + +struct _snd_pcm_hw_params +{ + int speed; + int fmt, channels; + int oss_fmt; + + oss_audioinfo *info; + snd_pcm_t *pcm; + + int frame_size; + int sample_bits; + int access; +}; + +static int +parse_device (const char *nme, char *dspname) +{ + char *s; + oss_audioinfo ai; + int card = 0, port = 0, i; + char tmp[256], *name = tmp; + + strcpy (name, nme); + + if (name[0] != 'h') + return 0; + if (name[1] != 'w') + return 0; + if (name[2] != ':') + return 0; + + name += 3; + s = name; + + while (*s && *s != ',') + s++; + + if (*s == ',') + { + int n = 0; + + *s++ = 0; + if (sscanf (name, "%d", &card) != 1) + return 0; + if (sscanf (s, "%d", &port) != 1) + return 0; + + n = -1; + for (i = 0; i < sysinfo.numaudios; i++) + { + ai.dev = i; + if (ioctl (mixer_fd, SNDCTL_AUDIOINFO, &ai) == -1) + continue; + + if (ai.card_number != card) + continue; + + n++; + if (port < n) + continue; + + sprintf (dspname, "/dev/dsp%d", i); + return 1; + } + return 0; + } + if (sscanf (name, "%d", &card) != 1) + return 0; + + for (i = 0; i < sysinfo.numaudios; i++) + { + ai.dev = i; + if (ioctl (mixer_fd, SNDCTL_AUDIOINFO, &ai) == -1) + continue; + + if (ai.card_number == card) + { + port = i; + sprintf (dspname, "/dev/dsp%d", port); + return 1; + } + } + + return 0; +} + +/** + * \brief Opens a PCM + * \param pcmp Returned PCM handle + * \param name ASCII identifier of the PCM handle + * \param stream Wanted stream + * \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC) + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_open (snd_pcm_t ** pcmp, const char *name, + snd_pcm_stream_t stream, int mode) +{ + snd_pcm_t *pcm; + int oss_mode = O_WRONLY; + char dspname[32]; + const char *devdsp; + + if ((devdsp = getenv("OSS_AUDIODEV")) == NULL) + devdsp = "/dev/dsp"; + + ALIB_INIT (); + + if (!alib_appcheck ()) + return -ENODEV; + + *pcmp = NULL; + + dbg_printf ("snd_pcm_open(%s, %x)\n", name, stream); + + if (mode != 0) + dbg_printf2 ("snd_pcm_open: mode=0x%x - not emulated.\n", mode); + + if (strcmp (name, "default") == 0) + name = devdsp; + else + { + if (!parse_device (name, dspname)) + { +#if 0 + fprintf (stderr, "OSS asound - Bad PCM device '%s'\n", name); + return -ENOENT; +#else + strcpy (dspname, devdsp); +#endif + } + + name = dspname; + } + + if ((pcm = malloc (sizeof (*pcm))) == NULL) + return -ENOMEM; + + switch (stream) + { + case SND_PCM_STREAM_CAPTURE: + oss_mode = O_RDONLY; + break; + case SND_PCM_STREAM_PLAYBACK: + oss_mode = O_WRONLY; + break; + default: + fprintf (stderr, "snd_pcm_open: Bad stream %d\n", stream); + return -EINVAL; + } + + memset (pcm, 0, sizeof (*pcm)); + pcm->stream = stream; + pcm->frame_size = 2; + pcm->sample_size = 1; + pcm->channels = 2; + pcm->oss_mode = oss_mode; + pcm->state = SND_PCM_STATE_OPEN; + pcm->period_size = PERIOD_SIZE; + pcm->periods = 4; + + if ((pcm->fd = open (name, oss_mode, 0)) == -1) + { + int err = errno; + free (pcm); + return -err; + } + + pcm->info.dev = -1; + if (ioctl (pcm->fd, SNDCTL_ENGINEINFO, &pcm->info) == -1) + { + int err = errno; + close (pcm->fd); + free (pcm); + return -err; + } + + dbg_printf ("Opened %s='%s' = %d\n", name, pcm->info.name, pcm->fd); + *pcmp = pcm; + return 0; +} + +/** + * \brief close PCM handle + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified PCM handle and frees all associated + * resources. + */ +int +snd_pcm_close (snd_pcm_t * pcm) +{ + if (pcm == NULL) + return -1; + close (pcm->fd); + if (pcm->area != NULL) + free (pcm->area); + free (pcm); + return 0; +} + +/** + * \brief Convert frames in bytes for a PCM + * \param pcm PCM handle + * \param frames quantity in frames + * \return quantity expressed in bytes + */ +ssize_t +snd_pcm_frames_to_bytes (snd_pcm_t * pcm, snd_pcm_sframes_t frames) +{ +// printf("snd_pcm_frames_to_bytes(%d)\n", frames); + return frames * pcm->frame_size; +} + +/** + * \brief Convert bytes in frames for a PCM + * \param pcm PCM handle + * \param bytes quantity in bytes + * \return quantity expressed in frames + */ +snd_pcm_sframes_t +snd_pcm_bytes_to_frames (snd_pcm_t * pcm, ssize_t bytes) +{ + dbg_printf3 ("snd_pcm_bytes_to_frames(%d)=%d\n", bytes, + bytes / pcm->frame_size); + return bytes / pcm->frame_size; +} + +/** + * \brief Stop a PCM preserving pending frames + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + * \retval -ESTRPIPE a suspend event occurred + * + * For playback wait for all pending frames to be played and then stop + * the PCM. + * For capture stop PCM permitting to retrieve residual frames. + * + * For stopping the PCM stream immediately, use \link ::snd_pcm_drop() \endlink + * instead. + */ +int +snd_pcm_drain (snd_pcm_t * pcm) +{ + dbg_printf2 ("snd_pcm_drain()\n"); + ioctl (pcm->fd, SNDCTL_DSP_SYNC, NULL); + return 0; +} + +/** + * \brief Stop a PCM dropping pending frames + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + * + * This function stops the PCM <i>immediately</i>. + * The pending samples on the buffer are ignored. + * + * For processing all pending samples, use \link ::snd_pcm_drain() \endlink + * instead. + */ +int +snd_pcm_drop (snd_pcm_t * pcm) +{ + dbg_printf2 ("snd_pcm_drop()\n"); + ioctl (pcm->fd, SNDCTL_DSP_HALT, NULL); + return 0; +} + +/** \brief Install one PCM hardware configuration chosen from a configuration sp +ace and #snd_pcm_prepare it + * \param pcm PCM handle + * \param params Configuration space definition container + * \return 0 on success otherwise a negative error code + * + * The configuration is chosen fixing single parameters in this order: + * first access, first format, first subformat, min channels, min rate, + * min period time, max buffer size, min tick time + * + * After this call, #snd_pcm_prepare() is called automatically and + * the stream is brought to \c #SND_PCM_STATE_PREPARED state. + */ +int +snd_pcm_hw_params (snd_pcm_t * pcm, snd_pcm_hw_params_t * params) +{ + int tmp; + + dbg_printf ("snd_pcm_hw_params(%d, %d ch, %x)\n", + params->speed, params->channels, params->oss_fmt); + + tmp = params->oss_fmt; + if (ioctl (pcm->fd, SNDCTL_DSP_SETFMT, &tmp) == -1) + return -errno; + if (tmp != params->oss_fmt) + return -EINVAL; + + tmp = params->channels; + if (ioctl (pcm->fd, SNDCTL_DSP_CHANNELS, &tmp) == -1) + return -errno; + if (tmp != params->channels) + return -EINVAL; + pcm->channels = tmp; + + tmp = params->speed; + if (ioctl (pcm->fd, SNDCTL_DSP_SPEED, &tmp) == -1) + return -errno; + if (tmp != params->speed) + return -EINVAL; + + tmp = params->channels * params->sample_bits; + pcm->frame_size = params->frame_size = tmp / 8; + pcm->sample_size = pcm->frame_size / pcm->channels; + dbg_printf ("Frame size = %d\n", params->frame_size); + return 0; +} + + +/** + * \brief Fill params with a full configuration space for a PCM + * \param pcm PCM handle + * \param params Configuration space + */ +int +snd_pcm_hw_params_any (snd_pcm_t * pcm, snd_pcm_hw_params_t * params) +{ + dbg_printf ("snd_pcm_hw_params_any()\n"); + + memset (params, 0, sizeof (params)); + if (ioctl (pcm->fd, SNDCTL_DSP_CHANNELS, ¶ms->channels) == -1) + return -errno; + pcm->channels = params->channels; + if (ioctl (pcm->fd, SNDCTL_DSP_SETFMT, ¶ms->oss_fmt) == -1) + return -errno; + if (ioctl (pcm->fd, SNDCTL_DSP_SPEED, ¶ms->speed) == -1) + return -errno; + dbg_printf ("Rate %d, channels %d, fmt %x\n", params->speed, + params->channels, params->oss_fmt); + +// TODO + params->frame_size = 2 * params->channels; + params->sample_bits = 16; + + params->info = &pcm->info; + params->pcm = pcm; + return 0; +} + +/** + * \brief Restrict a configuration space to contain only one access type + * \param pcm PCM handle + * \param params Configuration space + * \param access access type + * \return 0 otherwise a negative error code if configuration space would become empty + */ +int +snd_pcm_hw_params_set_access (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + snd_pcm_access_t access) +{ + dbg_printf2 ("snd_pcm_hw_params_set_access(%x/%s)\n", access, + snd_pcm_access_names[access]); + params->access = access; + if (access == SND_PCM_ACCESS_RW_INTERLEAVED) + return 0; + if (access == SND_PCM_ACCESS_MMAP_INTERLEAVED) + return 0; + return -EINVAL; +} + +/** + * \brief Restrict a configuration space to contain only one buffer size + * \param pcm PCM handle + * \param params Configuration space + * \param val buffer size in frames + * \return 0 otherwise a negative error code if configuration space would become empty + * + * Wanted exact value is <,=,> val following dir (-1,0,1) + */ +int +snd_pcm_hw_params_set_buffer_size (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_hw_params_set_buffer_size()\n"); + + return 0; +} + +int +snd_pcm_hw_params_set_buffer_size_min (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val) +{ + dbg_printf ("snd_pcm_hw_params_set_buffer_size_min()\n"); + + return 0; +} + +/** + * \brief Restrict a configuration space to have buffer size nearest to a target + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target buffer size in frames / returned chosen approxi +mate target buffer size in frames + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int +__snd_pcm_hw_params_set_buffer_size_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val) +{ + dbg_printf ("snd_pcm_hw_params_set_buffer_size_near(%d)\n", *val); + return 0; +} + +/** + * \brief Restrict a configuration space to contain only one channels count + * \param pcm PCM handle + * \param params Configuration space + * \param val channels count + * \return 0 otherwise a negative error code if configuration space would become empty + */ +int +snd_pcm_hw_params_set_channels (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + unsigned int val) +{ + dbg_printf2 ("snd_pcm_hw_params_set_channels(%d)\n", val); + + params->channels = val; + + if (ioctl (pcm->fd, SNDCTL_DSP_CHANNELS, ¶ms->channels) < 0) + return -errno; + + if (params->channels != val) + return -EINVAL; + pcm->channels = params->channels; + return 0; +} + +int +__snd_pcm_hw_params_set_channels_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val) +{ + dbg_printf2 ("snd_pcm_hw_params_set_channels_near(%d)\n", *val); + + params->channels = *val; + + if (ioctl (pcm->fd, SNDCTL_DSP_CHANNELS, ¶ms->channels) < 0) + return -errno; + + *val = params->channels; + pcm->channels = params->channels; + return 0; +} + +/** + * \brief Restrict a configuration space to contain only one format + * \param pcm PCM handle + * \param params Configuration space + * \param format format + * \return 0 otherwise a negative error code + */ +int +snd_pcm_hw_params_set_format (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + snd_pcm_format_t format) +{ + int err = -EINVAL; + int tmp; + + dbg_printf2 ("snd_pcm_hw_params_set_format(%d)\n", format); + if (format == SND_PCM_FORMAT_U8) + { + params->fmt = format; + params->oss_fmt = AFMT_U8; + params->sample_bits = 8; + err = 0; + } + + if (format == SND_PCM_FORMAT_S16_LE) + { + params->fmt = format; + params->oss_fmt = AFMT_S16_LE; + params->sample_bits = 16; + err = 0; + } + + if (err < 0) + return err; + + tmp = params->oss_fmt; + if (ioctl (pcm->fd, SNDCTL_DSP_SETFMT, &tmp) < 0) + return -errno; + + if (tmp != params->oss_fmt) + return -EINVAL; + + return 0; +} + +/** + * \brief Extract format from a configuration space + * \param params Configuration space + * \param format returned format + * \return format otherwise a negative error code if not exactly one is present + */ +int INTERNAL (snd_pcm_hw_params_get_format) (const snd_pcm_hw_params_t * + params, + snd_pcm_format_t * format) +{ + dbg_printf ("snd_pcm_hw_params_get_format(params=%x)\n", params); + + *format = params->fmt; +} + +/** + * \brief Verify if a format is available inside a configuration space for a PCM * \param pcm PCM handle + * \param params Configuration space + * \param format format + * \return 0 if available a negative error code otherwise + */ +int +snd_pcm_hw_params_test_format (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + snd_pcm_format_t format) +{ + dbg_printf2 ("snd_pcm_hw_params_test_format()\n"); + + if (format == SND_PCM_FORMAT_U8) + return 0; + if (format == SND_PCM_FORMAT_S16_LE) + return 0; + + return -EINVAL; +} + +/** + * \brief Restrict a configuration space to contain only one periods count + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate periods per buffer + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if configuration space would become empty + * + * Wanted exact value is <,=,> val following dir (-1,0,1) + */ +int +snd_pcm_hw_params_set_periods (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + unsigned int val, int dir) +{ + dbg_printf + ("snd_pcm_hw_params_set_periods(pcm=%x, params=%x, val=%d, dir=%d)\n", + pcm, params, val, dir); + pcm->periods = val; + + return 0; +} + +/** + * \brief Restrict a configuration space to contain only integer periods counts + * \param pcm PCM handle + * \param params Configuration space + * \return 0 otherwise a negative error code if configuration space would become empty + */ +int +snd_pcm_hw_params_set_periods_integer (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params) +{ + dbg_printf ("snd_pcm_hw_params_set_periods_integer()\n"); + return 0; +} + +/** * \brief Restrict a configuration space with a minimum periods count + * \param pcm PCM handle * \param params Configuration space + * \param val approximate minimum periods per buffer (on return filled with actual minimum) + * \param dir Sub unit direction (on return filled with actual direction) * \return 0 otherwise a negative error code if configuration space would become + empty * + * Wanted/actual exact minimum is <,=,> val following dir (-1,0,1) */ +int +snd_pcm_hw_params_set_periods_min (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_set_periods_min(%x, %x)\n", val, dir); + return 0; +} + +/** + * \brief Restrict a configuration space with a maximum periods count + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate maximum periods per buffer (on return filled with actual maximum) + * \param dir Sub unit direction (on return filled with actual direction) + * \return 0 otherwise a negative error code if configuration space would become empty + * + * Wanted/actual exact maximum is <,=,> val following dir (-1,0,1) + */ +int +snd_pcm_hw_params_set_periods_max (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_set_periods_max(%x, %x)\n", val, dir); + return 0; + return -EINVAL; +} + +/** + * \brief Restrict a configuration space to contain only one rate + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate rate + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if configuration space would become empty + * + * Wanted exact value is <,=,> val following dir (-1,0,1) + */ +int +snd_pcm_hw_params_set_rate (snd_pcm_t * pcm, snd_pcm_hw_params_t * params, + unsigned int val, int dir) +{ + dbg_printf2 ("snd_pcm_hw_params_set_rate(%d, %d)\n", val, dir); + + params->speed = val; + + if (ioctl (pcm->fd, SNDCTL_DSP_SPEED, ¶ms->speed) < 0) + return -errno; + + if (val != params->speed) + return -EINVAL; + + return 0; +} + +/** + * \brief Extract rate from a configuration space + * \param params Configuration space + * \param val Returned approximate rate + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if not exactly one is present + * + * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) + */ +int INTERNAL (snd_pcm_hw_params_get_rate) (const snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_rate(params=%x)=%d\n", + params, params->speed); + + *val = params->speed; + *dir = 0; + return 0; +} + +/** + * \brief Restrict a configuration space to have rate nearest to a target + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target rate / returned approximate set rate + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int +__snd_pcm_hw_params_set_rate_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + dbg_printf2 ("snd_pcm_hw_params_set_rate_near(%d)\n", *val); + + params->speed = *val; + + if (ioctl (pcm->fd, SNDCTL_DSP_SPEED, ¶ms->speed) < 0) + return -errno; + + *val = params->speed; + return 0; +} + +/** + * \brief get size of #snd_pcm_hw_params_t + * \return size in bytes + */ +size_t +snd_pcm_hw_params_sizeof () +{ + return sizeof (snd_pcm_hw_params_t); +} + +/** + * \brief set nonblock mode + * \param pcm PCM handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_nonblock (snd_pcm_t * pcm, int nonblock) +{ + dbg_printf2 ("snd_pcm_nonblock(%d)\n", nonblock); + + if (nonblock == 0) + return 0; + + return -EINVAL; +} + +/** + * \brief Prepare PCM for use + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_prepare (snd_pcm_t * pcm) +{ + dbg_printf2 ("snd_pcm_prepare()\n"); + return 0; +} + +/** + * \brief Read interleaved frames from a PCM + * \param pcm PCM handle + * \param buffer frames containing buffer + * \param size frames to be written + * \return a positive number of frames actually read otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SN +D_PCM_STATE_RUNNING) + * \retval -EPIPE an overrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting f +or an application recovery) + * + * If the blocking behaviour was selected, then routine waits until + * all requested bytes are filled. The count of bytes can be less only + * if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_readi (snd_pcm_t * pcm, void *buffer, snd_pcm_uframes_t size) +{ + int l; + + dbg_printf3 ("snd_pcm_readi(%d)\n", pcm->fd); + + pcm->state = SND_PCM_STATE_RUNNING; + + l = size * pcm->frame_size; + + if ((l = read (pcm->fd, buffer, l)) == -1) + return -errno; + return l / pcm->frame_size; +} + +/** \brief Remove PCM hardware configuration and free associated resources + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_hw_free (snd_pcm_t * pcm) +{ + dbg_printf ("snd_pcm_hw_free()\n"); + + return 0; +} + +/** + * \brief Remove a PCM from a linked group + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_unlink (snd_pcm_t * pcm) +{ + dbg_printf ("snd_pcm_unlink()\n"); + + return 0; +} + +/** + * \brief Extract tick time from a configuration space + * \param params Configuration space + * \param val Returned approximate tick duration in us + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if not exactly one is present + * + * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) + */ +int INTERNAL (snd_pcm_hw_params_get_tick_time) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_tick_time()\n"); + + *val = 1; + *dir = 0; + return 0; +} + +/** + * \brief Write interleaved frames to a PCM + * \param pcm PCM handle + * \param buffer frames containing buffer + * \param size frames to be written + * \return a positive number of frames actually written otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SN +D_PCM_STATE_RUNNING) + * \retval -EPIPE an underrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting f +or an application recovery) + * + * If the blocking behaviour is selected, then routine waits until + * all requested bytes are played or put to the playback ring buffer. + * The count of bytes can be less only if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_writei (snd_pcm_t * pcm, const void *buffer, snd_pcm_uframes_t size) +{ + int l; + + dbg_printf3 ("snd_pcm_writei(%d)\n", size); + + pcm->state = SND_PCM_STATE_RUNNING; + + l = size * pcm->frame_size; + + if ((l = write (pcm->fd, buffer, l)) == -1) + return -errno; + return l / pcm->frame_size; +} + +/** + * \brief Resume from suspend, no samples are lost + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + * \retval -EAGAIN resume can't be proceed immediately (audio hardware is probab +ly still suspended) + * \retval -ENOSYS hardware doesn't support this feature + * + * This function can be used when the stream is in the suspend state + * to do the fine resume from this state. Not all hardware supports + * this feature, when an -ENOSYS error is returned, use the \link ::snd_pcm_prep +are() \endlink + * function to recovery. + */ +int +snd_pcm_resume (snd_pcm_t * pcm) +{ + dbg_printf ("snd_pcm_resume()\n"); + return -EINVAL; +} + +/** + * \brief allocate an invalid #snd_pcm_status_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_pcm_status_malloc (snd_pcm_status_t ** ptr) +{ + assert (ptr); + *ptr = calloc (1, sizeof (snd_pcm_status_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_pcm_status_t + * \param pointer to object to free + */ +void +snd_pcm_status_free (snd_pcm_status_t * obj) +{ + free (obj); +} + + +/** + * \brief Obtain status (runtime) information for PCM handle + * \param pcm PCM handle + * \param status Status container + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_status (snd_pcm_t * pcm, snd_pcm_status_t * status) +{ + dbg_printf ("snd_pcm_status()\n"); + return -EINVAL; +} + +/** + * \brief Get state from a PCM status container (see #snd_pcm_state) + * \return PCM state + */ +snd_pcm_state_t +snd_pcm_status_get_state (const snd_pcm_status_t * obj) +{ + dbg_printf ("snd_pcm_status_get_state()\n"); + return -EINVAL; +} + +/** + * \brief Get number of frames available from a PCM status container (see #snd_pcm_avail_update) + * \return Number of frames ready to be read/written + */ +snd_pcm_uframes_t +snd_pcm_status_get_avail (const snd_pcm_status_t * obj) +{ + dbg_printf ("snd_pcm_status_get_avail()\n"); + + return 1; +} + +/** + * \brief get size of #snd_pcm_status_t + * \return size in bytes + */ +size_t +snd_pcm_status_sizeof () +{ + return sizeof (snd_pcm_status_t); +} + +/** + * \brief Dump PCM info + * \param pcm PCM handle + * \param out Output handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_dump (snd_pcm_t * pcm, snd_output_t * out) +{ + dbg_printf ("snd_pcm_dump()\n"); + return 0; +} + +void +snd_pcm_hw_params_copy (snd_pcm_hw_params_t * dst, + const snd_pcm_hw_params_t * src) +{ + memcpy (dst, src, sizeof (*dst)); +} + +void +snd_pcm_hw_params_free (snd_pcm_hw_params_t * p) +{ + free (p); +} + +int +snd_pcm_hw_params_can_mmap_sample_resolution (const snd_pcm_hw_params_t * + params) +{ + return 0; +} + +int +snd_pcm_hw_params_can_overrange (const snd_pcm_hw_params_t * params) +{ + return 0; +} + +int +snd_pcm_hw_params_can_pause (const snd_pcm_hw_params_t * params) +{ + return 0; +} + +int +snd_pcm_hw_params_can_resume (const snd_pcm_hw_params_t * params) +{ + return 0; +} + +int +snd_pcm_hw_params_can_sync_start (const snd_pcm_hw_params_t * params) +{ + return 1; +} + +int +snd_pcm_hw_params_get_channels (const snd_pcm_hw_params_t * params, + unsigned int *val) +{ + *val = params->channels; + return 0; +} + +int INTERNAL (snd_pcm_hw_params_get_channels_min) (const snd_pcm_hw_params_t * + params, unsigned int *val) +{ + *val = params->info->min_channels; + return 0; +} + +int INTERNAL (snd_pcm_hw_params_get_channels_max) (const snd_pcm_hw_params_t * + params, unsigned int *val) +{ + *val = params->info->max_channels; + return 0; +} + +int INTERNAL (snd_pcm_hw_params_get_rate_min) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + *val = params->info->min_rate; + return 0; +} + +int INTERNAL (snd_pcm_hw_params_get_rate_max) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + *val = params->info->max_rate; + return 0; +} + +// int snd_pcm_hw_params_get_(const snd_pcm_hw_params_t *params, unsigned int *val) { return 0;} + +/** + * \brief get name of PCM sample format + * \param format PCM sample format + * \return ascii name of PCM sample format + */ +const char * +snd_pcm_format_name (snd_pcm_format_t format) +{ + if (format > SND_PCM_FORMAT_LAST) + return NULL; + return snd_pcm_format_names[format]; +} + +/** + * \brief Return PCM state + * \param pcm PCM handle + * \return PCM state #snd_pcm_state_t of given PCM handle + * + * This is a faster way to obtain only the PCM state without calling + * \link ::snd_pcm_status() \endlink. + */ +snd_pcm_state_t +snd_pcm_state (snd_pcm_t * pcm) +{ + dbg_printf3 ("snd_pcm_state()\n"); + return pcm->state; +} + +/** + * \brief allocate an invalid #snd_pcm_info_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_pcm_info_malloc (snd_pcm_info_t ** ptr) +{ + assert (ptr); + *ptr = calloc (1, sizeof (snd_pcm_info_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_pcm_info_t + * \param pointer to object to free + */ +void +snd_pcm_info_free (snd_pcm_info_t * obj) +{ + free (obj); +} + + +/** + * \brief Obtain general (static) information for PCM handle + * \param pcm PCM handle + * \param info Information container + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_info (snd_pcm_t * pcm, snd_pcm_info_t * info) +{ + dbg_printf ("snd_pcm_info()\n"); + memset (info, 0, sizeof (*info)); + info->ainfo = &pcm->info; + + return 0; +} + +/** + * \brief Get card from a PCM info container + * \param obj PCM info container + * \return card number otherwise a negative error code if not associable to a card + */ +int +snd_pcm_info_get_card (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_card()\n"); + return obj->ainfo->card_number; +} + +/** + * \brief Get device from a PCM info container + * \param obj PCM info container + * \return device number + */ +unsigned int +snd_pcm_info_get_device (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_device()\n"); + return obj->ainfo->dev; +} + +/** + * \brief Get name from a PCM info container + * \param obj PCM info container + * \return name of PCM + */ +const char * +snd_pcm_info_get_name (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_name()\n"); + + return obj->ainfo->name; +} + +/** + * \brief Get subdevice from a PCM info container + * \param obj PCM info container + * \return subdevice number + */ +unsigned int +snd_pcm_info_get_subdevice (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_subdevice()\n"); + return 0; +} + +/** + * \brief Set wanted device inside a PCM info container (see #snd_ctl_pcm_info) + * \param obj PCM info container + * \param val Device number + */ +void +snd_pcm_info_set_device (snd_pcm_info_t * obj, unsigned int val) +{ + dbg_printf ("snd_pcm_info_set_device()\n"); +} + +/** + * \brief Set wanted subdevice inside a PCM info container (see #snd_ctl_pcm_info) + * \param obj PCM info container + * \param val Subdevice number + */ +void +snd_pcm_info_set_subdevice (snd_pcm_info_t * obj, unsigned int val) +{ + dbg_printf ("snd_pcm_info_set_subdevice()\n"); +} + +void +snd_pcm_info_set_stream (snd_pcm_info_t * obj, snd_pcm_stream_t val) +{ + dbg_printf ("snd_pcm_info_set_stream()\n"); +} + +/** + * \brief get size of #snd_pcm_info_t + * \return size in bytes + */ +size_t +snd_pcm_info_sizeof () +{ + return sizeof (snd_pcm_info_t); +} + + +/** + * \brief Return number of frames ready to be read/written + * \param pcm PCM handle + * \return a positive number of frames ready otherwise a negative + * error code + * + * On capture does all the actions needed to transport to application + * level all the ready frames across underlying layers. + * + * Using of this function is useless for the standard read/write + * operations. Use it only for mmap access. See to #snd_pcm_delay. + */ +snd_pcm_sframes_t +snd_pcm_avail_update (snd_pcm_t * pcm) +{ + audio_buf_info bi; + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + { + if (ioctl (pcm->fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) + return -errno; + } + else + { + if (ioctl (pcm->fd, SNDCTL_DSP_GETISPACE, &bi) == -1) + return -errno; + } + + dbg_printf3 ("snd_pcm_avail_update(pcm=%x)=%d\n", pcm, + bi.bytes / pcm->frame_size); + return bi.bytes / pcm->frame_size; +} + +/** + * \brief Obtain delay for a running PCM handle + * \param pcm PCM handle + * \param delayp Returned delay in frames + * \return 0 on success otherwise a negative error code + * + * Delay is distance between current application frame position and + * sound frame position. + * It's positive and less than buffer size in normal situation, + * negative on playback underrun and greater than buffer size on + * capture overrun. + * + * Note this function does not update the actual r/w pointer + * for applications. The function #snd_pcm_avail_update() + * have to be called before any begin+commit operation. + */ +int +snd_pcm_delay (snd_pcm_t * pcm, snd_pcm_sframes_t * delayp) +{ + int tmp; + + if (ioctl (pcm->fd, SNDCTL_DSP_GETODELAY, &tmp) < 0) + return -errno; + + *delayp = tmp / pcm->frame_size; + return 0; +} + +/** + * \brief Return bits needed to store a PCM sample + * \param format Sample format + * \return bits per sample, a negative error code if not applicable + */ +int +snd_pcm_format_physical_width (snd_pcm_format_t format) +{ + switch (format) + { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return 24; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + +/** + * \brief Return bytes needed to store a quantity of PCM sample + * \param format Sample format + * \param samples Samples count + * \return bytes needed, a negative error code if not integer or unknown + */ +ssize_t +snd_pcm_format_size (snd_pcm_format_t format, size_t samples) +{ + switch (format) + { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return samples; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return samples * 2; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return samples * 3; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return samples * 8; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return samples * 4; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return samples; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + if (samples & 1) + return -EINVAL; + return samples / 2; + default: + assert (0); + return -EINVAL; + } +} + +/** + * \brief Dump a PCM hardware configuration space + * \param params Configuration space + * \param out Output handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_hw_params_dump (snd_pcm_hw_params_t * params, snd_output_t * out) +{ + return 0; +} + +/** + * \brief Extract buffer size from a configuration space + * \param params Configuration space + * \param val Returned buffer size in frames + * \return 0 otherwise a negative error code if not exactly one is present + */ +int +__snd_pcm_hw_params_get_buffer_size (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val) +{ + *val = 65536; + + return 0; +} + +int +snd_pcm_hw_params_get_buffer_size_min (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val) +{ + *val = 65536; + + return 0; +} + +int +snd_pcm_hw_params_get_buffer_size_max (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val) +{ + *val = 65536; + + return 0; +} + +/** + * \brief Extract access type from a configuration space + * \param params Configuration space + * \param access Returned value + * \return access type otherwise a negative error code if not exactly one is present + */ +int INTERNAL (snd_pcm_hw_params_get_access) (const snd_pcm_hw_params_t * + params, + snd_pcm_access_t * access) +{ + dbg_printf ("snd_pcm_hw_params_get_access()\n"), *access = params->access; + + return 0; +} + +/** + * \brief Extract period size from a configuration space + * \param params Configuration space + * \param val Returned approximate period size in frames + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if not exactly one is present + * + * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) + */ +int +__snd_pcm_hw_params_get_period_size (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_period_size(params=%x)=%d\n", + params, params->pcm->period_size); + *val = params->pcm->period_size; + if (dir) *dir = 0; + return 0; +} + +int +snd_pcm_hw_params_get_period_size_min (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_period_size_min(params=%x)=%d\n", + params, params->pcm->period_size); + *val = params->pcm->period_size; + if (dir) *dir = 0; + return 0; +} + +int +snd_pcm_hw_params_get_period_size_max (const snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val, int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_period_size_min(params=%x)=%d\n", + params, params->pcm->period_size); + *val = params->pcm->period_size; + if (dir) *dir = 0; + return 0; +} + +/** + * \brief Extract periods from a configuration space + * \param params Configuration space + * \param val approximate periods per buffer + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if not exactly one is present + * + * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) + */ +int INTERNAL (snd_pcm_hw_params_get_periods) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_periods(params=%x)=%d\n", + params, params->pcm->periods); + + *val = params->pcm->periods; + + return 0; +} + +/** + * \brief Restrict a configuration space to have period size nearest to a target * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target period size in frames / returned chosen approximate target period size + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int +__snd_pcm_hw_params_set_period_size_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_uframes_t * val, int *dir) +{ + dbg_printf + ("snd_pcm_hw_params_set_period_size_near(pcm=%x, params=%x, val=%d, dir=%d)\n", + pcm, params, *val, dir?*dir:-1); + pcm->period_size = *val; + return 0; +} + +/** + * \brief Restrict a configuration space to have buffer time nearest to a target * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target buffer duration in us / returned chosen approximate target buffer duration + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int +__snd_pcm_hw_params_set_period_time_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + dbg_printf2 ("snd_pcm_hw_params_set_period_time_near()\n"); + return 0; +} + +/** + * \brief Application request to access a portion of direct (mmap) area + * \param pcm PCM handle + * \param areas Returned mmap channel areas + * \param offset Returned mmap area offset in area steps (== frames) + * \param frames mmap area portion size in frames (wanted on entry, contiguous available on exit) + * \return 0 on success otherwise a negative error code + * + * It is necessary to call the snd_pcm_avail_update() function directly before + * this call. Otherwise, this function can return a wrong count of available frames. + * + * The function should be called before a sample-direct area can be accessed. + * The resulting size parameter is always less or equal to the input count of frames + * and can be zero, if no frames can be processed (the ring buffer is full). + * + * See the snd_pcm_mmap_commit() function to finish the frame processing in + * the direct areas. + */ +int +snd_pcm_mmap_begin (snd_pcm_t * pcm, + const snd_pcm_channel_area_t ** areas, + snd_pcm_uframes_t * offset, snd_pcm_uframes_t * frames) +{ + dbg_printf2 ("snd_pcm_mmap_begin(frames=%d)\n", *frames); + if (pcm->area == NULL) + { + pcm->area = malloc (sizeof (*pcm->area) * pcm->channels); + pcm->area[0].addr = malloc (65536); + pcm->area[0].first = 0; + pcm->area[0].step = pcm->frame_size * 8; + pcm->area[1].addr = pcm->area[0].addr + pcm->sample_size; + pcm->area[1].first = 0; + pcm->area[1].step = pcm->frame_size * 8; + } + + *areas = pcm->area; + *offset = 0; + if (*frames * pcm->frame_size > 65536) + *frames = 65536 / pcm->frame_size; + return 0; +} + +/** + * \brief Application has completed the access to area requested with #snd_pcm_mmap_begin + * \param pcm PCM handle + * \param offset area offset in area steps (== frames) + * \param size area portion size in frames + * \return count of transferred frames otherwise a negative error code + * + * You should pass this function the offset value that + * snd_pcm_mmap_begin() returned. The frames parameter should hold the + * number of frames you have written or read to/from the audio + * buffer. The frames parameter must never exceed the contiguous frames + * count that snd_pcm_mmap_begin() returned. Each call to snd_pcm_mmap_begin() + * must be followed by a call to snd_pcm_mmap_commit(). + * + * Example: +\code + double phase = 0; + const snd_pcm_area_t *areas; + snd_pcm_sframes_t avail, size, commitres; + snd_pcm_uframes_t offset, frames; + int err; + + avail = snd_pcm_avail_update(pcm); + if (avail < 0) + error(avail); + // at this point, we can transfer at least 'avail' frames + + // we want to process frames in chunks (period_size) + if (avail < period_size) + goto _skip; + size = period_size; + // it is possible that contiguous areas are smaller, thus we use a loop + while (size > 0) { + frames = size; + + err = snd_pcm_mmap_begin(pcm_handle, &areas, &offset, &frames); + if (err < 0) + error(err); + // this function fills the areas from offset with count of frames + generate_sine(areas, offset, frames, &phase); + commitres = snd_pcm_mmap_commit(pcm_handle, offset, frames); + if (commitres < 0 || commitres != frames) + error(commitres >= 0 ? -EPIPE : commitres); + + size -= frames; + } + _skip: +\endcode + * + * Look to the \ref example_test_pcm "Sine-wave generator" example + * for more details about the generate_sine function. + */ +snd_pcm_sframes_t +snd_pcm_mmap_commit (snd_pcm_t * pcm, + snd_pcm_uframes_t offset, snd_pcm_uframes_t frames) +{ + dbg_printf2 ("snd_pcm_mmap_commit(frames=%d, offs=%d)\n", frames, offset); + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + return snd_pcm_writei (pcm, pcm->area[0].addr, frames); + + if (pcm->stream == SND_PCM_STREAM_CAPTURE) + return snd_pcm_readi (pcm, pcm->area[0].addr, frames); + + return -EFAULT; +} + +/** + * \brief Restrict a configuration space to have buffer time nearest to a target * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target buffer duration in us / returned chosen approximate target buffer duration + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int +__snd_pcm_hw_params_set_buffer_time_near (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + unsigned int *val, int *dir) +{ + *val = 10; + dbg_printf2 ("snd_pcm_hw_params_set_buffer_time_near()\n"); + return 0; +} + +/** + * \brief Pause/resume PCM + * \param pcm PCM handle + * \param pause 0 = resume, 1 = pause + * \return 0 on success otherwise a negative error code + * + * Note that this function works only on the hardware which supports + * pause feature. You can check it via \link ::snd_pcm_hw_params_can_pause() \endlink + * function. + */ +int +snd_pcm_pause (snd_pcm_t * pcm, int enable) +{ + dbg_printf ("snd_pcm_pause()\n"); + return -EINVAL; +} + +/** + * \brief Start a PCM + * \param pcm PCM handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_start (snd_pcm_t * pcm) +{ + dbg_printf ("snd_pcm_start()\n"); + return 0; +} + +/** + * \brief Convert samples in bytes for a PCM + * \param pcm PCM handle + * \param samples quantity in samples + * \return quantity expressed in bytes + */ +ssize_t +snd_pcm_samples_to_bytes (snd_pcm_t * pcm, long samples) +{ + dbg_printf2 ("snd_pcm_samples_to_bytes(%d)=%d\n", samples, + samples * pcm->sample_size); + return samples * pcm->sample_size; +} + + +/** + * \brief Dump status + * \param status Status container + * \param out Output handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_status_dump (snd_pcm_status_t * status, snd_output_t * out) +{ + return 0; +} + +/** \brief Install PCM software configuration defined by params + * \param pcm PCM handle + * \param params Configuration container + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_sw_params (snd_pcm_t * pcm, snd_pcm_sw_params_t * params) +{ + dbg_printf2 ("snd_pcm_sw_params()\n"); + // NOP + return 0; +} + +/** + * \brief Return current software configuration for a PCM + * \param pcm PCM handle + * \param params Software configuration container + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_sw_params_current (snd_pcm_t * pcm, snd_pcm_sw_params_t * params) +{ + dbg_printf2 ("snd_pcm_sw_params_current()\n"); + // NOP + return 0; +} + +/** + * \brief Dump a software configuration + * \param params Software configuration container + * \param out Output handle + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_sw_params_dump (snd_pcm_sw_params_t * params, snd_output_t * out) +{ + return 0; +} + +/** + * \brief Set start threshold inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Start threshold in frames + * \return 0 otherwise a negative error code + * + * PCM is automatically started when playback frames available to PCM + * are >= threshold or when requested capture frames are >= threshold + */ +int +snd_pcm_sw_params_set_start_threshold (snd_pcm_t * pcm, + snd_pcm_sw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf2 ("snd_pcm_sw_params_set_start_threshold(%d)\n", val); + // NOP + return 0; +} + +/** + * \brief get size of #snd_pcm_sw_params_t + * \return size in bytes + */ +size_t +snd_pcm_sw_params_sizeof () +{ + return sizeof (snd_pcm_sw_params_t); +} + +/** + * \brief Restrict a configuration space to have periods count nearest to a target + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate target periods per buffer / returned chosen approximate target periods per buffer + * \return 0 otherwise a negative error code if configuration space is empty + * + * target/chosen exact value is <,=,> val following dir (-1,0,1) + */ +int INTERNAL (snd_pcm_hw_params_set_periods_near) (snd_pcm_t * pcm, + snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + dbg_printf + ("snd_pcm_hw_params_set_periods_near(pcm=%x, params=%x, val=%d, dir=%d)\n"); + pcm->periods = *val; + return 0; +} + +/** + * \brief get poll descriptors + * \param pcm PCM handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + * + * This function fills the given poll descriptor structs for the specified + * PCM handle. The poll desctiptor array should have the size returned by + * \link ::snd_pcm_poll_descriptors_count() \endlink function. + * + * The result is intended for direct use with the poll() syscall. + * + * For reading the returned events of poll descriptor after poll() system + * call, use \link ::snd_pcm_poll_descriptors_revents() \endlink function. + * The field values in pollfd structs may be bogus regarding the stream + * direction from the application perspective (POLLIN might not imply read + * direction and POLLOUT might not imply write), but + * the \link ::snd_pcm_poll_descriptors_revents() \endlink function + * does the right "demangling". + * + * You can use output from this function as arguments for the select() + * syscall, too. + */ +int +snd_pcm_poll_descriptors (snd_pcm_t * pcm, struct pollfd *pfds, + unsigned int space) +{ + dbg_printf2 ("snd_pcm_poll_descriptors(pcm=%x)\n", pcm); + + pfds->fd = pcm->fd; + pfds->events = 0; + pfds->revents = 0; + + if (pcm->oss_mode == O_WRONLY || pcm->oss_mode == O_RDWR) + pfds->events = POLLOUT; + if (pcm->oss_mode == O_RDONLY || pcm->oss_mode == O_RDWR) + pfds->events = POLLIN; + + return 1; +} + +/** + * \brief get count of poll descriptors for PCM handle + * \param pcm PCM handle + * \return count of poll descriptors + */ +int +snd_pcm_poll_descriptors_count (snd_pcm_t * pcm) +{ + dbg_printf ("snd_pcm_poll_descriptors_count()\n"); + + return 1; +} + +/** + * \brief get returned events from poll descriptors + * \param pcm PCM handle + * \param pfds array of poll descriptors + * \param nfds count of poll descriptors + * \param revents returned events + * \return zero if success, otherwise a negative error code + * + * This function does "demangling" of the revents mask returned from + * the poll() syscall to correct semantics (POLLIN = read, POLLOUT = write). + * + * Note: The null event also exists. Even if poll() or select() + * syscall returned that some events are waiting, this function might + * return empty set of events. In this case, application should + * do next event waiting using poll() or select(). + */ +int +snd_pcm_poll_descriptors_revents (snd_pcm_t * pcm, struct pollfd *pfds, + unsigned int nfds, unsigned short *revents) +{ + dbg_printf3 ("snd_pcm_poll_descriptors_revents()\n"); + + *revents = pfds->revents; + return 0; +} + +/** + * \brief allocate an invalid #snd_pcm_hw_params_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_pcm_hw_params_malloc (snd_pcm_hw_params_t ** ptr) +{ + *ptr = calloc (1, sizeof (snd_pcm_hw_params_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_pcm_sw_params_t + * \param pointer to object to free + */ +void +snd_pcm_sw_params_free (snd_pcm_sw_params_t * obj) +{ + free (obj); +} + +/** + * \brief Write non interleaved frames to a PCM + * \param pcm PCM handle + * \param bufs frames containing buffers (one for each channel) + * \param size frames to be written + * \return a positive number of frames actually written otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an underrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour is selected, then routine waits until + * all requested bytes are played or put to the playback ring buffer. + * The count of bytes can be less only if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_writen (snd_pcm_t * pcm, void **bufs, snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_writen()\n"); + // Not supported + return -EINVAL; +} + +/** + * \brief Read non interleaved frames to a PCM + * \param pcm PCM handle + * \param bufs frames containing buffers (one for each channel) + * \param size frames to be written + * \return a positive number of frames actually read otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an overrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour was selected, then routine waits until + * all requested bytes are filled. The count of bytes can be less only + * if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_readn (snd_pcm_t * pcm, void **bufs, snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_readn()\n"); + // Not supported + return -EINVAL; +} + +/** + * \brief Get trigger timestamp from a PCM status container + * \param ptr Pointer to returned timestamp + */ +void +snd_pcm_status_get_trigger_tstamp (const snd_pcm_status_t * obj, + snd_timestamp_t * ptr) +{ + dbg_printf ("snd_pcm_status_get_trigger_tstamp()\n"); + ptr->tv_sec = 0; + ptr->tv_usec = 0; +} + +/** + * \brief Set xfer align inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Chunk size (frames are attempted to be transferred in chunks) + * \return 0 otherwise a negative error code + */ +int +snd_pcm_sw_params_set_xfer_align (snd_pcm_t * pcm, + snd_pcm_sw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_sw_params_set_xfer_align(%d)\n", val); + // TODO + return 0; +} + +/** + * \brief Get xfer align from a software configuration container + * \param params Software configuration container + * \param val returned chunk size (frames are attempted to be transferred in chunks) + * \param 0 otherwise a negative error code + */ +int INTERNAL (snd_pcm_sw_params_get_xfer_align) (const snd_pcm_sw_params_t * + params, + snd_pcm_uframes_t * val) +{ + dbg_printf ("snd_pcm_sw_params_get_xfer_align()\n"); + + return 8; +} + +/** + * \brief Set avail min inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Minimum avail frames to consider PCM ready + * \return 0 otherwise a negative error code + * + * Note: This is similar to setting an OSS wakeup point. The valid + * values for 'val' are determined by the specific hardware. Most PC + * sound cards can only accept power of 2 frame counts (i.e. 512, + * 1024, 2048). You cannot use this as a high resolution timer - it + * is limited to how often the sound card hardware raises an + * interrupt. Note that you can greatly improve the reponses using + * \ref snd_pcm_sw_params_set_sleep_min where another timing source + * is used. + */ +int +snd_pcm_sw_params_set_avail_min (snd_pcm_t * pcm ATTRIBUTE_UNUSED, + snd_pcm_sw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_sw_params_set_avail_min()\n"); + // NOP + return 0; +} + +/** + * \brief Set minimum number of ticks to sleep inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Minimum ticks to sleep or 0 to disable the use of tick timer + * \return 0 otherwise a negative error code + */ +int +snd_pcm_sw_params_set_sleep_min (snd_pcm_t * pcm ATTRIBUTE_UNUSED, + snd_pcm_sw_params_t * params, + unsigned int val) +{ + dbg_printf ("snd_pcm_sw_params_set_sleep_min()\n"); + // NOP + + return 0; +} + +/** + * \brief Wait for a PCM to become ready + * \param pcm PCM handle + * \param timeout maximum time in milliseconds to wait + * \return a positive value on success otherwise a negative error code + * (-EPIPE for the xrun and -ESTRPIPE for the suspended status, + * others for general errors) + * \retval 0 timeout occurred + * \retval 1 PCM stream is ready for I/O + */ +int +snd_pcm_wait (snd_pcm_t * pcm, int timeout) +{ + fd_set fds, *readfds = NULL, *writefds = NULL; + struct timeval wait, *w = NULL; + + dbg_printf ("snd_pcm_wait(%d)\n", timeout); + + FD_ZERO (&fds); + FD_SET (pcm->fd, &fds); + + if (timeout > 0) + { + wait.tv_sec = timeout / 1000; + wait.tv_usec = (timeout % 1000) * 1000; + w = &wait; + } + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + writefds = &fds; + else + readfds = &fds; + + dbg_printf ("select(%d, %x, %x, NULL, %x)\n", + pcm->fd + 1, readfds, writefds, w); + if (select (pcm->fd + 1, readfds, writefds, NULL, w) == -1) + return -errno; + + return FD_ISSET (pcm->fd, &fds); +} + +/** + * \brief allocate an invalid #snd_pcm_sw_params_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_pcm_sw_params_malloc (snd_pcm_sw_params_t ** ptr) +{ + *ptr = calloc (1, sizeof (snd_pcm_sw_params_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief Extract period time from a configuration space + * \param params Configuration space + * \param val Returned approximate period duration in us + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if not exactly one is present + * + * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) + */ +int INTERNAL (snd_pcm_hw_params_get_period_time) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_period_time()\n"); + // TODO + return -EINVAL; +} + +int INTERNAL (snd_pcm_hw_params_get_buffer_time) (const snd_pcm_hw_params_t * + params, unsigned int *val, + int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_buffer_time()\n"); + // What's this? + return -EINVAL; +} + +struct _snd_mask +{ + unsigned int bits[4]; +}; + +struct _snd_pcm_access_mask +{ + unsigned int bits[4]; +}; + +#define MASK_OFS(v) (v/32) +#define MASK_BIT(v) (1<<(v%32)) + +/** + * \brief reset all bits in a #snd_pcm_access_mask_t + * \param mask pointer to mask + */ +void +snd_pcm_access_mask_none (snd_pcm_access_mask_t * mask) +{ + dbg_printf ("snd_pcm_access_mask_none()\n"); + memset (mask, 0, sizeof (*mask)); +} + +/** + * \brief make an access type present in a #snd_pcm_access_mask_t + * \param mask pointer to mask + * \param val access type + */ +void +snd_pcm_access_mask_set (snd_pcm_access_mask_t * mask, snd_pcm_access_t val) +{ + dbg_printf ("snd_pcm_access_mask_t(%d)\n", val); + mask->bits[MASK_OFS (val)] |= MASK_BIT (val); +} + +/** + * \brief get size of #snd_pcm_access_mask_t + * \return size in bytes + */ +size_t +snd_pcm_access_mask_sizeof () +{ + return sizeof (snd_pcm_access_mask_t); +} + +/** + * \brief Restrict a configuration space to contain only a set of access types + * \param pcm PCM handle + * \param params Configuration space + * \param mask Access mask + * \return 0 otherwise a negative error code + */ +int +snd_pcm_hw_params_set_access_mask (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_access_mask_t * mask) +{ + dbg_printf ("snd_pcm_hw_params_set_access_mask()\n"); + // NOP + return 0; +} + +/** + * \brief Read interleaved frames from a PCM using direct buffer (mmap) + * \param pcm PCM handle + * \param buffer frames containing buffer + * \param size frames to be written + * \return a positive number of frames actually read otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an overrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour was selected, then routine waits until + * all requested bytes are filled. The count of bytes can be less only + * if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_mmap_readi (snd_pcm_t * pcm, void *buffer, snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_mmap_readi()\n"); + return snd_pcm_readi (pcm, buffer, size); +} + +/** + * \brief Read non interleaved frames to a PCM using direct buffer (mmap) + * \param pcm PCM handle + * \param bufs frames containing buffers (one for each channel) + * \param size frames to be written + * \return a positive number of frames actually read otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an overrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour was selected, then routine waits until + * all requested bytes are filled. The count of bytes can be less only + * if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_mmap_readn (snd_pcm_t * pcm, void **bufs, snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_mmap_readn()\n"); + return snd_pcm_readn (pcm, bufs, size); +} + +/** + * \brief Write interleaved frames to a PCM using direct buffer (mmap) + * \param pcm PCM handle + * \param buffer frames containing buffer + * \param size frames to be written + * \return a positive number of frames actually written otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an underrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour is selected, then routine waits until + * all requested bytes are played or put to the playback ring buffer. + * The count of bytes can be less only if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_mmap_writei (snd_pcm_t * pcm, const void *buffer, + snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_mmap_writei()\n"); + + return snd_pcm_writei (pcm, buffer, size); +} + +/** + * \brief Write non interleaved frames to a PCM using direct buffer (mmap) + * \param pcm PCM handle + * \param bufs frames containing buffers (one for each channel) + * \param size frames to be written + * \return a positive number of frames actually written otherwise a + * negative error code + * \retval -EBADFD PCM is not in the right state (#SND_PCM_STATE_PREPARED or #SND_PCM_STATE_RUNNING) + * \retval -EPIPE an underrun occurred + * \retval -ESTRPIPE a suspend event occurred (stream is suspended and waiting for an application recovery) + * + * If the blocking behaviour is selected, then routine waits until + * all requested bytes are played or put to the playback ring buffer. + * The count of bytes can be less only if a signal or underrun occurred. + * + * If the non-blocking behaviour is selected, then routine doesn't wait at all. + */ +snd_pcm_sframes_t +snd_pcm_mmap_writen (snd_pcm_t * pcm, void **bufs, snd_pcm_uframes_t size) +{ + dbg_printf ("snd_pcm_mmap_writen()\n"); + return snd_pcm_writen (pcm, bufs, size); +} + +/** + * \brief Link two PCMs + * \param pcm1 first PCM handle + * \param pcm2 first PCM handle + * \return 0 on success otherwise a negative error code + * + * The two PCMs will start/stop/prepare in sync. + */ +int +snd_pcm_link (snd_pcm_t * pcm1, snd_pcm_t * pcm2) +{ + dbg_printf ("snd_pcm_link()\n"); + return -EINVAL; +} + +/** + * \brief Set silence size inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Silence size in frames (0 for disabled) + * \return 0 otherwise a negative error code + * + * A portion of playback buffer is overwritten with silence when playback + * underrun is nearer than silence threshold (see + * #snd_pcm_sw_params_set_silence_threshold) + * + * The special case is when silence size value is equal or greater than + * boundary. The unused portion of the ring buffer (initial written samples + * are untouched) is filled with silence at start. Later, only just processed + * sample area is filled with silence. Note: silence_threshold must be set to zero. + */ +int +snd_pcm_sw_params_set_silence_size (snd_pcm_t * pcm, + snd_pcm_sw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_sw_params_set_silence_size()\n"); + + return 0; +} + +/** + * \brief Set silence threshold inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Silence threshold in frames + * \return 0 otherwise a negative error code + * + * A portion of playback buffer is overwritten with silence (see + * #snd_pcm_sw_params_set_silence_size) when playback underrun is nearer + * than silence threshold. + */ +int +snd_pcm_sw_params_set_silence_threshold (snd_pcm_t * pcm, snd_pcm_sw_params_t + * params, snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_sw_params_set_silence_threshold()\n"); + + return 0; +} + +/** + * \brief Restrict a configuration space to contain only one period size + * \param pcm PCM handle + * \param params Configuration space + * \param val approximate period size in frames + * \param dir Sub unit direction + * \return 0 otherwise a negative error code if configuration space would become empty + * + * Wanted exact value is <,=,> val following dir (-1,0,1) + */ +int +snd_pcm_hw_params_set_period_size (snd_pcm_t * pcm, + snd_pcm_hw_params_t * params, + snd_pcm_uframes_t val, int dir) +{ + dbg_printf + ("snd_pcm_hw_params_set_period_size(pcm=%x, params=%x, val=%d, dir=%d)\n", + pcm, params, val, dir); + pcm->period_size = val; + return 0; +} + +/** + * \brief Set stop threshold inside a software configuration container + * \param pcm PCM handle + * \param params Software configuration container + * \param val Stop threshold in frames + * \return 0 otherwise a negative error code + * + * PCM is automatically stopped in #SND_PCM_STATE_XRUN state when available + * frames is >= threshold. If the stop threshold is equal to boundary (also + * software parameter - sw_param) then automatic stop will be disabled + * (thus device will do the endless loop in the ring buffer). + */ +int +snd_pcm_sw_params_set_stop_threshold (snd_pcm_t * pcm, + snd_pcm_sw_params_t * params, + snd_pcm_uframes_t val) +{ + dbg_printf ("snd_pcm_sw_params_set_stop_threshold()\n"); + + return 0; +} + +/** + * \brief Return 16 bit expressing silence for a PCM sample format + * \param format Sample format + * \return silence 16 bit word + */ +u_int16_t +snd_pcm_format_silence_16 (snd_pcm_format_t format) +{ + dbg_printf ("snd_pcm_format_silence_16(%x)\n", format); + return 0; +} + +/** + * \brief Return 8 bit expressing silence for a PCM sample format + * \param format Sample format + * \return silence 8 bit word + */ +u_int8_t +snd_pcm_format_silence (snd_pcm_format_t format) +{ + + dbg_printf ("snd_pcm_format_silence(%x)\n", format); + + return 0x80; +} + + +/** + * \brief Return 32 bit expressing silence for a PCM sample format + * \param format Sample format + * \return silence 32 bit word + */ +u_int32_t +snd_pcm_format_silence_32 (snd_pcm_format_t format) +{ + dbg_printf ("snd_pcm_format_silence_32(%x)\n", format); + return 0; +} + + +/** + * \brief Get available subdevices count from a PCM info container + * \param obj PCM info container + * \return available subdevices count of PCM + */ +unsigned int +snd_pcm_info_get_subdevices_avail (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_subdevices_avail()\n"); + + return 1; +} + +/** + * \brief get description of PCM sample format + * \param format PCM sample format + * \return ascii description of PCM sample format + */ +const char * +snd_pcm_format_description (snd_pcm_format_t format) +{ + if (format > SND_PCM_FORMAT_LAST) + return NULL; + return snd_pcm_format_descriptions[format]; +} + +/** + * \brief Get subdevices count from a PCM info container + * \param obj PCM info container + * \return subdevices total count of PCM + */ +unsigned int +snd_pcm_info_get_subdevices_count (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_subdevices_count()\n"); + + return 1; +} + +/** + * \brief Silence a PCM samples buffer + * \param format Sample format + * \param data Buffer + * \return samples Samples count + */ +int +snd_pcm_format_set_silence (snd_pcm_format_t format, void *data, + unsigned int samples) +{ + dbg_printf ("snd_pcm_format_set_silence()\n"); + + return 0; +} + + +/** + * \brief get identifier of PCM handle + * \param pcm PCM handle + * \return ascii identifier of PCM handle + * + * Returns the ASCII identifier of given PCM handle. It's the same + * identifier specified in snd_pcm_open(). + */ +const char * +snd_pcm_name (snd_pcm_t * pcm) +{ + return pcm->info.name; +} + +/** + * \brief get name of PCM state + * \param state PCM state + * \return ascii name of PCM state + */ +const char * +snd_pcm_state_name (snd_pcm_state_t state) +{ + if (state > SND_PCM_STATE_LAST) + return NULL; + return snd_pcm_state_names[state]; + +} + +/** + * \brief get name of PCM stream type + * \param stream PCM stream type + * \return ascii name of PCM stream type + */ +const char * +snd_pcm_stream_name (snd_pcm_stream_t stream) +{ + assert (stream <= SND_PCM_STREAM_LAST); + return snd_pcm_stream_names[stream]; +} + +/** + * \brief Extract maximum buffer time from a configuration space + * \param params Configuration space + * \param val approximate maximum buffer duration in us + * \param dir Sub unit direction + * \return 0 otherwise a negative error code + * + * Exact value is <,=,> the returned one following dir (-1,0,1) + */ +int INTERNAL (snd_pcm_hw_params_get_buffer_time_max) (const + snd_pcm_hw_params_t * + params, + unsigned int *val, + int *dir) +{ + dbg_printf ("snd_pcm_hw_params_get_buffer_time_max()\n"); + return 0; +} + +/** + * \brief Get subdevice name from a PCM info container + * \param obj PCM info container + * \return name of used PCM subdevice + */ +const char * +snd_pcm_info_get_subdevice_name (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_subdevice_name()\n"); + + return "OSS subname"; +} + +int +snd_device_name_hint (int card, const char * iface, void *** hints) +{ + dbg_printf ("snd_device_name_hint()\n"); + + return -1; +} + +int +snd_pcm_hw_params_is_monotonic (const snd_pcm_hw_params_t * params) +{ + return 0; +} + +/** + * \brief get PCM sample format from name + * \param name PCM sample format name (case insensitive) + * \return PCM sample format + */ +snd_pcm_format_t +snd_pcm_format_value (const char *name) +{ + snd_pcm_format_t format; + for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) + { + if (snd_pcm_format_names[format] && + strcasecmp (name, snd_pcm_format_names[format]) == 0) + { + return format; + } + } + for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) + { + if (snd_pcm_format_descriptions[format] && + strcasecmp (name, snd_pcm_format_descriptions[format]) == 0) + { + return format; + } + } + return SND_PCM_FORMAT_UNKNOWN; +} + + +/** + * \brief Get id from a PCM info container + * \param obj PCM info container + * \return short id of PCM + */ +const char * +snd_pcm_info_get_id (const snd_pcm_info_t * obj) +{ + dbg_printf ("snd_pcm_info_get_id()\n"); + + return "OSS ID"; +} + +/** + * \brief Return nominal bits per a PCM sample + * \param format Sample format + * \return bits per sample, a negative error code if not applicable + */ +int +snd_pcm_format_width (snd_pcm_format_t format) +{ + switch (format) + { + case SNDRV_PCM_FORMAT_S8: + case SNDRV_PCM_FORMAT_U8: + return 8; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_U16_BE: + return 16; + case SNDRV_PCM_FORMAT_S18_3LE: + case SNDRV_PCM_FORMAT_S18_3BE: + case SNDRV_PCM_FORMAT_U18_3LE: + case SNDRV_PCM_FORMAT_U18_3BE: + return 18; + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_U20_3LE: + case SNDRV_PCM_FORMAT_U20_3BE: + return 20; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_BE: + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_U24_BE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_U24_3BE: + return 24; + case SNDRV_PCM_FORMAT_S32_LE: + case SNDRV_PCM_FORMAT_S32_BE: + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_U32_BE: + case SNDRV_PCM_FORMAT_FLOAT_LE: + case SNDRV_PCM_FORMAT_FLOAT_BE: + return 32; + case SNDRV_PCM_FORMAT_FLOAT64_LE: + case SNDRV_PCM_FORMAT_FLOAT64_BE: + return 64; + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: + return 32; + case SNDRV_PCM_FORMAT_MU_LAW: + case SNDRV_PCM_FORMAT_A_LAW: + return 8; + case SNDRV_PCM_FORMAT_IMA_ADPCM: + return 4; + default: + return -EINVAL; + } +} + + +/** + * \brief Add an async handler for a PCM + * \param handler Returned handler handle + * \param pcm PCM handle + * \param callback Callback function + * \param private_data Callback private data + * \return 0 otherwise a negative error code on failure + * + * The asynchronous callback is called when period boundary elapses. + */ +int +snd_async_add_pcm_handler (snd_async_handler_t ** handler, snd_pcm_t * pcm, + snd_async_callback_t callback, void *private_data) +{ + dbg_printf ("snd_async_add_pcm_handler()\n"); + + return -EINVAL; +} + +void * +snd_async_handler_get_callback_private (snd_async_handler_t * handler) +{ + // What's this? + dbg_printf ("snd_async_handler_get_callback_private()\n"); + return NULL; +} + +/* + * What's this? + */ +int +snd_pcm_format_linear (snd_pcm_format_t format) +{ + return 1; +} + +/* + * What's this? + */ +int +snd_pcm_format_float (snd_pcm_format_t format) +{ + return 0; +} + +/* + * What's this? + */ +int +snd_pcm_format_signed (snd_pcm_format_t format) +{ + return (format != SND_PCM_FORMAT_U8); +} + +/* + * What's this? + */ +snd_pcm_format_t +snd_pcm_build_linear_format (int width, int pwidth, int unsignd, + int big_endian) +{ + printf ("Invalid ALSA call snd_pcm_build_linear_format()\n"); + return 0; +} + +int +snd_pcm_format_cpu_endian (snd_pcm_format_t format) +{ + return 1; +} + +int +snd_pcm_format_little_endian (snd_pcm_format_t format) +{ + return 1; +} + +/** + * \brief get size of #snd_pcm_format_mask_t + * \return size in bytes + */ +size_t +snd_pcm_format_mask_sizeof () +{ + return sizeof (snd_pcm_format_mask_t); +} + +/** + * \brief test the presence of a format in a #snd_pcm_format_mask_t + * \param mask pointer to mask + * \param val format + */ +int +snd_pcm_format_mask_test (const snd_pcm_format_mask_t * mask, + snd_pcm_format_t val) +{ + printf ("snd_pcm_format_mask_test()\n"); + return 0; +} + +/** + * \brief Return PCM handle related to an async handler + * \param handler Async handler handle + * \return PCM handle + */ +snd_pcm_t * +snd_async_handler_get_pcm (snd_async_handler_t * handler) +{ + dbg_printf ("snd_async_handler_get_pcm()\n"); + return NULL; +} + +/** + * \brief Copy one or more areas + * \param dst_areas destination areas specification (one for each channel) + * \param dst_offset offset in frames inside destination area + * \param src_areas source areas specification (one for each channel) + * \param src_offset offset in frames inside source area + * \param channels channels count + * \param frames frames to copy + * \param format PCM sample format + * \return 0 on success otherwise a negative error code + */ +int +snd_pcm_areas_copy (const snd_pcm_channel_area_t * dst_areas, + snd_pcm_uframes_t dst_offset, + const snd_pcm_channel_area_t * src_areas, + snd_pcm_uframes_t src_offset, unsigned int channels, + snd_pcm_uframes_t frames, snd_pcm_format_t format) +{ + dbg_printf ("snd_pcm_areas_copy()\n"); + + return -EINVAL; +} + +/** + * \brief Get format mask from a configuration space + * \param params Configuration space + * \param mask Returned Format mask + */ +void +snd_pcm_hw_params_get_format_mask (snd_pcm_hw_params_t * params, + snd_pcm_format_mask_t * mask) +{ + printf ("snd_pcm_hw_params_get_format_mask()\n"); +} + +#if 1 + +#ifdef USE_VERSIONED_SYMBOLS + +#define OBSOLETE1(name, what, new) \ + symbol_version(__old_##name, name, what); \ + default_symbol_version(__##name, name, new); + +#else + +#define OBSOLETE1(name, what, new) \ + use_default_symbol_version(__##name, name, new); + +#endif /* USE_VERSIONED_SYMBOLS */ + +#define __P_OLD_GET(pfx, name, val_type, ret_type) \ +ret_type pfx##name(const snd_pcm_hw_params_t *params) \ +{ \ + val_type val; \ + if (INTERNAL(name)(params, &val) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __P_OLD_GET1(pfx, name, val_type, ret_type) \ +ret_type pfx##name(const snd_pcm_hw_params_t *params, int *dir) \ +{ \ + val_type val; \ + if (INTERNAL(name)(params, &val, dir) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __OLD_GET(name, val_type, ret_type) __P_OLD_GET(__old_, name, val_type, ret_type) +#define __OLD_GET1(name, val_type, ret_type) __P_OLD_GET1(__old_, name, val_type, ret_type) + +#define __P_OLD_NEAR(pfx, name, ret_type) \ +ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val) \ +{ \ + if (INTERNAL(name)(pcm, params, &val) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __P_OLD_NEAR1(pfx, name, ret_type) \ +ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ret_type val, int *dir) \ +{ \ + if (INTERNAL(name)(pcm, params, &val, dir) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __OLD_NEAR(name, ret_type) __P_OLD_NEAR(__old_, name, ret_type) +#define __OLD_NEAR1(name, ret_type) __P_OLD_NEAR1(__old_, name, ret_type) + +#define __P_OLD_SET_FL(pfx, name, ret_type) \ +ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) \ +{ \ + ret_type val; \ + if (INTERNAL(name)(pcm, params, &val) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __P_OLD_SET_FL1(pfx, name, ret_type) \ +ret_type pfx##name(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int *dir) \ +{ \ + ret_type val; \ + if (INTERNAL(name)(pcm, params, &val, dir) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __OLD_SET_FL(name, ret_type) __P_OLD_SET_FL(__old_, name, ret_type) +#define __OLD_SET_FL1(name, ret_type) __P_OLD_SET_FL1(__old_, name, ret_type) + +#define __P_OLD_GET_SW(pfx, name, ret_type) \ +ret_type pfx##name(snd_pcm_sw_params_t *params) \ +{ \ + ret_type val; \ + if (INTERNAL(name)(params, &val) < 0) \ + return 0; \ + return (ret_type)val; \ +} + +#define __OLD_GET_SW(name, ret_type) __P_OLD_GET_SW(__old_, name, ret_type) + +#endif /* DOC_HIDDEN */ + +__OLD_NEAR1 (snd_pcm_hw_params_set_rate_near, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_set_rate_near, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_NEAR (snd_pcm_hw_params_set_channels_near, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_set_channels_near, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_period_size, snd_pcm_uframes_t, + snd_pcm_sframes_t); +OBSOLETE1 (snd_pcm_hw_params_get_period_size, ALSA_0.9, ALSA_0.9.0rc4); + + +__OLD_NEAR1 (snd_pcm_hw_params_set_buffer_time_near, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_set_buffer_time_near, ALSA_0.9, + ALSA_0.9.0rc4) __OLD_GET (snd_pcm_hw_params_get_buffer_size, + snd_pcm_uframes_t, snd_pcm_sframes_t); +OBSOLETE1 (snd_pcm_hw_params_get_buffer_size, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_NEAR1 (snd_pcm_hw_params_set_period_time_near, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_set_period_time_near, ALSA_0.9, + ALSA_0.9.0rc4); + +__OLD_NEAR (snd_pcm_hw_params_set_buffer_size_near, snd_pcm_uframes_t); +OBSOLETE1 (snd_pcm_hw_params_set_buffer_size_near, ALSA_0.9, + ALSA_0.9.0rc4); + +__OLD_NEAR1 (snd_pcm_hw_params_set_period_size_near, snd_pcm_uframes_t); +OBSOLETE1 (snd_pcm_hw_params_set_period_size_near, ALSA_0.9, + ALSA_0.9.0rc4); + +__OLD_NEAR1 (snd_pcm_hw_params_set_periods_near, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_set_periods_near, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_period_time, unsigned int, int); +OBSOLETE1 (snd_pcm_hw_params_get_period_time, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_buffer_time, unsigned int, int); +OBSOLETE1 (snd_pcm_hw_params_get_buffer_time, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET_SW (snd_pcm_sw_params_get_xfer_align, snd_pcm_uframes_t); +OBSOLETE1 (snd_pcm_sw_params_get_xfer_align, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_periods, unsigned int, int); +OBSOLETE1 (snd_pcm_hw_params_get_periods, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET (snd_pcm_hw_params_get_access, snd_pcm_access_t, int); +OBSOLETE1 (snd_pcm_hw_params_get_access, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_buffer_time_max, unsigned int, + unsigned int); +OBSOLETE1 (snd_pcm_hw_params_get_buffer_time_max, ALSA_0.9, + ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_tick_time, unsigned int, int); +OBSOLETE1 (snd_pcm_hw_params_get_tick_time, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET (snd_pcm_hw_params_get_channels_max, unsigned int, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_get_channels_max, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET (snd_pcm_hw_params_get_channels_min, unsigned int, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_get_channels_min, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_rate_max, unsigned int, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_get_rate_max, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_rate_min, unsigned int, unsigned int); +OBSOLETE1 (snd_pcm_hw_params_get_rate_min, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET (snd_pcm_hw_params_get_format, snd_pcm_format_t, int); +OBSOLETE1 (snd_pcm_hw_params_get_format, ALSA_0.9, ALSA_0.9.0rc4); + +__OLD_GET1 (snd_pcm_hw_params_get_rate, unsigned int, int); +OBSOLETE1 (snd_pcm_hw_params_get_rate, ALSA_0.9, ALSA_0.9.0rc4); diff --git a/lib/libsalsa/rawmidi.c b/lib/libsalsa/rawmidi.c new file mode 100644 index 0000000..4246357 --- /dev/null +++ b/lib/libsalsa/rawmidi.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +/** + * \brief Opens a new connection to the RawMidi interface. + * \param inputp Returned input handle (NULL if not wanted) + * \param outputp Returned output handle (NULL if not wanted) + * \param name ASCII identifier of the RawMidi handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the RawMidi interface specified with + * an ASCII identifier and mode. + */ +int +snd_rawmidi_open (snd_rawmidi_t ** inputp, snd_rawmidi_t ** outputp, + const char *name, int mode) +{ + dbg_printf ("snd_rawmidi_open(%s)\n", name); + + ALIB_INIT (); + + if (!alib_appcheck ()) + return -ENODEV; + + return 0; +} + +/** + * \brief close RawMidi handle + * \param rawmidi RawMidi handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified RawMidi handle and frees all associated + * resources. + */ +int +snd_rawmidi_close (snd_rawmidi_t * rawmidi) +{ + dbg_printf ("snd_rawmidi_close()\n"); + + return 0; +} + +/** + * \brief read MIDI bytes from MIDI stream + * \param rawmidi RawMidi handle + * \param buffer buffer to store the input MIDI bytes + * \param size input buffer size in bytes + */ +ssize_t +snd_rawmidi_read (snd_rawmidi_t * rawmidi, void *buffer, size_t size) +{ + dbg_printf ("snd_rawmidi_read(%d)\n", size); +} + +/** + * \brief write MIDI bytes to MIDI stream + * \param rawmidi RawMidi handle + * \param buffer buffer containing MIDI bytes + * \param size output buffer size in bytes + */ +ssize_t +snd_rawmidi_write (snd_rawmidi_t * rawmidi, const void *buffer, size_t size) +{ + dbg_printf ("snd_rawmidi_write(%d)\n", size); + + return size; +} + + +/** + * \brief set nonblock mode + * \param rawmidi RawMidi handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + * + * The nonblock mode cannot be used when the stream is in + * #SND_RAWMIDI_APPEND state. + */ +int +snd_rawmidi_nonblock (snd_rawmidi_t * rawmidi, int nonblock) +{ + dbg_printf ("snd_rawmidi_nonblock(%d)\n", nonblock); + + return 0; +} + +/** + * \brief get count of poll descriptors for RawMidi handle + * \param rawmidi RawMidi handle + * \return count of poll descriptors + */ +int +snd_rawmidi_poll_descriptors_count (snd_rawmidi_t * rawmidi) +{ + dbg_printf ("snd_rawmidi_poll_descriptors_count()\n"); + + return 0; +} + +/** + * \brief get poll descriptors + * \param rawmidi RawMidi handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \return count of filled descriptors + */ +int +snd_rawmidi_poll_descriptors (snd_rawmidi_t * rawmidi, struct pollfd *pfds, + unsigned int space) +{ + dbg_printf ("snd_rawmidi_poll_descriptors()\n"); + + return 0; +} + +/** + * \brief get returned events from poll descriptors + * \param pcm rawmidi RawMidi handle + * \param pfds array of poll descriptors + * \param nfds count of poll descriptors + * \param revents returned events + * \return zero if success, otherwise a negative error code + */ +int +snd_rawmidi_poll_descriptors_revents (snd_rawmidi_t * rawmidi, struct pollfd + *pfds, unsigned int nfds, + unsigned short *revents) +{ + dbg_printf ("snd_rawmidi_poll_descriptors_revents()\n"); + + return 0; +} + +/** + * \brief get size of the snd_rawmidi_info_t structure in bytes + * \return size of the snd_rawmidi_info_t structure in bytes + */ +size_t +snd_rawmidi_info_sizeof () +{ + return sizeof (snd_rawmidi_info_t); +} + +/** + * \brief get rawmidi hardware driver name + * \param info pointer to a snd_rawmidi_info_t structure + * \return rawmidi hardware driver name + */ +const char * +snd_rawmidi_info_get_name (const snd_rawmidi_info_t * info) +{ + dbg_printf ("snd_rawmidi_info_get_name()\n"); + + return "OSS rawmidi"; +} + +/** + * \brief get rawmidi count of subdevices + * \param info pointer to a snd_rawmidi_info_t structure + * \return rawmidi count of subdevices + */ +unsigned int +snd_rawmidi_info_get_subdevices_count (const snd_rawmidi_info_t * info) +{ + dbg_printf ("snd_rawmidi_info_get_subdevices_count()\n"); + + return 1; +} + +/** + * \brief set rawmidi device number + * \param info pointer to a snd_rawmidi_info_t structure + * \param val device number + */ +void +snd_rawmidi_info_set_device (snd_rawmidi_info_t * info, unsigned int val) +{ + dbg_printf ("snd_rawmidi_info_set_device(%d)\n", val); + +} + + +/** + * \brief set rawmidi subdevice number + * \param info pointer to a snd_rawmidi_info_t structure + * \param val subdevice number + */ +void +snd_rawmidi_info_set_subdevice (snd_rawmidi_info_t * info, unsigned int val) +{ + dbg_printf ("snd_rawmidi_info_set_subdevice(%d)\n", val); +} + +/** + * \brief set rawmidi stream identifier + * \param info pointer to a snd_rawmidi_info_t structure + * \param val rawmidi stream identifier + */ +void +snd_rawmidi_info_set_stream (snd_rawmidi_info_t * info, + snd_rawmidi_stream_t val) +{ + dbg_printf ("snd_rawmidi_info_set_stream(%d)\n", val); +} + +/** + * \brief get rawmidi subdevice name + * \param info pointer to a snd_rawmidi_info_t structure + * \return rawmidi subdevice name + */ +const char * +snd_rawmidi_info_get_subdevice_name (const snd_rawmidi_info_t * info) +{ + dbg_printf ("snd_rawmidi_info_get_subdevice_name()\n"); + + return "OSS rawmidi subdevice"; +} diff --git a/lib/libsalsa/seq.c b/lib/libsalsa/seq.c new file mode 100644 index 0000000..67f134b --- /dev/null +++ b/lib/libsalsa/seq.c @@ -0,0 +1,2230 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +static int nports = 0; + +typedef struct _snd_seq_port_subscribe +{ + int dummy; +} snd_seq_port_subscribe_t; + +typedef struct _snd_seq_queue_tempo +{ + int dummy; +} snd_seq_queue_tempo_t; + +typedef struct _snd_seq_system_info +{ + int dummy; +} snd_seq_system_info_t; + +struct _snd_seq_remove_events +{ + int dummy; +}; + +struct _snd_seq_query_subscribe +{ + int dummy; +}; + +/** + * \brief Open the ALSA sequencer + * + * \param seqp Pointer to a snd_seq_t pointer. This pointer must be + * kept and passed to most of the other sequencer functions. + * \param name The sequencer's "name". This is \em not a name you make + * up for your own purposes; it has special significance to the ALSA + * library. Usually you need to pass \c "default" here. + * \param streams The read/write mode of the sequencer. Can be one of + * three values: + * - #SND_SEQ_OPEN_OUTPUT - open the sequencer for output only + * - #SND_SEQ_OPEN_INPUT - open the sequencer for input only + * - #SND_SEQ_OPEN_DUPLEX - open the sequencer for output and input + * \note Internally, these are translated to \c O_WRONLY, \c O_RDONLY and + * \c O_RDWR respectively and used as the second argument to the C library + * open() call. + * \param mode Optional modifier. Can be either 0, or + * #SND_SEQ_NONBLOCK, which will make read/write operations + * non-blocking. This can also be set later using #snd_seq_nonblock(). + * \return 0 on success otherwise a negative error code + * +* Creates a new handle and opens a connection to the kernel + * sequencer interface. + * After a client is created successfully, an event + * with #SND_SEQ_EVENT_CLIENT_START is broadcast to announce port. + * + * \sa snd_seq_open_lconf(), snd_seq_close(), snd_seq_type(), snd_seq_name(), + * snd_seq_nonblock(), snd_seq_client_id() + */ +int +snd_seq_open (snd_seq_t ** seqp, const char *name, int streams, int mode) +{ + snd_seq_t *seq; + int err, oss_mode; + static int instance = 0; + + char *fname = "/dev/midi", *envname = NULL, tmp[128]; + + ALIB_INIT (); + + dbg_printf ("snd_seq_open(name='%s', streams=%d, mode=%x)\n", name, streams, + mode); + + if (!alib_appcheck ()) + return -ENODEV; + + instance++; + + sprintf (tmp, "%s_mididev%d", alib_appname, instance); + + if ((envname = getenv (tmp)) != NULL) + { + fname = envname; + } +#if 0 + if (streams != 1) + { + fprintf (stderr, "salsa: snd_seq_open doesn't support streams=%d\n", + streams); + return -EIO; + } +#endif + + if ((seq = malloc (sizeof (*seq))) == NULL) + return -ENOMEM; + + dbg_printf ("Created sequencer seq=%x\n", seq); + + memset (seq, 0, sizeof (*seq)); + + switch (streams) + { + case SND_SEQ_OPEN_INPUT: + oss_mode = O_RDONLY; + dbg_printf ("Open SND_SEQ_OPEN_INPUT\n"); + break; + case SND_SEQ_OPEN_OUTPUT: + oss_mode = O_WRONLY; + dbg_printf ("Open SND_SEQ_OPEN_OUTPUT\n"); + break; + case SND_SEQ_OPEN_DUPLEX: + dbg_printf ("SND_SEQ_OPEN_DUPLEX\n"); + oss_mode = O_RDWR; + break; + + default: + fprintf (stderr, "snd_seq_open: Unknown stream %x\n", streams); + return -ENODEV; + } + + if ((seq->fd = open (fname, oss_mode, 0)) == -1) + { + err = errno; + perror (fname); + + if (envname == NULL) + { + fprintf (stderr, + "You can select another filename using environment variable %s_mididev%d\n", + alib_appname, instance); + } + return -err; + } + + seq->streams = streams; + seq->oss_mode = oss_mode; + + if (streams == SND_SEQ_OPEN_INPUT || streams == SND_SEQ_OPEN_DUPLEX) + { + seq->parser = midiparser_create (midiparser_callback, seq); + if (seq->parser == NULL) + { + fprintf (stderr, "libsalsa: Can't create MIDI parser\n"); + return -ENODEV; + } + } + + *seqp = seq; + return 0; +} + +/** + * \brief Close the sequencer + * \param handle Handle returned from #snd_seq_open() + * \return 0 on success otherwise a negative error code + * + * Closes the sequencer client and releases its resources. + * After a client is closed, an event with + * #SND_SEQ_EVENT_CLIENT_EXIT is broadcast to announce port. + * The connection between other clients are disconnected. + * Call this just before exiting your program. + * + * \sa snd_seq_close() + */ +int +snd_seq_close (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_close(seq=%x)\n", seq); + + close (seq->fd); + free (seq); +} + +/** + * \brief Set nonblock mode + * \param seq sequencer handle + * \param nonblock 0 = block, 1 = nonblock mode + * \return 0 on success otherwise a negative error code + * + * Change the blocking mode of the given client. + * In block mode, the client falls into sleep when it fills the + * output memory pool with full events. The client will be woken up + * after a certain amount of free space becomes available. + * + * \sa snd_seq_open() + */ +int +snd_seq_nonblock (snd_seq_t * seq, int nonblock) +{ + dbg_printf ("snd_seq_nonblock(seq=%x, nonblock=%d)\n", seq, nonblock); + + seq->nonblock = nonblock; + return 0; +} + +/** + * \brief create a sequencer port on the current client + * \param seq sequencer handle + * \param port port information for the new port + * \return 0 on success otherwise a negative error code + * + * Creates a sequencer port on the current client. + * The attributes of created port is specified in \a info argument. + * + * The client field in \a info argument is overwritten with the current client id. + * The port id to be created can be specified via #snd_seq_port_info_set_port_specified. + * You can get the created port id by reading the port pointer via #snd_seq_port_info_get_port. + * + * Each port has the capability bit-masks to specify the access capability + * of the port from other clients. + * The capability bit flags are defined as follows: + * - #SND_SEQ_PORT_CAP_READ Readable from this port + * - #SND_SEQ_PORT_CAP_WRITE Writable to this port. + * - #SND_SEQ_PORT_CAP_SYNC_READ For synchronization (not implemented) + * - #SND_SEQ_PORT_CAP_SYNC_WRITE For synchronization (not implemented) + * - #SND_SEQ_PORT_CAP_DUPLEX Read/write duplex access is supported + * - #SND_SEQ_PORT_CAP_SUBS_READ Read subscription is allowed + * - #SND_SEQ_PORT_CAP_SUBS_WRITE Write subscription is allowed + * - #SND_SEQ_PORT_CAP_SUBS_NO_EXPORT Subscription management from 3rd client is disallowed + * + * Each port has also the type bitmasks defined as follows: + * - #SND_SEQ_PORT_TYPE_SPECIFIC Hardware specific port + * - #SND_SEQ_PORT_TYPE_MIDI_GENERIC Generic MIDI device + * - #SND_SEQ_PORT_TYPE_MIDI_GM General MIDI compatible device + * - #SND_SEQ_PORT_TYPE_MIDI_GS GS compatible device + * - #SND_SEQ_PORT_TYPE_MIDI_XG XG compatible device + * - #SND_SEQ_PORT_TYPE_MIDI_MT32 MT-32 compatible device + * - #SND_SEQ_PORT_TYPE_SYNTH Synth device + * - #SND_SEQ_PORT_TYPE_DIRECT_SAMPLE Sampling device (supporting download) + * - #SND_SEQ_PORT_TYPE_SAMPLE Sampling device (sample can be downloaded at any time) + * - #SND_SEQ_PORT_TYPE_APPLICATION Application (sequencer/editor) + * + * A port may contain specific midi channels, midi voices and synth voices. + * These values could be zero as default. + * + * \sa snd_seq_delete_port(), snd_seq_get_port_info(), + * snd_seq_create_simple_port() + */ +int +snd_seq_create_port (snd_seq_t * seq, snd_seq_port_info_t * port) +{ + dbg_printf ("snd_seq_create_port(seq=%x, port=%x)\n", seq, port); + + port->port = nports++; + return 0; +} + +/** + * \brief Get the client id + * \param seq sequencer handle + * \return the client id + * + * Returns the id of the specified client. + * If an error occurs, function returns the negative error code. + * A client id is necessary to inquiry or to set the client information. + * A user client is assigned from 128 to 191. + * + * \sa snd_seq_open() + */ +int +snd_seq_client_id (snd_seq_t * seq) +{ + static int client_id = 128; + + dbg_printf ("snd_seq_client_id(seq=%x)=%d\n", seq, client_id); + + return client_id++; +} + +/** + * \brief Get client id of a client_info container + * \param info client_info container + * \return client id + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_set_client(), snd_seq_client_id() + */ +int +snd_seq_client_info_get_client (const snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_client_info_get_client()\n"); + + return 0; +} + +/** + * \brief Get client type of a client_info container + * \param info client_info container + * \return client type + * + * The client type is either #SEQ_CLIENT_TYPE_KERNEL or #SEQ_CLIENT_TYPE_USER + * for kernel or user client respectively. + * + * \sa snd_seq_get_client_info() + */ +snd_seq_client_type_t +snd_seq_client_info_get_type (const snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_client_info_get_type(infp=%x)\n", info); + + return SND_SEQ_KERNEL_CLIENT; // TODO +} + +/** + * \brief Set the client id of a client_info container + * \param info client_info container + * \param client client id + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_get_client() + */ +void +snd_seq_client_info_set_client (snd_seq_client_info_t * info, int client) +{ + dbg_printf ("snd_seq_client_info_set_client(%x, %d)\n", info, client); + + info->client = client; +} + +/** + * \brief get size of #snd_seq_client_info_t + * \return size in bytes + */ +size_t +snd_seq_client_info_sizeof () +{ + dbg_printf ("snd_seq_client_info_sizeof()\n"); + + return sizeof (snd_seq_client_info_t); +} + +/** + * \brief retrieve an event from sequencer + * \param seq sequencer handle + * \param ev event pointer to be stored + * \return + * + * Obtains an input event from sequencer. + * The event is created via snd_seq_create_event(), and its pointer is stored on * ev argument. + * + * This function firstly receives the event byte-stream data from sequencer + * as much as possible at once. Then it retrieves the first event record + * and store the pointer on ev. + * By calling this function sequentially, events are extracted from the input buffer. + * + * If there is no input from sequencer, function falls into sleep + * in blocking mode until an event is received, + * or returns \c -EAGAIN error in non-blocking mode. + * Occasionally, this function may return \c -ENOSPC error. + * This means that the input FIFO of sequencer overran, and some events are + * lost. + * Once this error is returned, the input FIFO is cleared automatically. + * + * Function returns the byte size of remaining events on the input buffer + * if an event is successfully received. + * Application can determine from the returned value whether to call + * input once more or not. + * + * \sa snd_seq_event_input_pending(), snd_seq_drop_input() + */ +int +snd_seq_event_input (snd_seq_t * seq, snd_seq_event_t ** evp) +{ + static snd_seq_event_t *ev; + unsigned char buf[256]; + int i, l; + + dbg_printf2 ("snd_seq_event_input(seq=%x)\n", seq); + + while (1) + { + + if (seq->nextevent < seq->nevents) + { + *evp = &seq->events[seq->nextevent++]; + + return (seq->nevents - seq->nextevent) * sizeof (snd_seq_event_t); + } + + seq->nextevent = seq->nevents = 0; + memset (seq->events, 0, sizeof (seq->events)); + + // TODO Handling of nonblocking mode + if ((l = read (seq->fd, buf, sizeof (buf))) == -1) + return -errno; + + midiparser_input_buf (seq->parser, buf, l); + + } + +} + +/** + * \brief output an event directly to the sequencer NOT through output buffer + * \param seq sequencer handle + * \param ev event to be output + * \return the byte size sent to sequencer or a negative error code + * + * This function sends an event to the sequencer directly not through the + * output buffer. When the event is a variable length event, a temporary + * buffer is allocated inside alsa-lib and the data is copied there before + * actually sent. + * + * \sa snd_seq_event_output() + */ +int +snd_seq_event_output_direct (snd_seq_t * seq, snd_seq_event_t * ev) +{ + int err; + + dbg_printf3 ("snd_seq_event_output_direct()\n"); + + if ((err = convert_event (seq, ev)) < 0) + return err; + if ((err = snd_seq_drain_output (seq)) < 0) + return err; + + return 0; +} + +/** + * \brief (DEPRECATED) free an event + * + * In the former version, this function was used to + * release the event pointer which was allocated by snd_seq_event_input(). + * In the current version, the event record is not allocated, so + * you don't have to call this function any more. + */ +int +snd_seq_free_event (snd_seq_event_t * ev) +{ + return 0; +} + +/** + * \brief obtain the current client information + * \param seq sequencer handle + * \param info the pointer to be stored + * \return 0 on success otherwise a negative error code + * + * Obtains the information of the current client stored on info. + * client and type fields are ignored. + * + * \sa snd_seq_get_any_client_info(), snd_seq_set_client_info(), + * snd_seq_query_next_client() + */ +int +snd_seq_get_client_info (snd_seq_t * seq, snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_get_client_info(seq=%x, info=%x)\n", seq, info); + + return 0; +} + +/** + * \brief query the next matching port + * \param seq sequencer handle + * \param info query pattern and result + + * Queries the next matching port on the client specified in + * \a info argument. + * The search begins at the next port specified in + * port field of \a info argument. + * For finding the first port at a certain client, give -1. + * + * If a matching port is found, its attributes are stored on + * \a info and function returns zero. + * Otherwise, a negative error code is returned. + * + * \sa snd_seq_get_port_info() + */ +int +snd_seq_query_next_port (snd_seq_t * seq, snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_query_next_port()\n"); + + return -EINVAL; +} + +/** + * \brief Set the name of a client_info container + * \param info client_info container + * \param name name string + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_get_name(), + * snd_seq_set_client_name() + */ +void +snd_seq_client_info_set_name (snd_seq_client_info_t * info, const char *name) +{ + dbg_printf ("snd_seq_client_info_set_name(%s)\n", name); + + strncpy (info->name, name, sizeof (info->name) - 1); + info->name[sizeof (info->name) - 1] = 0; +} + +/** + * \brief subscribe a port connection + * \param seq sequencer handle + * \param sub subscription information + * \return 0 on success otherwise a negative error code + * + * Subscribes a connection between two ports. + * The subscription information is stored in sub argument. + * + * \sa snd_seq_get_port_subscription(), snd_seq_unsubscribe_port(), + * snd_seq_connect_from(), snd_seq_connect_to() + */ +int +snd_seq_subscribe_port (snd_seq_t * seq, snd_seq_port_subscribe_t * sub) +{ + dbg_printf ("snd_seq_subscribe_port()\n"); + + return -EINVAL; +} + +/** + * \brief get size of #snd_seq_port_subscribe_t + * \return size in bytes + */ +size_t +snd_seq_port_subscribe_sizeof () +{ + return sizeof (snd_seq_port_subscribe_t); +} + +/** + * \brief Set sender address of a port_subscribe container + * \param info port_subscribe container + * \param addr sender address + * + * \sa snd_seq_subscribe_port(), snd_seq_port_subscribe_get_sender() + */ +void +snd_seq_port_subscribe_set_sender (snd_seq_port_subscribe_t * info, + const snd_seq_addr_t * addr) +{ + dbg_printf ("snd_seq_port_subscribe_set_sender()\n"); +} + +/** + * \brief Set destination address of a port_subscribe container + * \param info port_subscribe container + * \param addr destination address + * + * \sa snd_seq_subscribe_port(), snd_seq_port_subscribe_get_dest() + */ +void +snd_seq_port_subscribe_set_dest (snd_seq_port_subscribe_t * info, + const snd_seq_addr_t * addr) +{ + dbg_printf ("snd_seq_port_subscribe_set_dest()\n"); +} + +/** + * \brief Set the queue id of a port_subscribe container + * \param info port_subscribe container + * \param q queue id + * + * \sa snd_seq_subscribe_port(), snd_seq_port_subscribe_get_queue() + */ +void +snd_seq_port_subscribe_set_queue (snd_seq_port_subscribe_t * info, int q) +{ + dbg_printf ("snd_seq_port_subscribe_set_queue()\n"); +} + +/** + * \brief Set the real-time mode of a port_subscribe container + * \param info port_subscribe container + * \param val non-zero to enable + * + * \sa snd_seq_subscribe_port(), snd_seq_port_subscribe_get_time_real() + */ +void +snd_seq_port_subscribe_set_time_real (snd_seq_port_subscribe_t * info, + int val) +{ + dbg_printf ("snd_seq_port_subscribe_set_time_real()\n"); +} + +/** + * \brief Set the time-update mode of a port_subscribe container + * \param info port_subscribe container + * \param val non-zero to enable + * + * \sa snd_seq_subscribe_port(), snd_seq_port_subscribe_get_time_update() + */ +void +snd_seq_port_subscribe_set_time_update (snd_seq_port_subscribe_t * info, int + val) +{ + dbg_printf ("snd_seq_port_subscribe_set_time_update()\n"); +} + +/* + * Port + */ + +/** + * \brief get size of #snd_seq_port_info_t + * \return size in bytes + */ +size_t +snd_seq_port_info_sizeof () +{ + return sizeof (snd_seq_port_info_t); +} + +/** + * \brief Get the capability bits of a port_info container + * \param info port_info container + * \return capability bits + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_capability() + */ +unsigned int +snd_seq_port_info_get_capability (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_capability()\n"); + + return info->capability; +} + +/** + * \brief Get client/port address of a port_info container + * \param info port_info container + * \return client/port address pointer + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_addr() + */ +const snd_seq_addr_t * +snd_seq_port_info_get_addr (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_addr(info=%x)\n", info); + + return NULL; // TODO +} + +/** + * \brief set the capability bits of a port_info container + * \param info port_info container + * \param capability capability bits + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_capability() + */ +void +snd_seq_port_info_set_capability (snd_seq_port_info_t * info, + unsigned int capability) +{ + dbg_printf ("snd_seq_port_info_set_capability()\n"); +} + +/** + * \brief Set the port-specified mode of a port_info container + * \param info port_info container + * \param val non-zero if specifying the port id at creation + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_port_specified() + */ +void +snd_seq_port_info_set_port_specified (snd_seq_port_info_t * info, int val) +{ + dbg_printf ("snd_seq_port_info_set_port_specified()\n"); +} + +/** + * \brief Get the midi channels of a port_info container + * \param info port_info container + * \return number of midi channels (default 0) + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_midi_channels() + */ +int +snd_seq_port_info_get_midi_channels (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_midi_channels(info=%x)=%d\n", + info, info->midi_channels); + + return info->midi_channels; +} + +/** + * \brief set the midi channels of a port_info container + * \param info port_info container + * \param channels midi channels (default 0) + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_midi_channels() + */ +void +snd_seq_port_info_set_midi_channels (snd_seq_port_info_t * info, int channels) +{ + dbg_printf ("snd_seq_port_info_set_midi_channels(info=%x, channels=%d)\n", + info, channels); + info->midi_channels = channels; +} + +/** + * \brief Get the queue id to update timestamps + * \param info port_info container + * \return the queue id to get the timestamps + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_timestamp_queue() + */ +int +snd_seq_port_info_get_timestamp_queue (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_timestamp_queue(info=%x)\n", info); + + return 0; // TODO +} + +/** + * \brief Set the queue id for timestamping + * \param info port_info container + * \param queue the queue id to get timestamps + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_timestamp_queue() + */ +void +snd_seq_port_info_set_timestamp_queue (snd_seq_port_info_t * info, int queue) +{ + dbg_printf ("snd_seq_port_info_set_timestamp_queue(info=%x, queue=%d)\n", + info, queue); + + // TODO +} + +/** + * \brief Get whether the time-stamping of the given port is real-time mode + * \param info port_info container + * \return 1 if the time-stamping is in the real-time mode + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_timestamp_real() + */ +int +snd_seq_port_info_get_timestamp_real (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_timestamp_real(info=%x)\n", info); + + return 0; // TODO +} + +/** + * \brief Set whether the timestime is updated in the real-time mode + * \param info port_info container + * \param enable non-zero if updating the timestamps in real-time mode + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_timestamp_real() + */ +void +snd_seq_port_info_set_timestamp_real (snd_seq_port_info_t * info, int enable) +{ + dbg_printf ("snd_seq_port_info_set_timestamp_real(infp=%x, enable=%d)\n", + info, enable); +} + +/** + * \brief Get the time-stamping mode of the given port in a port_info container * \param info port_info container + * \return 1 if the port updates timestamps of incoming events + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_timestamping() + */ +int +snd_seq_port_info_get_timestamping (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_timestamping(info=%x)\n", info); + + return 0; // TODO +} + +/** + * \brief Set the time-stamping mode of the given port + * \param info port_info container + * \param enable non-zero if updating the timestamps of incoming events + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_timestamping() + */ +void +snd_seq_port_info_set_timestamping (snd_seq_port_info_t * info, int enable) +{ + dbg_printf ("snd_seq_port_info_set_timestamping(info=%x, enable=%d)\n", + info, enable); + + // TODO +} + +/** + * \brief Set the name of a port_info container + * \param info port_info container + * \param name name string + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_name() + */ +void +snd_seq_port_info_set_name (snd_seq_port_info_t * info, const char *name) +{ + dbg_printf ("snd_seq_port_info_set_name()\n"); + + strncpy (info->name, name, sizeof (info->name)); +} + +/** + * \brief Get the name of a port_info container + * \param info port_info container + * \return name string + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_name() + */ +const char * +snd_seq_port_info_get_name (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_name()\n"); + + return "Port Name"; +} + +/** + * \brief Get port id of a port_info container + * \param info port_info container + * \return port id + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_port() + */ +int +snd_seq_port_info_get_port (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_port()\n"); + + return -EIO; +} + +/** + * \brief Get the type bits of a port_info container + * \param info port_info container + * \return port type bits + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_type() + */ +unsigned int +snd_seq_port_info_get_type (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_type()\n"); + + return info->type; +} + +/** + * \brief Set the client id of a port_info container + * \param info port_info container + * \param client client id + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_client() + */ +void +snd_seq_port_info_set_client (snd_seq_port_info_t * info, int client) +{ + dbg_printf ("snd_seq_port_info_set_client()\n"); +} + +/** + * \brief Set the port id of a port_info container + * \param info port_info container + * \param port port id + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_port() + */ +void +snd_seq_port_info_set_port (snd_seq_port_info_t * info, int port) +{ + dbg_printf ("snd_seq_port_info_set_port()\n"); +} + +/** + * \brief query the next matching client + * \param seq sequencer handle + * \param info query pattern and result + * + * Queries the next matching client with the given condition in + * info argument. + * The search begins at the client with an id one greater than + * client field in info. + * If name field in info is not empty, the client name is compared. + * If a matching client is found, its attributes are stored o + * info and returns zero. + * Otherwise returns a negative error code. + * + * \sa snd_seq_get_any_client_info() + */ +int +snd_seq_query_next_client (snd_seq_t * seq, snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_query_next_client()\n"); + + return -EIO; +} + +/** + * \brief set the current client information + * \param seq sequencer handle + * \param info the client info data to set + * \return 0 on success otherwise a negative error code + * + * Obtains the information of the current client stored on info. + * client and type fields are ignored. + * + * \sa snd_seq_get_client_info() + */ +int +snd_seq_set_client_info (snd_seq_t * seq, snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_set_client_info(seq=%x, info=%x)\n", seq, info); + + if (*info->name) + { + strcpy (seq->name, info->name); + ioctl (seq->fd, SNDCTL_SETNAME, seq->name); + ioctl (seq->fd, SNDCTL_SETSONG, seq->name); + } + + return 0; +} + +/** + * \brief check events in input buffer + * \return the byte size of remaining input events on input buffer. + * + * If events remain on the input buffer of user-space, function returns + * the total byte size of events on it. + * If fetch_sequencer argument is non-zero, + * this function checks the presence of events on sequencer FIFO + * When events exist, they are transferred to the input buffer, + * and the number of received events are returned. + * If fetch_sequencer argument is zero and + * no events remain on the input buffer, function simply returns zero. + * + * \sa snd_seq_event_input() + */ +int +snd_seq_event_input_pending (snd_seq_t * seq, int fetch_sequencer) +{ + dbg_printf ("snd_seq_event_input_pending()\n"); + + return 0; +} + +/** + * \brief Get poll descriptors + * \param seq sequencer handle + * \param pfds array of poll descriptors + * \param space space in the poll descriptor array + * \param events polling events to be checked (\c POLLIN and \c POLLOUT) + * \return count of filled descriptors + * + * Get poll descriptors assigned to the sequencer handle. + * Since a sequencer handle can duplex streams, you need to set which direction(s) + * is/are polled in \a events argument. When \c POLLIN bit is specified, + * the incoming events to the ports are checked. + * + * To check the returned poll-events, call #snd_seq_poll_descriptors_revents() + * instead of reading the pollfd structs directly. + * + * \sa snd_seq_poll_descriptors_count(), snd_seq_poll_descriptors_revents() + */ +int +snd_seq_poll_descriptors (snd_seq_t * seq, struct pollfd *pfds, + unsigned int space, short events) +{ + dbg_printf ("snd_seq_poll_descriptors(seq=%x)\n", seq); + + pfds->fd = seq->fd; + pfds->events = 0; + pfds->revents = 0; + + if (seq->oss_mode == O_WRONLY || seq->oss_mode == O_RDWR) + pfds->events = POLLOUT; + if (seq->oss_mode == O_RDONLY || seq->oss_mode == O_RDWR) + pfds->events = POLLIN; + + return 1; +} + + +/** + * \brief Returns the number of poll descriptors + * \param seq sequencer handle + * \param events the poll events to be checked (\c POLLIN and \c POLLOUT) + * \return the number of poll descriptors. + * + * Get the number of poll descriptors. The polling events to be checked + * can be specified by the second argument. When both input and output + * are checked, pass \c POLLIN|POLLOUT + * + * \sa snd_seq_poll_descriptors() + */ +int +snd_seq_poll_descriptors_count (snd_seq_t * seq, short events) +{ + dbg_printf ("snd_seq_poll_descriptors_count(seq=%x, events=%x)\n", + seq, events); + return 1; +} + +/** + * \brief create a queue + * \param seq sequencer handle + * \param info queue information to initialize + * \return the queue id (zero or positive) on success otherwise a negative error code + * + * \sa snd_seq_alloc_queue() + */ +int +snd_seq_create_queue (snd_seq_t * seq, snd_seq_queue_info_t * info) +{ + dbg_printf ("snd_seq_create_queue()\n"); + + return 0; +} + +/** + * \brief simple subscription (w/o exclusive & time conversion) + * \param myport the port id as sender + * \param dest_client destination client id + * \param dest_port destination port id + * \return 0 on success or negative error code + * + * Connect from the given receiver port in the current client + * to the given destination client:port. + * + * \sa snd_seq_subscribe_port(), snd_seq_disconnect_to() + */ +int +snd_seq_connect_to (snd_seq_t * seq, int myport, int dest_client, + int dest_port) +{ + dbg_printf ("snd_seq_connect_to(%d->%d/%d)\n", myport, dest_client, + dest_port); + + return 0; +} + +/** + * \brief simple subscription (w/o exclusive & time conversion) + * \param myport the port id as receiver + * \param src_client sender client id + * \param src_port sender port id + * \return 0 on success or negative error code + * + * Connect from the given sender client:port to the given destination port in the + * current client. + * + * \sa snd_seq_subscribe_port(), snd_seq_disconnect_from() + */ +int +snd_seq_connect_from (snd_seq_t * seq, int myport, int src_client, + int src_port) +{ + dbg_printf + ("snd_seq_connect_from(seq=%x, myport=%d, src_client=%d, src_port=%d)\n", + seq, myport, src_client, src_port); + + return 0; +} + +/** + * \brief queue controls - start/stop/continue + * \param seq sequencer handle + * \param q queue id to control + * \param type event type + * \param value event value + * \param ev event instance + * + * This function sets up general queue control event and sends it. + * To send at scheduled time, set the schedule in \a ev. + * If \a ev is NULL, the event is composed locally and sent immediately + * to the specified queue. In any cases, you need to call #snd_seq_drain_output() + * appropriately to feed the event. + * + * \sa snd_seq_alloc_queue() + */ +int +snd_seq_control_queue (snd_seq_t * seq, int q, int type, int value, + snd_seq_event_t * ev) +{ + dbg_printf ("snd_seq_control_queue()\n"); + + return 0; +} + +/** + * \brief drain output buffer to sequencer + * \param seq sequencer handle + * \return 0 when all events are drained and sent to sequencer. + * When events still remain on the buffer, the byte size of remaining + * events are returned. On error a negative error code is returned. + * + * This function drains all pending events on the output buffer. + * The function returns immediately after the events are sent to the queues + * regardless whether the events are processed or not. + * To get synchronization with the all event processes, use + * #snd_seq_sync_output_queue() after calling this function. + * + * \sa snd_seq_event_output(), snd_seq_sync_output_queue() + */ +int +snd_seq_drain_output (snd_seq_t * seq) +{ + dbg_printf3 ("snd_seq_drain_output(seq=%x)\n", seq); + + return 0; +} + + +/** + * \brief remove all events on output buffer + * \param seq sequencer handle + * + * Removes all events on both user-space output buffer and + * output memory pool on kernel. + * + * \sa snd_seq_drain_output(), snd_seq_drop_output_buffer(), snd_seq_remove_events() + */ +int +snd_seq_drop_output (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_drop_output()\n"); + + return 0; +} + +/** + * \brief output an event + * \param seq sequencer handle + * \param ev event to be output + * \return the number of remaining events or a negative error code + * + * An event is once expanded on the output buffer. + * The output buffer will be drained automatically if it becomes full. + * + * If events remain unprocessed on output buffer before drained, + * the size of total byte data on output buffer is returned. + * If the output buffer is empty, this returns zero. + * + * \sa snd_seq_event_output_direct(), snd_seq_event_output_buffer(), + * snd_seq_event_output_pending(), snd_seq_drain_output(), + * snd_seq_drop_output(), snd_seq_extract_output(), + * snd_seq_remove_events() + */ +int +snd_seq_event_output (snd_seq_t * seq, snd_seq_event_t * ev) +{ + dbg_printf3 ("snd_seq_event_output(seq=%x, ev=%x)\n", seq, ev); + + return convert_event (seq, ev); +} + +/** + * \brief delete the specified queue + * \param seq sequencer handle + * \param q queue id to delete + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_alloc_queue() + */ +int +snd_seq_free_queue (snd_seq_t * seq, int q) +{ + dbg_printf ("snd_seq_free_queue()\n"); + + return 0; +} + +/** + * \brief obtain the information of the given client + * \param seq sequencer handle + * \param client client id + * \param info the pointer to be stored + * \return 0 on success otherwise a negative error code + * + * Obtains the information of the client with a client id specified by + * info argument. + * The obtained information is written on info parameter. + * + * \sa snd_seq_get_client_info() + */ +int +snd_seq_get_any_client_info (snd_seq_t * seq, int client, + snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_get_any_client_info()\n"); + + strcpy (info->name, seq->name); + + return 0; +} + +/** + * \brief obtain the information of a port on an arbitrary client + * \param seq sequencer handle + * \param client client id to get + * \param port port id to get + * \param info pointer information returns + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_get_port_info() + */ +int +snd_seq_get_any_port_info (snd_seq_t * seq, int client, int port, + snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_get_any_port_info()\n"); + + return 0; +} + +/** + * \brief allocate an empty #snd_seq_port_info_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_port_info_malloc (snd_seq_port_info_t ** ptr) +{ + snd_seq_port_info_t *p; + + dbg_printf ("snd_seq_port_info_malloc()\n"); + + if ((p = malloc (sizeof (*p))) == NULL) + return -ENOMEM; + + *ptr = p; + + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_port_info_t + * \param pointer to object to free + */ +void +snd_seq_port_info_free (snd_seq_port_info_t * obj) +{ + free (obj); +} + +/** + * \brief allocate an empty #snd_seq_queue_tempo_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_queue_tempo_malloc (snd_seq_queue_tempo_t ** ptr) +{ + assert (ptr); + *ptr = calloc (1, sizeof (snd_seq_queue_tempo_t)); + dbg_printf ("snd_seq_queue_tempo_malloc()=%x\n", *ptr); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_queue_tempo_t + * \param pointer to object to free + */ +void +snd_seq_queue_tempo_free (snd_seq_queue_tempo_t * obj) +{ + dbg_printf ("snd_seq_queue_tempo_free(%x)\n", obj); + free (obj); +} + +/** + * \brief Set the ppq of a queue_status container + * \param info queue_status container + * \param ppq ppq value + * + * \sa snd_seq_get_queue_tempo() + */ +void +snd_seq_queue_tempo_set_ppq (snd_seq_queue_tempo_t * info, int ppq) +{ + dbg_printf ("snd_seq_queue_tempo_set_ppq(info=%x, %d)\n", info, ppq); +} + +/** + * \brief Set the tempo of a queue_status container + * \param info queue_status container + * \param tempo tempo value + * + * \sa snd_seq_get_queue_tempo() + */ +void +snd_seq_queue_tempo_set_tempo (snd_seq_queue_tempo_t * info, + unsigned int tempo) +{ + dbg_printf ("snd_seq_queue_tempo_set_tempo(info=%x, %d)\n", info, tempo); +} + +/** + * \brief get size of #snd_seq_queue_tempo_t + * \return size in bytes + */ +size_t +snd_seq_queue_tempo_sizeof () +{ + return sizeof (snd_seq_queue_tempo_t); +} + +/** + * \brief set the queue timer information + * \param seq sequencer handle + * \param q queue id to change the timer + * \param timer timer information + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_get_queue_timer() + */ +int +snd_seq_set_queue_timer (snd_seq_t * seq, int q, + snd_seq_queue_timer_t * timer) +{ + dbg_printf ("snd_seq_get_queue_timer(seq=%x, q=%d, timer=%X)\n", seq, q, + timer); + return -ENXIO; // TODO +} + + +/** + * \brief Opens a new connection to the timer query interface. + * \param timer Returned handle (NULL if not wanted) + * \param name ASCII identifier of the RawMidi handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the RawMidi interface specified with + * an ASCII identifier and mode. + */ +int +snd_timer_query_open (snd_timer_query_t ** timer, const char *name, int mode) +{ + dbg_printf ("snd_timer_query_open(name=%s, mode=%x)\n", name, mode); + return -ENXIO; // TODO +} + +/** + * \brief obtain the current tempo of the queue + * \param seq sequencer handle + * \param q queue id to be queried + * \param tempo pointer to store the current tempo + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_set_queue_tempo() + */ +int +snd_seq_get_queue_tempo (snd_seq_t * seq, int q, + snd_seq_queue_tempo_t * tempo) +{ + dbg_printf ("snd_seq_get_queue_tempo(seq=%x, q=%d, tempo=%x)\n", seq, q, + tempo); + + return 0; +} + +/** + * \brief allocate an empty #snd_seq_client_info_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_client_info_malloc (snd_seq_client_info_t ** ptr) +{ + dbg_printf ("snd_seq_client_info_malloc()\n"); + + *ptr = malloc (sizeof (snd_seq_client_info_t)); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_client_info_t + * \param pointer to object to free + */ +void +snd_seq_client_info_free (snd_seq_client_info_t * obj) +{ + dbg_printf ("snd_seq_client_info_free()\n"); + + free (obj); +} + +/** + * \brief Get the name of a client_info container + * \param info client_info container + * \return name string + * + * \sa snd_seq_get_client_info(), snd_seq_client_info_set_name() + */ +const char * +snd_seq_client_info_get_name (snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_client_info_get_name()\n"); + return "OSS seq client"; +} + +/** + * \brief Get the number of opened ports of a client_info container + * \param info client_info container + * \return number of opened ports + * + * \sa snd_seq_get_client_info() + */ +int +snd_seq_client_info_get_num_ports (const snd_seq_client_info_t * info) +{ + dbg_printf ("snd_seq_client_info_get_num_ports()\n"); + + return 1; +} + +/** + * \brief Get size of #snd_seq_system_info_t + * \return size in bytes + */ +size_t +snd_seq_system_info_sizeof () +{ + return sizeof (snd_seq_system_info_t); +} + + +/** + * \brief Allocate an empty #snd_seq_system_info_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_system_info_malloc (snd_seq_system_info_t ** ptr) +{ + dbg_printf ("snd_seq_system_info_malloc()\n"); + + *ptr = malloc (sizeof (snd_seq_system_info_t)); + if (*ptr == NULL) + return -ENOMEM; + return 0; +} + +/** + * \brief Frees a previously allocated #snd_seq_system_info_t + * \param pointer to object to free + */ +void +snd_seq_system_info_free (snd_seq_system_info_t * obj) +{ + free (obj); +} + +/** + * \brief obtain the sequencer system information + * \param seq sequencer handle + * \param info the pointer to be stored + * \return 0 on success otherwise a negative error code + * + * Stores the global system information of ALSA sequencer system. + * The returned data contains + * the maximum available numbers of queues, clients, ports and channels. + */ +int +snd_seq_system_info (snd_seq_t * seq, snd_seq_system_info_t * info) +{ + dbg_printf ("snd_seq_system_info()\n"); + + return 0; +} + +/** + * \brief Get maximum number of clients + * \param info #snd_seq_system_info_t container + * \return maximum number of clients + * + * \sa snd_seq_system_info() + */ +int +snd_seq_system_info_get_clients (const snd_seq_system_info_t * info) +{ + dbg_printf ("snd_seq_system_info_get_clients()\n"); + + return 4; +} + +/** + * \brief Get maximum number of queues + * \param info #snd_seq_system_info_t container + * \return maximum number of queues + * + * \sa snd_seq_system_info() + */ +int +snd_seq_system_info_get_queues (const snd_seq_system_info_t * info) +{ + dbg_printf ("snd_seq_system_info_get_queues(info=%x)\n", info); + + return 1; // TODO +} + +/** + * \brief Get maximum number of ports + * \param info #snd_seq_system_info_t container + * \return maximum number of ports + * + * \sa snd_seq_system_info() + */ +int +snd_seq_system_info_get_ports (const snd_seq_system_info_t * info) +{ + dbg_printf ("snd_seq_system_info_get_ports()\n"); + + return 4; +} + +/** + * \brief allocate a queue with the specified name + * \param seq sequencer handle + * \param name the name of the new queue + * \return the queue id (zero or positive) on success otherwise a negative error code + * + * \sa snd_seq_alloc_queue() + */ +int +snd_seq_alloc_named_queue (snd_seq_t * seq, const char *name) +{ + dbg_printf ("snd_seq_alloc_named_queue(seq=%x, name=%s)\n", seq, name); + + return 0; +} + + +/** + * \brief Get client id of a port_info container + * \param info port_info container + * \return client id + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_client() + */ +int +snd_seq_port_info_get_client (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_client()\n"); + + return 0; +} + +/** + * \brief Get the port-specified mode of a port_info container + * \param info port_info container + * \return 1 if port id is specified at creation + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_set_port_specified() + */ +int +snd_seq_port_info_get_port_specified (const snd_seq_port_info_t * info) +{ + dbg_printf ("snd_seq_port_info_get_port_specified()\n"); + + return 0; +} + +/** + * \brief Get the type bits of a port_info container + * \param info port_info container + * \return port type bits + * + * \sa snd_seq_get_port_info(), snd_seq_port_info_get_type() + */ +void +snd_seq_port_info_set_type (snd_seq_port_info_t * info, unsigned int type) +{ + dbg_printf ("snd_seq_port_info_set_type(%u)\n", type); +} + + +/** + * \brief Get the ppq of a queue_status container + * \param info queue_status container + * \return ppq value + * + * \sa snd_seq_get_queue_tempo() + */ +int +snd_seq_queue_tempo_get_ppq (const snd_seq_queue_tempo_t * info) +{ + dbg_printf ("snd_seq_queue_tempo_get_ppq()\n"); + + return 0; +} + + +/** + * \brief Get the tempo of a queue_status container + * \param info queue_status container + * \return tempo value + * + * \sa snd_seq_get_queue_tempo() + */ +unsigned int +snd_seq_queue_tempo_get_tempo (const snd_seq_queue_tempo_t * info) +{ + dbg_printf ("snd_seq_queue_tempo_get_tempo()\n"); + + return 0; +} + +/** + * \brief set the tempo of the queue + * \param seq sequencer handle + * \param q queue id to change the tempo + * \param tempo tempo information + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_get_queue_tempo() + */ +int +snd_seq_set_queue_tempo (snd_seq_t * seq, int q, + snd_seq_queue_tempo_t * tempo) +{ + dbg_printf ("snd_seq_set_queue_tempo(seq=%x, q=%d, tempo=%x)\n", seq, q, + tempo); + + return 0; +} + +/** + * \brief allocate a queue + * \param seq sequencer handle + * \return the queue id (zero or positive) on success otherwise a negative error code + * + * \sa snd_seq_alloc_named_queue(), snd_seq_create_queue(), snd_seq_free_queue(), + * snd_seq_get_queue_info() + */ +int +snd_seq_alloc_queue (snd_seq_t * seq) +{ + static int queue_num = 0; + dbg_printf ("snd_seq_alloc_queue(seq=%x)=%d\n", seq, queue_num); + + return queue_num++; +} + +#define FIXED_EV(x) (_SND_SEQ_TYPE(SND_SEQ_EVFLG_FIXED) | _SND_SEQ_TYPE(x)) + +/** Event types conversion array */ +const unsigned int snd_seq_event_types[256] = { + [SND_SEQ_EVENT_SYSTEM...SND_SEQ_EVENT_RESULT] + = FIXED_EV (SND_SEQ_EVFLG_RESULT), + [SND_SEQ_EVENT_NOTE] + = + FIXED_EV (SND_SEQ_EVFLG_NOTE) | + _SND_SEQ_TYPE_OPT (SND_SEQ_EVFLG_NOTE_TWOARG), + [SND_SEQ_EVENT_NOTEON...SND_SEQ_EVENT_KEYPRESS] = + FIXED_EV (SND_SEQ_EVFLG_NOTE), + [SND_SEQ_EVENT_CONTROLLER...SND_SEQ_EVENT_REGPARAM] = + FIXED_EV (SND_SEQ_EVFLG_CONTROL), + [SND_SEQ_EVENT_START...SND_SEQ_EVENT_STOP] = FIXED_EV (SND_SEQ_EVFLG_QUEUE), + [SND_SEQ_EVENT_SETPOS_TICK] + = + FIXED_EV (SND_SEQ_EVFLG_QUEUE) | + _SND_SEQ_TYPE_OPT (SND_SEQ_EVFLG_QUEUE_TICK), + [SND_SEQ_EVENT_SETPOS_TIME] = + FIXED_EV (SND_SEQ_EVFLG_QUEUE) | + _SND_SEQ_TYPE_OPT (SND_SEQ_EVFLG_QUEUE_TIME), + [SND_SEQ_EVENT_TEMPO...SND_SEQ_EVENT_SYNC_POS] = + FIXED_EV (SND_SEQ_EVFLG_QUEUE) | + _SND_SEQ_TYPE_OPT (SND_SEQ_EVFLG_QUEUE_VALUE), + [SND_SEQ_EVENT_TUNE_REQUEST...SND_SEQ_EVENT_SENSING] = + FIXED_EV (SND_SEQ_EVFLG_NONE), + [SND_SEQ_EVENT_ECHO...SND_SEQ_EVENT_OSS] = + FIXED_EV (SND_SEQ_EVFLG_RAW) | FIXED_EV (SND_SEQ_EVFLG_SYSTEM), + [SND_SEQ_EVENT_CLIENT_START...SND_SEQ_EVENT_PORT_CHANGE] = + FIXED_EV (SND_SEQ_EVFLG_MESSAGE), + [SND_SEQ_EVENT_PORT_SUBSCRIBED...SND_SEQ_EVENT_PORT_UNSUBSCRIBED] = + FIXED_EV (SND_SEQ_EVFLG_CONNECTION), + [SND_SEQ_EVENT_USR0...SND_SEQ_EVENT_USR9] = + FIXED_EV (SND_SEQ_EVFLG_RAW) | FIXED_EV (SND_SEQ_EVFLG_USERS), + [SND_SEQ_EVENT_SYSEX...SND_SEQ_EVENT_BOUNCE] = + _SND_SEQ_TYPE (SND_SEQ_EVFLG_VARIABLE), + [SND_SEQ_EVENT_USR_VAR0...SND_SEQ_EVENT_USR_VAR4] = + _SND_SEQ_TYPE (SND_SEQ_EVFLG_VARIABLE) | + _SND_SEQ_TYPE (SND_SEQ_EVFLG_USERS), + [SND_SEQ_EVENT_NONE] = FIXED_EV (SND_SEQ_EVFLG_NONE), +}; + +/** + * \brief obtain the running state of the queue + * \param seq sequencer handle + * \param q queue id to query + * \param status pointer to store the current status + * \return 0 on success otherwise a negative error code + * + * Obtains the running state of the specified queue q. + */ +int +snd_seq_get_queue_status (snd_seq_t * seq, int q, + snd_seq_queue_status_t * status) +{ + dbg_printf ("snd_seq_get_queue_status(seq=%x. q=%d)\n", seq, q); + + return 0; +} + +/** + * \brief allocate an empty #snd_seq_queue_status_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_queue_status_malloc (snd_seq_queue_status_t ** ptr) +{ + dbg_printf ("snd_seq_queue_status_malloc()\n"); + *ptr = calloc (1, sizeof (snd_seq_queue_status_t)); + dbg_printf ("snd_seq_queue_status_malloc()=%x\n", *ptr); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_queue_status_t + * \param pointer to object to free + */ +void +snd_seq_queue_status_free (snd_seq_queue_status_t * obj) +{ + dbg_printf ("snd_seq_queue_status_free(%x)\n", obj); + + free (obj); +} + +/** + * \brief Get the tick time of a queue_status container + * \param info queue_status container + * \return tick time + * + * \sa snd_seq_get_queue_status() + */ +snd_seq_tick_time_t +snd_seq_queue_status_get_tick_time (const snd_seq_queue_status_t * info) +{ + dbg_printf ("snd_seq_queue_status_get_tick_time(info=%x)\n", info); + + return 0; +} + +/** + * \brief get size of #snd_seq_remove_events_t + * \return size in bytes + */ +size_t +snd_seq_remove_events_sizeof () +{ + return sizeof (snd_seq_remove_events_t); +} + +/** + * \brief allocate an empty #snd_seq_remove_events_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_remove_events_malloc (snd_seq_remove_events_t ** ptr) +{ + assert (ptr); + *ptr = calloc (1, sizeof (snd_seq_remove_events_t)); + dbg_printf ("snd_seq_remove_events_malloc()=%x\n", *ptr); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_remove_events_t + * \param pointer to object to free + */ +void +snd_seq_remove_events_free (snd_seq_remove_events_t * obj) +{ + free (obj); +} + +/** + * \brief Set the removal condition bits + * \param info remove_events container + * \param flags removal condition bits + * + * \sa snd_seq_remove_events() + */ +void +snd_seq_remove_events_set_condition (snd_seq_remove_events_t * info, + unsigned int flags) +{ + dbg_printf ("snd_seq_remove_events_set_condition(rmp=%x, flags=%lu)\n", + info, flags); +} + +/** + * \brief Set the queue as removal condition + * \param info remove_events container + * \param queue queue id + * + * \sa snd_seq_remove_events() + */ +void +snd_seq_remove_events_set_queue (snd_seq_remove_events_t * info, int queue) +{ + dbg_printf ("snd_seq_remove_events_set_queue(rmp=%x, q=%d)\n", info, queue); +} + +/** + * \brief remove events on input/output buffers and pools + * \param seq sequencer handle + * \param rmp remove event container + * + * Removes matching events with the given condition from input/output buffers + * and pools. + * The removal condition is specified in \a rmp argument. + * + * \sa snd_seq_event_output(), snd_seq_drop_output(), snd_seq_reset_pool_output() + */ +int +snd_seq_remove_events (snd_seq_t * seq, snd_seq_remove_events_t * rmp) +{ + dbg_printf ("snd_seq_remove_events(seq=%x, rmp=%x)\n", seq, rmp); + + return 1; +} + + +/** + * \brief clear input buffer and and remove events in sequencer queue + * \param seq sequencer handle + * + * \sa snd_seq_drop_input_buffer(), snd_seq_remove_events() + */ +int +snd_seq_drop_input (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_drop_input(seq=%x)\n", seq); + + return 0; +} + +/** + * \brief remove all events on user-space output buffer + * \param seq sequencer handle + * + * Removes all events on user-space output buffer. + * Unlike snd_seq_drain_output(), this function doesn't remove + * events on output memory pool of sequencer. + * + * \sa snd_seq_drop_output() + */ +int +snd_seq_drop_output_buffer (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_drop_output_buffer(seq=%x)\n", seq); + + return 0; +} + +/** + * \brief extract the first event in output buffer + * \param seq sequencer handle + * \param ev_res event pointer to be extracted + * \return 0 on success otherwise a negative error code + * + * Extracts the first event in output buffer. + * If ev_res is NULL, just remove the event. + * + * \sa snd_seq_event_output() + */ +int +snd_seq_extract_output (snd_seq_t * seq, snd_seq_event_t ** ev_res) +{ + dbg_printf ("snd_seq_extract_output(seq=%x)\n", seq); + + return -EIO; +} + +/** + * \brief get size of #snd_seq_queue_status_t + * \return size in bytes + */ +size_t +snd_seq_queue_status_sizeof () +{ + return sizeof (snd_seq_queue_status_t); +} + +/** + * \brief Return the size of output buffer + * \param seq sequencer handle + * \return the size of output buffer in bytes + * + * Obtains the size of output buffer. + * This buffer is used to store decoded byte-stream of output events + * before transferring to sequencer. + * + * \sa snd_seq_set_output_buffer_size() + */ +size_t +snd_seq_get_output_buffer_size (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_get_output_buffer_size(seq=%x)\n", seq); + + return 1024; +} + +/** + * \brief Change the size of output buffer + * \param seq sequencer handle + * \param size the size of output buffer to be changed in bytes + * \return 0 on success otherwise a negative error code + * + * Changes the size of output buffer. + * + * \sa snd_seq_get_output_buffer_size() + */ +int +snd_seq_set_output_buffer_size (snd_seq_t * seq, size_t size) +{ + dbg_printf ("snd_seq_set_output_buffer_size(seq=%x, size=%d)\n", seq, size); + return 0; +} + +/** + * \brief unsubscribe a connection between ports + * \param seq sequencer handle + * \param sub subscription information to disconnect + * \return 0 on success otherwise a negative error code + * + * Unsubscribes a connection between two ports, + * described in sender and dest fields in sub argument. + * + * \sa snd_seq_subscribe_port(), snd_seq_disconnect_from(), snd_seq_disconnect_to() + */ +int +snd_seq_unsubscribe_port (snd_seq_t * seq, snd_seq_port_subscribe_t * sub) +{ + dbg_printf ("snd_seq_unsubscribe_port(seq=%x, sub=%x)\n", seq, sub); + + return 0; +} + +/** + * \brief Get the queue id of a queue_timer container + * \param info queue_timer container + * \return queue id + * + * \sa snd_seq_get_queue_timer() + */ +int +snd_seq_queue_timer_get_queue (const snd_seq_queue_timer_t * info) +{ + dbg_printf ("snd_seq_queue_timer_get_queue(timer=%x)\n", info); + + return 0; +} + +/** + * \brief obtain the queue timer information + * \param seq sequencer handle + * \param q queue id to query + * \param timer pointer to store the timer information + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_set_queue_timer() + */ +int +snd_seq_get_queue_timer (snd_seq_t * seq, int q, + snd_seq_queue_timer_t * timer) +{ + dbg_printf ("snd_seq_get_queue_timer(seq=%x, q=%d, timer=%x)\n", + seq, 1, timer); + + return 0; +} + +/** + * \brief Set the timer id of a queue_timer container + * \param info queue_timer container + * \param id timer id pointer + * + * \sa snd_seq_get_queue_timer() + */ +void +snd_seq_queue_timer_set_id (snd_seq_queue_timer_t * info, + const snd_timer_id_t * id) +{ + dbg_printf ("snd_seq_queue_timer_set_id(timer=%x, id=%x)\n", info, id); +} + +/** + * \brief get size of #snd_seq_queue_timer_t + * \return size in bytes + */ +size_t +snd_seq_queue_timer_sizeof () +{ + return sizeof (snd_seq_queue_timer_t); +} + +/** + * \brief get size of #snd_seq_query_subscribe_t + * \return size in bytes + */ +size_t +snd_seq_query_subscribe_sizeof () +{ + return sizeof (snd_seq_query_subscribe_t); +} + +/** + * \brief allocate an empty #snd_seq_query_subscribe_t using standard malloc + * \param ptr returned pointer + * \return 0 on success otherwise negative error code + */ +int +snd_seq_query_subscribe_malloc (snd_seq_query_subscribe_t ** ptr) +{ + *ptr = calloc (1, sizeof (snd_seq_query_subscribe_t)); + + dbg_printf ("snd_seq_query_subscribe_malloc()=%x\n", *ptr); + if (!*ptr) + return -ENOMEM; + return 0; +} + +/** + * \brief frees a previously allocated #snd_seq_query_subscribe_t + * \param pointer to object to free + */ +void +snd_seq_query_subscribe_free (snd_seq_query_subscribe_t * obj) +{ + dbg_printf ("snd_seq_query_subscribe_free(obj=%x)\n", obj); + free (obj); +} + +/** + * \brief Get the address of subscriber of a query_subscribe container + * \param info query_subscribe container + * \return subscriber's address pointer + * + * \sa snd_seq_query_port_subscribers() + */ +const snd_seq_addr_t * +snd_seq_query_subscribe_get_addr (const snd_seq_query_subscribe_t * info) +{ + dbg_printf ("snd_seq_query_subscribe_get_addr(info=%x)\n", info); + + return NULL; // TODO +} + +/** + * \brief Get the index of subscriber of a query_subscribe container + * \param info query_subscribe container + * \return subscriber's index + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_set_index() + */ +int +snd_seq_query_subscribe_get_index (const snd_seq_query_subscribe_t * info) +{ + dbg_printf ("snd_seq_query_subscribe_get_index(info=%x)\n", info); + + return 0; // TODO +} + +/** + * \brief Set the subscriber's index to be queried + * \param info query_subscribe container + * \param index index to be queried + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_get_index() + */ +void +snd_seq_query_subscribe_set_index (snd_seq_query_subscribe_t * info, + int index) +{ + dbg_printf ("snd_seq_query_subscribe_t(info=%x, index=%d)\n", info, index); + + // TODO +} + +/** + * \brief Get the client/port address of a query_subscribe container + * \param info query_subscribe container + * \return client/port address pointer + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_set_root() + */ +const snd_seq_addr_t * +snd_seq_query_subscribe_get_root (const snd_seq_query_subscribe_t * info) +{ + dbg_printf ("snd_seq_query_subscribe_get_root(info=%x)\n", info); + + return NULL; // TODO +} + +/** + * \brief Set the client/port address of a query_subscribe container + * \param info query_subscribe container + * \param addr client/port address pointer + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_get_root() + */ +void +snd_seq_query_subscribe_set_root (snd_seq_query_subscribe_t * info, + const snd_seq_addr_t * addr) +{ + dbg_printf ("snd_seq_query_subscribe_set_root(info=%d, addr=%x)\n", + info, addr); +} + +/** + * \brief Get the query type of a query_subscribe container + * \param info query_subscribe container + * \return query type + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_set_type() + */ +snd_seq_query_subs_type_t +snd_seq_query_subscribe_get_type (const snd_seq_query_subscribe_t * info) +{ + dbg_printf ("snd_seq_query_subscribe_get_type(info=%x)\n", info); + + return 0; // TODO +} + +/** + * \brief Set the query type of a query_subscribe container + * \param info query_subscribe container + * \param type query type + * + * \sa snd_seq_query_port_subscribers(), snd_seq_query_subscribe_get_type() + */ +void +snd_seq_query_subscribe_set_type (snd_seq_query_subscribe_t * info, + snd_seq_query_subs_type_t type) +{ + dbg_printf ("snd_seq_query_subscribe_set_type(info=%x, type=%x)\n", + info, type); + + // TODO +} + +/** + * \brief obtain subscription information + * \param seq sequencer handle + * \param sub pointer to return the subscription information + * \return 0 on success otherwise a negative error code + * + * \sa snd_seq_subscribe_port(), snd_seq_query_port_subscribers() + */ +int +snd_seq_get_port_subscription (snd_seq_t * seq, + snd_seq_port_subscribe_t * sub) +{ + dbg_printf ("snd_seq_get_port_subscription(seq=%x, sub=%x)\n", seq, sub); + + return 0; // TODO +} + +/** + * \brief query port subscriber list + * \param seq sequencer handle + * \param subs subscription to query + * \return 0 on success otherwise a negative error code + * + * Queries the subscribers accessing to a port. + * The query information is specified in subs argument. + * + * At least, the client id, the port id, the index number and + * the query type must be set to perform a proper query. + * As the query type, #SND_SEQ_QUERY_SUBS_READ or #SND_SEQ_QUERY_SUBS_WRITE + * can be specified to check whether the readers or the writers to the port. + * To query the first subscription, set 0 to the index number. To list up + * all the subscriptions, call this function with the index numbers from 0 + * until this returns a negative value. + * + * \sa snd_seq_get_port_subscription() + */ +int +snd_seq_query_port_subscribers (snd_seq_t * seq, snd_seq_query_subscribe_t * + subs) +{ + dbg_printf ("snd_seq_query_port_subscribers(seq=%x, subs=%x)\n", seq, subs); + + return 0; // TODO +} + +/** + * \brief Get the real time of a queue_status container + * \param info queue_status container + * \param time real time + * + * \sa snd_seq_get_queue_status() + */ +const snd_seq_real_time_t * +snd_seq_queue_status_get_real_time (const snd_seq_queue_status_t * info) +{ + dbg_printf ("snd_seq_queue_status_get_real_time(info=%x)\n", info); + + return NULL; // TODO +} diff --git a/lib/libsalsa/seq_input.c b/lib/libsalsa/seq_input.c new file mode 100644 index 0000000..b61d22a --- /dev/null +++ b/lib/libsalsa/seq_input.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +static snd_seq_event_t * +alloc_event (snd_seq_t * seq) +{ + snd_seq_event_t *ev; + + if (seq->nevents >= MAX_EVENTS) + { + fprintf (stderr, "libsalsa: Local buffer overflow - event dropped\n"); + return NULL; + } + + ev = &seq->events[seq->nevents++]; + + ev->type = SND_SEQ_EVENT_SENSING; /* NOP message */ + + return ev; +} + +static void +voice_message (snd_seq_t * seq, unsigned char msg, unsigned char ch, + unsigned char *parms, int len) +{ + snd_seq_event_t *ev; + if ((ev = alloc_event (seq)) == NULL) + return; + + dbg_printf3 ("Voice message %02x, ch=%d, %3d, %3d\n", msg, ch, parms[0], + parms[1]); + + switch (msg) + { + case MIDI_NOTEON: + ev->type = SND_SEQ_EVENT_NOTEON; + ev->data.note.channel = ch; + ev->data.note.note = parms[0]; + ev->data.note.velocity = parms[1]; + break; + + case MIDI_NOTEOFF: + ev->type = SND_SEQ_EVENT_NOTEOFF; + ev->data.note.channel = ch; + ev->data.note.note = parms[0]; + ev->data.note.velocity = parms[1]; + break; + } +} + +static void +channel_message (snd_seq_t * seq, unsigned char msg, unsigned char ch, + unsigned char *parms, int len) +{ + snd_seq_event_t *ev; + if ((ev = alloc_event (seq)) == NULL) + return; + + dbg_printf3 ("Channel message %02x, ch=%d, %3d, %3d\n", msg, ch, parms[0], + parms[1]); +} + +static void +realtime_message (snd_seq_t * seq, unsigned char msg, unsigned char *parms, + int len) +{ + snd_seq_event_t *ev; + if ((ev = alloc_event (seq)) == NULL) + return; + + dbg_printf3 ("Realtime message %02x, %2x\n", msg, parms[0]); +} + +static void +sysex_message (snd_seq_t * seq, unsigned char *parms, int len) +{ + int i; + snd_seq_event_t *ev; + if ((ev = alloc_event (seq)) == NULL) + return; + + + if (alib_verbose > 2) + { + printf ("Sysex message: "); + for (i = 0; i < len; i++) + printf ("%02x ", parms[i]); + printf ("\n"); + } +} + +void +midiparser_callback (void *context, int category, unsigned char msg, + unsigned char ch, unsigned char *parms, int len) +{ + + switch (category) + { + case CAT_VOICE: + voice_message ((snd_seq_t *) context, msg, ch, parms, len); + break; + + case CAT_CHN: + channel_message ((snd_seq_t *) context, msg, ch, parms, len); + break; + + case CAT_REALTIME: + realtime_message ((snd_seq_t *) context, msg, parms, len); + break; + + case CAT_SYSEX: + sysex_message ((snd_seq_t *) context, parms, len); + break; + + case CAT_MTC: + default: + dbg_printf ("Unknown MIDI message category %d\n", category); + } +} diff --git a/lib/libsalsa/seq_output.c b/lib/libsalsa/seq_output.c new file mode 100644 index 0000000..6170dc2 --- /dev/null +++ b/lib/libsalsa/seq_output.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +static int +midi_out3 (snd_seq_t * seq, int msg, int parm1, int parm2) +{ + unsigned char buf[3]; + int l; + + if (msg < 0 || msg >= 0xff) + return -ERANGE; + if (parm1 < 0 || parm1 > 0x7f) + return -ERANGE; + if (parm2 < 0 || parm2 > 0x7f) + return -ERANGE; + + buf[0] = msg; + buf[1] = parm1; + buf[2] = parm2; + + if ((l = write (seq->fd, buf, 3)) != 3) + { + if (l == -1) + return -errno; + + return -EBADE; /* Randomly selected error */ + } + + return 0; +} + +static int +midi_out2 (snd_seq_t * seq, int msg, int parm1) +{ + unsigned char buf[3]; + int l; + + if (msg < 0 || msg >= 0xff) + return -ERANGE; + if (parm1 < 0 || parm1 > 0x7f) + return -ERANGE; + + buf[0] = msg; + buf[1] = parm1; + + if ((l = write (seq->fd, buf, 2)) != 2) + { + if (l == -1) + return -errno; + + return -EBADE; /* Randomly selected error */ + } + + return 0; +} + +int +convert_event (snd_seq_t * seq, snd_seq_event_t * ev) +{ + int value; + + dbg_printf3 + ("Event %2d: flags=%08x tag=%08x, q=%2d, time=%d, src=%x, dst=%x\n", + ev->type, ev->flags, ev->tag, ev->queue, ev->time, ev->source, ev->dest); + + switch (ev->type) + { + case SND_SEQ_EVENT_CONTROLLER: + dbg_printf3 ("\tSND_SEQ_EVENT_CONTRLLER %2d, %3d, %3d\n", + ev->data.control.channel, + ev->data.control.param, ev->data.control.value); + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0xB0 + ev->data.control.channel, + ev->data.control.param, ev->data.control.value); + break; + + case SND_SEQ_EVENT_PGMCHANGE: + dbg_printf3 ("\tSND_SEQ_EVENT_PGMCHANGE %2d, %3d, %3d\n", + ev->data.control.channel, + ev->data.control.param, ev->data.control.value); + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out2 (seq, 0xC0 + ev->data.control.channel, + ev->data.control.value); + break; + + case SND_SEQ_EVENT_CHANPRESS: + dbg_printf3 ("\tSND_SEQ_EVENT_CHANPRESS %2d, %5d\n", + ev->data.control.channel, ev->data.control.value); + value = ev->data.control.value + 8192; + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0xD0 + ev->data.control.channel, + value & 0x7f, (value >> 7) & 0x7f); + break; + + case SND_SEQ_EVENT_PITCHBEND: + dbg_printf3 ("\tSND_SEQ_EVENT_PITCHBEND %2d, %5d\n", + ev->data.control.channel, ev->data.control.value); + value = ev->data.control.value + 8192; + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0xE0 + ev->data.control.channel, + value & 0x7f, (value >> 7) & 0x7f); + break; + + case SND_SEQ_EVENT_NOTEON: + dbg_printf3 ("\tSND_SEQ_EVENT_NOTEON %2d, %3d, %d\n", + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0x90 + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_NOTEOFF: + dbg_printf3 ("\tSND_SEQ_EVENT_NOTEOFF %2d, %3d, %d\n", + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0x80 + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_KEYPRESS: + dbg_printf3 ("\tSND_SEQ_EVENT_KEYPRESS %2d, %3d, %d\n", + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + if (ev->data.control.channel > 15) + return -ERANGE; + return midi_out3 (seq, 0xA0 + ev->data.note.channel, + ev->data.note.note, ev->data.note.velocity); + break; + + case SND_SEQ_EVENT_SYSTEM: + dbg_printf ("\tSND_SEQ_EVENT_SYSTEM\n"); + break; + case SND_SEQ_EVENT_RESULT: + dbg_printf ("\tSND_SEQ_EVENT_RESULT\n"); + break; + case SND_SEQ_EVENT_NOTE: + dbg_printf ("\tSND_SEQ_EVENT_NOTE\n"); + break; + case SND_SEQ_EVENT_CONTROL14: + dbg_printf ("\tSND_SEQ_EVENT_CONTROL14\n"); + break; + case SND_SEQ_EVENT_NONREGPARAM: + dbg_printf ("\tSND_SEQ_EVENT_NONREGPARAM\n"); + break; + case SND_SEQ_EVENT_REGPARAM: + dbg_printf ("\tSND_SEQ_EVENT_REGPARAM\n"); + break; + case SND_SEQ_EVENT_SONGPOS: + dbg_printf ("\tSND_SEQ_EVENT_SONGPOS\n"); + break; + case SND_SEQ_EVENT_SONGSEL: + dbg_printf ("\tSND_SEQ_EVENT_SONGSEL\n"); + break; + case SND_SEQ_EVENT_QFRAME: + dbg_printf ("\tSND_SEQ_EVENT_QFRAME\n"); + break; + case SND_SEQ_EVENT_TIMESIGN: + dbg_printf ("\tSND_SEQ_EVENT_TIMESIGN\n"); + break; + case SND_SEQ_EVENT_KEYSIGN: + dbg_printf ("\tSND_SEQ_EVENT_KEYSIGN\n"); + break; + case SND_SEQ_EVENT_START: + dbg_printf ("\tSND_SEQ_EVENT_START\n"); + break; + case SND_SEQ_EVENT_CONTINUE: + dbg_printf ("\tSND_SEQ_EVENT_CONTINUE\n"); + break; + case SND_SEQ_EVENT_STOP: + dbg_printf ("\tSND_SEQ_EVENT_STOP\n"); + break; + case SND_SEQ_EVENT_SETPOS_TICK: + dbg_printf ("\tSND_SEQ_EVENT_SETPOS_TICK\n"); + break; + case SND_SEQ_EVENT_SETPOS_TIME: + dbg_printf ("\tSND_SEQ_EVENT_SETPOS_TIME\n"); + break; + case SND_SEQ_EVENT_TEMPO: + dbg_printf ("\tSND_SEQ_EVENT_TEMPO\n"); + break; + case SND_SEQ_EVENT_CLOCK: + dbg_printf ("\tSND_SEQ_EVENT_CLOCK\n"); + break; + case SND_SEQ_EVENT_TICK: + dbg_printf ("\tSND_SEQ_EVENT_TICK\n"); + break; + case SND_SEQ_EVENT_QUEUE_SKEW: + dbg_printf ("\tSND_SEQ_EVENT_QUEUE_SKEW\n"); + break; + case SND_SEQ_EVENT_SYNC_POS: + dbg_printf ("\tSND_SEQ_EVENT_SYNC_POS\n"); + break; + case SND_SEQ_EVENT_TUNE_REQUEST: + dbg_printf ("\tSND_SEQ_EVENT_TUNE_REQUEST\n"); + break; + case SND_SEQ_EVENT_RESET: + dbg_printf ("\tSND_SEQ_EVENT_RESET\n"); + break; + case SND_SEQ_EVENT_SENSING: + dbg_printf ("\tSND_SEQ_EVENT_SENSING\n"); + break; + case SND_SEQ_EVENT_ECHO: + dbg_printf ("\tSND_SEQ_EVENT_ECHO\n"); + break; + case SND_SEQ_EVENT_OSS: + dbg_printf ("\tSND_SEQ_EVENT_OSS\n"); + break; + case SND_SEQ_EVENT_CLIENT_START: + dbg_printf ("\tSND_SEQ_EVENT_CLIENT_START\n"); + break; + case SND_SEQ_EVENT_CLIENT_EXIT: + dbg_printf ("\tSND_SEQ_EVENT_CLIENT_EXIT\n"); + break; + case SND_SEQ_EVENT_CLIENT_CHANGE: + dbg_printf ("\tSND_SEQ_EVENT_CLIENT_CHANGE\n"); + break; + case SND_SEQ_EVENT_PORT_START: + dbg_printf ("\tSND_SEQ_EVENT_PORT_START\n"); + break; + case SND_SEQ_EVENT_PORT_EXIT: + dbg_printf ("\tSND_SEQ_EVENT_PORT_EXIT\n"); + break; + case SND_SEQ_EVENT_PORT_CHANGE: + dbg_printf ("\tSND_SEQ_EVENT_PORT_CHANGE\n"); + break; + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + dbg_printf ("\tSND_SEQ_EVENT_PORT_SUBSCRIBED\n"); + break; + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + dbg_printf ("\tSND_SEQ_EVENT_PORT_UNSUBSCRIBED\n"); + break; + case SND_SEQ_EVENT_USR0: + dbg_printf ("\tSND_SEQ_EVENT_USR0\n"); + break; + case SND_SEQ_EVENT_USR1: + dbg_printf ("\tSND_SEQ_EVENT_USR1\n"); + break; + case SND_SEQ_EVENT_USR2: + dbg_printf ("\tSND_SEQ_EVENT_USR2\n"); + break; + case SND_SEQ_EVENT_USR3: + dbg_printf ("\tSND_SEQ_EVENT_USR3\n"); + break; + case SND_SEQ_EVENT_USR4: + dbg_printf ("\tSND_SEQ_EVENT_USR4\n"); + break; + case SND_SEQ_EVENT_USR5: + dbg_printf ("\tSND_SEQ_EVENT_USR5\n"); + break; + case SND_SEQ_EVENT_USR6: + dbg_printf ("\tSND_SEQ_EVENT_USR6\n"); + break; + case SND_SEQ_EVENT_USR7: + dbg_printf ("\tSND_SEQ_EVENT_USR7\n"); + break; + case SND_SEQ_EVENT_USR8: + dbg_printf ("\tSND_SEQ_EVENT_USR8\n"); + break; + case SND_SEQ_EVENT_USR9: + dbg_printf ("\tSND_SEQ_EVENT_USR9\n"); + break; + case SND_SEQ_EVENT_SYSEX: + dbg_printf ("\tSND_SEQ_EVENT_SYSEX\n"); + break; + case SND_SEQ_EVENT_BOUNCE: + dbg_printf ("\tSND_SEQ_EVENT_BOUNCE\n"); + break; + case SND_SEQ_EVENT_USR_VAR0: + dbg_printf ("\tSND_SEQ_EVENT_USR_VAR0\n"); + break; + case SND_SEQ_EVENT_USR_VAR1: + dbg_printf ("\tSND_SEQ_EVENT_USR_VAR1\n"); + break; + case SND_SEQ_EVENT_USR_VAR2: + dbg_printf ("\tSND_SEQ_EVENT_USR_VAR2\n"); + break; + case SND_SEQ_EVENT_USR_VAR3: + dbg_printf ("\tSND_SEQ_EVENT_USR_VAR3\n"); + break; + case SND_SEQ_EVENT_USR_VAR4: + dbg_printf ("\tSND_SEQ_EVENT_USR_VAR4\n"); + break; + case SND_SEQ_EVENT_NONE: + dbg_printf ("\tSND_SEQ_EVENT_NONE\n"); + break; + default: + dbg_printf ("\tUnknown event type %d\n", ev->type); + } + return 0; +} diff --git a/lib/libsalsa/seqmid.c b/lib/libsalsa/seqmid.c new file mode 100644 index 0000000..e3ad1d0 --- /dev/null +++ b/lib/libsalsa/seqmid.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +/** + * \brief create a port - simple version + * \param seq sequencer handle + * \param name the name of the port + * \param caps capability bits + * \param type type bits + * \return the created port number or negative error code + * + * Creates a port with the given capability and type bits. + * + * \sa snd_seq_create_port(), snd_seq_delete_simple_port() + */ +int +snd_seq_create_simple_port (snd_seq_t * seq, const char *name, + unsigned int caps, unsigned int type) +{ + snd_seq_port_info_t pinfo; + int result; + + dbg_printf + ("snd_seq_create_simple_port(seq=%x, name='%s', caps=%x, type=%x)\n", seq, + name, caps, type); + + memset (&pinfo, 0, sizeof (pinfo)); + if (name) + strncpy (pinfo.name, name, sizeof (pinfo.name) - 1); + pinfo.capability = caps; + pinfo.type = type; + pinfo.midi_channels = 16; + pinfo.midi_voices = 64; /* XXX */ + pinfo.synth_voices = 0; /* XXX */ + + result = snd_seq_create_port (seq, &pinfo); + if (result < 0) + return result; + else + return pinfo.port; +} + +/** + * \brief delete the port + * \param seq sequencer handle + * \param port port id + * \return 0 on success or negative error code + * + * \sa snd_seq_delete_port(), snd_seq_create_simple_port() + */ +int +snd_seq_delete_simple_port (snd_seq_t * seq, int port) +{ + dbg_printf ("snd_seq_delete_simple_port()\n"); + + return 0; +} + +/** + * \brief set client name + * \param seq sequencer handle + * \param name name string + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_info() + */ +int +snd_seq_set_client_name (snd_seq_t * seq, const char *name) +{ + snd_seq_client_info_t info; + int err; + + dbg_printf ("snd_seq_set_client_name(seq=%x, name='%s')\n", seq, name); + + if ((err = snd_seq_get_client_info (seq, &info)) < 0) + return err; + strncpy (info.name, name, sizeof (info.name) - 1); + return snd_seq_set_client_info (seq, &info); +} + +/** + * \brief change the output pool size of the given client + * \param seq sequencer handle + * \param size output pool size + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_pool() + */ +int +snd_seq_set_client_pool_output (snd_seq_t * seq, size_t size) +{ + dbg_printf ("snd_seq_set_client_pool_output(seq=%x, size=%d)\n", seq, size); + return 0; +} + +/** + * \brief parse the given string and get the sequencer address + * \param seq sequencer handle + * \param addr the address pointer to be returned + * \param arg the string to be parsed + * \return 0 on success or negative error code + * + * This function parses the sequencer client and port numbers from the given string. + * The client and port tokes are separated by either colon or period, e.g. 128:1. + * When \a seq is not NULL, the function accepts also a client name not only + * digit numbers. + */ +int +snd_seq_parse_address (snd_seq_t * seq, snd_seq_addr_t * addr, + const char *arg) +{ + dbg_printf ("snd_seq_parse_address()\n"); + + return 0; +} + +/** + * \brief wait until all events are processed + * \param seq sequencer handle + * \return 0 on success or negative error code + * + * This function waits until all events of this client are processed. + * + * \sa snd_seq_drain_output() + */ +int +snd_seq_sync_output_queue (snd_seq_t * seq) +{ + dbg_printf ("snd_seq_sync_output_queue()\n"); + + return 0; +} + +/** + * \brief simple disconnection + * \param myport the port id as sender + * \param dest_client destination client id + * \param dest_port destination port id + * \return 0 on success or negative error code + * + * Remove connection from the given sender client:port + * to the given destination port in the current client. + * + * \sa snd_seq_unsubscribe_port(), snd_seq_connect_to() + */ +int +snd_seq_disconnect_to (snd_seq_t * seq, int myport, int dest_client, + int dest_port) +{ + dbg_printf + ("snd_seq_disconnect_to(seq=%x, myport=%d, dest_client=%d, dest_port=%d)\n", + seq, myport, dest_client, dest_port); + + return 0; +} + +/** + * \brief change the input pool size of the given client + * \param seq sequencer handle + * \param size input pool size + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_pool() + */ +int +snd_seq_set_client_pool_input (snd_seq_t * seq, size_t size) +{ + dbg_printf ("snd_seq_set_client_pool_input(seq=%x, size=%d)\n", seq, size); + + return 0; +} + +/** + * \brief change the output room size of the given client + * \param seq sequencer handle + * \param size output room size + * \return 0 on success or negative error code + * + * \sa snd_seq_set_client_pool() + */ +int +snd_seq_set_client_pool_output_room (snd_seq_t * seq, size_t size) +{ + dbg_printf ("snd_seq_set_client_pool_output_room(seq=%x, size=%d)\n", seq, + size); + + return 0; +} diff --git a/lib/libsalsa/stubs.c b/lib/libsalsa/stubs.c new file mode 100644 index 0000000..f13771f --- /dev/null +++ b/lib/libsalsa/stubs.c @@ -0,0 +1,59 @@ +#include <stdio.h> +extern int alib_verbose; + +#define STUB(x) \ +int x(void) \ +{ \ + if (alib_verbose>0)printf("libsalsa: Stub for " #x " called.\n"); \ + return -53; \ +} + +#define STUB_NULL(x) \ +void* x(void) \ +{ \ + if (alib_verbose>0)printf("libsalsa: Stub for " #x " called.\n"); \ + return (void*)0; \ +} + +STUB (snd_ctl_elem_info_get_count) +STUB (snd_hctl_elem_write) +STUB (snd_hctl_first_elem) +STUB (snd_ctl_elem_info_get_item_name) +STUB (snd_hctl_elem_get_callback_private) +STUB_NULL (snd_ctl_card_info_get_driver) +STUB (snd_ctl_elem_value_get_enumerated) +STUB (snd_hctl_set_callback) +STUB (snd_ctl_elem_info_set_item) +STUB (snd_hctl_load) +STUB (snd_ctl_elem_info_get_step) +STUB (snd_ctl_elem_value_set_enumerated) +STUB (snd_ctl_elem_info_get_name) +STUB (snd_hctl_handle_events) +STUB (snd_ctl_elem_info_sizeof) +STUB (snd_ctl_elem_value_get_boolean) +STUB (snd_ctl_elem_value_malloc) +STUB (snd_ctl_elem_id_set_index) +STUB (snd_ctl_elem_value_get_integer) +STUB (snd_hctl_nonblock) +STUB (snd_hctl_get_count) +STUB (snd_ctl_card_info_get_longname) +STUB (snd_hctl_elem_next) +STUB (snd_ctl_elem_info_get_index) +STUB (snd_hctl_elem_read) +STUB (snd_hctl_find_elem) +STUB (snd_ctl_elem_value_set_integer) +STUB (snd_hctl_elem_set_callback) +STUB (snd_hctl_poll_descriptors) +STUB (snd_ctl_elem_info_get_items) +STUB (snd_ctl_elem_info_get_max) +STUB (snd_hctl_open) +STUB (snd_ctl_elem_value_set_boolean) +STUB (snd_ctl_elem_id_set_interface) +STUB (snd_hctl_set_callback_private) +STUB (snd_ctl_elem_id_sizeof) +STUB (snd_ctl_elem_info_get_type) +STUB (snd_hctl_close) +STUB (snd_ctl_elem_info_malloc) +STUB (snd_ctl_elem_info_get_min) +STUB (snd_hctl_elem_info) +STUB (snd_hctl_elem_set_callback_private) STUB (snd_ctl_elem_id_set_name) diff --git a/lib/libsalsa/timer.c b/lib/libsalsa/timer.c new file mode 100644 index 0000000..3ea5da6 --- /dev/null +++ b/lib/libsalsa/timer.c @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2004 by Hannu Savolainen < hannu@opensound.com> + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 2.1 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include <stdio.h> +#include "local.h" + +struct _snd_timer +{ + int dummy; +}; + +struct _snd_timer_id +{ + int dummy; +}; + +struct _snd_timer_info +{ + int dummy; +}; + +/** + * \brief Opens a new connection to the timer interface. + * \param timer Returned handle (NULL if not wanted) + * \param name ASCII identifier of the timer handle + * \param mode Open mode + * \return 0 on success otherwise a negative error code + * + * Opens a new connection to the timer interface specified with + * an ASCII identifier and mode. + */ +int +snd_timer_open (snd_timer_t ** tmr, const char *name, int mode) +{ + snd_timer_t *timer; + + ALIB_INIT (); + if (!alib_appcheck ()) + { + dbg_printf ("snd_timer_open(%s, %x) refused,\n", name, mode); + return -ENODEV; + } + + timer = malloc (sizeof (*timer)); + + dbg_printf ("snd_timer_open(name='%s', mode=%x)=%x\n", name, mode, timer); + + if (timer == NULL) + return -ENOMEM; + + *tmr = timer; + + return 0; +} + +/** + * \brief close timer handle + * \param timer timer handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified timer handle and frees all associated + * resources. + */ +int +snd_timer_close (snd_timer_t * timer) +{ + dbg_printf ("snd_timer_close(%x)\n", timer); + + free (timer); + return 0; +} + +/** + * \brief get size of the snd_timer_id_t structure in bytes + * \return size of the snd_timer_id_t structure in bytes + */ +size_t +snd_timer_id_sizeof () +{ + return sizeof (snd_timer_id_t); +} + +/** + * \brief get timer card + * \param params pointer to #snd_timer_id_t structure + * \return timer card number + */ +int +snd_timer_id_get_card (snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_id_get_card(tid=%x)\n", tid); + + return 0; // TODO +} + +/** + * \brief get timer class + * \param tid pointer to #snd_timer_id_t structure + * \return timer class + */ +int +snd_timer_id_get_class (snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_id_get_class(tid=%x)\n", tid); + + return 0; // TODO +} + + +/** + * \brief get timer device + * \param params pointer to #snd_timer_id_t structure + * \return timer device number + */ +int +snd_timer_id_get_device (snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_id_get_device(tid=%x)\n", tid); + + return 0; // TODO +} + +/** + * \brief get timer sub-class + * \param params pointer to #snd_timer_id_t structure + * \return timer sub-class + */ +int +snd_timer_id_get_sclass (snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_id_get_sclass(tid=%x)\n", tid); + + return 0; // TODO +} + +/** + * \brief get timer subdevice + * \param params pointer to #snd_timer_id_t structure + * \return timer subdevice number + */ +int +snd_timer_id_get_subdevice (snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_id_get_subdevice(tid=%x)\n", tid); + + return 0; // TODO +} + +/** + * \brief set timer card + * \param tid pointer to #snd_timer_id_t structure + * \param card card number + */ +void +snd_timer_id_set_card (snd_timer_id_t * tid, int card) +{ + dbg_printf ("snd_timer_id_set_card(tid=%x, card=%d)\n", tid, card); + + // TODO +} + +/** + * \brief set timer class + * \param tid pointer to #snd_timer_id_t structure + * \param dev_class class of timer device + */ +void +snd_timer_id_set_class (snd_timer_id_t * tid, int dev_class) +{ + dbg_printf ("snd_timer_id_set_class(tid=%x, dev_class=%d)\n", tid, + dev_class); + // TODO +} + +/** + * \brief set timer device + * \param tid pointer to #snd_timer_id_t structure + * \param device device number + */ +void +snd_timer_id_set_device (snd_timer_id_t * tid, int device) +{ + dbg_printf ("snd_timer_id_set_device(tid=%x, device=%d)\n", tid, device); + + // TODO +} + +/** + * \brief set timer sub-class + * \param tid pointer to #snd_timer_id_t structure + * \param dev_sclass sub-class of timer device + */ +void +snd_timer_id_set_sclass (snd_timer_id_t * tid, int dev_sclass) +{ + dbg_printf ("snd_timer_id_set_sclass(tid=%x, dev_sclass=%d)\n", + tid, dev_sclass); + // TODO +} + +/** + * \brief set timer subdevice + * \param tid pointer to #snd_timer_id_t structure + * \param subdevice subdevice number + */ +void +snd_timer_id_set_subdevice (snd_timer_id_t * tid, int subdevice) +{ + dbg_printf ("snd_timer_id_set_subdevice(tid=%x, subdevice=%d)\n", + tid, subdevice); + + // TODO +} + +/** + * \brief get size of the snd_timer_info_t structure in bytes + * \return size of the snd_timer_info_t structure in bytes + */ +size_t +snd_timer_info_sizeof () +{ + return sizeof (snd_timer_info_t); +} + +/** + * \brief allocate a new snd_timer_info_t structure + * \param ptr returned pointer + * \return 0 on success otherwise a negative error code if fails + * + * Allocates a new snd_timer_info_t structure using the standard + * malloc C library function. + */ +int +snd_timer_info_malloc (snd_timer_info_t ** info) +{ + *info = calloc (1, sizeof (snd_timer_info_t)); + dbg_printf ("snd_timer_info_malloc()=%x\n", *info); + if (!*info) + return -ENOMEM; + return 0; +} + +/** + * \brief frees the snd_timer_info_t structure + * \param info pointer to the snd_timer_info_t structure to free + * + * Frees the given snd_timer_info_t structure using the standard + * free C library function. + */ +void +snd_timer_info_free (snd_timer_info_t * info) +{ + dbg_printf ("snd_timer_info_free(%x)\n", info); + free (info); +} + +/** + * \brief get information about timer handle + * \param timer timer handle + * \param info pointer to a snd_timer_info_t structure to be filled + * \return 0 on success otherwise a negative error code + */ +int +snd_timer_info (snd_timer_t * timer, snd_timer_info_t * info) +{ + dbg_printf ("snd_timer_info(timer=%x, info=%x)\n", timer, info); + + // TODO + + return 0; +} + +/** + * \brief get timer name + * \param info pointer to #snd_timer_info_t structure + * \return timer name + */ +const char * +snd_timer_info_get_name (snd_timer_info_t * info) +{ + dbg_printf ("snd_timer_info_get_name(info=%x)\n", info); + + return "OSS Timer"; // TODO +} + + +/** + * \brief get timer resolution in us + * \param info pointer to #snd_timer_info_t structure + * \return timer resolution + */ +long +snd_timer_info_get_resolution (snd_timer_info_t * info) +{ + dbg_printf ("snd_timer_info_get_resolution(info=%x)\n", info); + + return 1000; // TODO +} + + +static int +snd_timer_query_open_conf (snd_timer_query_t ** timer, + const char *name, snd_config_t * timer_root, + snd_config_t * timer_conf, int mode) +{ + dbg_printf + ("snd_timer_query_open_conf(name='%s', root=%x, conf=%x, mode=%x)\n", + name, timer_root, timer_conf, mode); + ALIB_INIT (); + if (!alib_appcheck ()) + return -ENODEV; + + return -EIO; +} + +/** + * \brief close timer query handle + * \param timer timer handle + * \return 0 on success otherwise a negative error code + * + * Closes the specified timer handle and frees all associated + * resources. + */ +int +snd_timer_query_close (snd_timer_query_t * timer) +{ + dbg_printf ("snd_timer_query_close(timer=%x)\n", timer); + + return 0; +} + +/** + * \brief obtain the next timer identification + * \param timer timer handle + * \param tid timer identification + * \return 0 on success otherwise a negative error code + * + * if tid->dev_class is -1, then the first device is returned + * if result tid->dev_class is -1, no more devices are left + */ +int +snd_timer_query_next_device (snd_timer_query_t * timer, snd_timer_id_t * tid) +{ + dbg_printf ("snd_timer_query_next_device(timer=%x, tid=%x)\n", timer, tid); + + return -1; +} |