Q: I'm a student on University of XYZ. May I Get your source code for my own 
   mp3 project?
A: No!
   (Maybe later, when I will finish (I don't want to continue) the development
   of Mpxplay, I will public my source)

Q: I would like to write my own MP3 Player. Do you have any tips, or know any 
   sites where I can download sample codes or any mpeg documents?
A: You can find some usable freeware mp3 decoder sources on the net:
   - Freeamp (www.dailymp3.com)
   - MPG123  (www-ti.informatik.uni-tuebingen.de/~hippm/mpg123.html)
   - OpenCP  (www.cubic.org/player)
   and some other usable sources on 
   - www.programmersheaven.com

Q: Will your mp3 player give me the same sound quality as Winamp or another 
   win32 product? 
A: I think so there isn't too much sound quality difference between Mpxplay
   and other players. 
   (I know the Sonique is better, but try to use it on a 486 CPU ...)
   Maybe the problem is your sound card, my program supports only some of them.
   Mpxplay can handle SBpro compatible cards only on 8 bit and 22khz. 
   You get the best quality (16 bit,44khz) with an original SB16, ESS, 
   or WSS compatible card (Yamaha, Crystal Sound)

Q: Why don't you implement a normal CD player instead of real-time CD-ripper?
A: 1. The primary reason is in the DPMI (32 bit DOS environment for C++), 
      I can't send correct informations (through it) to DOS (to the MSCDEX)
      from my program. I try to solve this problem in the future.
   2. It was easier to implement.
   3. On this way, you can use CROSSFADE, AUTOVOLUME, SPEED CONTROL and
      surround on AUDIO CD tracks. You couldn't do these in a normal
      CD audio player.
   
   In fact, the CDW doesn't work on every CD-ROMs (especially on old ones).

Q: Mpxplay sometimes cuts the end of the songs (losing 4-5 seconds). 
   Is there any way to play the song completly?
A: You can disable this function (cut) with the '-sl 0' command line option
   or with the SoundLimit=0 setting (in the mpxplay.ini).
   (or try a low value: 1-5)

Q: 1. I have a 386-25 computer. Could you optimize your player that runs
      on my pc too?
   2. I have a 386-DX40 and I use your program to convert mp3 files to wav.
      And sometimes it makes a zero wav file.
A: My program is optimized (compiled) for pentium processor, but it can
   work on a 486 CPU too. But it CAN'T on a 386, nor with external FPU.
   (sometimes it may works, but usually: NOT)
   And the speed... Maybe with full asm decoding I can reach that my program
   runs on a 486 DX2-66, but it never will be faster.

Q: Can you tell me, in which language you wrote this player ?
A: Mpxlay contains about 15000 source lines, most of them written in C,
   but I rewrote some mpeg decoding routines into asm.

Q: I would like to use an external (TSR) program to control Mpxplay, 
   but it doesn't work. Why?
A: I don't use the 'normal' BIOS or DOS routines to read keyboard,
   I wrote my own low-level functions. You can find them bellow.

Here are my keyboard read functions:
(maybe some compilers can't compile them, but Watcom C can ...)

unsigned char *biosp=(unsigned char)(0);

unsigned int mykbhit(void)
{
 if((unsigned short)(biosp[0x41a])==(unsigned short)(biosp[0x41c]))
  return 0;
 return 1;
}

unsigned short myextgetch(void)
{
 unsigned short pointer;

 while(!mykbhit());
 pointer=0x400+(unsigned short)biosp[0x41a];
 (unsigned short)biosp[0x41a]+=2;
 if((unsigned short)biosp[0x41a]>=(unsigned short)biosp[0x482])
  (unsigned short)biosp[0x41a]=(unsigned short)biosp[0x480];
 return (unsigned short)(biosp[pointer+1]<<8)|(unsigned short)(biosp[pointer]);
}

And somebody wrote me, my extgetch function is not reversible.
I think so he can't do programming.
Here is my solution to put (push) a keycode (2 bytes) into the keyboard buffer.

void pushkey(unsigned short newkey)
{
 unsigned short pointer=0x400+(unsigned short)(biosp[0x41c]);
 biosp[pointer+1]=newkey>>8;
 biosp[pointer]  =newkey&0xff;
 (unsigned short)(biosp[0x41c])+=2;
 if((unsigned short)biosp[0x41c]>=(unsigned short)biosp[0x482])
  (unsigned short)biosp[0x41c]=(unsigned short)biosp[0x480];
}

And here are some hardware and software infos to connect an LCD display
to the LPT port:

Hardware connection between LPT port and LCD-module for LCDtype=1

LPT Pin:        LCD Pin: 
GND              1 (GND) 
Use External 5V  2 (+5V) 
GND              3 (GND Contrast should be connected to variable resistor, but this works for me!) 
16               4 (RS) 
14               5 (R/W If Reading not used, tie to GND) 
1                6 (E) 
2                7 (D0) 
3                8 (D1) 
4                9 (D2) 
5               10 (D3) 
6               11 (D4) 
7               12 (D5) 
8               13 (D6) 
9               14 (D7) 
Use External 5V 15 (LED Backlight Anode+ Dont forget to current limit the LEDS if required, they my blow) 
GND             16 (LED Backlight Cathode -) 


Hardware connection between LPT port and LCD-module for LCDtype=4

LPT Pin:         LCD Pin: 
GND              1 (GND) 
Use External 5V  2 (+5V) 
GND              3 (GND Contrast should be connected to variable resistor, but this works for me!) 
16               4 Register Select 
GND              5 Read/Write. 
17               6 Enable 
2                7 (D0) 
3                8 (D1) 
4                9 (D2) 
5               10 (D3) 
6               11 (D4) 
7               12 (D5) 
8               13 (D6) 
9               14 (D7) 
Use External 5V 15 (LED Backlight Anode+ Dont forget to current limit the LEDS if required, they my blow) 
GND             16 (LED Backlight Cathode -) 


LCD routines. I couldn't test them, if you find bugs, or you know a better
solution, please send it to me.

#define LCDdataport   LCD_lptport    // 0x378 or 0x278 or 0x3BC
#define LCDstatusport LCD_lptport+1
#define LCDcntrlport  LCD_lptport+2

#define Cmd_Register  0x0a
#define Data_Register 0x0e

#define Enable_High   0x00
#define Enable_Low    0x01

#define Clear_Display                0x01
#define Set_Entry_Mode               0x04
#define Set_Display                  0x08
#define Set_Function                 0x20
#define Increment_Address            0x02
#define Shift_Display_Off            0x00
#define Display_On                   0x40
#define Cursor_Off                   0x00
#define Blink_Off                    0x00
#define Data_Length_8                0x10
#define One_Display_Line             0x00
#define Two_Display_Lines            0x08
#define Font_5x7                     0x00

void LCDwait(void)
{
 unsigned int i=LCD_delay;
 while(i--);
}

void LCD_put_command(int Cmd) {
 switch(LCD_type){
  case 1:{
   outp(LCDdataport,Cmd);
   outp(LCDcntrlport,Cmd_Register + Enable_High);
   LCDwait();
   outp(LCDcntrlport,Cmd_Register + Enable_Low);
   LCDwait();
   break;
  }
  case 2:{
   outp(LCDcntrlport,inp(LCDcntrlport)&0xDF);// reset control port - Parallel Port direction = Out
   outp(LCDcntrlport,inp(LCDcntrlport)|0x08);// enable register select on LCD - set COMMAND mode (not data)
   outp(LCDdataport, Cmd);                   // send command
   outp(LCDcntrlport,inp(LCDcntrlport)|0x01);// raise enable (Read/Write) signal
   delay(5);                                 // command execute time
   outp(LCDcntrlport,inp(LCDcntrlport)&0xFE);// reset enable (Read/Write) signal
   delay(20);                                // faster PC, larger volume
   outp(LCDcntrlport,inp(LCDcntrlport)&0xF7);// reset register select line
   break;
  }
  case 3:{
   unsigned int timeout=65535;
   do{
    if(!(timeout--))
     return;
   }while((inp(LCDstatusport)&0x80));
   outp(LCDdataport,Cmd);
   outp(LCDcntrlport,8);
   outp(LCDcntrlport,9);
   break;
  }
  case 4:{
   outp(LCDcntrlport,inp(LCDcntrlport) & 0xFB);
   outp(LCDdataport,Cmd);
   outp(LCDcntrlport,inp(LCDcntrlport) | 0x08);
   delay(2);
   outp(LCDcntrlport,inp(LCDcntrlport) & 0xF7);
   delay(20);
   break;
  }
 }
}

void LCD_put_char(unsigned char Chr) {
 switch(LCD_type){
  case 1:{
   outp(LCDdataport,Chr);
   outp(LCDcntrlport,Data_Register + Enable_High);
   LCDwait();
   outp(LCDcntrlport,Data_Register + Enable_Low);
   LCDwait();
   break;
  }
  case 2:{
   outp(LCDdataport, Chr);                   // send data (char)
   outp(LCDcntrlport,inp(LCDcntrlport)|0x01);// raise enable (read/write) signal
   outp(LCDcntrlport,inp(LCDcntrlport)&0xFE);// reset enable (read/write) signal
   delay(2);                                 // faster PC larger value
   outp(LCDcntrlport,inp(LCDcntrlport)&0xF7);// reset register select line
   break;
  }
  case 3:{
   unsigned int timeout=65535;
   do{
    if(!(timeout--))
     return;
   }while((inp(LCDstatusport)&0x80));
   outp(LCDdataport,Chr);
   outp(LCDcntrlport,0);
   outp(LCDcntrlport,9);
   break;
  }
  case 4:{
   outp(LCDcntrlport,inp(LCDcntrlport) | 0x04);
   outp(LCDdataport, Chr);
   outp(LCDcntrlport,inp(LCDcntrlport) | 0x08);
   delay(2);
   outp(LCDcntrlport,inp(LCDcntrlport) & 0xF7);
   outp(LCDcntrlport,inp(LCDcntrlport) & 0xFB);
   break;
  }
 }
}

void LCD_putstr(char *string) {
 unsigned int i,l=mystrlen(string);
 for (i=0;i<l;i++)
  LCD_put_char(string[i]);
}

static void initLCD(void)
{
 char tmp;
 if(!LCD_lptport || !LCD_rows || !LCD_lines)
  return;
 switch(LCD_type){
  case 1:{
   LCD_put_command(Set_Function + Data_Length_8);     // 0x30
   LCD_put_command(Set_Function + Data_Length_8);     // 0x30
   LCD_put_command(Set_Function + Data_Length_8);     // 0x30
   tmp=Set_Function + Data_Length_8 + Font_5x7;       // 0x30
   if(LCD_lines>1)
    tmp+=Two_Display_Lines;                           // + 8
   LCD_put_command(tmp);                              // = 0x38
   LCD_put_command(Set_Display + Cursor_Off + Blink_Off); // 0x08
   LCD_put_command(Clear_Display);                        // 0x01
   LCD_put_command(Set_Entry_Mode + Increment_Address + Shift_Display_Off); //0x06
   LCD_put_command(0x0c); // display ON, cursor OFF, blink Off
   break;
  }
  case 2:      // init commands for type 2 and for type 4 are same
  case 4:{
   LCD_put_command(0x0f); // init command
   LCD_put_command(0x01); // clear
   LCD_put_command(0x38); // 2 lines / 8bits
   LCD_put_command(0x0C); // cursor off
   outp(LCDcntrlport,inp(LCDcntrlport) & 0xDF); // reset control port
   break;
  }
  case 3:{
   tmp=32+16+0;  // set_function + 8 bits + 5x7 dots
   if(LCD_lines>1)
    tmp+=8;        // + 2 lines
   LCD_put_command(tmp);
   LCD_put_command(12); // Display ON, Cursor OFF, Blink OFF
   LCD_put_command(6);  // Cursor Move Right, Display Shift Off
   LCD_put_command(1);  // Clear Display
   break;
  }
 }
}
