::
:: proposal:
::
:: int lame_open();
:: // allocates internal structures
::
:: int lame_set_param( int handle, LameParameter Parameter, int Value );
:: // assigns Lame's parameter a value
::
a) Not every variable is an integer. C programmer try to store everything
(food, women, cars ;-) in an int, but from the state of good programming
style this is brain dead and not far from FORTRAN 4.
b) Brain dead interfaces creates things like:
double d = 3.14159265358979;
float f = d;
func ( *(int*)&f ); // transmit float value through int
func ( (int)stdout ); // fails on sizeof(void*) > sizeof(int)
c) It is wise to return the actual parameter
d) it is wise to return an status word (error code)
e) it is wise not to use in-band-signaling for error codes
f) Sometimes it is useful to change some parameters atomically
(For RT systems this is often an indispensable need).
So we got something like:
int lame_set_param (
int handle,
LameParameter ParameterType,
... );
// assigns Lame's parameter a value
Examples:
long double ld;
double d;
int i1;
int i2;
FILE* fp;
ld = 44055.938461538; // NTSC PCM, lips synchr.
printf ( "Input fs is %lf, ", ld );
err = lame_set_param ( lh, Lame_LD_InputSampleFreq, &ld )
printf ( "set to %lf.\n", ld );
i1 = 128000;
i2 = 256000;
printf ( "VBR bitrate should be between %7.3f...%7.3f, ", i1*1.e-3, i2*1.e-3 );
err = lame_set_param ( lh, Lame_I2_VBRBitrateRange, &i1, &i2 )
printf ( "set to %7.3f...%7.3f\n", i1*1.e-3, i2*1.e-3 );
fp = fopen ( "report", "w" );
err = lame_set_param ( lh, Lame_fp_Reportfp, &fp );
Another question: Why not using a set of functions to setup parameters.
It is not more difficult, but less, it does not disable type checking and
link checking.
This is also something I call: "cosmetic fake simplification".
It:
* moves complexity instead of reducing (virtual hiding)
* disables type checking
* disables functionality checking by the linker
* it clouds errors (compile/link time => run time or later)
Okay. It's C ;-)
:: int lame_init_params( int handle );
:: // fills up rest of needed parameters by default values
::
Not necessary. Can be done by lame_encode stuff.
lame_...._flags has an internal flag:
0: invalid, before open() or after close()
1: valid, open() was done, ready for setup
2: the first lame_encode_...() stuff was called (finalization of params)
::
:: usual lame_encode() stuff
::
:: void lame_close( int handle );
:: // frees all allocated internal structures
::
::
:: just enumerate all parameters LAME will use, we can allways append
:: new ones if needed. A 32 bit int should provide us enough internal
:: parameters ;)
::
:: typedef enum{
:: lp_bitrate,
:: lp_vbrmode,
:: lp_vbrquality,
:: lp_vbrmin,
:: lp_vbrmax,
:: lp_crc,
:: :
:: } LameParameter;
::
Another point is: It is not wise to mix
-vbr -b and -cbr -b
I would use three parameters for -vbr -b, -vbr -B and -cbr -b.
::
:: // different channel modes
:: #define CM_MONO 1
:: #define CM_LR_STEREO 2
:: #define CM_J_STEREO 3
:: #define CM_MS_STEREO 4
:: ...
::
:: Example setup sequence:
::
:: handle = lame_open();
::
:: lame_set_param( handle, lp_bitrate, 128 );
:: lame_set_param( handle, lp_crc, FALSE );
:: lame_set_param( handle, lp_original, FALSE );
:: lame_set_param( handle, lp_channelmode, CM_J_STEREO );
:: lame_set_param( handle, lp_quality, 2 );
::
:: lame_set_param( handle, lp_lowpass, 5500 );
:: lame_set_param( handle, lp_lowpasswidth, 500 );
:: lame_set_param( handle, lp_highpass, 100 );
::
:: lame_set_param( handle, lp_resample, 11025 );
::
:: lame_set_param( handle, lp_voice, TRUE );
::
:: lame_init_params( handle );
::
:: <encoding>
::
:: lame_close( handle );
::
:: This way there is no need to parse any strings, we don't pass
:: any pointers, the setup routine would just be a big switch/case.
::
An additional demultiplexer/multiplexer.
::
handle = lame_open ();
lame_set_cbr_bitrate ( handle, 128000 );
lame_set_crc ( handle, disable );
lame_set_genuine ( handle, false );
lame_set_channel_mode ( handle, channelmode_jstereo );
lame_set_quality ( handle, 100-2*100/9 ); // arbitrary values are scaled
(bad=0,best=100)
lame_set_lowpass ( handle, 5500.0 );
lame_set_lowpass_width( handle, 500.0 );
lame_set_lowpass_width_ratio ( handle, 0.0909 );
lame_set_highpass ( handle, 100.0 );
lame_set_output_samplefreq ( handle, 11025.0 );
lame_set_presets ( handle, preset_voice );
<encoding>
lame_close ( handle );
Handles have advantages and disadvantages:
- you need an internal pointer array, with affects the maximum number of
handles:
lame_global_flags *Table [20];
lame_open() searches a free slot and allocates memory.
A forget lame_close() makes difficult to find problems.
- no type security. Noone avoids things like:
lame_set_highpass ( 44100/1000, 100.0 );
+ you can't access the structure itself.
- this can also be achieved via a simple trick.
You use different structure definitions for the interface and for the lib.
interface:
typedef enum {
valid_setup = 1, // ready for setup
valid_encode = 2 // ready for encoding
// otherwise invalid
} valid_t;
#ifndef ____LAME_LIB
typedef struct {
valid_t valid;
} LAME;
#else
typedef struct {
valid_t valid;
long double input_sample_freq;
long double output_sample_freq;
unsigned quality; // unsigned is better to ease boundary checks
bool crc;
unsigned long cbr_bitrate; // bps, not Gbps or thinks like that
unsigned long vbr_min_bitrate;
unsigned long vbr_max_bitrate;
...
} LAME;
#endif
LAME* lame_open ( void );
int lame_set_crc ( LAME*, enable_t );
int lame_set_genuine ( LAME*, bool );
int lame_set_lowpass ( LAME*, double );
int lame_close ( LAME* );
double lame_report_lowpass ( const LAME* ); // I hate set and get
--
Mit freundlichen Grüßen
Frank Klemm
eMail | [EMAIL PROTECTED] home: [EMAIL PROTECTED]
phone | +49 (3641) 64-2721 home: +49 (3641) 390545
sMail | R.-Breitscheid-Str. 43, 07747 Jena, Germany
--
MP3 ENCODER mailing list ( http://geek.rcc.se/mp3encoder/ )