#include #include #include #include #include #include #include #include "audio_lib.h" #include "lt_debug.h" #define AUDIO_DEVICE "/dev/dvb/adapter0/audio0" #define lt_debug(args...) _lt_debug(TRIPLE_DEBUG_AUDIO, this, args) #define lt_info(args...) _lt_info(TRIPLE_DEBUG_AUDIO, this, args) #include cAudio * audioDecoder = NULL; static int proc_put(const char *path, const char *value, const int len) { int ret, ret2; int pfd = open(path, O_WRONLY); if (pfd < 0) return pfd; ret = write(pfd, value, len); ret2 = close(pfd); if (ret2 < 0) return ret2; return ret; } cAudio::cAudio(void *, void *, void *) { fd = -1; clipfd = -1; mixer_fd = -1; openDevice(); Muted = false; #ifdef MARTII percent = 100; #endif } cAudio::~cAudio(void) { closeDevice(); } void cAudio::openDevice(void) { if (fd < 0) { if ((fd = open(AUDIO_DEVICE, O_RDWR)) < 0) lt_info("openDevice: open failed (%m)\n"); fcntl(fd, F_SETFD, FD_CLOEXEC); do_mute(true, false); } else lt_info("openDevice: already open (fd = %d)\n", fd); } void cAudio::closeDevice(void) { if (fd >= 0) close(fd); fd = -1; if (clipfd >= 0) close(clipfd); clipfd = -1; if (mixer_fd >= 0) close(mixer_fd); mixer_fd = -1; } int cAudio::do_mute(bool enable, bool remember) { lt_debug("%s(%d, %d)\n", __FUNCTION__, enable, remember); char str[4]; if (remember) Muted = enable; sprintf(str, "%d", Muted); proc_put("/proc/stb/audio/j1_mute", str, strlen(str)); if (!enable) { int f = open("/proc/stb/avs/0/volume", O_RDWR); read(f, str, 4); close(f); str[3] = '\0'; proc_put("/proc/stb/avs/0/volume", str, strlen(str)); } return 0; } int map_volume(const int volume) { unsigned char vol = volume; if (vol > 100) vol = 100; #ifdef MARTII vol = 64 - vol * 64 / 100; #else vol = 63 - vol * 63 / 100; #endif return vol; } int cAudio::setVolume(unsigned int left, unsigned int right) { lt_debug("%s(%d, %d)\n", __func__, left, right); volume = (left + right) / 2; #ifdef MARTII int v = map_volume((volume * percent)/100); #else int v = map_volume(volume); #endif if (clipfd != -1 && mixer_fd != -1) { int tmp = 0; /* not sure if left / right is correct here, but it is always the same anyways ;-) */ if (! Muted) tmp = left << 8 | right; int ret = ioctl(mixer_fd, MIXER_WRITE(mixer_num), &tmp); if (ret == -1) lt_info("%s: MIXER_WRITE(%d),%04x: %m\n", __func__, mixer_num, tmp); return ret; } #ifndef MARTII /* compensate for different decoding volume of mpeg and ac3 streams * TODO: check if this is correct for all channels * and if we need to do something with DTS? */ if (StreamType == AUDIO_FMT_MPEG) v = map_volume(volume * 30 / 53); #endif char str[4]; sprintf(str, "%d", v); proc_put("/proc/stb/avs/0/volume", str, strlen(str)); return 0; } int cAudio::Start(void) { int ret; ret = ioctl(fd, AUDIO_PLAY); return ret; } int cAudio::Stop(void) { return ioctl(fd, AUDIO_STOP); } #ifdef MARTII bool cAudio::Pause(bool Pcm) { ioctl(fd, Pcm ? AUDIO_PAUSE : AUDIO_CONTINUE, 1); return true; } #else bool cAudio::Pause(bool /*Pcm*/) { return true; }; #endif void cAudio::SetSyncMode(AVSYNC_TYPE Mode) { lt_debug("%s %d\n", __func__, Mode); ioctl(fd, AUDIO_SET_AV_SYNC, Mode); }; //AUDIO_ENCODING_AC3 #define AUDIO_STREAMTYPE_AC3 0 //AUDIO_ENCODING_MPEG2 #define AUDIO_STREAMTYPE_MPEG 1 //AUDIO_ENCODING_DTS #define AUDIO_STREAMTYPE_DTS 2 #define AUDIO_ENCODING_LPCM 2 #define AUDIO_ENCODING_LPCMA 11 void cAudio::SetStreamType(AUDIO_FORMAT type) { int bypass = AUDIO_STREAMTYPE_MPEG; #ifndef MARTII bool update_volume = (StreamType != type); #endif lt_debug("%s %d\n", __FUNCTION__, type); StreamType = type; switch (type) { case AUDIO_FMT_DOLBY_DIGITAL: bypass = AUDIO_STREAMTYPE_AC3; break; case AUDIO_FMT_DTS: bypass = AUDIO_STREAMTYPE_DTS; break; case AUDIO_FMT_MPEG: default: break; } // Normaly the encoding should be set using AUDIO_SET_ENCODING // But as we implemented the behavior to bypass (cause of e2) this is correct here if (ioctl(fd, AUDIO_SET_BYPASS_MODE, bypass) < 0) lt_info("%s: AUDIO_SET_BYPASS_MODE failed (%m)\n", __func__); #ifndef MARTII if (update_volume) setVolume(volume, volume); #endif }; int cAudio::setChannel(int channel) { lt_debug("%s %d\n", __FUNCTION__, channel); return 0; }; int cAudio::PrepareClipPlay(int ch, int srate, int bits, int little_endian) { int fmt; unsigned int devmask, stereo, usable; const char *dsp_dev = getenv("DSP_DEVICE"); const char *mix_dev = getenv("MIX_DEVICE"); lt_debug("%s ch %d srate %d bits %d le %d\n", __FUNCTION__, ch, srate, bits, little_endian); if (clipfd >= 0) { lt_info("%s: clipfd already opened (%d)\n", __FUNCTION__, clipfd); return -1; } mixer_num = -1; mixer_fd = -1; /* a different DSP device can be given with DSP_DEVICE and MIX_DEVICE * if this device cannot be opened, we fall back to the internal OSS device * Example: * modprobe snd-usb-audio * export DSP_DEVICE=/dev/sound/dsp2 * export MIX_DEVICE=/dev/sound/mixer2 * neutrino */ if ((!dsp_dev) || (access(dsp_dev, W_OK))) { if (dsp_dev) lt_info("%s: DSP_DEVICE is set (%s) but cannot be opened," " fall back to /dev/dsp1\n", __func__, dsp_dev); dsp_dev = "/dev/dsp1"; } lt_info("%s: dsp_dev %s mix_dev %s\n", __func__, dsp_dev, mix_dev); /* NULL mix_dev is ok */ /* the tdoss dsp driver seems to work only on the second open(). really. */ clipfd = open(dsp_dev, O_WRONLY); if (clipfd < 0) { lt_info("%s open %s: %m\n", dsp_dev, __FUNCTION__); return -1; } fcntl(clipfd, F_SETFD, FD_CLOEXEC); /* no idea if we ever get little_endian == 0 */ if (little_endian) fmt = AFMT_S16_BE; else fmt = AFMT_S16_LE; if (ioctl(clipfd, SNDCTL_DSP_SETFMT, &fmt)) perror("SNDCTL_DSP_SETFMT"); if (ioctl(clipfd, SNDCTL_DSP_CHANNELS, &ch)) perror("SNDCTL_DSP_CHANNELS"); if (ioctl(clipfd, SNDCTL_DSP_SPEED, &srate)) perror("SNDCTL_DSP_SPEED"); if (ioctl(clipfd, SNDCTL_DSP_RESET)) perror("SNDCTL_DSP_RESET"); if (!mix_dev) return 0; mixer_fd = open(mix_dev, O_RDWR); if (mixer_fd < 0) { lt_info("%s: open mixer %s failed (%m)\n", __func__, mix_dev); /* not a real error */ return 0; } if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) { lt_info("%s: SOUND_MIXER_READ_DEVMASK %m\n", __func__); devmask = 0; } if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stereo) == -1) { lt_info("%s: SOUND_MIXER_READ_STEREODEVS %m\n", __func__); stereo = 0; } usable = devmask & stereo; if (usable == 0) { lt_info("%s: devmask: %08x stereo: %08x, no usable dev :-(\n", __func__, devmask, stereo); close(mixer_fd); mixer_fd = -1; return 0; /* TODO: should we treat this as error? */ } /* __builtin_popcount needs GCC, it counts the set bits... */ if (__builtin_popcount (usable) != 1) { /* TODO: this code is not yet tested as I have only single-mixer devices... */ lt_info("%s: more than one mixer control: devmask %08x stereo %08x\n" "%s: querying MIX_NUMBER environment variable...\n", __func__, devmask, stereo, __func__); const char *tmp = getenv("MIX_NUMBER"); if (tmp) mixer_num = atoi(tmp); lt_info("%s: mixer_num is %d -> device %08x\n", __func__, mixer_num, (mixer_num >= 0) ? (1 << mixer_num) : 0); /* no error checking, you'd better know what you are doing... */ } else { mixer_num = 0; while (!(usable & 0x01)) { mixer_num++; usable >>= 1; } } setVolume(volume, volume); return 0; }; int cAudio::WriteClip(unsigned char *buffer, int size) { int ret; // lt_debug("cAudio::%s\n", __FUNCTION__); if (clipfd <= 0) { lt_info("%s: clipfd not yet opened\n", __FUNCTION__); return -1; } ret = write(clipfd, buffer, size); if (ret < 0) lt_info("%s: write error (%m)\n", __FUNCTION__); return ret; }; int cAudio::StopClip() { lt_debug("%s\n", __FUNCTION__); if (clipfd <= 0) { lt_info("%s: clipfd not yet opened\n", __FUNCTION__); return -1; } close(clipfd); clipfd = -1; if (mixer_fd >= 0) close(mixer_fd); mixer_fd = -1; setVolume(volume, volume); return 0; }; void cAudio::getAudioInfo(int &type, int &layer, int &freq, int &bitrate, int &mode) { lt_debug("%s\n", __FUNCTION__); type = 0; layer = 0; freq = 0; bitrate = 0; mode = 0; #if 0 unsigned int atype; static const int freq_mpg[] = {44100, 48000, 32000, 0}; static const int freq_ac3[] = {48000, 44100, 32000, 0}; scratchl2 i; if (ioctl(fd, MPEG_AUD_GET_DECTYP, &atype) < 0) perror("cAudio::getAudioInfo MPEG_AUD_GET_DECTYP"); if (ioctl(fd, MPEG_AUD_GET_STATUS, &i) < 0) perror("cAudio::getAudioInfo MPEG_AUD_GET_STATUS"); type = atype; #if 0 /* this does not work, some of the values are negative?? */ AMPEGStatus A; memcpy(&A, &i.word00, sizeof(i.word00)); layer = A.audio_mpeg_layer; mode = A.audio_mpeg_mode; bitrate = A.audio_mpeg_bitrate; switch(A.audio_mpeg_frequency) #endif /* layer and bitrate are not used anyway... */ layer = 0; //(i.word00 >> 17) & 3; bitrate = 0; //(i.word00 >> 12) & 3; switch (type) { case 0: /* MPEG */ mode = (i.word00 >> 6) & 3; freq = freq_mpg[(i.word00 >> 10) & 3]; break; case 1: /* AC3 */ mode = (i.word00 >> 28) & 7; freq = freq_ac3[(i.word00 >> 16) & 3]; break; default: mode = 0; freq = 0; } //fprintf(stderr, "type: %d layer: %d freq: %d bitrate: %d mode: %d\n", type, layer, freq, bitrate, mode); #endif }; void cAudio::SetSRS(int /*iq_enable*/, int /*nmgr_enable*/, int /*iq_mode*/, int /*iq_level*/) { lt_debug("%s\n", __FUNCTION__); }; void cAudio::SetHdmiDD(bool enable) { const char *opt[] = { "pcm", "spdif" }; lt_debug("%s %d\n", __func__, enable); proc_put("/proc/stb/hdmi/audio_source", opt[enable], strlen(opt[enable])); } void cAudio::SetSpdifDD(bool enable) { lt_debug("%s %d\n", __func__, enable); setBypassMode(!enable); }; void cAudio::ScheduleMute(bool On) { lt_debug("%s %d\n", __FUNCTION__, On); }; void cAudio::EnableAnalogOut(bool enable) { lt_debug("%s %d\n", __FUNCTION__, enable); }; #define AUDIO_BYPASS_ON 0 #define AUDIO_BYPASS_OFF 1 void cAudio::setBypassMode(bool disable) { const char *opt[] = { "passthrough", "downmix" }; lt_debug("%s %d\n", __func__, disable); proc_put("/proc/stb/audio/ac3", opt[disable], strlen(opt[disable])); } #ifdef MARTII int cAudio::getPercent(void) { return percent; } int cAudio::setPercent(int perc) { lt_debug("%s %d (muted: %d)\n", __func__, perc, Muted); int old_percent = percent; percent = perc; if (percent < 0 || percent > 999) percent = 100; if(!Muted) setVolume(volume, volume); return old_percent; } #endif