diff -Naur trunkex/Makefile.am moceq/Makefile.am --- trunkex/Makefile.am 2008-08-03 20:19:00.000000000 +0200 +++ moceq/Makefile.am 2008-09-06 15:36:39.000000000 +0200 @@ -41,8 +41,6 @@ themes.h \ keys.c \ keys.h \ - ltdl.c \ - ltdl.h \ io.c \ io.h \ compat.c \ @@ -55,10 +53,14 @@ tags_cache.h \ utf8.c \ utf8.h \ + audio_helper.h \ + audio_helper.c \ softmixer.c \ softmixer.h \ - lyrics.h \ - lyrics.c + lyrics.h \ + lyrics.c \ + equalizer.h \ + equalizer.c EXTRA_mocp_SOURCES = gnugetopt.h \ getopt.c \ getopt1.c \ @@ -74,7 +76,7 @@ jack.h \ gettext.h man_MANS = mocp.1 -mocp_LDADD = @EXTRA_OBJS@ @LIBINTL@ +mocp_LDADD = @EXTRA_OBJS@ @LIBINTL@ -lltdl mocp_DEPENDENCIES = @EXTRA_OBJS@ mocp_LDFLAGS = @EXTRA_LIBS@ $(RCC_LIBS) EXTRA_DIST = config.example mocp.1 THANKS keymap.example Doxyfile \ diff -Naur trunkex/README_equalizer moceq/README_equalizer --- trunkex/README_equalizer 1970-01-01 01:00:00.000000000 +0100 +++ moceq/README_equalizer 2008-09-07 19:51:09.000000000 +0200 @@ -0,0 +1,164 @@ +Preamble +--- +This document is meant to give you an overview on the idea of having a +parametric equalizer for sound enhancement and how you can create your +own presets. Also the interaction with the equalizer in MOC is +described. +I would like to improve this document to make it more usable; so if you +have any comments and/or ideas feel free to contact me. + +- Hendrik Iben (hibentzi(dot)de) + +Content +--- +0. Document History +1. Motivation +2. Usage +3. Preset Format +4. Creating Presets +5. TODO +6. References + + +0. Document History +--- +07.09.2008 - Initial version + + +1. Nuts and Bolts / Motivation for implementing the equalizer +--- +The equalizer is an implementation of a biquadratic peaking equalizer +filter looked up from the Audio EQ Cookbook[1]. +It happens to be a parametric equalizer and this means that different to +other equalizer implementations the number of bands* is not fixed. +When I started the idea of implementing the equalizer I looked around in +the source of other audio playback software and found that a lot of them +are recycling the code used by the famous XMMS[2] audio player. I would +have liked to also recycle the code but I decided against it for two +reasons: +The first reason is that there is almost no documentation on the +algorithm used. Maybe the signal processing folks have fun finding out +what makes this thing work but I was totally lost. So I decided that I +wanted to *know* what I am doing if I do it. +As for the second reason, the code used by XMMS is totally optimized to +integer arithmetics. There is no problem with this in general but I had +the goal of implementing something as accurate as I could and I wanted +to use floating point arithmetics. +So I am no signals processing guy but I have - I think - a solid +understanding of the matter. I sat down and started to read about +equalizing, audio processing and signal theory in general. After some +time I found a mathematical description and a C implementation of +biquadratic filters in the Audio Cookbook. I made an implementation of +the XMMS equalizer and the biquadratic filter using Octave[3] to compare +the outcome of both filters. I was a bit surprised how different filters +can be but in the end succeeded (?) in finding a quite good biquadratic +filter set that would produce results not unlike the XMMS equalizer. +Although I did not use the XMMS-code I think that people will be more +happy to accept this equalizer if they can use their presets with it. +There is some conversion needed, but it's a straightforward process. I +converted all presets provided by XMMS into presets for this mixer. They +should be available at [4]. + + +2. Using the equalizer +--- +The default keys for the equalizer are: +'e' - Refresh equalizer +'E' - Toggle equalizer (on/off) +'k' - Select next preset +'K' - Select previous preset + +Each of these actions results in a message displayed in the message area. +This message will be overriden by the next action. + + +3. Preset format +--- +Presets for the equalizer are to be placed in a directory called +'eqsets' in MOCs home directory (e.g. $HOME/.moc/eqsets). There is no +convention for the filename, but the filename will serve as the name in +the selection process. +File format in pseudo EBNF: +EQSET +(( )|(0 ))* + +CF: Center frequency (sane values are from ~20 to ~20000) +BW: Bandwith in Octaves. This defines how fast the bands influence + vanishes over the frequencies +PREAMP: Specifies an amplification factor applied before equalizing + +So a valid equalizer set would be: + # this is a comment + EQSET + # amplify audio by 1.4dB + 0 1.4 + # damp frequencies at 100Hz by -4dB, filter bandwidth 1.5 octaves + 100 1.5 -4 + # amplifiy frequencies at 4000Hz by 2dB, filter bandwidth 1.5 octaves + 4000 1.5 2 + +There is no order to stick to when specifying frequencies. + +* A band is a chosen center frequency where a filter has most impact. + If you look at WinAmp / XMMS / Beep Media Player you will find that + they settled on a common set of 10 bands. + + +4. Creating your own presets +--- +For a start you should have a look at the converted presets[4]. The +bandwidths used in the conversion have been extracted by taking a look +at the filters signal response (implementation and analysis in octave). +I tried to do this as accurate as possible but I don't know if I made a +mistake. They sound correct though... :-) +You might note that there is never a positive amplification factor in +the presets although there are in the original preset. The reason for +this is that I used the maximum amplification in the preset as zero +amplification and adjusted the other values accordingly. +In general, when creating a preset get used to the following idea: +Do not amplify the frequencies you want but damp those that are of no +interest. This has the same effect but avoids clipping and this +equalizer type seems to be very prone to clipping... +Also be very careful with pre-amplifying the audio for the same reason. +With this said, the next confusing thing is the bandwidth definition. +Every band needs a defined bandwidth in octaves where the bandwidth +defines where the filters effect has been reduced by 3dB*. This means +that if you define a band at 1000Hz with a bandwidth of 1.5 octaves and +an amplification of -10dB, at 353.6Hz* and at 2828.4Hz the amplification +will be reduced to -7dB. +If unsure, stay in between 1.0 and 2.0. Just keep in mind that if two +bands overlap you might get an undesired amplification. +When designing presets, just save the preset and select it in MOC. After +each change press the refresh key (default 'e'). This will re-create the +equalizer reflecting your changes. +If your preset is not found, have a look at the output of MOC's server +thread. Parsing errors are emitted there. + +* 3dB is commonly used for bandwidth. -3dB equals about 70.7% of + original amplification. +* 353.6 =~ 1000*(2^-1.5), 2828.4 =~ 1000*(2^1.5) + + +5. TODO +--- +- The equalizer is currently not optimized in any way. +- It converts all sound data into floating point values to perform the + equalization and converts them back afterwards. + A better approach would be to either provide algorithms for integer + equalizing or to leave the audio data in floating point format. +- There is no sorting for the presets, their order is defined by reading + the directory content. +- Maybe it would be nice to add a name to the preset different than the + file. + + +6. References +--- +[1] Cookbook formulae for audio EQ biquad filter coefficients + http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +[2] X Multimedia System + http://xmms.org +[3] GNU Octave + http://www.gnu.org/software/octave/ +[4] Converted WinAmp / XMMS Equalizer sets + http://www.informatik.uni-bremen.de/~hiben/moc/eqsets.tar.gz diff -Naur trunkex/audio.c moceq/audio.c --- trunkex/audio.c 2007-12-12 12:49:04.000000000 +0100 +++ moceq/audio.c 2008-08-23 00:45:41.000000000 +0200 @@ -46,6 +46,7 @@ #endif #include "softmixer.h" +#include "equalizer.h" #include "out_buf.h" #include "protocol.h" @@ -755,11 +756,29 @@ int audio_send_pcm (const char *buf, const size_t size) { char *softmixed = NULL; + char *equalized = NULL; + + if(equalizer_is_active()) + { + equalized = xmalloc(size); + memcpy(equalized, buf, size); + + equalizer_process_buffer(equalized, size, &driver_sound_params); + + buf = equalized; + } if(softmixer_is_active()) { - softmixed = xmalloc(size); - memcpy(softmixed, buf, size); + if(equalized) + { + softmixed = equalized; + } + else + { + softmixed = xmalloc(size); + memcpy(softmixed, buf, size); + } softmixer_process_buffer ( @@ -778,9 +797,12 @@ if (played == 0) fatal ("Audio output error."); - if(softmixed!=NULL) + if(softmixed && !equalized) free(softmixed); + if(equalized) + free(equalized); + return played; } @@ -892,6 +914,7 @@ out_buf_init (&out_buf, options_get_int("OutputBuffer") * 1024); softmixer_init(); + equalizer_init(); plist_init (&playlist); plist_init (&shuffled_plist); @@ -918,6 +941,7 @@ free (last_stream_url); softmixer_shutdown(); + equalizer_shutdown(); } void audio_seek (const int sec) diff -Naur trunkex/audio_helper.c moceq/audio_helper.c --- trunkex/audio_helper.c 1970-01-01 01:00:00.000000000 +0100 +++ moceq/audio_helper.c 2008-09-06 15:36:39.000000000 +0200 @@ -0,0 +1,43 @@ +#include "audio_helper.h" + +#include "audio.h" + +int sample_size(long sfmt) +{ + long fmt = sfmt & SFMT_MASK_FORMAT; + switch(fmt) + { + case SFMT_U8: + case SFMT_S8: + return 1; + case SFMT_U16: + case SFMT_S16: + return 2; + case SFMT_U32: + case SFMT_S32: + return 4; + case SFMT_FLOAT: + return 2; + default: + return -1; + } +} + +void swap_endianess_32(int32_t *buf, size_t size) +{ + size_t i; + for(i=0; i +#endif + +#include + +#define swap_32bit_endianess(i32) \ + ( ((i32&0x000000FF)<<24) | ((i32&0x0000FF00)<<8)| \ + ((i32&0x00FF0000)>>8) | ((i32&0xFF000000)>>24) ) + +#define swap_16bit_endianess(i16) \ + ( ((i16&0x00FF)<<8) | ((i16&0xFF00)>>8) ) + +void swap_endianess_32(int32_t *buf, size_t size); +void swap_endianess_16(int16_t *buf, size_t size); + +int sample_size(long sfmt); + +#endif diff -Naur trunkex/config.example moceq/config.example --- trunkex/config.example 2008-04-11 17:56:12.000000000 +0200 +++ moceq/config.example 2008-09-06 16:12:36.000000000 +0200 @@ -100,6 +100,18 @@ # produce clipping. #Softmixer_SaveState = yes +# Save equalizer state ? +# If enabled, a file 'equalizer' will +# be created in '~/.moc/' storing the +# equalizer settings when the server is shut-down. +# There is a 'hidden' 'Mixin' setting in that file. +# Mixin (0.0-1.0) is used to determine how much of +# the original signal is used after equalizing. +# 0 means to only use the equalized sound, 1 +# effectively disabled the mixer. +# Default is 0.25 +#Equalizer_SaveState = yes + # Show files with dot at the beginning? ShowHiddenFiles = no diff -Naur trunkex/configure.in moceq/configure.in --- trunkex/configure.in 2008-08-03 20:14:19.000000000 +0200 +++ moceq/configure.in 2008-08-27 15:12:30.000000000 +0200 @@ -8,6 +8,7 @@ AC_CANONICAL_HOST AC_PROG_CC +AC_PROG_CXX AC_PROG_INSTALL AC_PROG_AWK diff -Naur trunkex/equalizer.c moceq/equalizer.c --- trunkex/equalizer.c 1970-01-01 01:00:00.000000000 +0100 +++ moceq/equalizer.c 2008-09-06 16:12:36.000000000 +0200 @@ -0,0 +1,1102 @@ +/* + * MOC - music on console + * Copyright (C) 2004-2008 Damian Pietras + * + * Equalizer-extension Copyright (C) 2008 Hendrik Iben + * Provides a parametric biquadratic equalizer. + * + * This code is based on the 'Cookbook formulae for audio EQ biquad filter + * coefficients' by Robert Bristow-Johnson. + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif +#define _GNU_SOURCE +#include +#include +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_LIMITS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "audio.h" +#include "audio_helper.h" +#include "options.h" +#include "common.h" +#include "log.h" + +#include "equalizer.h" + +/* config processing */ +int read_setup(char *name, char *desc, t_eq_setup **sp); +void equalizer_adjust_preamp(); +void equalizer_read_config(); +void equalizer_write_config(); + +/* biquad application */ +/* inline void biquad(float *src, float *dst, int len, t_biquad *b); */ +inline void apply_biquads(float *src, float *dst, int channels, int len, t_biquad *b, int blen); + +/* biquad filter creation */ +t_biquad *mk_biquad(float dbgain, float cf, float srate, float bw, t_biquad *b); + +/* equalizer list processing */ +t_eq_set_list *append_eq_set(t_eq_set *eqs, t_eq_set_list *l); +void clear_eq_set(t_eq_set_list *l); + +/* sound processing */ +void equ_process_buffer_u8(uint8_t *buf, size_t size); +void equ_process_buffer_s8(int8_t *buf, size_t size); +void equ_process_buffer_u16(uint16_t *buf, size_t size); +void equ_process_buffer_s16(int16_t *buf, size_t size); +void equ_process_buffer_u32(uint32_t *buf, size_t size); +void equ_process_buffer_s32(int32_t *buf, size_t size); +void equ_process_buffer_float(float *buf, size_t size); + +/* static global variables */ +static t_eq_set_list equ_list, *current_equ; + +static int sample_rate, equ_active, equ_channels; + +static float mixin_rate, r_mixin_rate; +static float preamp, preampf; + +static char *eqsetdir; + +static char *config_preset_name; + +/* public functions */ +int equalizer_is_active() +{ + return equ_active?1:0; +} + +int equalizer_set_active(int active) +{ + return equ_active = active?1:0; +} + +char *equalizer_current_eqname() +{ + if(equ_active && current_equ && current_equ->set) + { + return xstrdup(current_equ->set->name); + } + + return xstrdup("off"); +} + +void equalizer_next() +{ + if(current_equ) + { + if(current_equ->next) + { + current_equ = current_equ->next; + } + else + { + current_equ = &equ_list; + } + + if(!current_equ->set && !(current_equ == &equ_list && !current_equ->next)) + equalizer_next(); + } + + equalizer_adjust_preamp(); +} + +void equalizer_prev() +{ + if(current_equ) + { + if(current_equ->prev) + { + current_equ = current_equ->prev; + } + else + { + while(current_equ->next) + current_equ = current_equ->next; + } + + if(!current_equ->set && !(current_equ == &equ_list && !current_equ->next)) + equalizer_prev(); + } + + equalizer_adjust_preamp(); +} + +/* biquad functions */ + +/* Create a Peaking EQ Filter + * See 'Audio EQ Cookbook' for more information + */ +t_biquad *mk_biquad(float dbgain, float cf, float srate, float bw, t_biquad *b) +{ + if(b==NULL) + b = (t_biquad *)xmalloc(sizeof(t_biquad)); + + float A = powf(10.0f, dbgain / 40.0f); + float omega = TWOPI * cf / srate; + float sn = sin(omega); + float cs = cos(omega); + float alpha = sn * sinh(M_LN2 / 2.0f * bw * omega / sn); + + float alpha_m_A = alpha * A; + float alpha_d_A = alpha / A; + + float b0 = 1.0f + alpha_m_A; + float b1 = -2.0f * cs; + float b2 = 1.0f - alpha_m_A; + float a0 = 1.0f + alpha_d_A; + float a1 = b1; + float a2 = 1.0f - alpha_d_A; + + b->a0 = b0 / a0; + b->a1 = b1 / a0; + b->a2 = b2 / a0; + b->a3 = a1 / a0; + b->a4 = a2 / a0; + + b->x1 = 0.0f; + b->x2 = 0.0f; + b->y1 = 0.0f; + b->y2 = 0.0f; + + b->cf = cf; + b->bw = bw; + b->srate = srate; + b->israte = (int)srate; + b->gain = dbgain; + + return b; +}; + +/* + * not used but keep as example use for biquad filter +inline void biquad(float *src, float *dst, int len, t_biquad *b) +{ + while(len-->0) + { + float s = *src++; + float f = s * b->a0 + b->a1 * b->x1 + b->a2 * b->x2 - b->a3 * b->y1 - b->a4 * b->y2; + *dst++=f; + b->x2 = b->x1; + b->x1 = s; + b->y2 = b->y1; + b->y1 = f; + } + +}; +*/ + +/* Applies a set of biquadratic filters to a buffer of floating point + * samples. + * It is safe to have the same input and output buffer. + * + * blen is the sample-count ignoring channels (samples per channel * channels) + */ +inline void apply_biquads(float *src, float *dst, int channels, int len, t_biquad *b, int blen) +{ + int bi, ci, boffs, idx; + while(len>0) + { + boffs = 0; + for(ci=0; ciset) + { + preamp = current_equ->set->preamp; + preampf = powf(10.0f, current_equ->set->preamp / 20.0f); + } +} + +void equalizer_read_config() +{ + char *curloc = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); // posix decimal point + + char *sfile = xstrdup(create_file_name("equalizer")); + + FILE *cf = fopen(sfile, "r"); + + if(cf==NULL) + { + logit("Unable to read equalizer configuration"); + return; + } + + char *linebuffer = NULL; + char presetbuf[128]; + presetbuf[0] = 0; + + size_t buffersize = -1; + ssize_t readbytes=-1; + int tmp; + float ftmp; + + while((readbytes=getline(&linebuffer, &buffersize, cf)>-1)) + { + if( + strncasecmp + ( + linebuffer + , EQUALIZER_CFG_ACTIVE + , strlen(EQUALIZER_CFG_ACTIVE) + ) == 0 + ) + { + if(sscanf(linebuffer, "%*s %i", &tmp)>0) + { + if(tmp>0) + { + equ_active = 1; + } + else + { + equ_active = 0; + } + } + } + if( + strncasecmp + ( + linebuffer + , EQUALIZER_CFG_MIXIN + , strlen(EQUALIZER_CFG_MIXIN) + ) == 0 + ) + { + if(sscanf(linebuffer, "%*s %f", &ftmp)>0) + { + if(ftmp>=0.0f && ftmp<=1.0f) + { + mixin_rate = ftmp; + } + } + } + if( + strncasecmp + ( + linebuffer + , EQUALIZER_CFG_PRESET + , strlen(EQUALIZER_CFG_PRESET) + ) == 0 + ) + { + if(sscanf(linebuffer, "%*s %127s", presetbuf)>0) + { + /* ignore too large strings... */ + if(strlen(presetbuf)<127) + { + if(config_preset_name) + free(config_preset_name); + config_preset_name = xstrdup(presetbuf); + } + } + } + } + + free(linebuffer); + + fclose(cf); + + setlocale(LC_NUMERIC, curloc); +} + +void equalizer_write_config() +{ + char *curloc = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); /* posix decimal point */ + + char *cfname = create_file_name(EQUALIZER_SAVE_FILE); + + FILE *cf = fopen(cfname, "w"); + + if(cf==NULL) + { + logit ("Unable to write equalizer configuration"); + return; + } + + fprintf(cf, "%s %i\n", EQUALIZER_CFG_ACTIVE, equ_active); + if(current_equ && current_equ->set) + fprintf(cf, "%s %s\n", EQUALIZER_CFG_PRESET, current_equ->set->name); + fprintf(cf, "%s %f\n", EQUALIZER_CFG_MIXIN, mixin_rate); + + fclose(cf); + + setlocale(LC_NUMERIC, curloc); + + logit("Equalizer configuration written"); +} + +void equalizer_init() +{ + equ_active = 1; + + equ_list.set = NULL; + equ_list.next = NULL; + equ_list.prev = NULL; + + sample_rate = 44100; + + equ_channels = 2; + + preamp = 0.0f; + + preampf = powf(10.0f, preamp / 20.0f); + + eqsetdir = xstrdup(create_file_name("eqsets")); + + config_preset_name = NULL; + + mixin_rate = 0.25f; + + equalizer_read_config(); + + r_mixin_rate = 1.0f - mixin_rate; + + equalizer_refresh(); + + logit("Equalizer initialized"); +} + +void equalizer_shutdown() +{ + if(options_get_int(EQUALIZER_SAVE_OPTION)) + equalizer_write_config(); + + clear_eq_set(&equ_list); + + logit("Equalizer stopped"); +} + +void equalizer_refresh() +{ + t_eq_setup *eqs = NULL; + char buf[1024]; + + char *current_set_name = NULL; + + if(current_equ && current_equ->set) + { + current_set_name = xstrdup(current_equ->set->name); + } + else + { + if(config_preset_name) + current_set_name = config_preset_name; + } + + clear_eq_set(&equ_list); + + current_equ = NULL; + + DIR *d = opendir(eqsetdir); + + if(!d) + { + return; + } + + struct dirent *de = readdir(d); + struct stat st; + + t_eq_set_list *last_elem; + + last_elem = &equ_list; + + while(de) + { + sprintf(buf, "eqsets/%s", de->d_name); + + char *filename = xstrdup(create_file_name(buf)); + + stat(filename, &st); + + if( S_ISREG(st.st_mode) ) + { + FILE *f = fopen(filename, "r"); + + if(f) + { + char filebuffer[4096]; + + char *fb = filebuffer; + + int maxread = 4095 - (fb - filebuffer); + + // read in whole file + while(!feof(f) && maxread>0) + { + maxread = 4095 - (fb - filebuffer); + int rb = fread(fb, sizeof(char), maxread, f); + fb+=rb; + } + + fclose(f); + + *fb = 0; + int r = read_setup(de->d_name, filebuffer, &eqs); + + if(r==0) + { + int i, channel; + t_eq_set *eqset = (t_eq_set *)xmalloc(sizeof(t_eq_set)); + eqset->b = (t_biquad *)xmalloc(sizeof(t_biquad)*eqs->bcount*equ_channels); + + eqset->name = xstrdup(eqs->name); + eqset->preamp = eqs->preamp; + eqset->bcount = eqs->bcount; + eqset->channels = equ_channels; + + for(i=0; ibcount; i++) + { + mk_biquad(eqs->dg[i], eqs->cf[i], sample_rate, eqs->bw[i], &eqset->b[i]); + + for(channel=1; channelb[channel*eqset->bcount + i] = eqset->b[i]; + } + } + + last_elem = append_eq_set(eqset, last_elem); + + free(eqs->name); + free(eqs->cf); + free(eqs->bw); + free(eqs->dg); + + } + else + { + switch(r) + { + case 0: + logit("This should not happen: No error but no EQSET was parsed: %s", filename); + break; + case -1: + logit("Not an EQSET (empty file): %s", filename); + break; + case -2: + logit("Not an EQSET (invalid header): %s", filename); + break; + case -3: + logit("Error while parsing settings from EQSET: %s", filename); + break; + default: + logit("Unknown error while parsing EQSET: %s", filename); + break; + } + } + + if(eqs) + free(eqs); + + eqs = NULL; + } + } + + free(filename); + + de = readdir(d); + } + + closedir(d); + + current_equ = &equ_list; + + if(current_set_name) + { + current_equ = &equ_list; + + while(current_equ) + { + if(current_equ->set) + { + if(strcmp(current_set_name, current_equ->set->name)==0) + break; + } + current_equ = current_equ->next; + } + + free(current_set_name); + } + + if(current_equ && !current_equ->set) + equalizer_next(); + + equalizer_adjust_preamp(); +} + +/* sound processing code */ +void equalizer_process_buffer(char *buf, size_t size, const struct sound_params *sound_params) +{ +#ifdef DEBUG + logit("EQ Processing %u bytes...", size); +#endif + if(!equ_active || !current_equ || !current_equ->set) + return; + + if(sound_params->rate != current_equ->set->b->israte || sound_params->channels != equ_channels) + { + logit("Recreating filters due to sound parameter changes..."); + sample_rate = sound_params->rate; + equ_channels = sound_params->channels; + + equalizer_refresh(); + } + + long sound_endianess = sound_params->fmt & SFMT_MASK_ENDIANES; + long sound_format = sound_params->fmt & SFMT_MASK_FORMAT; + + int samplesize = sample_size(sound_format); + int is_float = (sound_params->fmt & SFMT_MASK_FORMAT) == SFMT_FLOAT; + + int need_endianess_swap = 0; + + if((sound_endianess != SFMT_NE) && (samplesize > 1) && (!is_float)) + { + need_endianess_swap = 1; + } + + /* setup samples to perform arithmetic */ + if(need_endianess_swap) + { +#ifdef DEBUG + logit("Converting endianess before mixing"); +#endif + if(samplesize == 4) + swap_endianess_32((int32_t *)buf, size>>2); + else + swap_endianess_16((int16_t *)buf, size>>1); + } + + switch(sound_format) + { + case SFMT_U8: + equ_process_buffer_u8((uint8_t *)buf, size); + break; + case SFMT_S8: + equ_process_buffer_s8((int8_t *)buf, size); + break; + case SFMT_U16: + equ_process_buffer_u16((uint16_t *)buf, size >> 1); + break; + case SFMT_S16: + equ_process_buffer_s16((int16_t *)buf, size >> 1); + break; + case SFMT_U32: + equ_process_buffer_u32((uint32_t *)buf, size >> 2); + break; + case SFMT_S32: + equ_process_buffer_s32((int32_t *)buf, size >> 2); + break; + case SFMT_FLOAT: + equ_process_buffer_float((float *)buf, size >> 1); + break; + } + + /* restore sample-endianess */ + if(need_endianess_swap) + { +#ifdef DEBUG + logit("Restoring endianess after mixing"); +#endif + if(samplesize == 4) + swap_endianess_32((int32_t *)buf, size>>2); + else + swap_endianess_16((int16_t *)buf, size>>1); + } +} + +void equ_process_buffer_u8(uint8_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i UINT8_MAX) + tmp[i] = UINT8_MAX; + else + if(tmp[i] < 0) + tmp[i] = 0; + + buf[i] = (uint8_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_s8(int8_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i INT8_MAX) + tmp[i] = INT8_MAX; + else + if(tmp[i] < INT8_MIN) + tmp[i] = INT8_MIN; + + buf[i] = (int8_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_u16(uint16_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i UINT16_MAX) + tmp[i] = UINT16_MAX; + else + if(tmp[i] < 0) + tmp[i] = 0; + + buf[i] = (uint16_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_s16(int16_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i INT16_MAX) + tmp[i] = INT16_MAX; + else + if(tmp[i] < INT16_MIN) + tmp[i] = INT16_MIN; + + buf[i] = (int16_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_u32(uint32_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i UINT32_MAX) + tmp[i] = UINT32_MAX; + else + if(tmp[i] < 0) + tmp[i] = 0; + + buf[i] = (uint32_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_s32(int32_t *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + for(i=0; i INT32_MAX) + tmp[i] = INT32_MAX; + else + if(tmp[i] < INT32_MIN) + tmp[i] = INT32_MIN; + + buf[i] = (int32_t)tmp[i]; + } + + free(tmp); +} + +void equ_process_buffer_float(float *buf, size_t size) +{ +#ifdef DEBUG + logit("equalizing"); +#endif + float *tmp = (float *)xmalloc(size * sizeof(float)); + + size_t i; + + for(i=0; iset->b, current_equ->set->bcount); + + + for(i=0; i 1.0f) + tmp[i] = 1.0f; + else + if(tmp[i] < -1.0f) + tmp[i] = -1.0f; + + buf[i] = tmp[i]; + } + + free(tmp); +} + +/* equalizer list maintenance */ +t_eq_set_list *append_eq_set(t_eq_set *eqs, t_eq_set_list *l) +{ + if(l->set == NULL) + { + l->set = eqs; + } + else + { + if(l->next) + { + append_eq_set(eqs, l->next); + } + else + { + l->next = (t_eq_set_list *)xmalloc(sizeof(t_eq_set_list)); + l->next->set = NULL; + l->next->next = NULL; + l->next->prev = l; + l = append_eq_set(eqs, l->next); + } + }; + + return l; +}; + +void clear_eq_set(t_eq_set_list *l) +{ + if(l->set) + { + free(l->set->name); + free(l->set->b); + free(l->set); + l->set = NULL; + } + if(l->next) + { + clear_eq_set(l->next); + free(l->next); + l->next = NULL; + } +}; + +/* parsing stuff */ +int read_setup(char *name, char *desc, t_eq_setup **sp) +{ + char *curloc = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); // posix decimal point + + t_eq_setup *s = *sp; + + desc = skip_whitespace(desc); + + if(!*desc) + { + return -1; + } + + if(strncasecmp(desc, EQSET_HEADER, sizeof(EQSET_HEADER)-1)) + { + return -2; + } + + desc+=5; + + desc = skip_whitespace(skip_line(desc)); + + if(s==NULL) + { + s=(t_eq_setup *)xmalloc(sizeof(t_eq_setup)); + *sp = s; + } + + s->name = xstrdup(name); + s->bcount = 0; + s->preamp = 0.0f; + int max_values = 16; + s->cf = (float *)xmalloc(max_values*sizeof(float)); + s->bw = (float *)xmalloc(max_values*sizeof(float)); + s->dg = (float *)xmalloc(max_values*sizeof(float)); + + int r; + + while(*desc) + { + char *endp; + + float cf = 0.0f; + + r = read_float(desc, &cf, &endp); + + if(r!=0) + { + free(s->name); + free(s->cf); + free(s->bw); + free(s->dg); + return -3; + } + + desc = skip_whitespace(endp); + + float bw = 0.0f; + + r = read_float(desc, &bw, &endp); + + if(r!=0) + { + free(s->name); + free(s->cf); + free(s->bw); + free(s->dg); + return -3; + } + + desc = skip_whitespace(endp); + + float dg = 0.0f; + + /* 0Hz means preamp, only one parameter then */ + if(cf!=0.0f) + { + r = read_float(desc, &dg, &endp); + + if(r!=0) + { + free(s->name); + free(s->cf); + free(s->bw); + free(s->dg); + return -3; + } + + desc = skip_whitespace(endp); + + if(s->bcount>=(max_values-1)) + { + max_values*=2; + s->cf=xrealloc(s->cf, max_values*sizeof(float)); + s->bw=xrealloc(s->bw, max_values*sizeof(float)); + s->dg=xrealloc(s->dg, max_values*sizeof(float)); + } + + s->cf[s->bcount]=cf; + s->bw[s->bcount]=bw; + s->dg[s->bcount]=dg; + + s->bcount++; + } + else + { + s->preamp = bw; + } + } + + setlocale(LC_NUMERIC, curloc); // posix decimal point + + return 0; +} + +char *skip_line(char *s) +{ + int dos_line = 0; + while(*s && (*s!=CRETURN && *s!=NEWLINE) ) + s++; + + if(*s==CRETURN) + dos_line = 1; + + if(*s) + s++; + + if(dos_line && *s==NEWLINE) + s++; + + return s; +} + +char *skip_whitespace(char *s) +{ + while(*s && (*s<=SPACE)) + s++; + + if(!*s) + return s; + + if(*s=='#') + { + s = skip_line(s); + + s = skip_whitespace(s); + } + + return s; +} + +int read_float(char *s, float *f, char **endp) +{ + errno = 0; + + float t = strtof(s, endp); + + if(errno==ERANGE) + return -1; + + if(*endp == s) + return -2; + + *f = t; + + return 0; +}; diff -Naur trunkex/equalizer.h moceq/equalizer.h --- trunkex/equalizer.h 1970-01-01 01:00:00.000000000 +0100 +++ moceq/equalizer.h 2008-09-06 15:36:39.000000000 +0200 @@ -0,0 +1,101 @@ +#ifndef EQUALIZER_H +#define EQUALIZER_H + +#define TWOPI (2.0 * M_PI) + +#define swap_32bit_endianess(i32) \ + ( ((i32&0x000000FF)<<24) | ((i32&0x0000FF00)<<8)| \ + ((i32&0x00FF0000)>>8) | ((i32&0xFF000000)>>24) ) + +#define swap_16bit_endianess(i16) \ + ( ((i16&0x00FF)<<8) | ((i16&0xFF00)>>8) ) + +#define NEWLINE 0x0A +#define CRETURN 0x0D +#define SPACE 0x20 + +#define EQSET_HEADER "EQSET" + +#define EQUALIZER_CFG_ACTIVE "Active:" +#define EQUALIZER_CFG_PRESET "Preset:" +#define EQUALIZER_CFG_MIXIN "Mixin:" + +#define EQUALIZER_SAVE_FILE "equalizer" +#define EQUALIZER_SAVE_OPTION "Equalizer_SaveState" + + +char *skip_line(char *s); +char *skip_whitespace(char *s); +char *word(char *s, char *buf, int *len, int nmax); +int read_float(char *s, float *f, char **endp); + +typedef struct t_biquad t_biquad; + +struct t_biquad +{ + float a0, a1, a2, a3, a4; + float x1, x2, y1, y2; + float cf, bw, gain, srate; + int israte; +}; + +typedef struct t_eq_setup t_eq_setup; + +struct t_eq_setup +{ + char *name; + float preamp; + int bcount; + float *cf; + float *bw; + float *dg; +}; + +typedef struct t_eq_set t_eq_set; + +struct t_eq_set +{ + char *name; + int channels; + float preamp; + int bcount; + t_biquad *b; +}; + +typedef struct t_eq_set_list t_eq_set_list; + +struct t_eq_set_list +{ + t_eq_set *set; + t_eq_set_list *prev, *next; +}; + +typedef struct t_active_set t_active_set; + +struct t_active_set +{ + int srate; + t_eq_set *set; +}; + +typedef struct t_eq_settings t_eq_settings; + +struct t_eq_settings +{ + char *preset_name; + int bcount; + float *gain; + t_eq_settings *next; +}; + +void equalizer_init(); +void equalizer_shutdown(); +void equalizer_process_buffer(char *buf, size_t size, const struct sound_params *sound_params); +void equalizer_refresh(); +int equalizer_is_active(); +int equalizer_set_active(); +char *equalizer_current_eqname(); +void equalizer_next(); +void equalizer_prev(); + +#endif diff -Naur trunkex/interface.c moceq/interface.c --- trunkex/interface.c 2008-08-03 20:19:00.000000000 +0200 +++ moceq/interface.c 2008-09-06 15:36:39.000000000 +0200 @@ -3176,6 +3176,26 @@ debug ("Toggle softmixer."); send_int_to_srv (CMD_TOGGLE_SOFTMIXER); break; + case KEY_CMD_TOGGLE_EQUALIZER: + debug ("Toggle equalizer."); + send_int_to_srv (CMD_TOGGLE_EQUALIZER); + break; + case KEY_CMD_EQUALIZER_REFRESH: + debug ("Equalizer Refresh."); + send_int_to_srv (CMD_EQUALIZER_REFRESH); + break; + case KEY_CMD_EQUALIZER_PREV: + debug ("Equalizer Prev."); + send_int_to_srv (CMD_EQUALIZER_PREV); + break; + case KEY_CMD_EQUALIZER_NEXT: + debug ("Equalizer Next."); + send_int_to_srv (CMD_EQUALIZER_NEXT); + break; + case KEY_CMD_TOGGLE_MAKE_MONO: + debug ("Toggle Mono-Mixing."); + send_int_to_srv (CMD_TOGGLE_MAKE_MONO); + break; case KEY_CMD_TOGGLE_LAYOUT: iface_toggle_layout (); break; diff -Naur trunkex/keys.c moceq/keys.c --- trunkex/keys.c 2008-08-03 20:19:00.000000000 +0200 +++ moceq/keys.c 2008-09-06 15:36:39.000000000 +0200 @@ -625,6 +625,46 @@ 1 }, { + KEY_CMD_TOGGLE_EQUALIZER, + "toggle_equalizer", + "Toggles the equalizer", + CON_MENU, + { 'E', -1 }, + 1 + }, + { + KEY_CMD_EQUALIZER_REFRESH, + "equalizer_refresh", + "Reload EQ-presets", + CON_MENU, + { 'e', -1 }, + 1 + }, + { + KEY_CMD_EQUALIZER_PREV, + "equalizer_prev", + "Select previous equalizer-preset", + CON_MENU, + { 'K', -1 }, + 1 + }, + { + KEY_CMD_EQUALIZER_NEXT, + "equalizer_next", + "Select next equalizer-preset", + CON_MENU, + { 'k', -1 }, + 1 + }, + { + KEY_CMD_TOGGLE_MAKE_MONO, + "toggle_make_mono", + "Toggle mono-mixing", + CON_MENU, + { 'J', -1 }, + 1 + }, + { KEY_CMD_PLIST_MOVE_UP, "plist_move_up", "Move playlist item up", diff -Naur trunkex/keys.h moceq/keys.h --- trunkex/keys.h 2008-08-03 20:19:00.000000000 +0200 +++ moceq/keys.h 2008-09-06 15:36:39.000000000 +0200 @@ -89,6 +89,11 @@ KEY_CMD_EXEC10, KEY_CMD_TOGGLE_PLAYLIST_FULL_PATHS, KEY_CMD_TOGGLE_SOFTMIXER, + KEY_CMD_TOGGLE_EQUALIZER, + KEY_CMD_EQUALIZER_REFRESH, + KEY_CMD_EQUALIZER_PREV, + KEY_CMD_EQUALIZER_NEXT, + KEY_CMD_TOGGLE_MAKE_MONO, KEY_CMD_LYRICS, KEY_CMD_WRONG }; diff -Naur trunkex/options.c moceq/options.c --- trunkex/options.c 2007-12-12 12:49:04.000000000 +0100 +++ moceq/options.c 2008-09-06 15:36:39.000000000 +0200 @@ -260,6 +260,8 @@ option_add_str ("OnStop", NULL); option_add_int ("Softmixer_SaveState", 1); + + option_add_int ("Equalizer_SaveState", 1); } /* Return 1 if a parameter to an integer option is valid. */ @@ -291,6 +293,7 @@ || !strcasecmp(name, "SidPlay2_StartAtStart") || !strcasecmp(name, "SidPlay2_PlaySubTunes") || !strcasecmp(name, "Softmixer_SaveState") + || !strcasecmp(name, "Equalizer_SaveState") ) { if (!(val == 1 || val == 0)) return 0; diff -Naur trunkex/protocol.h moceq/protocol.h --- trunkex/protocol.h 2007-12-12 12:49:04.000000000 +0100 +++ moceq/protocol.h 2008-09-06 15:36:39.000000000 +0200 @@ -122,6 +122,12 @@ #define CMD_GET_AVG_BITRATE 0x33 /* get the average bitrate */ #define CMD_TOGGLE_SOFTMIXER 0x34 /* toggle use of softmixer */ +#define CMD_TOGGLE_EQUALIZER 0x35 /* toggle use of equalizer */ +#define CMD_EQUALIZER_REFRESH 0x36 /* refresh EQ-presets */ +#define CMD_EQUALIZER_PREV 0x37 /* select previous eq-preset */ +#define CMD_EQUALIZER_NEXT 0x38 /* select next eq-preset */ + +#define CMD_TOGGLE_MAKE_MONO 0x39 /* toggle mono mixing */ char *socket_name (); int get_int (int sock, int *i); diff -Naur trunkex/server.c moceq/server.c --- trunkex/server.c 2008-03-11 16:55:42.000000000 +0100 +++ moceq/server.c 2008-09-06 15:36:39.000000000 +0200 @@ -47,6 +47,7 @@ #include "tags_cache.h" #include "files.h" #include "softmixer.h" +#include "equalizer.h" #define SERVER_LOG "mocp_server_log" #define PID_FILE "pid" @@ -1064,6 +1065,76 @@ add_event_all (EV_MIXER_CHANGE, NULL); } +void update_eq_name() +{ + char buffer[27]; + + char *n = equalizer_current_eqname(); + + int l = strlen(n); + + /* status message can only take strings up to 25 chars + * (without terminating zero) + * The message header has 11 chars (EQ set to...) + */ + if(l>14) + { + n[14] = 0; + n[13] = '.'; + n[12] = '.'; + n[11] = '.'; + } + + sprintf(buffer, "EQ set to: %s", n); + + logit("%s", buffer); + + free(n); + + status_msg(buffer); +} + +void req_toggle_equalizer () +{ + equalizer_set_active(!equalizer_is_active()); + + update_eq_name(); +} + +void req_equalizer_refresh() +{ + equalizer_refresh(); + + status_msg("Equalizer refreshed"); + + logit("Equalizer refreshed"); +} + +void req_equalizer_prev() +{ + equalizer_prev(); + + update_eq_name(); +} + +void req_equalizer_next() +{ + equalizer_next(); + + update_eq_name(); +} + +void req_toggle_make_mono() +{ + char buffer[128]; + + softmixer_set_mono(!softmixer_is_mono()); + + sprintf(buffer, "Mono-Mixing set to: %s", softmixer_is_mono()?"on":"off"); + + status_msg(buffer); +} + /* Handle CMD_GET_FILE_TAGS. Return 0 on error. */ static int get_file_tags (const int cli_id) { @@ -1296,6 +1367,21 @@ if (!req_list_move(cli)) err = 1; break; + case CMD_TOGGLE_EQUALIZER: + req_toggle_equalizer(); + break; + case CMD_EQUALIZER_REFRESH: + req_equalizer_refresh(); + break; + case CMD_EQUALIZER_PREV: + req_equalizer_prev(); + break; + case CMD_EQUALIZER_NEXT: + req_equalizer_next(); + break; + case CMD_TOGGLE_MAKE_MONO: + req_toggle_make_mono(); + break; default: logit ("Bad command (0x%x) from the client.", cmd); err = 1; diff -Naur trunkex/softmixer.c moceq/softmixer.c --- trunkex/softmixer.c 2008-08-25 11:36:11.000000000 +0200 +++ moceq/softmixer.c 2008-09-08 15:37:37.000000000 +0200 @@ -17,7 +17,6 @@ #include "config.h" #endif -#define _GNU_SOURCE #include #include #ifdef HAVE_STDINT_H @@ -31,24 +30,20 @@ #endif #include "softmixer.h" +#include "audio_helper.h" #include "options.h" #include "common.h" #include "files.h" #include "log.h" -#define swap_32bit_endianess(i32) \ - ( ((i32&0x000000FF)<<24) | ((i32&0x0000FF00)<<8)| \ - ((i32&0x00FF0000)>>8) | ((i32&0xFF000000)>>24) ) - -#define swap_16bit_endianess(i16) \ - ( ((i16&0x00FF)<<8) | ((i16&0xFF00)>>8) ) /* #define DEBUG */ /* public code */ - int active; +int mix_mono; + int mixer_val, mixer_amp, mixer_real; float mixer_realf; @@ -62,6 +57,7 @@ void softmixer_init() { active = 0; + mix_mono = 0; mixer_amp = 100; softmixer_set_value(100); softmixer_read_config(); @@ -116,10 +112,20 @@ return active; } -/* private code */ +void softmixer_set_mono(int mono) +{ + if(mono) + mix_mono = 1; + else + mix_mono = 0; +} -int sample_size(long sfmt); +int softmixer_is_mono() +{ + return mix_mono; +} +/* private code */ void process_buffer_u8(uint8_t *buf, size_t size); void process_buffer_s8(int8_t *buf, size_t size); void process_buffer_u16(uint16_t *buf, size_t size); @@ -127,9 +133,13 @@ void process_buffer_u32(uint32_t *buf, size_t size); void process_buffer_s32(int32_t *buf, size_t size); void process_buffer_float(float *buf, size_t size); - -void swap_endianess_32(int32_t *buf, size_t size); -void swap_endianess_16(int16_t *buf, size_t size); +void mix_mono_u8(uint8_t *buf, int channels, size_t size); +void mix_mono_s8(int8_t *buf, int channels, size_t size); +void mix_mono_u16(uint16_t *buf, int channels, size_t size); +void mix_mono_s16(int16_t *buf, int channels, size_t size); +void mix_mono_u32(uint32_t *buf, int channels, size_t size); +void mix_mono_s32(int32_t *buf, int channels, size_t size); +void mix_mono_float(float *buf, int channels, size_t size); void softmixer_read_config() { @@ -212,6 +222,28 @@ } } } + if( + strncasecmp + ( + linebuffer + , SOFTMIXER_CFG_MONO + , strlen(SOFTMIXER_CFG_MONO) + ) == 0 + ) + { + if(sscanf(linebuffer, "%*s %i", &tmp)>0) + { + if(tmp>0) + { + mix_mono = 1; + } + else + { + mix_mono = 0; + } + } + } + free(linebuffer); } @@ -234,6 +266,7 @@ fprintf(cf, "%s %i\n", SOFTMIXER_CFG_ACTIVE, active); fprintf(cf, "%s %i\n", SOFTMIXER_CFG_AMP, mixer_amp); fprintf(cf, "%s %i\n", SOFTMIXER_CFG_VALUE, mixer_val); + fprintf(cf, "%s %i\n", SOFTMIXER_CFG_MONO, mix_mono); fclose(cf); @@ -245,9 +278,11 @@ #ifdef DEBUG logit("Processing %u bytes...", size); #endif - if(mixer_real==100) + if(mixer_real==100 && !mix_mono) return; + int do_softmix = mixer_real != 100; + long sound_endianess = sound_params->fmt & SFMT_MASK_ENDIANES; long sound_format = sound_params->fmt & SFMT_MASK_FORMAT; @@ -276,25 +311,46 @@ switch(sound_format) { case SFMT_U8: - process_buffer_u8((uint8_t *)buf, size); + if(do_softmix) + process_buffer_u8((uint8_t *)buf, size); + if(mix_mono) + mix_mono_u8((uint8_t *)buf, sound_params->channels, size); break; case SFMT_S8: - process_buffer_s8((int8_t *)buf, size); + if(do_softmix) + process_buffer_s8((int8_t *)buf, size); + if(mix_mono) + mix_mono_s8((int8_t *)buf, sound_params->channels, size); break; case SFMT_U16: - process_buffer_u16((uint16_t *)buf, size >> 1); + if(do_softmix) + process_buffer_u16((uint16_t *)buf, size >> 1); + if(mix_mono) + mix_mono_u16((uint16_t *)buf, sound_params->channels, size >> 1); break; case SFMT_S16: - process_buffer_s16((int16_t *)buf, size >> 1); + if(do_softmix) + process_buffer_s16((int16_t *)buf, size >> 1); + if(mix_mono) + mix_mono_s16((int16_t *)buf, sound_params->channels, size >> 1); break; case SFMT_U32: - process_buffer_u32((uint32_t *)buf, size >> 2); + if(do_softmix) + process_buffer_u32((uint32_t *)buf, size >> 2); + if(mix_mono) + mix_mono_u32((uint32_t *)buf, sound_params->channels, size >> 2); break; case SFMT_S32: - process_buffer_s32((int32_t *)buf, size >> 2); + if(do_softmix) + process_buffer_s32((int32_t *)buf, size >> 2); + if(mix_mono) + mix_mono_s32((int32_t *)buf, sound_params->channels, size >> 2); break; case SFMT_FLOAT: - process_buffer_float((float *)buf, size >> 1); + if(do_softmix) + process_buffer_float((float *)buf, size >> 1); + if(mix_mono) + mix_mono_float((float *)buf, sound_params->channels, size >> 1); break; } @@ -311,28 +367,6 @@ } } -int sample_size(long sfmt) -{ - long fmt = sfmt & SFMT_MASK_FORMAT; - switch(fmt) - { - case SFMT_U8: - case SFMT_S8: - return 1; - case SFMT_U16: - case SFMT_S16: - return 2; - case SFMT_U32: - case SFMT_S32: - return 4; - case SFMT_FLOAT: - return 2; - default: - return -1; - } -} - - void process_buffer_u8(uint8_t *buf, size_t size) { #ifdef DEBUG @@ -476,20 +510,243 @@ } } -void swap_endianess_32(int32_t *buf, size_t size) +// Mono-Mixing +void mix_mono_u8(uint8_t *buf, int channels, size_t size) { - size_t i; - for(i=0; i UINT8_MAX) + mono = UINT8_MAX; + // can't be negative + + for(c=0; c INT8_MAX) + mono = INT8_MAX; + else + if(mono < INT8_MIN) + mono = INT8_MIN; + + for(c=0; c UINT16_MAX) + mono = UINT16_MAX; + // can't be negative + + for(c=0; c INT16_MAX) + mono = INT16_MAX; + else + if(mono < INT16_MIN) + mono = INT16_MIN; + + for(c=0; c UINT32_MAX) + mono = UINT32_MAX; + // can't be negative + + for(c=0; c INT32_MAX) + mono = INT32_MAX; + else + if(mono < INT32_MIN) + mono = INT32_MIN; + + for(c=0; c 1.0f) + mono = 1.0f; + else + if(mono < -1.0f) + mono = -1.0f; + + for(c=0; c