enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm) { register int nsamples = pcm->length; mad_fixed_t const *left_ch = pcm->samples[0], *right_ch = pcm->samples[1]; static unsigned char stream[1152*4]; /* 1152 because that's what mad has as a max; *4 because there are 4 distinct bytes per sample (in 2 channel case) */ static unsigned int rate = 0; static int channels = 0; static struct audio_dither dither; register char * ptr = stream; register signed int sample; register mad_fixed_t tempsample; /* We need to know information about the file before we can open the playdevice in some cases. So, we do it here. */ if (!playdevice) { channels = MAD_NCHANNELS(header); rate = header->samplerate; open_ao_playdevice(header); } else if ((channels != MAD_NCHANNELS(header) || rate != header->samplerate) && playdevice_is_live()) { ao_close(playdevice); channels = MAD_NCHANNELS(header); rate = header->samplerate; open_ao_playdevice(header); } if (pcm->channels == 2) { while (nsamples--) { tempsample = (mad_fixed_t)((*left_ch++ * (double)options.volume)/MAD_F_ONE); sample = (signed int) audio_linear_dither(16, tempsample, &dither); #ifndef WORDS_BIGENDIAN *ptr++ = (unsigned char) (sample >> 0); *ptr++ = (unsigned char) (sample >> 8); #else *ptr++ = (unsigned char) (sample >> 8); *ptr++ = (unsigned char) (sample >> 0); #endif tempsample = (mad_fixed_t)((*right_ch++ * (double)options.volume)/MAD_F_ONE); sample = (signed int) audio_linear_dither(16, tempsample, &dither); #ifndef WORDS_BIGENDIAN *ptr++ = (unsigned char) (sample >> 0); *ptr++ = (unsigned char) (sample >> 8); #else *ptr++ = (unsigned char) (sample >> 8); *ptr++ = (unsigned char) (sample >> 0); #endif } ao_play(playdevice, stream, pcm->length * 4); } else if (options.opt & MPG321_FORCE_STEREO)
enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm) { register int nsamples = pcm->length; mad_fixed_t const *left_ch = pcm->samples[0], *right_ch = pcm->samples[1]; static unsigned char stream[1152*4]; /* 1152 because that's what mad has as a max; *4 because there are 4 distinct bytes per sample (in 2 channel case) */ static unsigned int rate = 0; static int channels = 0; static struct audio_dither dither; register char * ptr = stream; register signed int sample; register mad_fixed_t tempsample; /* Semaphore operations */ static struct sembuf read_sops = {0, -1, 0}; static struct sembuf write_sops = {1, 1, 0}; if(options.opt & MPG321_ENABLE_BUFFER) { semop(semarray,&read_sops,1); ptr = (Output_Queue+mad_decoder_position)->data; memcpy(&((Output_Queue+mad_decoder_position)->header),header,sizeof(struct mad_header)); }else{ /* We need to know information about the file before we can open the playdevice in some cases. So, we do it here. */ if (!playdevice) { channels = MAD_NCHANNELS(header); rate = header->samplerate; open_ao_playdevice(header); } else if ((channels != MAD_NCHANNELS(header) || rate != header->samplerate) && playdevice_is_live()) { ao_close(playdevice); channels = MAD_NCHANNELS(header); rate = header->samplerate; open_ao_playdevice(header); } } static int peak_rate = 0; static unsigned short int peak = 0; if (pcm->channels == 2) { while (nsamples--) { tempsample = mad_f_mul(*left_ch++, options.volume); sample = (signed int) audio_linear_dither(16, tempsample, &dither); peak = abs(sample) > peak ? abs(sample) : peak; #ifndef WORDS_BIGENDIAN *ptr++ = (unsigned char) (sample >> 0); *ptr++ = (unsigned char) (sample >> 8); #else *ptr++ = (unsigned char) (sample >> 8); *ptr++ = (unsigned char) (sample >> 0); #endif tempsample = mad_f_mul(*right_ch++, options.volume); sample = (signed int) audio_linear_dither(16, tempsample, &dither); peak = abs(sample) > peak ? abs(sample) : peak; #ifndef WORDS_BIGENDIAN *ptr++ = (unsigned char) (sample >> 0); *ptr++ = (unsigned char) (sample >> 8); #else *ptr++ = (unsigned char) (sample >> 8); *ptr++ = (unsigned char) (sample >> 0); #endif } process_fft(stream, pcm->length * 4); if(options.opt & MPG321_ENABLE_BUFFER) { (Output_Queue+mad_decoder_position)->length = pcm->length * 4; }else { ao_play(playdevice, stream, pcm->length * 4); } } else if (options.opt & MPG321_FORCE_STEREO)
ao_device *open_ao_playdevice_buffer(struct mad_header const *header) { ao_sample_format format; ao_device *pldev = NULL; /* Because these can sometimes block, we stop our custom signal handler, and restore it afterwards */ signal(SIGINT, SIG_DFL); format.bits = 16; format.rate = header->samplerate; format.channels = (options.opt & MPG321_FORCE_STEREO) ? 2 : MAD_NCHANNELS(header); /* Add this element as an option to mpg321 */ format.matrix = "L,R"; /* mad gives us little-endian data; we swap it on big-endian targets, to big-endian format, because that's what most drivers expect. */ format.byte_format = AO_FMT_NATIVE; if(options.opt & MPG321_USE_AU) { int driver_id = ao_driver_id("au"); ao_option *ao_options = NULL; /* Don't have to check options.device here: we only define MPG321_USE_AU when --au <aufile> is defined, and <aufile> is pointd to by options.device */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao file output driver to write AU data.\n"); return NULL; } } else if (options.opt & MPG321_USE_CDR) { ao_option * ao_options = NULL; int driver_id = ao_driver_id("raw"); /* because CDR is a special format, i.e. headerless PCM, big endian, this is a special case. */ ao_append_option(&ao_options, "byteorder", "big"); if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao file output driver to write CDR data.\n"); return NULL; } } /* if the user specifies both au and wave, wav will be prefered, so testing * later */ else if(options.opt & MPG321_USE_WAV) { int driver_id = ao_driver_id("wav"); ao_option *ao_options = NULL; /* Don't have to check options.device here: we only define MPG321_USE_WAV when -w <wavfile> is defined, and <wavfile> is pointd to by options.device */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao wav file driver. (Do you have write permissions?)\n"); return NULL; } } else if(options.opt & MPG321_USE_NULL) { int driver_id = ao_driver_id("null"); /* null is dirty, create a proper options struct later */ if((pldev = ao_open_live(driver_id, &format, NULL)) == NULL) { fprintf(stderr, "Error opening libao null driver. (This shouldn't have happened.)\n"); return NULL; } } else if (options.opt & MPG321_USE_STDOUT) { ao_option * ao_options = NULL; int driver_id = ao_driver_id("raw"); /* stdout output is expected to be little-endian generally */ ao_append_option(&ao_options, "byteorder", "little"); if((pldev=ao_open_file(driver_id, "-", 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening libao raw output driver.\n"); return NULL; } } else if (options.opt & MPG321_USE_USERDEF) { ao_option *ao_options = NULL; int driver_id = ao_driver_id(options.devicetype); if (driver_id < 0) { fprintf(stderr, "Can't open unknown ao driver %s\n", options.devicetype); exit(1); } if (playdevice_is_live()) { if (options.device) { fprintf(stderr, "Can't set output device to %s for unknown ao plugin %s", options.device, options.devicetype); } if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { fprintf(stderr, "Error opening unknown libao %s driver. (Is device in use?)\n", options.devicetype); return NULL; } } else { if (options.device) { /* Just assume that options.device is a filename. The user can shoot themselves in the foot all they like... */ if((pldev=ao_open_file(driver_id, options.device, 1 /*overwrite*/, &format, ao_options))==NULL) { fprintf(stderr, "Error opening unknown libao %s file driver for file %s. (Do you have write permissions?)\n", options.devicetype, options.device); return NULL; } } else { fprintf(stderr, "Filename must be specified (with -a filename) for unknown ao driver %s\n", options.devicetype); } } } else { /* Hack-tacular. This code tries to the device as specified; if it can't, it'll fall through to the other devices, trying each in turn. If the user specified a device to use, though, it won't fall through: principle of least surprise */ int opened = 0; if(!opened && options.opt & MPG321_USE_ALSA) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("alsa"); char *c; char *card=(char *)malloc((int)16); memset(card,0,16); strncat(card,"alsa:\0",6); if (options.device) { strcat(card,options.device); //if ((c = strchr(options.device, ':')) == NULL || strlen(c+1) < 1) if ((c = strchr(card, ':')) == NULL || strlen(c+1) < 1) { fprintf(stderr, "Poorly formed ALSA card:device specification %s", options.device); exit(1); } *(c++) = '\0'; /* change the : to a null to create two separate strings */ //ao_append_option(&ao_options, "card", options.device); ao_append_option(&ao_options, "card", card); ao_append_option(&ao_options, "dev", c); } if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ALSA09; } else opened++; } if(!opened && options.opt & MPG321_USE_ALSA09) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("alsa09"); if(options.device) ao_append_option(&ao_options, "dev", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_OSS; } else opened++; } if(!opened && options.opt & MPG321_USE_OSS) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("oss"); if(options.device) ao_append_option(&ao_options, "dsp", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_SUN; } else opened++; } if(!opened && options.opt & MPG321_USE_SUN) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("sun"); if(options.device) ao_append_option(&ao_options, "dev", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ESD; } } if(!opened && options.opt & MPG321_USE_ESD) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("esd"); if(options.device) ao_append_option(&ao_options, "host", options.device); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { if (options.device) { fprintf(stderr, "Can't open libao driver with device %s (is device in use?)\n", options.device); return NULL; } else options.opt |= MPG321_USE_ARTS; } else opened++; } if(!opened && options.opt & MPG321_USE_ARTS) { ao_option *ao_options = NULL; int driver_id = ao_driver_id("arts"); if((pldev=ao_open_live(driver_id, &format, ao_options))==NULL) { fprintf(stderr, "Can't find a suitable libao driver. (Is device in use?)\n"); return NULL; } } } /* Restore signal handler */ signal(SIGINT, handle_signals); return pldev; }