Thread: Audio Player Thread

  1. #1
    Registered User
    Join Date
    Nov 2010
    Posts
    13

    Audio Player Thread

    Being fed up with Windows Media Player lagging and screwing up my playlist I decided to pursue my own audio player. I used the Windows WaveOut API and made a thread that would control the position while the main thread set it's play/stop/restart flags, and it loads the entire file into memory removing the need for a file handle. It works, but the audio sounds a bit choppy and plays slower than on other players, and I don't understand why. Here's the code for the player:

    Code:
    #include <windows.h>
    #include <stdbool.h>
    #define MAXBUFFSIZE 44100
    #define WM_PLYBCKERR (WM_USER+1)
    #define WM_ENDPLAY (WM_USER+2)
    #define FLAG_RTHR 0x00000001
    #define FLAG_STHR 0xFFFFFFFE
    #define FLAG_PLAY 0x00000002
    #define FLAG_STOP 0xFFFFFFFD
    #define FLAG_REST 0x00000004
    #define FLAG_STAR 0xFFFFFFFB
    #define FLAG_DONE 0x00000008
    #define FLAG_NDON 0xFFFFFFF7
    /*  Declare Windows procedure  */
    LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
    typedef struct _wave_struct {
          WAVEFORMATEX wsFormat;
          PBYTE FileData;
          LPWAVEHDR Hdrs;
          unsigned int HdrC;
    } WAVE_STRUCT, *PWAVE_STRUCT;
    typedef struct _wave_file_hdr {
          char wfhSign[4];
          unsigned int wfhChnkSize;
          char wfhFormat[4];
          char wfhSubChnk1ID[4];
          unsigned int wfhSubChnk1Size;
          unsigned short wfhAudFormat;
          unsigned short wfhChannels;
          unsigned int wfhSampPerSec;
          unsigned int wfhAvgBytesPerSec;
          unsigned short wfhBlockAlign;
          unsigned short wfhBitsPerSamp;
          char wfhSubChnk2Sign[4];
          unsigned int wfhSubChnk2Size;
    } WAVE_FILE_HDR, *PWAVE_FILE_HDR;
    typedef HANDLE THREAD;
    typedef struct _thr_data {
          THREAD Thr;
          WAVE_STRUCT ws;
          HWAVEOUT wOut;
          unsigned int Flags;
          char *wLoc;
          HWND hwndPar;
          ULONG ID;
    } THREAD_DATA, *PTHREAD_DATA;
    CRITICAL_SECTION wCrit;
    bool LoadWaveFile(PWAVE_STRUCT ws,char *Loc){
          if(!ws||!Loc){
                SetLastError(ERROR_INVALID_PARAMETER);
                return false;
          }
          WAVE_FILE_HDR Head = {{0,0,0,0},0,{0,0,0,0},{0,0,0,0},0,0,0,0,0,0,0,{0,0,0,0},0};
          DWORD Read = 0, i = 0;
          PBYTE wPoint = 0;
          HANDLE wFile = CreateFile(Loc,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL);
          if(wFile==INVALID_HANDLE_VALUE) return false;
          if(!ReadFile(wFile,&Head,sizeof(Head),&Read,NULL)){
                CloseHandle(wFile);
                return false;
          }
          if(Head.wfhSign[0] != 'R' || Head.wfhSign[1] != 'I' || Head.wfhSign[2] != 'F' || Head.wfhSign[3] != 'F'){
                SetLastError(ERROR_NOT_SUPPORTED);
                CloseHandle(wFile);
                return false;
          }
          if(Head.wfhFormat[0] != 'W' || Head.wfhFormat[1] != 'A' || Head.wfhFormat[2] != 'V' || Head.wfhFormat[3] != 'E'){
                CloseHandle(wFile);
                SetLastError(ERROR_NOT_SUPPORTED);
                return false;
          }
          if(Head.wfhAudFormat!=WAVE_FORMAT_PCM){
                CloseHandle(wFile);
                SetLastError(ERROR_NOT_SUPPORTED);
                return false;
          }
          Read = 1 + (Head.wfhChnkSize-36) / MAXBUFFSIZE;
          ws->Hdrs = (WAVEHDR*)malloc(Read*sizeof(WAVEHDR)+(Head.wfhChnkSize-36));
          ZeroMemory(ws->Hdrs,Read*sizeof(WAVEHDR)+(Head.wfhChnkSize-36));
          if(!ws->Hdrs){
                CloseHandle(wFile);
                SetLastError(ERROR_OUTOFMEMORY);
                return false;
          }
          ws->FileData = (PBYTE)ws->Hdrs+Read*sizeof(WAVEHDR);
          ws->HdrC = Read;
          ReadFile(wFile,ws->FileData,Head.wfhChnkSize-36,&Read,NULL);
          if(Read!=(Head.wfhChnkSize-36)){
                free(ws->Hdrs);
                CloseHandle(wFile);
                free(ws->FileData);
                return false;
          }
          ws->wsFormat.wFormatTag = WAVE_FORMAT_PCM;
          ws->wsFormat.nChannels = Head.wfhChannels;
          ws->wsFormat.nSamplesPerSec = Head.wfhSampPerSec;
          ws->wsFormat.nAvgBytesPerSec = Head.wfhAvgBytesPerSec;
          ws->wsFormat.nBlockAlign = Head.wfhBlockAlign;
          ws->wsFormat.wBitsPerSample = Head.wfhBitsPerSamp;
          ws->wsFormat.cbSize = 0;
          CloseHandle(wFile);
          wPoint = ws->FileData;
          for(i=0;i<ws->HdrC-1;i++){
                ws->Hdrs[i].lpData = wPoint;
                ws->Hdrs[i].dwBufferLength = MAXBUFFSIZE;
                wPoint += MAXBUFFSIZE;
          }
          ws->Hdrs[ws->HdrC-1].lpData = wPoint;
          ws->Hdrs[ws->HdrC-1].dwBufferLength = (Head.wfhChnkSize-36) % MAXBUFFSIZE;
          SetLastError(ERROR_SUCCESS);
          return true;
    }
    void CALLBACK waveyProc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,DWORD Param1,DWORD Param2){
          if(uMsg==WOM_DONE){
                unsigned int *Flags = (unsigned int*)dwInstance;
                EnterCriticalSection(&wCrit);
                *Flags |= FLAG_DONE;
                LeaveCriticalSection(&wCrit);
          }
          return;
    }
    ULONG WINAPI PlayThr(volatile PTHREAD_DATA pThrDat){
          MMRESULT mRes = MMSYSERR_NOERROR;
          unsigned int Pos = 0;
          if(!LoadWaveFile(&pThrDat->ws,pThrDat->wLoc)){
                PostMessage(pThrDat->hwndPar,WM_PLYBCKERR,0,GetLastError());
                ExitThread(0);
          }
          mRes = waveOutOpen(&pThrDat->wOut,WAVE_MAPPER,&pThrDat->ws.wsFormat,(DWORD_PTR)waveyProc,(DWORD_PTR)&pThrDat->Flags,CALLBACK_FUNCTION);
          if(mRes!=MMSYSERR_NOERROR){
                PostMessage(pThrDat->hwndPar,WM_PLYBCKERR,0,mRes);
                free(pThrDat->ws.Hdrs);
                ExitThread(0);
          }
          for(Pos=0;Pos<pThrDat->ws.HdrC;Pos++) waveOutPrepareHeader(pThrDat->wOut,&pThrDat->ws.Hdrs[Pos],sizeof(WAVEHDR));
          Pos = 0;
          waveOutWrite(pThrDat->wOut,&pThrDat->ws.Hdrs[Pos],sizeof(WAVEHDR));
          while(pThrDat->Flags & FLAG_RTHR){
                while(pThrDat->Flags & FLAG_PLAY){
                      if(pThrDat->Flags & FLAG_DONE){
                            Pos++;
                            if(Pos == pThrDat->ws.HdrC){
                                  pThrDat->Flags &= FLAG_STOP;
                                  Pos = 0;
                            }else{
                                  waveOutWrite(pThrDat->wOut,&pThrDat->ws.Hdrs[Pos],sizeof(WAVEHDR));
                            }
                            pThrDat->Flags &= FLAG_NDON;
                      }
                      Sleep(10);
                }
                if(pThrDat->Flags & FLAG_REST){
                      Pos = 0;
                      pThrDat->Flags &= FLAG_STAR;
                      pThrDat->Flags |= FLAG_PLAY;
                }
                Sleep(250);
          }
          for(Pos=0;Pos<pThrDat->ws.HdrC;Pos++) waveOutUnprepareHeader(pThrDat->wOut,&pThrDat->ws.Hdrs[Pos],sizeof(WAVEHDR));
          free(pThrDat->ws.Hdrs);
          waveOutClose(pThrDat->wOut);
          PostMessage(pThrDat->hwndPar,WM_ENDPLAY,0,0);
          ExitThread(0);
    }
    char szClassName[ ] = "WavePlayer";
    char err[512];
    char *wLoc = NULL;
    int WINAPI WinMain (HINSTANCE hThisInstance,
                        HINSTANCE hPrevInstance,
                        LPSTR lpszArgument,
                        int nFunsterStil){
        HWND hwnd;
        MSG messages;
        WNDCLASSEX wincl;
        if(!SLen(lpszArgument)){
              ErrorBox(NULL,"No sound file specified!");
              return 0;
        }else{
              wLoc = lpszArgument;
        }
        wincl.hInstance = hThisInstance;
        wincl.lpszClassName = szClassName;
        wincl.lpfnWndProc = WindowProcedure;
        wincl.style = CS_DBLCLKS;
        wincl.cbSize = sizeof(WNDCLASSEX);
        wincl.hIcon = LoadIcon (NULL,IDI_APPLICATION);
        wincl.hIconSm = LoadIcon (NULL,IDI_APPLICATION);
        wincl.hCursor = LoadCursor (NULL,IDC_ARROW);
        wincl.lpszMenuName = NULL;
        wincl.cbClsExtra = 0;
        wincl.cbWndExtra = 0;
        wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
        if(!RegisterClassEx(&wincl)) return 0;
        hwnd = CreateWindowEx(
              0,
              szClassName,
              "Wavey",
              WS_OVERLAPPEDWINDOW|WS_VISIBLE,
              CW_USEDEFAULT,
              CW_USEDEFAULT,
              400,
              400,
              NULL,
              NULL,
              hThisInstance,
              NULL);
        while(GetMessage(&messages,NULL,0,0)){
              TranslateMessage(&messages);
              DispatchMessage(&messages);
        }
        return messages.wParam;
    }
    THREAD_DATA ThrData = {NULL,{{0,0,0,0,0,0,0},NULL,NULL,0},NULL,0,NULL};
    /*  This function is called by the Windows function DispatchMessage()  */
    LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
        switch (message){
            case WM_CREATE: ;
                 ThrData.hwndPar = hwnd;
                 ThrData.wLoc = wLoc;
                 ThrData.Flags = FLAG_PLAY|FLAG_RTHR;
                 ThrData.Thr = CreateThread(NULL,1024,(LPTHREAD_START_ROUTINE)PlayThr,&ThrData,0,&ThrData.ID);
                 if(!ThrData.Thr){
                       ErrorBox(hwnd,"Unable to initialize playback thread!");
                       PostQuitMessage(0);
                 }
                 InitializeCriticalSection(&wCrit);
                 break;
            case WM_LBUTTONDOWN: ;
                 if(ThrData.Thr){
                       if(ThrData.Flags & FLAG_PLAY){
                             ThrData.Flags &= FLAG_STOP;
                       }else{
                             ThrData.Flags |= FLAG_PLAY;
                       }
                 }
                 break;
            case WM_RBUTTONDOWN: ;
                 if(ThrData.Thr){
                       if(ThrData.Flags & FLAG_PLAY){
                             ThrData.Flags &= FLAG_STOP;
                       }
                       ThrData.Flags |= FLAG_REST;
                 }
                 break;
            case WM_PLYBCKERR: ;
                 sprintf(err,"Playback Error:\n(0x%X)",lParam);
                 ErrorBox(hwnd,err);
                 CloseHandle(ThrData.Thr);
                 break;
            case WM_CLOSE: ;
                 if(ThrData.Thr){
                       ThrData.Flags &= FLAG_STOP;
                       ThrData.Flags &= FLAG_STHR;
                       break;
                 }
                 PostQuitMessage(0);
                 break;
            case WM_ENDPLAY: ;
                 DestroyWindow(hwnd);
                 CloseHandle(ThrData.Thr);
                 break;
            case WM_DESTROY: ;
                 DeleteCriticalSection(&wCrit);
                 PostQuitMessage(0);
                 break;
            default: ;
                 return DefWindowProc (hwnd, message, wParam, lParam);
        }
        return ERROR_SUCCESS;
    }
    I'ts only supposed to play .wav files passed in the command line, and I'm compiling this program on Windows using the Bloodshed DevC++ IDE. I based this off an example from Playing Audio in Windows using waveOut Interface by David Overton - Translated by humans. I'd rather not use DirectShow or audio libraries for my program though. Any help would be appriciated.

  2. #2
    Fountain of knowledge.
    Join Date
    May 2006
    Posts
    794
    maybe it is something to do with the priority it is running at?

  3. #3
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    I changed the priority of the process with task manager to different priorities, including Realtime but nothing changed. Still choppy.

  4. #4
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Lane... take a look at MCI ... The windows prefab multimedia api. It provides playback for just about any file type windows can handle through codecs.

    However, from what you're describing the problem may not be WMP or your code at all. It sounds like you have something that's not multitasking well, causing the sound card buffer to run dry... (It's actually a common problem, worse in Win7)

    Visit my website ( HTPC Remote Control: Remote Media ) in the extras section you will find the DPC Latency tool... run it and see if anything is causing spikes. If you are getting spikes, drop me a note through my feedback link and we can try to sort it out via email.

    Also... you should download and try Media Player Classic Home Cinema, also linked on my site. I've seen in play files in situations where no other player could.

  5. #5
    Registered User
    Join Date
    Nov 2010
    Posts
    13

    Talking Problem found

    I downloaded the DPC Latency Checker and apparently there is a problem with my sound card, there are spikes and the program evens says that some drivers are causing drop-outs. You were right, my sound cards the problem, probably because my netbook runs Windows 7 Starter.

  6. #6
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Lane the Great View Post
    I downloaded the DPC Latency Checker and apparently there is a problem with my sound card, there are spikes and the program evens says that some drivers are causing drop-outs. You were right, my sound cards the problem, probably because my netbook runs Windows 7 Starter.
    As I explained in the email... it's most likely to be something other than your sound card. It's process of elimination time but my money is on anti-virus software. As I mentioned, Norton is particularly bad for this...

    Now... about using MCI instead of waveout...
    Last edited by CommonTater; 02-01-2011 at 09:33 AM.

  7. #7
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    To all reading this thread the sound card wasn't the problem with my program. I still can't find the cause of the slower playback and chopiness. Any one have an idea?

  8. #8
    Registered User Codeplug's Avatar
    Join Date
    Mar 2003
    Posts
    4,981
    Diagnose your system using WMP and DPC-latency-checker. The code that's been posted has plenty of issues on its own.

    Busy looping while calling Sleep() is never the way to implement a thread loop, and there's unsynchronized access to shared variables (ThrData). Both of these can be fixed by converting bit flags to Win32 events and use a WaitForMultipleObjects thead loop.

    >> ULONG WINAPI PlayThr(volatile PTHREAD_DATA pThrDat)
    This is an incorrect signature for a thread function. Calls to CreateThread should never cast the 3rd parameter. The "volatile" keyword has nothing to multi-threaded coding. (MSVC added their own extensions to the keyword, which no one should use, but you're not using a MS compiler.) Always use proper synchronization primitives provided by your OS/threading library.

    gg

  9. #9
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Lane the Great View Post
    To all reading this thread the sound card wasn't the problem with my program. I still can't find the cause of the slower playback and chopiness. Any one have an idea?
    Per your email... your wireless adaptor is the hardware problem causing the choppiness in WMP... You need to follow the directions in my email and get that fixed before you do much else because, without that fixed, you won't know if it's the adaptor or your code that's causing the problems.

    I see codeplug has had a look at your code... once you get WMP working correctly, you should work with him to sort out the code. He's clearly on the right track.

  10. #10
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    I installed the latest drive for my network adapter and also Driver Genius which scans for outdated drivers. It says my audio driver isn't up to date. I'll try and update that to see if it helps.

  11. #11
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Lane the Great View Post
    I installed the latest drive for my network adapter and also Driver Genius which scans for outdated drivers. It says my audio driver isn't up to date. I'll try and update that to see if it helps.
    Good idea... But a little hint first... Don't use the drivers from Microsoft update. Most of them are incomplete or limited. Go directly to the manufacturer's websites, that's usually where you get the best versions.

  12. #12
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    I updated the driver for my audio controller but it didn't help. I think my problem is with the synchronization of my threads like Codeplug said. The example mine's based off used Critical Sections to synchronize it's threads shared variables. I'm not too good with Waitable Timers and have only used them in OpenGL programs. How would I go about using WaitForMultipleObjects? or should it use WaitForSingleObject?

  13. #13
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Quote Originally Posted by Lane the Great View Post
    I updated the driver for my audio controller but it didn't help.
    Ok... as I said, you need to get WMP to play correctly before proceeding.
    Do you still have latency spikes?

  14. #14
    Registered User
    Join Date
    Nov 2010
    Posts
    13
    Latency Checker still spikes but only when my adapter is trying to connect to a wireless connection and Windows Media Player now plays with no problems. Finally!

  15. #15
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Ok, that's good... keep an eye on the latency...

    If you have Norton installed on that system you may want to remove it. It also causes these kinds of problems. Go with Windows Defender which is much better.

    Now I'll step aside and let codeplug help you with the software...

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Built simple audio player without gui
    By sauronnikko in forum C++ Programming
    Replies: 6
    Last Post: 02-01-2011, 01:09 AM
  2. Terminating secondary thread from another thread
    By wssoh85 in forum C++ Programming
    Replies: 13
    Last Post: 12-19-2008, 05:14 AM
  3. Thread Prog in C language (seg fault)
    By kumars in forum C Programming
    Replies: 22
    Last Post: 10-09-2008, 01:17 PM
  4. My first python project =)
    By Desolation in forum Tech Board
    Replies: 14
    Last Post: 06-26-2007, 10:52 PM
  5. Calling a Thread with a Function Pointer.
    By ScrollMaster in forum Windows Programming
    Replies: 6
    Last Post: 06-10-2006, 08:56 AM

Tags for this Thread