diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-24 21:28:16 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-24 21:28:16 +0000 |
commit | 526b5580dabbfed7cfe5439dc3a90ec727f563c2 (patch) | |
tree | 22b1af92348785daad16714ee5e2b633017e0e48 /apps/plugins/pdbox/PDa/src/s_audio_oss.c | |
parent | 4f2dfcc01b260d946044ef2b6af5fe36cb772c8d (diff) | |
download | rockbox-526b5580dabbfed7cfe5439dc3a90ec727f563c2.tar.gz rockbox-526b5580dabbfed7cfe5439dc3a90ec727f563c2.zip |
Cut the files in half and it might work better (note to self: check your tree is really clean before patching)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21070 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/src/s_audio_oss.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/src/s_audio_oss.c | 843 |
1 files changed, 0 insertions, 843 deletions
diff --git a/apps/plugins/pdbox/PDa/src/s_audio_oss.c b/apps/plugins/pdbox/PDa/src/s_audio_oss.c index efb15d1bc1..de11f66243 100644 --- a/apps/plugins/pdbox/PDa/src/s_audio_oss.c +++ b/apps/plugins/pdbox/PDa/src/s_audio_oss.c | |||
@@ -842,847 +842,4 @@ void oss_getdevs(char *indevlist, int *nindevs, | |||
842 | } | 842 | } |
843 | 843 | ||
844 | #endif | 844 | #endif |
845 | /* Copyright (c) 1997-2003 Guenter Geiger, Miller Puckette, Larry Troxler, | ||
846 | * Winfried Ritsch, Karl MacMillan, and others. | ||
847 | * For information on usage and redistribution, and for a DISCLAIMER OF ALL | ||
848 | * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ | ||
849 | |||
850 | /* this file inputs and outputs audio using the OSS API available on linux. */ | ||
851 | |||
852 | #ifdef USEAPI_OSS | ||
853 | |||
854 | #include <linux/soundcard.h> | ||
855 | |||
856 | #include "m_pd.h" | ||
857 | #include "s_stuff.h" | ||
858 | #include <errno.h> | ||
859 | #include <stdio.h> | ||
860 | #include <unistd.h> | ||
861 | #include <stdlib.h> | ||
862 | #include <string.h> | ||
863 | #include <sys/types.h> | ||
864 | #include <sys/time.h> | ||
865 | #include <sys/stat.h> | ||
866 | #include <sys/ioctl.h> | ||
867 | #include <fcntl.h> | ||
868 | #include <sched.h> | ||
869 | #include <sys/mman.h> | ||
870 | |||
871 | |||
872 | /* Defines */ | ||
873 | #define DEBUG(x) x | ||
874 | #define DEBUG2(x) {x;} | ||
875 | |||
876 | #define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ | ||
877 | #define OSS_MAXDEV 4 /* maximum number of input or output devices */ | ||
878 | #define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ | ||
879 | #define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ | ||
880 | #define OSS_DEFAULTCH 2 | ||
881 | #define RME_DEFAULTCH 8 /* need this even if RME undefined */ | ||
882 | typedef int16_t t_oss_int16; | ||
883 | typedef int32_t t_oss_int32; | ||
884 | #define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) | ||
885 | #define OSS_BYTESPERCHAN(width) (DEFDACBLKSIZE * (width)) | ||
886 | #define OSS_XFERSAMPS(chans) (DEFDACBLKSIZE* (chans)) | ||
887 | #define OSS_XFERSIZE(chans, width) (DEFDACBLKSIZE * (chans) * (width)) | ||
888 | |||
889 | /* GLOBALS */ | ||
890 | static int linux_meters; /* true if we're metering */ | ||
891 | static float linux_inmax; /* max input amplitude */ | ||
892 | static float linux_outmax; /* max output amplitude */ | ||
893 | static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ | ||
894 | |||
895 | /* our device handles */ | ||
896 | |||
897 | typedef struct _oss_dev | ||
898 | { | ||
899 | int d_fd; | ||
900 | unsigned int d_space; /* bytes available for writing/reading */ | ||
901 | int d_bufsize; /* total buffer size in blocks for this device */ | ||
902 | int d_dropcount; /* # of buffers to drop for resync (output only) */ | ||
903 | unsigned int d_nchannels; /* number of channels for this device */ | ||
904 | unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ | ||
905 | } t_oss_dev; | ||
906 | |||
907 | static t_oss_dev linux_dacs[OSS_MAXDEV]; | ||
908 | static t_oss_dev linux_adcs[OSS_MAXDEV]; | ||
909 | static int linux_noutdevs = 0; | ||
910 | static int linux_nindevs = 0; | ||
911 | |||
912 | /* exported variables */ | ||
913 | float sys_dacsr; | ||
914 | t_sample *sys_soundout; | ||
915 | t_sample *sys_soundin; | ||
916 | |||
917 | /* OSS-specific private variables */ | ||
918 | static int oss_blockmode = 1; /* flag to use "blockmode" */ | ||
919 | static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ | ||
920 | static char ossdsp[] = "/dev/dsp%d"; | ||
921 | |||
922 | /* don't assume we can turn all 31 bits when doing float-to-fix; | ||
923 | otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ | ||
924 | #define FMAX 0x7ffff000 | ||
925 | #define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) | ||
926 | |||
927 | |||
928 | /* ------------- private routines for all APIS ------------------- */ | ||
929 | |||
930 | static void linux_flush_all_underflows_to_zero(void) | ||
931 | { | ||
932 | /* | ||
933 | TODO: Implement similar thing for linux (GGeiger) | ||
934 | |||
935 | One day we will figure this out, I hope, because it | ||
936 | costs CPU time dearly on Intel - LT | ||
937 | */ | ||
938 | /* union fpc_csr f; | ||
939 | f.fc_word = get_fpc_csr(); | ||
940 | f.fc_struct.flush = 1; | ||
941 | set_fpc_csr(f.fc_word); | ||
942 | */ | ||
943 | } | ||
944 | |||
945 | static int oss_ndev = 0; | ||
946 | |||
947 | /* find out how many OSS devices we have. Since this has to | ||
948 | open the devices to find out if they're there, we have | ||
949 | to be called before audio is actually started up. So we | ||
950 | cache the results, which in effect are the number of available | ||
951 | devices. */ | ||
952 | void oss_init(void) | ||
953 | { | ||
954 | int fd, i; | ||
955 | static int countedthem = 0; | ||
956 | if (countedthem) | ||
957 | return; | ||
958 | for (i = 0; i < 10; i++) | ||
959 | { | ||
960 | char devname[100]; | ||
961 | if (i == 0) | ||
962 | strcpy(devname, "/dev/dsp"); | ||
963 | else sprintf(devname, "/dev/dsp%d", i); | ||
964 | if ( (fd = open(devname, O_WRONLY|O_NONBLOCK)) != -1) | ||
965 | { | ||
966 | oss_ndev++; | ||
967 | close(fd); | ||
968 | } | ||
969 | else break; | ||
970 | } | ||
971 | countedthem = 1; | ||
972 | } | ||
973 | |||
974 | |||
975 | void oss_set32bit( void) | ||
976 | { | ||
977 | oss_32bit = 1; | ||
978 | } | ||
979 | |||
980 | |||
981 | typedef struct _multidev { | ||
982 | int fd; | ||
983 | int channels; | ||
984 | int format; | ||
985 | } t_multidev; | ||
986 | |||
987 | int oss_reset(int fd) { | ||
988 | int err; | ||
989 | if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0) | ||
990 | error("OSS: Could not reset"); | ||
991 | return err; | ||
992 | } | ||
993 | |||
994 | /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels | ||
995 | but is proposed by Guenter Geiger to support extending OSS to handle | ||
996 | 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. | ||
997 | I'm not clear why this isn't called AFMT_S32_[SLN]E... */ | ||
998 | |||
999 | #ifndef AFMT_S32_BLOCKED | ||
1000 | #define AFMT_S32_BLOCKED 0x0000400 | ||
1001 | #endif | ||
1002 | |||
1003 | void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) | ||
1004 | { /* IOhannes */ | ||
1005 | int orig, param, nblk, fd = dev->d_fd, wantformat; | ||
1006 | int nchannels = dev->d_nchannels; | ||
1007 | int advwas = sys_schedadvance; | ||
1008 | |||
1009 | audio_buf_info ainfo; | ||
1010 | |||
1011 | /* IOhannes : | ||
1012 | * pd is very likely to crash if different formats are used on | ||
1013 | multiple soundcards | ||
1014 | */ | ||
1015 | |||
1016 | /* set resolution - first try 4 byte samples */ | ||
1017 | if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && | ||
1018 | (param & AFMT_S32_BLOCKED)) | ||
1019 | { | ||
1020 | wantformat = AFMT_S32_BLOCKED; | ||
1021 | dev->d_bytespersamp = 4; | ||
1022 | } | ||
1023 | else | ||
1024 | { | ||
1025 | wantformat = AFMT_S16_NE; | ||
1026 | dev->d_bytespersamp = 2; | ||
1027 | } | ||
1028 | param = wantformat; | ||
1029 | |||
1030 | if (sys_verbose) | ||
1031 | post("bytes per sample = %d", dev->d_bytespersamp); | ||
1032 | if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) | ||
1033 | fprintf(stderr,"OSS: Could not set DSP format\n"); | ||
1034 | else if (wantformat != param) | ||
1035 | fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", | ||
1036 | wantformat, param); | ||
1037 | |||
1038 | /* sample rate */ | ||
1039 | orig = param = srate; | ||
1040 | if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) | ||
1041 | fprintf(stderr,"OSS: Could not set sampling rate for device\n"); | ||
1042 | else if( orig != param ) | ||
1043 | fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", | ||
1044 | orig, param ); | ||
1045 | |||
1046 | if (oss_blockmode && !skipblocksize) | ||
1047 | { | ||
1048 | int fragbytes, logfragsize, nfragment; | ||
1049 | /* setting fragment count and size. */ | ||
1050 | if (!linux_fragsize) | ||
1051 | { | ||
1052 | linux_fragsize = OSS_DEFFRAGSIZE; | ||
1053 | while (linux_fragsize > DEFDACBLKSIZE | ||
1054 | && linux_fragsize * 4 > sys_advance_samples) | ||
1055 | linux_fragsize = linux_fragsize/2; | ||
1056 | } | ||
1057 | |||
1058 | /* post("adv_samples %d", sys_advance_samples); */ | ||
1059 | nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; | ||
1060 | |||
1061 | fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); | ||
1062 | logfragsize = ilog2(fragbytes); | ||
1063 | |||
1064 | if (fragbytes != (1 << logfragsize)) | ||
1065 | post("warning: OSS takes only power of 2 blocksize; using %d", | ||
1066 | (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); | ||
1067 | if (sys_verbose) | ||
1068 | post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); | ||
1069 | |||
1070 | param = orig = (nfragment<<16) + logfragsize; | ||
1071 | if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) | ||
1072 | error("OSS: Could not set or read fragment size\n"); | ||
1073 | if (param != orig) | ||
1074 | { | ||
1075 | nfragment = ((param >> 16) & 0xffff); | ||
1076 | logfragsize = (param & 0xffff); | ||
1077 | post("warning: actual fragments %d, blocksize %d", | ||
1078 | nfragment, (1 << logfragsize)); | ||
1079 | } | ||
1080 | if (sys_verbose) | ||
1081 | post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); | ||
1082 | } | ||
1083 | if (dac) | ||
1084 | { | ||
1085 | /* use "free space" to learn the buffer size. Normally you | ||
1086 | should set this to your own desired value; but this seems not | ||
1087 | to be implemented uniformly across different sound cards. LATER | ||
1088 | we should figure out what to do if the requested scheduler advance | ||
1089 | is greater than this buffer size; for now, we just print something | ||
1090 | out. */ | ||
1091 | |||
1092 | int defect; | ||
1093 | if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) | ||
1094 | fprintf(stderr,"OSS: ioctl on output device failed"); | ||
1095 | dev->d_bufsize = ainfo.bytes; | ||
1096 | |||
1097 | defect = sys_advance_samples * (dev->d_bytespersamp * nchannels) | ||
1098 | - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); | ||
1099 | if (defect > 0) | ||
1100 | { | ||
1101 | if (sys_verbose || defect > (dev->d_bufsize >> 2)) | ||
1102 | fprintf(stderr, | ||
1103 | "OSS: requested audio buffer size %d limited to %d\n", | ||
1104 | sys_advance_samples * (dev->d_bytespersamp * nchannels), | ||
1105 | dev->d_bufsize); | ||
1106 | sys_advance_samples = | ||
1107 | (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / | ||
1108 | (dev->d_bytespersamp *nchannels); | ||
1109 | } | ||
1110 | } | ||
1111 | } | ||
1112 | |||
1113 | static int oss_setchannels(int fd, int wantchannels, char *devname) | ||
1114 | { /* IOhannes */ | ||
1115 | int param = wantchannels; | ||
1116 | 845 | ||
1117 | while (param>1) { | ||
1118 | int save = param; | ||
1119 | if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { | ||
1120 | error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); | ||
1121 | } else { | ||
1122 | if (param == save) return (param); | ||
1123 | } | ||
1124 | param=save-1; | ||
1125 | } | ||
1126 | |||
1127 | return (0); | ||
1128 | } | ||
1129 | |||
1130 | #define O_AUDIOFLAG 0 /* O_NDELAY */ | ||
1131 | |||
1132 | int oss_open_audio(int nindev, int *indev, int nchin, int *chin, | ||
1133 | int noutdev, int *outdev, int nchout, int *chout, int rate) | ||
1134 | { /* IOhannes */ | ||
1135 | int capabilities = 0; | ||
1136 | int inchannels = 0, outchannels = 0; | ||
1137 | char devname[20]; | ||
1138 | int n, i, fd; | ||
1139 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1140 | int num_devs = 0; | ||
1141 | int wantmore=0; | ||
1142 | int spread = 0; | ||
1143 | audio_buf_info ainfo; | ||
1144 | |||
1145 | linux_nindevs = linux_noutdevs = 0; | ||
1146 | |||
1147 | |||
1148 | /* mark input devices unopened */ | ||
1149 | for (i = 0; i < OSS_MAXDEV; i++) | ||
1150 | linux_adcs[i].d_fd = -1; | ||
1151 | |||
1152 | /* open output devices */ | ||
1153 | wantmore=0; | ||
1154 | if (noutdev < 0 || nindev < 0) | ||
1155 | bug("linux_open_audio"); | ||
1156 | |||
1157 | for (n = 0; n < noutdev; n++) | ||
1158 | { | ||
1159 | int gotchans, j, inindex = -1; | ||
1160 | int thisdevice = (outdev[n] >= 0 ? outdev[n] : n-1); | ||
1161 | int wantchannels = (nchout>n) ? chout[n] : wantmore; | ||
1162 | fd = -1; | ||
1163 | if (!wantchannels) | ||
1164 | goto end_out_loop; | ||
1165 | |||
1166 | if (thisdevice > 1) | ||
1167 | sprintf(devname, "/dev/dsp%d", thisdevice-1); | ||
1168 | else sprintf(devname, "/dev/dsp"); | ||
1169 | |||
1170 | /* search for input request for same device. Succeed only | ||
1171 | if the number of channels matches. */ | ||
1172 | for (j = 0; j < nindev; j++) | ||
1173 | if (indev[j] == thisdevice && chin[j] == wantchannels) | ||
1174 | inindex = j; | ||
1175 | |||
1176 | /* if the same device is requested for input and output, | ||
1177 | try to open it read/write */ | ||
1178 | if (inindex >= 0) | ||
1179 | { | ||
1180 | sys_setalarm(1000000); | ||
1181 | if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1) | ||
1182 | { | ||
1183 | post("%s (read/write): %s", devname, strerror(errno)); | ||
1184 | post("(now will try write-only...)"); | ||
1185 | } | ||
1186 | else | ||
1187 | { | ||
1188 | if (sys_verbose) | ||
1189 | post("opened %s for reading and writing\n", devname); | ||
1190 | linux_adcs[inindex].d_fd = fd; | ||
1191 | } | ||
1192 | } | ||
1193 | /* if that didn't happen or if it failed, try write-only */ | ||
1194 | if (fd == -1) | ||
1195 | { | ||
1196 | sys_setalarm(1000000); | ||
1197 | if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1) | ||
1198 | { | ||
1199 | post("%s (writeonly): %s", | ||
1200 | devname, strerror(errno)); | ||
1201 | break; | ||
1202 | } | ||
1203 | if (sys_verbose) | ||
1204 | post("opened %s for writing only\n", devname); | ||
1205 | } | ||
1206 | if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) | ||
1207 | error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); | ||
1208 | |||
1209 | gotchans = oss_setchannels(fd, | ||
1210 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
1211 | devname); | ||
1212 | |||
1213 | if (sys_verbose) | ||
1214 | post("opened audio output on %s; got %d channels", | ||
1215 | devname, gotchans); | ||
1216 | |||
1217 | if (gotchans < 2) | ||
1218 | { | ||
1219 | /* can't even do stereo? just give up. */ | ||
1220 | close(fd); | ||
1221 | } | ||
1222 | else | ||
1223 | { | ||
1224 | linux_dacs[linux_noutdevs].d_nchannels = gotchans; | ||
1225 | linux_dacs[linux_noutdevs].d_fd = fd; | ||
1226 | oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); | ||
1227 | |||
1228 | linux_noutdevs++; | ||
1229 | outchannels += gotchans; | ||
1230 | if (inindex >= 0) | ||
1231 | { | ||
1232 | linux_adcs[inindex].d_nchannels = gotchans; | ||
1233 | chin[inindex] = gotchans; | ||
1234 | } | ||
1235 | } | ||
1236 | /* LATER think about spreading large numbers of channels over | ||
1237 | various dsp's and vice-versa */ | ||
1238 | wantmore = wantchannels - gotchans; | ||
1239 | end_out_loop: ; | ||
1240 | } | ||
1241 | |||
1242 | /* open input devices */ | ||
1243 | wantmore = 0; | ||
1244 | for (n = 0; n < nindev; n++) | ||
1245 | { | ||
1246 | int gotchans=0; | ||
1247 | int thisdevice = (indev[n] >= 0 ? indev[n] : n-1); | ||
1248 | int wantchannels = (nchin>n)?chin[n]:wantmore; | ||
1249 | int alreadyopened = 0; | ||
1250 | if (!wantchannels) | ||
1251 | goto end_in_loop; | ||
1252 | |||
1253 | if (thisdevice > 1) | ||
1254 | sprintf(devname, "/dev/dsp%d", thisdevice - 1); | ||
1255 | else sprintf(devname, "/dev/dsp"); | ||
1256 | |||
1257 | sys_setalarm(1000000); | ||
1258 | |||
1259 | /* perhaps it's already open from the above? */ | ||
1260 | if (linux_dacs[n].d_fd >= 0) | ||
1261 | { | ||
1262 | fd = linux_dacs[n].d_fd; | ||
1263 | alreadyopened = 1; | ||
1264 | } | ||
1265 | else | ||
1266 | { | ||
1267 | /* otherwise try to open it here. */ | ||
1268 | if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1) | ||
1269 | { | ||
1270 | post("%s (readonly): %s", devname, strerror(errno)); | ||
1271 | goto end_in_loop; | ||
1272 | } | ||
1273 | if (sys_verbose) | ||
1274 | post("opened %s for reading only\n", devname); | ||
1275 | } | ||
1276 | linux_adcs[linux_nindevs].d_fd = fd; | ||
1277 | gotchans = oss_setchannels(fd, | ||
1278 | (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, | ||
1279 | devname); | ||
1280 | if (sys_verbose) | ||
1281 | post("opened audio input device %s; got %d channels", | ||
1282 | devname, gotchans); | ||
1283 | |||
1284 | if (gotchans < 1) | ||
1285 | { | ||
1286 | close(fd); | ||
1287 | goto end_in_loop; | ||
1288 | } | ||
1289 | |||
1290 | linux_adcs[linux_nindevs].d_nchannels = gotchans; | ||
1291 | |||
1292 | oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); | ||
1293 | |||
1294 | inchannels += gotchans; | ||
1295 | linux_nindevs++; | ||
1296 | |||
1297 | wantmore = wantchannels-gotchans; | ||
1298 | /* LATER think about spreading large numbers of channels over | ||
1299 | various dsp's and vice-versa */ | ||
1300 | end_in_loop: ; | ||
1301 | } | ||
1302 | |||
1303 | /* We have to do a read to start the engine. This is | ||
1304 | necessary because sys_send_dacs waits until the input | ||
1305 | buffer is filled and only reads on a filled buffer. | ||
1306 | This is good, because it's a way to make sure that we | ||
1307 | will not block. But I wonder why we only have to read | ||
1308 | from one of the devices and not all of them??? */ | ||
1309 | |||
1310 | if (linux_nindevs) | ||
1311 | { | ||
1312 | if (sys_verbose) | ||
1313 | fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); | ||
1314 | read(linux_adcs[0].d_fd, buf, | ||
1315 | linux_adcs[0].d_bytespersamp * | ||
1316 | linux_adcs[0].d_nchannels * DEFDACBLKSIZE); | ||
1317 | if (sys_verbose) | ||
1318 | fprintf(stderr, "...done.\n"); | ||
1319 | } | ||
1320 | sys_setalarm(0); | ||
1321 | return (0); | ||
1322 | } | ||
1323 | |||
1324 | void oss_close_audio( void) | ||
1325 | { | ||
1326 | int i; | ||
1327 | for (i=0;i<linux_nindevs;i++) | ||
1328 | close(linux_adcs[i].d_fd); | ||
1329 | |||
1330 | for (i=0;i<linux_noutdevs;i++) | ||
1331 | close(linux_dacs[i].d_fd); | ||
1332 | |||
1333 | linux_nindevs = linux_noutdevs = 0; | ||
1334 | } | ||
1335 | |||
1336 | static int linux_dacs_write(int fd,void* buf,long bytes) | ||
1337 | { | ||
1338 | return write(fd, buf, bytes); | ||
1339 | } | ||
1340 | |||
1341 | static int linux_adcs_read(int fd,void* buf,long bytes) | ||
1342 | { | ||
1343 | return read(fd, buf, bytes); | ||
1344 | } | ||
1345 | |||
1346 | /* query audio devices for "available" data size. */ | ||
1347 | static void oss_calcspace(void) | ||
1348 | { | ||
1349 | int dev; | ||
1350 | audio_buf_info ainfo; | ||
1351 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1352 | { | ||
1353 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
1354 | fprintf(stderr,"OSS: ioctl on output device %d failed",dev); | ||
1355 | linux_dacs[dev].d_space = ainfo.bytes; | ||
1356 | } | ||
1357 | |||
1358 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1359 | { | ||
1360 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE,&ainfo) < 0) | ||
1361 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
1362 | dev, linux_adcs[dev].d_fd); | ||
1363 | linux_adcs[dev].d_space = ainfo.bytes; | ||
1364 | } | ||
1365 | } | ||
1366 | |||
1367 | void linux_audiostatus(void) | ||
1368 | { | ||
1369 | int dev; | ||
1370 | if (!oss_blockmode) | ||
1371 | { | ||
1372 | oss_calcspace(); | ||
1373 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1374 | fprintf(stderr, "dac %d space %d\n", dev, linux_dacs[dev].d_space); | ||
1375 | |||
1376 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1377 | fprintf(stderr, "adc %d space %d\n", dev, linux_adcs[dev].d_space); | ||
1378 | |||
1379 | } | ||
1380 | } | ||
1381 | |||
1382 | /* this call resyncs audio output and input which will cause discontinuities | ||
1383 | in audio output and/or input. */ | ||
1384 | |||
1385 | static void oss_doresync( void) | ||
1386 | { | ||
1387 | int dev, zeroed = 0, wantsize; | ||
1388 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1389 | audio_buf_info ainfo; | ||
1390 | |||
1391 | /* 1. if any input devices are ahead (have more than 1 buffer stored), | ||
1392 | drop one or more buffers worth */ | ||
1393 | for (dev = 0; dev < linux_nindevs; dev++) | ||
1394 | { | ||
1395 | if (linux_adcs[dev].d_space == 0) | ||
1396 | { | ||
1397 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1398 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1399 | linux_adcs[dev].d_bytespersamp)); | ||
1400 | } | ||
1401 | else while (linux_adcs[dev].d_space > | ||
1402 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1403 | linux_adcs[dev].d_bytespersamp)) | ||
1404 | { | ||
1405 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1406 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1407 | linux_adcs[dev].d_bytespersamp)); | ||
1408 | if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) | ||
1409 | { | ||
1410 | fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", | ||
1411 | dev, linux_adcs[dev].d_fd); | ||
1412 | break; | ||
1413 | } | ||
1414 | linux_adcs[dev].d_space = ainfo.bytes; | ||
1415 | } | ||
1416 | } | ||
1417 | |||
1418 | /* 2. if any output devices are behind, feed them zeros to catch them | ||
1419 | up */ | ||
1420 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
1421 | { | ||
1422 | while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
1423 | sys_advance_samples * (linux_dacs[dev].d_nchannels * | ||
1424 | linux_dacs[dev].d_bytespersamp)) | ||
1425 | { | ||
1426 | if (!zeroed) | ||
1427 | { | ||
1428 | unsigned int i; | ||
1429 | for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); | ||
1430 | i++) | ||
1431 | buf[i] = 0; | ||
1432 | zeroed = 1; | ||
1433 | } | ||
1434 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
1435 | OSS_XFERSIZE(linux_dacs[dev].d_nchannels, | ||
1436 | linux_dacs[dev].d_bytespersamp)); | ||
1437 | if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) | ||
1438 | { | ||
1439 | fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", | ||
1440 | dev, linux_dacs[dev].d_fd); | ||
1441 | break; | ||
1442 | } | ||
1443 | linux_dacs[dev].d_space = ainfo.bytes; | ||
1444 | } | ||
1445 | } | ||
1446 | /* 3. if any DAC devices are too far ahead, plan to drop the | ||
1447 | number of frames which will let the others catch up. */ | ||
1448 | for (dev = 0; dev < linux_noutdevs; dev++) | ||
1449 | { | ||
1450 | if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - | ||
1451 | (sys_advance_samples - 1) * linux_dacs[dev].d_nchannels * | ||
1452 | linux_dacs[dev].d_bytespersamp) | ||
1453 | { | ||
1454 | linux_dacs[dev].d_dropcount = sys_advance_samples - 1 - | ||
1455 | (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / | ||
1456 | (linux_dacs[dev].d_nchannels * | ||
1457 | linux_dacs[dev].d_bytespersamp) ; | ||
1458 | } | ||
1459 | else linux_dacs[dev].d_dropcount = 0; | ||
1460 | } | ||
1461 | } | ||
1462 | |||
1463 | int oss_send_dacs(void) | ||
1464 | { | ||
1465 | t_sample *fp1, *fp2; | ||
1466 | long fill; | ||
1467 | int i, j, dev, rtnval = SENDDACS_YES; | ||
1468 | char buf[OSS_MAXSAMPLEWIDTH * DEFDACBLKSIZE * OSS_MAXCHPERDEV]; | ||
1469 | t_oss_int16 *sp; | ||
1470 | t_oss_int32 *lp; | ||
1471 | /* the maximum number of samples we should have in the ADC buffer */ | ||
1472 | int idle = 0; | ||
1473 | int thischan; | ||
1474 | t_time timeref, timenow; | ||
1475 | |||
1476 | if (!linux_nindevs && !linux_noutdevs) | ||
1477 | return (SENDDACS_NO); | ||
1478 | |||
1479 | if (!oss_blockmode) | ||
1480 | { | ||
1481 | /* determine whether we're idle. This is true if either (1) | ||
1482 | some input device has less than one buffer to read or (2) some | ||
1483 | output device has fewer than (sys_advance_samples) blocks buffered | ||
1484 | already. */ | ||
1485 | oss_calcspace(); | ||
1486 | |||
1487 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1488 | if (linux_dacs[dev].d_dropcount || | ||
1489 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > | ||
1490 | sys_advance_samples * linux_dacs[dev].d_bytespersamp * | ||
1491 | linux_dacs[dev].d_nchannels)) | ||
1492 | idle = 1; | ||
1493 | for (dev=0; dev < linux_nindevs; dev++) | ||
1494 | if (linux_adcs[dev].d_space < | ||
1495 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1496 | linux_adcs[dev].d_bytespersamp)) | ||
1497 | idle = 1; | ||
1498 | } | ||
1499 | |||
1500 | if (idle && !oss_blockmode) | ||
1501 | { | ||
1502 | /* sometimes---rarely---when the ADC available-byte-count is | ||
1503 | zero, it's genuine, but usually it's because we're so | ||
1504 | late that the ADC has overrun its entire kernel buffer. We | ||
1505 | distinguish between the two by waiting 2 msec and asking again. | ||
1506 | There should be an error flag we could check instead; look for this | ||
1507 | someday... */ | ||
1508 | for (dev = 0;dev < linux_nindevs; dev++) | ||
1509 | if (linux_adcs[dev].d_space == 0) | ||
1510 | { | ||
1511 | audio_buf_info ainfo; | ||
1512 | sys_microsleep(2000); | ||
1513 | oss_calcspace(); | ||
1514 | if (linux_adcs[dev].d_space != 0) continue; | ||
1515 | |||
1516 | /* here's the bad case. Give up and resync. */ | ||
1517 | sys_log_error(ERR_DATALATE); | ||
1518 | oss_doresync(); | ||
1519 | return (SENDDACS_NO); | ||
1520 | } | ||
1521 | /* check for slippage between devices, either because | ||
1522 | data got lost in the driver from a previous late condition, or | ||
1523 | because the devices aren't synced. When we're idle, no | ||
1524 | input device should have more than one buffer readable and | ||
1525 | no output device should have less than sys_advance_samples-1 | ||
1526 | */ | ||
1527 | |||
1528 | for (dev=0; dev < linux_noutdevs; dev++) | ||
1529 | if (!linux_dacs[dev].d_dropcount && | ||
1530 | (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < | ||
1531 | (sys_advance_samples - 2) * | ||
1532 | (linux_dacs[dev].d_bytespersamp * | ||
1533 | linux_dacs[dev].d_nchannels))) | ||
1534 | goto badsync; | ||
1535 | for (dev=0; dev < linux_nindevs; dev++) | ||
1536 | if (linux_adcs[dev].d_space > 3 * | ||
1537 | OSS_XFERSIZE(linux_adcs[dev].d_nchannels, | ||
1538 | linux_adcs[dev].d_bytespersamp)) | ||
1539 | goto badsync; | ||
1540 | |||
1541 | /* return zero to tell the scheduler we're idle. */ | ||
1542 | return (SENDDACS_NO); | ||
1543 | badsync: | ||
1544 | sys_log_error(ERR_RESYNC); | ||
1545 | oss_doresync(); | ||
1546 | return (SENDDACS_NO); | ||
1547 | |||
1548 | } | ||
1549 | |||
1550 | |||
1551 | /* do output */ | ||
1552 | |||
1553 | timeref = sys_getrealtime(); | ||
1554 | for (dev=0, thischan = 0; dev < linux_noutdevs; dev++) | ||
1555 | { | ||
1556 | int nchannels = linux_dacs[dev].d_nchannels; | ||
1557 | if (linux_dacs[dev].d_dropcount) | ||
1558 | linux_dacs[dev].d_dropcount--; | ||
1559 | else | ||
1560 | { | ||
1561 | if (linux_dacs[dev].d_bytespersamp == 4) | ||
1562 | { | ||
1563 | for (i = DEFDACBLKSIZE * nchannels, fp1 = sys_soundout + | ||
1564 | DEFDACBLKSIZE*thischan, | ||
1565 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
1566 | { | ||
1567 | t_sample f = SCALE32(*fp1); | ||
1568 | *lp = (f >= 2147483647 ? 2147483647 : | ||
1569 | (f < -2147483647 ? -2147483647 : f)); | ||
1570 | } | ||
1571 | } | ||
1572 | else | ||
1573 | { | ||
1574 | for (i = DEFDACBLKSIZE, fp1 = sys_soundout + | ||
1575 | DEFDACBLKSIZE*thischan, | ||
1576 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
1577 | { | ||
1578 | for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += DEFDACBLKSIZE) | ||
1579 | { | ||
1580 | int s = SCALE16(*fp2); | ||
1581 | if (s > 32767) s = 32767; | ||
1582 | else if (s < -32767) s = -32767; | ||
1583 | sp[j] = s; | ||
1584 | } | ||
1585 | } | ||
1586 | } | ||
1587 | |||
1588 | |||
1589 | #if 0 | ||
1590 | #define PR_S "%8d" | ||
1591 | { | ||
1592 | int nm = 64; | ||
1593 | int* sp1 = buf; | ||
1594 | post("dac:"); | ||
1595 | while (nm > 0) | ||
1596 | { | ||
1597 | post(PR_S PR_S PR_S PR_S PR_S PR_S PR_S PR_S, | ||
1598 | sp1[0], sp1[1], sp1[2], sp1[3], sp1[4], sp1[5], sp1[6], sp1[7]); | ||
1599 | nm -= 8; | ||
1600 | sp1 += 8; | ||
1601 | } | ||
1602 | } | ||
1603 | #endif | ||
1604 | linux_dacs_write(linux_dacs[dev].d_fd, buf, | ||
1605 | OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp)); | ||
1606 | |||
1607 | #if 0 | ||
1608 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
1609 | { | ||
1610 | post("dacslept %d",sys_getrealtime() - timeref); | ||
1611 | if (!oss_blockmode) | ||
1612 | sys_log_error(ERR_DACSLEPT); | ||
1613 | else rtnval = SENDDACS_SLEPT; | ||
1614 | } | ||
1615 | #endif | ||
1616 | timeref = timenow; | ||
1617 | } | ||
1618 | thischan += nchannels; | ||
1619 | } | ||
1620 | memset(sys_soundout, 0, | ||
1621 | sys_outchannels * (sizeof(float) * DEFDACBLKSIZE)); | ||
1622 | |||
1623 | /* do input */ | ||
1624 | |||
1625 | for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) | ||
1626 | { | ||
1627 | int nchannels = linux_adcs[dev].d_nchannels; | ||
1628 | linux_adcs_read(linux_adcs[dev].d_fd, buf, | ||
1629 | OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); | ||
1630 | |||
1631 | #if 0 | ||
1632 | if ((timenow = sys_getrealtime()) - timeref > 200) | ||
1633 | { | ||
1634 | if (!oss_blockmode) | ||
1635 | sys_log_error(ERR_ADCSLEPT); | ||
1636 | else | ||
1637 | rtnval = SENDDACS_SLEPT; | ||
1638 | } | ||
1639 | #endif | ||
1640 | timeref = timenow; | ||
1641 | |||
1642 | if (linux_adcs[dev].d_bytespersamp == 4) | ||
1643 | { | ||
1644 | for (i = DEFDACBLKSIZE*nchannels, | ||
1645 | fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
1646 | lp = (t_oss_int32 *)buf; i--; fp1++, lp++) | ||
1647 | { | ||
1648 | *fp1 = ((t_sample)(*lp))*(t_sample)(1./2147483648.); | ||
1649 | } | ||
1650 | } | ||
1651 | else | ||
1652 | { | ||
1653 | for (i = DEFDACBLKSIZE,fp1 = sys_soundin + thischan*DEFDACBLKSIZE, | ||
1654 | sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) | ||
1655 | { | ||
1656 | for (j=0;j<sys_inchannels;j++) | ||
1657 | fp1[j*DEFDACBLKSIZE] = INVSCALE16(sp[j]); | ||
1658 | } | ||
1659 | } | ||
1660 | thischan += nchannels; | ||
1661 | } | ||
1662 | if (thischan != sys_inchannels) | ||
1663 | bug("inchannels"); | ||
1664 | return (rtnval); | ||
1665 | } | ||
1666 | |||
1667 | void oss_listdevs( void) | ||
1668 | { | ||
1669 | post("device listing not implemented in OSS yet\n"); | ||
1670 | } | ||
1671 | |||
1672 | void oss_getdevs(char *indevlist, int *nindevs, | ||
1673 | char *outdevlist, int *noutdevs, int *canmulti, | ||
1674 | int maxndev, int devdescsize) | ||
1675 | { | ||
1676 | int i, ndev; | ||
1677 | *canmulti = 2; /* supports multiple devices */ | ||
1678 | if ((ndev = oss_ndev) > maxndev) | ||
1679 | ndev = maxndev; | ||
1680 | for (i = 0; i < ndev; i++) | ||
1681 | { | ||
1682 | sprintf(indevlist + i * devdescsize, "OSS device #%d", i+1); | ||
1683 | sprintf(outdevlist + i * devdescsize, "OSS device #%d", i+1); | ||
1684 | } | ||
1685 | *nindevs = *noutdevs = ndev; | ||
1686 | } | ||
1687 | |||
1688 | #endif | ||