// // This file is part of the AC97 mp3 player. // (C) copyright Serge 2006-2007 infinity_sound@mail.ru // (C) copyright Quantum 2007 ufmod@users.sf.net // // 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. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. #include "../kolibri.h" #include "string.h" #include "ac97wav.h" #include "../mpg/mpg123.h" #include "../sound.h" #include "../ufmod-codec.h" /* uFMOD integration */ void exit(); /* uFMOD integration */ #define DOCKABLE_WINDOW #define MP3_ERROR_OUT_OF_BUFFER 5 int m_last_error; void _stdcall thread_proc(void *param); int _stdcall create_thread(void *proc, void *param, int stack_size); #ifdef DOCKABLE_WINDOW void GetThreadInfo (char *info, int slot); //Asper+ #endif void touch(char *buf, int size); int mp3FindSync(byte* buf, int size, int* sync); int stream_read_raw(struct reader *rd,unsigned char *buf, int size); int __cdecl _stricmp (const char * dst, const char * src); char *__cdecl strrchr (const char * string,int ch); int _strncmp(char *src, char *dst, DWORD n); //Asper+ int _strncpy (char *dst, char *src, int n); //Asper+ void uint2str(unsigned int value, char *string); //Asper+ struct reader rd; struct frame fr; DWORD hDrv; DWORD hSound; SNDBUF hBuff; CTRL_INFO info; FILEINFO fileinfo; const char filename[256]; const char *fileext; char full_filename[4096]; int m_vol; int l_vol=-700; //-7db int r_vol=-700; int pan =0; DWORD status; DWORD first_sync; DWORD PLStatus=0; //Asper+ byte replay=0; byte hidden=0; #ifdef DOCKABLE_WINDOW byte thread_info[1024]; //Asper+ #endif int tid, pl_tid; const DWORD main_wh=92, pl_ww=300, pl_wh=382; //Asper+ DWORD pl_wx=100, pl_wy=101+92; //Asper+ //DWORD main_wc=0x404040, main_bc=0x808080; //Asper+ ac97snd Classic style //DWORD main_wc=0x002040, main_bc=0x008080, main_ic=0x002040, selected_ic=0x1010F0; //Asper+ DWORD main_wc=0x101030, main_bc=0x008080, main_ic=0x000000, selected_ic=0x1010F0; //Asper+ unsigned char *testbuff; unsigned char *outbuf; unsigned char *inpbuf; unsigned char *outPtr; int inpsize; int outsize; int outremain; int totalout; int done; char header[] = "AC97 MP3 player"; char header_PL[] = "PLAYLIST"; char buttons_xm[] = " Play Stop Vol- Vol+"; /* uFMOD integration */ char buttons_wav[] = " Play Stop << >> Vol- Vol+"; /* uFMOD integration */ char button_PL[] = "PL"; //Asper+ char button_R[] = "R"; //Asper+ char *buttons_text = buttons_wav; /* uFMOD integration */ void play_xm(); /* uFMOD integration */ void (*snd_play)(); //Asper_____________________Play List code start_____________________________ #define PLI_BUTTON_HEIGHT 13 #define PL_MAX_SHOWN_ITEMS (pl_wh-PLI_BUTTON_HEIGHT-40)/PLI_BUTTON_HEIGHT #define MAX_TEXT_WIDTH 46 #define MAX_PATH_LEN 1024 int currSelected, currActive, currFirstShowed; unsigned char *pl_buff; char pl_path[4096]; int pl_items_number; int ShowPLContent(char *filebuffer); int GetFileNameFromPL(const char *fbuff, int index, char *name); void Win2Dos (char *st, int len); void HidePLWindow() { BeginDraw(); ResizeReplaceWindow(pl_wx,pl_wy,0,0); EndDraw(); } void ShowPLWindow() { unsigned int i; BeginDraw(); DrawWindow(pl_wx,pl_wy,pl_ww,pl_wh,main_ic,4,0,0,0); for (i=0; i0) currFirstShowed--; else break; ShowPLContent(pl_buff); break; } currSelected--; ShowPLContent(pl_buff); break; case 0x50: //Down if (currSelected+currFirstShowed > pl_items_number-2) break; if (currSelected==PL_MAX_SHOWN_ITEMS-1) { //if (currFirstShowed -10000) { l_vol-=100; r_vol-=100; SetVolume(hBuff,l_vol,r_vol); }; break; case 0x53: if(pan > -10000) { pan -=100; SetPan(hBuff,pan); }; break; case 0x51: if(pan < 10000) { pan +=100; SetPan(hBuff,pan); }; break; } }; }; break; case EV_BUTTON: button=get_button_id(); if (button==1) { PLStatus=0x00; exit(); } currActive=currFirstShowed+button-0x10-1; status = ST_TRACK; break; case EV_IPC: *ipc_buff='\0'; ShowPLContent(pl_buff); break; } } } void Win2Dos (char *st, int len) { int i; unsigned char ch; for (i=0; i=192) { if (ch>=240) ch-=16; else ch-=64; } else { if (ch==168) ch = 240; if (ch==184) ch = 241; if (ch==185) ch = 252; if ((ch==147) || (ch==148) || (ch==171) || (ch==187)) ch = 34; if ((ch==150) || (ch==151)) ch = 45; } st[i]=ch; } } int GetFileNameFromPL(const char *plbuff, int index, char *name) { int count=0,i=0,j=0; char ch; do{ ch=plbuff[i]; if (ch!='#' && i && plbuff[i-1]=='\n') count++; if (count-1==index) { if (j>MAX_PATH_LEN || ch=='\r' || ch=='\n') { name[j]='\0'; break; } if (ch=='\\') ch='/'; name[j++]=ch; } else if (count-1>index) break; i++; }while (ch); if (!ch) return 0; return j; } int CountFileNamesInPL(const char *plbuff) { int count=0,i=0; char ch; do{ ch=plbuff[i]; if (ch!='#' && i && plbuff[i-1]=='\n') count++; i++; }while (ch); if (count) count--; return count; } int ShowPLContent(char *filebuffer) { char st[MAX_PATH_LEN+10]="", tmp[MAX_PATH_LEN+1]=""; unsigned int len=8,i; DWORD text_color; draw_bar(7,24,285,PLI_BUTTON_HEIGHT*(PL_MAX_SHOWN_ITEMS+2), main_ic); draw_bar(7,24+currSelected*(PLI_BUTTON_HEIGHT+1),285,PLI_BUTTON_HEIGHT, selected_ic); for (i=0; i MAX_TEXT_WIDTH) len = MAX_TEXT_WIDTH; write_text(11,i*(PLI_BUTTON_HEIGHT+1)+27,text_color, st, len); } return 1; } //Asper_____________________Play List code end_____________________________ void redraw_R_button() //Asper + { DWORD rc = 0xA0FFA0; //R button text color if (!replay) rc = 0x404040; write_text(14,74,rc|FONT0,button_R,sizeof(button_R)-1); } void update_dynamic_content() //Asper + { int len = strlen(filename); if (len > 47) len = 47; draw_bar(7,41,286,11,main_wc); draw_bar(7,55,286,11,main_wc); write_text(11,57,0x00FF20|FONT0, filename, len); } void draw_window() { if (!hidden) { BeginDraw(); DrawWindow(100,100,299,main_wh,main_wc,4,0,0,0); //Asper+ make_button(7,24,45,13, 0x10|BT_NORMAL,main_bc); make_button(56,24,45,13, 0x11|BT_NORMAL,main_bc); make_button(104,24,45,13, 0x12|BT_NORMAL,main_bc); make_button(152,24,45,13, 0x13|BT_NORMAL,main_bc); make_button(200,24,45,13, 0x14|BT_NORMAL,main_bc); make_button(248,24,45,13, 0x15|BT_NORMAL,main_bc); make_button(268,70,25,13, 0x16|BT_NORMAL,main_bc); //Asper+ PL button make_button(10,70,12,13, 0x17|BT_NORMAL,main_wc); //Asper+ R button make_button(7,41,286,11, 0x30|BT_HIDE|BT_NOFRAME,main_wc); update_dynamic_content(); write_text(8,8,FONT0, header, sizeof(header)-1); /* uFMOD integration */ write_text(12,28,main_wc|FONT0,buttons_text,sizeof(buttons_wav)-1); /* uFMOD integration */ write_text(11,27,0xA0FFA0|FONT0,buttons_text,sizeof(buttons_wav)-1); /* uFMOD integration */ write_text(276,74,main_wc|FONT0,button_PL,sizeof(button_PL)-1); //Asper+ PL button text write_text(275,73,0xA0FFA0|FONT0,button_PL,sizeof(button_PL)-1); //Asper+ redraw_R_button(); EndDraw(); } }; void draw_progress_bar() { DWORD x; x = (DWORD)(287.0f * (float)(rd.filepos-rd.strremain)/(float)fileinfo.size); /* uFMOD integration */ if(x==0) return; draw_bar(7,41,x,11,0xA0A0A0); draw_bar(x+7,41,287-x,11,main_wc); }; void debug_out_str(const char* str) { while (*str != 0) { debug_out(*str); str++; } } void ExitInHiddenMode() { status = ST_EXIT; //send message to second process about exit uFMOD_StopSong(); StopBuffer(hBuff); DestroyBuffer(hBuff); exit(); } int LoadPL(char *fname) { DWORD fmt; DWORD r_bytes; int retval; int i; // char st[100]=""; char *pch; r_bytes=0; pch=strrchr(fname, '/'); if (pch) i=pch-fname+1; else i=strlen(fname); _strncpy (pl_path, fname, i); pl_path[i]='\0'; if (!pl_buff) pl_buff = UserAlloc(0x40000); retval=read_file (fname,pl_buff,0,0x40000,&r_bytes); if ( retval && (r_bytes==0)) return 0; Win2Dos(pl_buff, r_bytes); pl_items_number=CountFileNamesInPL(pl_buff); /* debug_out_str("\n\rPlay List files number = "); itoa(pl_items_number, st, 10); debug_out_str(st); */ fmt = test_m3u(pl_buff); if(!fmt) { debug_out_str("\n\rInvalid M3U file"); return 0; } debug_out_str("\n\rValid M3U file"); currSelected=currFirstShowed=0; currActive=-1; return 1; } int LoadFile(char *fname) { DWORD fmt; DWORD r_bytes; int retval; int err; unsigned char *ttl, *cur; /* uFMOD integration */ debug_out_str("\n\rPlay file "); debug_out_str(fname); debug_out_str("\n\r"); if(get_fileinfo(fname, &fileinfo)==FILE_NOT_FOUND) { debug_out_str("\n\rfile not found\n\r"); return 0; } r_bytes=0; strcpy(filename, strrchr(fname,'/')+1); if( !(fileext = strrchr(filename,'.'))) return 0; if(!_stricmp(fileext,".m3u")) { LoadPL(fname); status=ST_TRACK; return 1; } if (!testbuff) testbuff = UserAlloc(4096); retval=read_file (fname,testbuff,0,2048,&r_bytes); if ( retval && (r_bytes==0)) return 0; if (!inpbuf) { inpbuf = UserAlloc(0x10000); touch(inpbuf, 0x10000); } create_reader(&rd, inpbuf, 0x10000); init_reader(&rd,fname); if(!_stricmp(fileext,".mp3")) { fmt = test_mp3(testbuff); if(!fmt) { debug_out_str("\n\rInvalid MP3 file"); return 0; }; snd_play = &play_mp3; outremain = 0x40000; if (!outbuf) { outbuf = UserAlloc(outremain); touch(outbuf, outremain); } make_decode_tables(32767); init_layer2(); init_layer3(32); fr.single = -1; goto play; }; if(!_stricmp(fileext,".xm")) { if(uFMOD_LoadSong(fname)) { buttons_text = buttons_xm; /* uFMOD integration */ fmt = PCM_2_16_48; /* uFMOD integration */ snd_play = &play_xm; /* uFMOD integration */ ttl = uFMOD_GetTitle(); /* uFMOD integration */ cur = ttl; /* uFMOD integration */ err = 0; /* uFMOD integration */ while(*cur && *cur++ != ' ') err++; /* uFMOD integration */ if(err){ /* uFMOD integration */ cur = fname; /* uFMOD integration */ while(*cur) cur++; /* uFMOD integration */ *cur++ = ' '; /* uFMOD integration */ *cur++ = '|'; /* uFMOD integration */ *cur++ = ' '; /* uFMOD integration */ while(*ttl) *cur++ = *ttl++; /* uFMOD integration */ } goto play; } debug_out_str("\n\rInvalid XM file"); return 0; }; if(!_stricmp(fileext, ".wav")) { fmt = test_wav((WAVEHEADER*)testbuff); if(fmt) { snd_play = &play_wave; set_reader(&rd, 44); outbuf = UserAlloc(32*1024); touch(outbuf, 32768); goto play; } debug_out_str("\n\rInvalid WAV file"); return 0; }; debug_out_str("\n\rUnsupported file"); return 0; play: status = ST_PLAY; SetFormat(hBuff, fmt); SetVolume(hBuff,l_vol,r_vol); GetVolume(hBuff,&l_vol,&r_vol); return 1; } int main(int argc, char *argv[]) { int err, ver; int i; char ipc_msg[2]="\0\0"; pl_items_number=0; if (argv[1][0]=='-' && argv[1][1]=='h') { hidden = 1; argv[1]+=3; } strcpy (full_filename, argv[1]); InitHeap(1024*1024); if(err = InitSound(&ver)) { debug_out_str("Sound service not installed\n\r"); return 0; } if( (SOUND_VERSION>(ver&0xFFFF)) || (SOUND_VERSION<(ver >> 16))) { debug_out_str("Sound service version mismatch\n\r"); return 0; } if (err = CreateBuffer(PCM_2_16_48, 0, &hBuff)) { debug_out_str("create buffer return error\n\r"); return 0; } if (!LoadFile(full_filename)) return 0; if (!hidden) tid=create_thread(thread_proc, 0, 4096); while(1) { delay(10); switch(status) { case ST_TRACK: StopBuffer(hBuff); if (hidden) ExitInHiddenMode(); if (LoadTrack(++currActive)) { if (LoadFile(full_filename)) status = ST_PLAY; } else { currSelected=currFirstShowed=0; currActive=-1; if (!replay) status = ST_STOP; continue; } //Update ac97snd and PL windows i=currActive-currFirstShowed; if (i>PL_MAX_SHOWN_ITEMS-1) currFirstShowed = currActive - PL_MAX_SHOWN_ITEMS/2; ipc_send_msg(tid, ipc_msg); ipc_send_msg(pl_tid, ipc_msg); continue; case ST_PLAY: snd_play(); continue; case ST_STOP: StopBuffer(hBuff); status = ST_DONE; if (hidden) ExitInHiddenMode(); continue; case ST_EXIT: uFMOD_StopSong(); /* uFMOD integration */ StopBuffer(hBuff); DestroyBuffer(hBuff); return 0; }; }; return 0; }; void touch(char *buf, int size) { int i; char a; for ( i = 0;i < size; i+=4096) //alloc all pages a = buf[i]; }; DWORD test_m3u(char *buf) //Asper+ { char *sign="#EXTM3U"; return _strncmp(buf, sign, 7); } DWORD test_mp3(char *buf) { unsigned long hdr; WAVEHEADER whdr; int attempts = 0; for (;;attempts++) { if(attempts > 1000) return 0; if(!rd.head_read(&rd,&hdr)) return 0; if(!decode_header(&fr,hdr)) { if((hdr & 0xffffff00) == 0x49443300) { int id3length = 0; id3length = parse_new_id3(&rd, hdr); continue; }; rd.strpos-=3; rd.stream-=3; rd.strremain+=3; continue; }; break; }; first_sync = rd.filepos-rd.strremain-4; whdr.riff_id = 0x46464952; whdr.riff_format = 0x45564157; whdr.wFormatTag = 0x01; whdr.nSamplesPerSec = freqs[fr.sampling_frequency]; whdr.nChannels = 2; whdr.wBitsPerSample = 16; return test_wav(&whdr); }; void play_mp3() { char *outPtr; int totalout; int outcount; // memset(&fr,0,sizeof(fr)); fr.down_sample_sblimit = 32; fr.single = -1; reset_mpg(); outPtr = outbuf; totalout=0; done = 0; outremain=0x40000; memset(outbuf,0,0x40000); set_reader(&rd, 0); //;first_sync); while(1) { if(status!=ST_PLAY) break; for(;;) { outcount = 0; if( !read_frame(&rd, &fr)) { done = 1; break; }; fr.do_layer(&fr, outPtr,&outcount); outPtr+= outcount; totalout+=outcount; outremain-=outcount; if(outremain < outcount*2) break; }; if(done) { if(totalout < 4096) { memset(outPtr,0,4096-totalout); totalout = 4096; }; } else if(totalout < 8192) continue; outPtr = outbuf; while (totalout >= 4096) { WaveOut(hBuff,outPtr,4096); if(status!=ST_PLAY) { if ((status != ST_EXIT) && (status != ST_STOP)) status = ST_TRACK; return; }; totalout-=4096; outPtr+=4096; outremain+=4096; }; if(done) break; memmove(outbuf,outPtr, totalout); outPtr = outbuf+totalout; } if ((status != ST_EXIT) && (status != ST_STOP)) status = ST_TRACK; }; void play_wave() { int count; set_reader(&rd,44); while(1) { if(status!=ST_PLAY) break; if( count=stream_read_raw(&rd,outbuf,32768)) { WaveOut(hBuff,outbuf,count); continue; } done = 1; break; }; if ((status != ST_EXIT) && (status != ST_STOP)) status = ST_TRACK; }; void play_xm(){ /* uFMOD integration */ while(status == ST_PLAY){ /* uFMOD integration */ uFMOD_WaveOut(hBuff); /* uFMOD integration */ delay(8); /* uFMOD integration */ } /* uFMOD integration */ if ((status != ST_EXIT) && (status != ST_STOP)) status = ST_TRACK; /* uFMOD integration */ } /* uFMOD integration */ void snd_stop() { StopBuffer(hBuff); }; void _stdcall thread_proc(void *param) { int evnt; int pos; int key; DWORD offset; char ipc_buff[16]; set_event_mask(0x47); //Asper + IPC event ipc_init(ipc_buff, 16); _asm { mov eax, 66 mov ebx, 1 mov ecx, 1 int 0x40 }; draw_window(); while(1) { if(status==ST_PLAY) { draw_progress_bar(); evnt = wait_for_event(80); } else evnt = wait_for_event_infinite(); #ifdef DOCKABLE_WINDOW GetThreadInfo(thread_info, -1); #endif switch(evnt) { case EV_REDRAW: draw_window(); break; case EV_KEY: if(!get_key(&key)) { switch(key) { case 0xE0: case 0xE1: break; default: switch (key) { case 0x01: //Esc status = ST_EXIT; exit(); break; case 0x13: //R - repeat on/off replay=!replay; redraw_R_button(); break; case 0x19: //P - playlist switch (PLStatus) { case 0x00: //PL not started. pl_tid=create_thread(pl_thread_proc, 0, 4096); PLStatus=0x12; break; case 0x01: //PL started, but hidden. PLStatus=0x12; break; case 0x02: //PL started and showed. PLStatus=0x11; break; } break; case 0x2C: //Z - previous currActive-=2; status = ST_TRACK; break; case 0x2D: //X - play status = ST_PLAY; break; case 0x2E: //C - pause break; case 0x2F: //V - stop status = ST_STOP; break; case 0x30: //B - next status = ST_TRACK; break; case 0x47: //Home if(l_vol < 0) { l_vol+=100; r_vol+=100; SetVolume(hBuff,l_vol,r_vol); }; break; case 0x4F: //End if(l_vol > -10000) { l_vol-=100; r_vol-=100; SetVolume(hBuff,l_vol,r_vol); }; break; case 0x53: if(pan > -10000) { pan -=100; SetPan(hBuff,pan); }; break; case 0x51: if(pan < 10000) { pan +=100; SetPan(hBuff,pan); }; break; } }; }; break; case EV_BUTTON: switch(get_button_id()) { case 1: status = ST_EXIT; exit(); break; case 0x10: status = ST_PLAY; break;//continue; case 0x11: status = ST_STOP; break; case 0x12: currActive-=2; status = ST_TRACK; break; case 0x13: status = ST_TRACK; break; case 0x14: if(l_vol > -10000) { l_vol-=100; r_vol-=100; SetVolume(hBuff,l_vol,r_vol); }; break; case 0x15: if(l_vol < 0) { l_vol+=100; r_vol+=100; SetVolume(hBuff,l_vol,r_vol); }; break; case 0x16: //Asper+ PL button action switch (PLStatus) { case 0x00: //PL not started. pl_tid=create_thread(pl_thread_proc, 0, 4096); PLStatus=0x12; break; case 0x01: //PL started, but hidden. PLStatus=0x12; break; case 0x02: //PL started and showed. PLStatus=0x11; break; } break; case 0x17: //Asper+ PL button action replay=!replay; redraw_R_button(); break; case 0x30: if(status==ST_DONE) break; pos = (GetMousePos(REL_WINDOW)>>16)-7; offset = ((fileinfo.size-44)/286*pos+44)&0xFFFFFFFC; set_reader(&rd, offset); draw_progress_bar(); break; }; break; case EV_IPC: *ipc_buff='\0'; update_dynamic_content(); break; }; }; }; void delay (int val) { _asm { mov eax,5 mov ebx, [val] int 0x40 }; } int wait_for_event(int time) { int retval; _asm { mov eax,23 mov ebx,[time] int 0x40 mov [retval], eax }; return retval; }; int wait_for_event_infinite() { int retval; _asm { mov eax,10 int 0x40 mov [retval], eax }; return retval; }; void BeginDraw() {_asm { mov eax,12 mov ebx, 1 int 0x40 }; }; void EndDraw() { _asm { mov eax,12 mov ebx, 2 int 0x40 }; }; //Asper+_______start KolibriOS sys functions___________________ void ResizeReplaceWindow (DWORD x, DWORD y, DWORD w, DWORD h) //Asper+ { _asm { mov eax, 67 mov ebx, [x] mov ecx, [y] mov edx, [w] mov esi, [h] int 0x40 }; } #ifdef DOCKABLE_WINDOW void GetThreadInfo (char *info, int slot) //Asper+ { _asm { mov eax, 9 mov ebx, [info] mov ecx, [slot] int 0x40 } } #endif void set_event_mask(int mask) { _asm { mov eax, 40 mov ebx, [mask] int 0x40 } } void ipc_init(char *buf, int bufsize) { _asm { mov eax, 60 mov ebx, 1 mov ecx, [buf] mov edx, [bufsize] int 0x40 } } int ipc_send_msg(int PID, char *msg) { int len = strlen(msg); int retval; _asm { mov eax, 60 mov ebx, 2 mov ecx, [PID] mov edx, [msg] mov esi, [len] int 0x40 mov [retval], eax } } //Asper+_______end KolibriOS sys functions___________________ //Asper+_______start strings routines___________________ int _strncmp(char *src, char *dst, DWORD n) { _asm{ mov esi, src mov edi, dst mov ecx, n } l1: _asm{ cmpsb jne err loop l1 } return 1; err: return 0; } int _strncpy (char *dst, char *src, int n) { int i; for (i=0; i= ((char *)src + count)) { while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; } } else { dst = (char *)dst + count - 1; src = (char *)src + count - 1; while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst - 1; src = (char *)src - 1; } } return ret; }; //**********/ void * __cdecl mem_cpy(void * dst,const void * src,size_t count) { void * ret = dst; while (count--) { *(char *)dst = *(char *)src; dst = (char *)dst + 1; src = (char *)src + 1; }; return(ret); } char * __cdecl strrchr (const char * string,int ch) { char *start = (char *)string; while (*string++) /* find end of string */ ; /* search towards front */ while (--string != start && *string != (char)ch) ; if (*string == (char)ch) /* char found ? */ return( (char *)string ); return(NULL); } int __cdecl _stricmp (const char * dst, const char * src) { int f, l; do { if ( ((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z') ) f -= 'A' - 'a'; if ( ((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z') ) l -= 'A' - 'a'; } while ( f && (f == l) ); return(f - l); }