Ignore:
Timestamp:
Feb 21, 2009 2:21:59 PM (15 years ago)
Author:
bennylp
Message:

Updated libraries and applications to use the new Audio Device API

File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/aps-direct/pjmedia/src/pjmedia/wmme_sound.c

    r2366 r2468  
    1 #include <pjmedia/sound.h> 
    2 #include <pjmedia/errno.h> 
    3 #include <pj/assert.h> 
    4 #include <pj/log.h> 
    5 #include <pj/os.h> 
    6 #include <pj/string.h> 
    7  
    8 #if PJMEDIA_SOUND_IMPLEMENTATION == PJMEDIA_SOUND_WIN32_MME_SOUND 
    9  
    10 #ifdef _MSC_VER 
    11 #   pragma warning(push, 3) 
    12 #endif 
    13  
    14 #include <windows.h> 
    15 #include <mmsystem.h> 
    16  
    17 #ifdef _MSC_VER 
    18 #   pragma warning(pop) 
    19 #endif 
    20  
    21 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0 
    22 #   pragma comment(lib, "Coredll.lib") 
    23 #elif defined(_MSC_VER) 
    24 #   pragma comment(lib, "winmm.lib") 
    25 #endif 
    26  
    27  
    28 #define THIS_FILE                       "wmme_sound.c" 
    29 #define BITS_PER_SAMPLE                 16 
    30 #define BYTES_PER_SAMPLE                (BITS_PER_SAMPLE/8) 
    31  
    32 #define MAX_PACKET_BUFFER_COUNT         32 
    33 #define MAX_HARDWARE                    16 
    34  
    35 struct wmme_dev_info 
    36 { 
    37     pjmedia_snd_dev_info info; 
    38     unsigned             deviceId; 
    39 }; 
    40  
    41 static unsigned dev_count; 
    42 static struct wmme_dev_info dev_info[MAX_HARDWARE]; 
    43 static pj_bool_t snd_initialized = PJ_FALSE; 
    44  
    45 /* Latency settings */ 
    46 static unsigned snd_input_latency  = PJMEDIA_SND_DEFAULT_REC_LATENCY; 
    47 static unsigned snd_output_latency = PJMEDIA_SND_DEFAULT_PLAY_LATENCY; 
    48  
    49  
    50 /* Individual WMME capture/playback stream descriptor */ 
    51 struct wmme_stream 
    52 { 
    53     union 
    54     { 
    55         HWAVEIN   In; 
    56         HWAVEOUT  Out; 
    57     } hWave; 
    58  
    59     WAVEHDR      *WaveHdr; 
    60     HANDLE        hEvent; 
    61     DWORD         dwBufIdx; 
    62     DWORD         dwMaxBufIdx; 
    63     pj_timestamp  timestamp; 
    64 }; 
    65  
    66  
    67 /* Sound stream. */ 
    68 struct pjmedia_snd_stream 
    69 { 
    70     pjmedia_dir          dir;               /**< Sound direction.      */ 
    71     int                  play_id;           /**< Playback dev id.      */ 
    72     int                  rec_id;            /**< Recording dev id.     */ 
    73     pj_pool_t           *pool;              /**< Memory pool.          */ 
    74  
    75     pjmedia_snd_rec_cb   rec_cb;            /**< Capture callback.     */ 
    76     pjmedia_snd_play_cb  play_cb;           /**< Playback callback.    */ 
    77     void                *user_data;         /**< Application data.     */ 
    78  
    79     struct wmme_stream   play_strm;         /**< Playback stream.      */ 
    80     struct wmme_stream   rec_strm;          /**< Capture stream.       */ 
    81  
    82     void                *buffer;            /**< Temp. frame buffer.   */ 
    83     unsigned             clock_rate;        /**< Clock rate.           */ 
    84     unsigned             samples_per_frame; /**< Samples per frame.    */ 
    85     unsigned             bits_per_sample;   /**< Bits per sample.      */ 
    86     unsigned             channel_count;     /**< Channel count.        */ 
    87  
    88     pj_thread_t         *thread;            /**< Thread handle.        */ 
    89     HANDLE               thread_quit_event; /**< Quit signal to thread */ 
    90 }; 
    91  
    92  
    93 static pj_pool_factory *pool_factory; 
    94  
    95 static void init_waveformatex (LPWAVEFORMATEX pcmwf,  
    96                                unsigned clock_rate, 
    97                                unsigned channel_count) 
    98 { 
    99     pj_bzero(pcmwf, sizeof(PCMWAVEFORMAT));  
    100     pcmwf->wFormatTag = WAVE_FORMAT_PCM;  
    101     pcmwf->nChannels = (pj_uint16_t)channel_count; 
    102     pcmwf->nSamplesPerSec = clock_rate; 
    103     pcmwf->nBlockAlign = (pj_uint16_t)(channel_count * BYTES_PER_SAMPLE); 
    104     pcmwf->nAvgBytesPerSec = clock_rate * channel_count * BYTES_PER_SAMPLE; 
    105     pcmwf->wBitsPerSample = BITS_PER_SAMPLE; 
    106 } 
    107  
    108  
    109 /* 
    110  * Initialize WMME player device. 
    111  */ 
    112 static pj_status_t init_player_stream(  pj_pool_t *pool, 
    113                                         struct wmme_stream *wmme_strm, 
    114                                         int dev_id, 
    115                                         unsigned clock_rate, 
    116                                         unsigned channel_count, 
    117                                         unsigned samples_per_frame, 
    118                                         unsigned buffer_count) 
    119 { 
    120     MMRESULT mr; 
    121     WAVEFORMATEX pcmwf;  
    122     unsigned bytes_per_frame; 
    123     unsigned i; 
    124  
    125     PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); 
    126  
    127     /* Check device ID */ 
    128     if (dev_id == -1) 
    129         dev_id = 0; 
    130  
    131     PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL); 
    132  
    133     /* 
    134      * Create a wait event. 
    135      */ 
    136     wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
    137     if (NULL == wmme_strm->hEvent) 
    138         return pj_get_os_error(); 
    139  
    140     /* 
    141      * Set up wave format structure for opening the device. 
    142      */ 
    143     init_waveformatex(&pcmwf, clock_rate, channel_count); 
    144     bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; 
    145  
    146     /* 
    147      * Open wave device. 
    148      */ 
    149     mr = waveOutOpen(&wmme_strm->hWave.Out, dev_info[dev_id].deviceId, &pcmwf,  
    150                      (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT); 
    151     if (mr != MMSYSERR_NOERROR) 
    152         /* TODO: This is for HRESULT/GetLastError() */ 
    153         PJ_RETURN_OS_ERROR(mr); 
    154  
    155     /* Pause the wave out device */ 
    156     mr = waveOutPause(wmme_strm->hWave.Out); 
    157     if (mr != MMSYSERR_NOERROR) 
    158         /* TODO: This is for HRESULT/GetLastError() */ 
    159         PJ_RETURN_OS_ERROR(mr); 
    160  
    161     /* 
    162      * Create the buffers.  
    163      */ 
    164     wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count); 
    165     for (i = 0; i < buffer_count; ++i) 
    166     { 
    167         wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame); 
    168         wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame; 
    169         mr = waveOutPrepareHeader(wmme_strm->hWave.Out,  
    170                                   &(wmme_strm->WaveHdr[i]), 
    171                                   sizeof(WAVEHDR)); 
    172         if (mr != MMSYSERR_NOERROR) 
    173             /* TODO: This is for HRESULT/GetLastError() */ 
    174             PJ_RETURN_OS_ERROR(mr);  
    175         mr = waveOutWrite(wmme_strm->hWave.Out, &(wmme_strm->WaveHdr[i]),  
    176                           sizeof(WAVEHDR)); 
    177         if (mr != MMSYSERR_NOERROR) 
    178             /* TODO: This is for HRESULT/GetLastError() */ 
    179             PJ_RETURN_OS_ERROR(mr); 
    180     } 
    181  
    182     wmme_strm->dwBufIdx = 0; 
    183     wmme_strm->dwMaxBufIdx = buffer_count; 
    184     wmme_strm->timestamp.u64 = 0; 
    185  
    186     /* Done setting up play device. */ 
    187     PJ_LOG(5, (THIS_FILE,  
    188                " WaveAPI Sound player \"%s\" initialized (clock_rate=%d, " 
    189                "channel_count=%d, samples_per_frame=%d (%dms))", 
    190                dev_info[dev_id].info.name, 
    191                clock_rate, channel_count, samples_per_frame, 
    192                samples_per_frame * 1000 / clock_rate)); 
    193  
    194     return PJ_SUCCESS; 
    195 } 
    196  
    197  
    198 /* 
    199  * Initialize Windows Multimedia recorder device 
    200  */ 
    201 static pj_status_t init_capture_stream( pj_pool_t *pool, 
    202                                         struct wmme_stream *wmme_strm, 
    203                                         int dev_id, 
    204                                         unsigned clock_rate, 
    205                                         unsigned channel_count, 
    206                                         unsigned samples_per_frame, 
    207                                         unsigned buffer_count) 
    208 { 
    209     MMRESULT mr; 
    210     WAVEFORMATEX pcmwf;  
    211     unsigned bytes_per_frame; 
    212     unsigned i; 
    213  
    214     PJ_ASSERT_RETURN(buffer_count <= MAX_PACKET_BUFFER_COUNT, PJ_EINVAL); 
    215  
    216     /* Check device ID */ 
    217     if (dev_id == -1) 
    218         dev_id = 0; 
    219  
    220     PJ_ASSERT_RETURN(dev_id >= 0 && dev_id < (int)dev_count, PJ_EINVAL); 
    221  
    222     /* 
    223     * Create a wait event. 
    224     */ 
    225     wmme_strm->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); 
    226     if (NULL == wmme_strm->hEvent) 
    227         return pj_get_os_error(); 
    228  
    229     /* 
    230      * Set up wave format structure for opening the device. 
    231      */ 
    232     init_waveformatex(&pcmwf, clock_rate, channel_count); 
    233     bytes_per_frame = samples_per_frame * BYTES_PER_SAMPLE; 
    234  
    235     /* 
    236      * Open wave device. 
    237      */ 
    238     mr = waveInOpen(&wmme_strm->hWave.In, dev_info[dev_id].deviceId, &pcmwf,  
    239                     (DWORD)wmme_strm->hEvent, 0, CALLBACK_EVENT); 
    240     if (mr != MMSYSERR_NOERROR) 
    241         /* TODO: This is for HRESULT/GetLastError() */ 
    242         PJ_RETURN_OS_ERROR(mr); 
    243  
    244     /* 
    245      * Create the buffers.  
    246      */ 
    247     wmme_strm->WaveHdr = pj_pool_zalloc(pool, sizeof(WAVEHDR) * buffer_count); 
    248     for (i = 0; i < buffer_count; ++i) 
    249     { 
    250         wmme_strm->WaveHdr[i].lpData = pj_pool_zalloc(pool, bytes_per_frame); 
    251         wmme_strm->WaveHdr[i].dwBufferLength = bytes_per_frame; 
    252         mr = waveInPrepareHeader(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]), 
    253                                                         sizeof(WAVEHDR)); 
    254         if (mr != MMSYSERR_NOERROR) 
    255             /* TODO: This is for HRESULT/GetLastError() */ 
    256             PJ_RETURN_OS_ERROR(mr); 
    257         mr = waveInAddBuffer(wmme_strm->hWave.In, &(wmme_strm->WaveHdr[i]),  
    258                              sizeof(WAVEHDR)); 
    259         if (mr != MMSYSERR_NOERROR) 
    260             /* TODO: This is for HRESULT/GetLastError() */ 
    261             PJ_RETURN_OS_ERROR(mr); 
    262     } 
    263  
    264     wmme_strm->dwBufIdx = 0; 
    265     wmme_strm->dwMaxBufIdx = buffer_count; 
    266     wmme_strm->timestamp.u64 = 0; 
    267  
    268     /* Done setting up play device. */ 
    269     PJ_LOG(5,(THIS_FILE,  
    270         " WaveAPI Sound recorder \"%s\" initialized (clock_rate=%d, " 
    271         "channel_count=%d, samples_per_frame=%d (%dms))", 
    272         dev_info[dev_id].info.name, 
    273         clock_rate, channel_count, samples_per_frame, 
    274         samples_per_frame * 1000 / clock_rate)); 
    275  
    276     return PJ_SUCCESS; 
    277 } 
    278  
    279  
    280  
    281 /* 
    282 * WMME capture and playback thread. 
    283 */ 
    284 static int PJ_THREAD_FUNC wmme_dev_thread(void *arg) 
    285 { 
    286     pjmedia_snd_stream *strm = arg; 
    287     HANDLE events[3]; 
    288     unsigned eventCount; 
    289     unsigned bytes_per_frame; 
    290     pj_status_t status = PJ_SUCCESS; 
    291  
    292  
    293     eventCount = 0; 
    294     events[eventCount++] = strm->thread_quit_event; 
    295     if (strm->dir & PJMEDIA_DIR_PLAYBACK) 
    296         events[eventCount++] = strm->play_strm.hEvent; 
    297     if (strm->dir & PJMEDIA_DIR_CAPTURE) 
    298         events[eventCount++] = strm->rec_strm.hEvent; 
    299  
    300  
    301     /* Raise self priority. We don't want the audio to be distorted by 
    302      * system activity. 
    303      */ 
    304 #if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE != 0 
    305     if (strm->dir & PJMEDIA_DIR_PLAYBACK) 
    306         CeSetThreadPriority(GetCurrentThread(), 153); 
    307     else 
    308         CeSetThreadPriority(GetCurrentThread(), 247); 
    309 #else 
    310     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); 
    311 #endif 
    312  
    313     /* Calculate bytes per frame */ 
    314     bytes_per_frame = strm->samples_per_frame * BYTES_PER_SAMPLE; 
    315  
    316     /* 
    317      * Loop while not signalled to quit, wait for event objects to be  
    318      * signalled by WMME capture and play buffer. 
    319      */ 
    320     while (status == PJ_SUCCESS) 
    321     { 
    322  
    323         DWORD rc; 
    324         pjmedia_dir signalled_dir; 
    325  
    326         rc = WaitForMultipleObjects(eventCount, events, FALSE, INFINITE); 
    327         if (rc < WAIT_OBJECT_0 || rc >= WAIT_OBJECT_0 + eventCount) 
    328             continue; 
    329  
    330         if (rc == WAIT_OBJECT_0) 
    331             break; 
    332  
    333         if (rc == (WAIT_OBJECT_0 + 1)) 
    334         { 
    335             if (events[1] == strm->play_strm.hEvent) 
    336                 signalled_dir = PJMEDIA_DIR_PLAYBACK; 
    337             else 
    338                 signalled_dir = PJMEDIA_DIR_CAPTURE; 
    339         } 
    340         else 
    341         { 
    342             if (events[2] == strm->play_strm.hEvent) 
    343                 signalled_dir = PJMEDIA_DIR_PLAYBACK; 
    344             else 
    345                 signalled_dir = PJMEDIA_DIR_CAPTURE; 
    346         } 
    347  
    348  
    349         if (signalled_dir == PJMEDIA_DIR_PLAYBACK) 
    350         { 
    351             struct wmme_stream *wmme_strm = &strm->play_strm; 
    352             MMRESULT mr = MMSYSERR_NOERROR; 
    353             status = PJ_SUCCESS; 
    354  
    355             /* 
    356             * Windows Multimedia has requested us to feed some frames to 
    357             * playback buffer. 
    358             */ 
    359  
    360             while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE) 
    361             { 
    362                 void* buffer = wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; 
    363  
    364                 PJ_LOG(5,(THIS_FILE, "Finished writing buffer %d",  
    365                           wmme_strm->dwBufIdx)); 
    366  
    367                 /* Get frame from application. */ 
    368                 status = (*strm->play_cb)(strm->user_data,  
    369                                           wmme_strm->timestamp.u32.lo, 
    370                                           buffer, 
    371                                           bytes_per_frame); 
    372  
    373                 if (status != PJ_SUCCESS) 
    374                     break; 
    375  
    376                 /* Write to the device. */ 
    377                 mr = waveOutWrite(wmme_strm->hWave.Out,  
    378                                   &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),  
    379                                   sizeof(WAVEHDR)); 
    380                 if (mr != MMSYSERR_NOERROR) 
    381                 { 
    382                     status = PJ_STATUS_FROM_OS(mr); 
    383                     break; 
    384                 } 
    385  
    386                 /* Increment position. */ 
    387                 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) 
    388                     wmme_strm->dwBufIdx = 0; 
    389                 wmme_strm->timestamp.u64 += strm->samples_per_frame /  
    390                                             strm->channel_count; 
    391             } 
    392         } 
    393         else 
    394         { 
    395             struct wmme_stream *wmme_strm = &strm->rec_strm; 
    396             MMRESULT mr = MMSYSERR_NOERROR; 
    397             status = PJ_SUCCESS; 
    398  
    399             /* 
    400             * Windows Multimedia has indicated that it has some frames ready 
    401             * in the capture buffer. Get as much frames as possible to 
    402             * prevent overflows. 
    403             */ 
    404 #if 0 
    405             { 
    406                 static DWORD tc = 0; 
    407                 DWORD now = GetTickCount(); 
    408                 DWORD i = 0; 
    409                 DWORD bits = 0; 
    410  
    411                 if (tc == 0) tc = now; 
    412  
    413                 for (i = 0; i < wmme_strm->dwMaxBufIdx; ++i) 
    414                 { 
    415                     bits = bits << 4; 
    416                     bits |= wmme_strm->WaveHdr[i].dwFlags & WHDR_DONE; 
    417                 } 
    418                 PJ_LOG(5,(THIS_FILE, "Record Signal> Index: %d, Delta: %4.4d, " 
    419                           "Flags: %6.6x\n", 
    420                           wmme_strm->dwBufIdx, 
    421                           now - tc, 
    422                           bits)); 
    423                 tc = now; 
    424             } 
    425 #endif 
    426  
    427             while (wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwFlags & WHDR_DONE) 
    428             { 
    429                 char* buffer = (char*) 
    430                                wmme_strm->WaveHdr[wmme_strm->dwBufIdx].lpData; 
    431                 unsigned cap_len =  
    432                         wmme_strm->WaveHdr[wmme_strm->dwBufIdx].dwBytesRecorded; 
    433  
    434                 /* 
    435                 PJ_LOG(5,(THIS_FILE, "Read %d bytes from buffer %d", cap_len,  
    436                           wmme_strm->dwBufIdx)); 
    437                 */ 
    438  
    439                 if (cap_len < bytes_per_frame) 
    440                     pj_bzero(buffer + cap_len, bytes_per_frame - cap_len); 
    441  
    442                 /* Copy the audio data out of the wave buffer. */ 
    443                 pj_memcpy(strm->buffer, buffer, bytes_per_frame); 
    444  
    445                 /* Re-add the buffer to the device. */ 
    446                 mr = waveInAddBuffer(wmme_strm->hWave.In,  
    447                                      &(wmme_strm->WaveHdr[wmme_strm->dwBufIdx]),  
    448                                      sizeof(WAVEHDR)); 
    449                 if (mr != MMSYSERR_NOERROR) { 
    450                     status = PJ_STATUS_FROM_OS(mr); 
    451                     break; 
    452                 } 
    453  
    454                 /* Call callback */ 
    455                 status = (*strm->rec_cb)(strm->user_data,  
    456                                          wmme_strm->timestamp.u32.lo,  
    457                                          strm->buffer,  
    458                                          bytes_per_frame); 
    459  
    460                 if (status != PJ_SUCCESS) 
    461                     break; 
    462  
    463                 /* Increment position. */ 
    464                 if (++wmme_strm->dwBufIdx >= wmme_strm->dwMaxBufIdx) 
    465                     wmme_strm->dwBufIdx = 0; 
    466                 wmme_strm->timestamp.u64 += strm->samples_per_frame /  
    467                                             strm->channel_count; 
    468             } 
    469         } 
    470     } 
    471  
    472     PJ_LOG(5,(THIS_FILE, "WMME: thread stopping..")); 
    473     return 0; 
    474 } 
    475  
    476  
    477 /* 
    478 * Init sound library. 
    479 */ 
    480 PJ_DEF(pj_status_t) pjmedia_snd_init(pj_pool_factory *factory) 
    481 { 
    482     unsigned c; 
    483     int i; 
    484     int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount; 
    485  
    486     if (snd_initialized) 
    487         return PJ_SUCCESS; 
    488  
    489     pj_bzero(&dev_info, sizeof(dev_info)); 
    490  
    491     dev_count = 0; 
    492     pool_factory = factory; 
    493  
    494     /* Enumerate sound playback devices */ 
    495     maximumPossibleDeviceCount = 0; 
    496  
    497     inputDeviceCount = waveInGetNumDevs(); 
    498     if (inputDeviceCount > 0) 
    499         /* assume there is a WAVE_MAPPER */ 
    500         maximumPossibleDeviceCount += inputDeviceCount + 1; 
    501  
    502     outputDeviceCount = waveOutGetNumDevs(); 
    503     if (outputDeviceCount > 0) 
    504         /* assume there is a WAVE_MAPPER */ 
    505         maximumPossibleDeviceCount += outputDeviceCount + 1; 
    506  
    507     if (maximumPossibleDeviceCount >= MAX_HARDWARE) 
    508     { 
    509         pj_assert(!"Too many hardware found"); 
    510         PJ_LOG(3,(THIS_FILE, "Too many hardware found, " 
    511                   "some devices will not be listed")); 
    512     } 
    513  
    514     if (inputDeviceCount > 0) 
    515     { 
    516         /* -1 is the WAVE_MAPPER */ 
    517         for (i = -1; i < inputDeviceCount && dev_count < MAX_HARDWARE; ++i) 
    518         { 
    519             UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); 
    520             WAVEINCAPS wic; 
    521             MMRESULT mr; 
    522  
    523             pj_bzero(&wic, sizeof(WAVEINCAPS)); 
    524  
    525             mr = waveInGetDevCaps(uDeviceID, &wic, sizeof(WAVEINCAPS)); 
    526  
    527             if (mr == MMSYSERR_NOMEM) 
    528                 return PJ_ENOMEM; 
    529  
    530             if (mr != MMSYSERR_NOERROR) 
    531                 continue; 
    532  
    533 #ifdef UNICODE 
    534             WideCharToMultiByte(CP_ACP, 0, wic.szPname, wcslen(wic.szPname),  
    535                                 dev_info[dev_count].info.name, 64, NULL, NULL); 
    536 #else 
    537             strncpy(dev_info[dev_count].info.name, wic.szPname, MAXPNAMELEN); 
    538 #endif 
    539             if (uDeviceID == WAVE_MAPPER) 
    540                 strcat(dev_info[dev_count].info.name, " - Input"); 
    541  
    542             dev_info[dev_count].info.input_count = wic.wChannels; 
    543             dev_info[dev_count].info.output_count = 0; 
    544             dev_info[dev_count].info.default_samples_per_sec = 44100; 
    545             dev_info[dev_count].deviceId = uDeviceID; 
    546  
    547             /* Sometimes a device can return a rediculously large number of  
    548              * channels. This happened with an SBLive card on a Windows ME box. 
    549              * It also happens on Win XP! 
    550              */ 
    551             if ((dev_info[dev_count].info.input_count < 1) ||  
    552                 (dev_info[dev_count].info.input_count > 256)) 
    553                 dev_info[dev_count].info.input_count = 2; 
    554  
    555             ++dev_count; 
    556         } 
    557     } 
    558  
    559     if( outputDeviceCount > 0 ) 
    560     { 
    561         /* -1 is the WAVE_MAPPER */ 
    562         for (i = -1; i < outputDeviceCount && dev_count < MAX_HARDWARE; ++i) 
    563         { 
    564             UINT uDeviceID = (UINT)((i==-1) ? WAVE_MAPPER : i); 
    565             WAVEOUTCAPS woc; 
    566             MMRESULT mr; 
    567  
    568             pj_bzero(&woc, sizeof(WAVEOUTCAPS)); 
    569  
    570             mr = waveOutGetDevCaps(uDeviceID, &woc, sizeof(WAVEOUTCAPS)); 
    571  
    572             if (mr == MMSYSERR_NOMEM) 
    573                 return PJ_ENOMEM; 
    574  
    575             if (mr != MMSYSERR_NOERROR) 
    576                 continue; 
    577  
    578 #ifdef UNICODE 
    579             WideCharToMultiByte(CP_ACP, 0, woc.szPname, wcslen(woc.szPname), 
    580                                 dev_info[dev_count].info.name, 64, NULL, NULL); 
    581 #else 
    582             strncpy(dev_info[dev_count].info.name, woc.szPname, MAXPNAMELEN); 
    583 #endif 
    584             if (uDeviceID == WAVE_MAPPER) 
    585                 strcat(dev_info[dev_count].info.name, " - Output"); 
    586  
    587             dev_info[dev_count].info.output_count = woc.wChannels; 
    588             dev_info[dev_count].info.input_count = 0; 
    589             dev_info[dev_count].deviceId = uDeviceID; 
    590             /* TODO: Perform a search! */ 
    591             dev_info[dev_count].info.default_samples_per_sec = 44100; 
    592  
    593             /* Sometimes a device can return a rediculously large number of channels. 
    594              * This happened with an SBLive card on a Windows ME box. 
    595              * It also happens on Win XP! 
    596              */ 
    597             if ((dev_info[dev_count].info.output_count < 1) ||  
    598                 (dev_info[dev_count].info.output_count > 256)) 
    599                 dev_info[dev_count].info.output_count = 2; 
    600  
    601             ++dev_count; 
    602         } 
    603     } 
    604  
    605     PJ_LOG(4, (THIS_FILE, "WMME initialized, found %d devices:", dev_count)); 
    606     for (c = 0; c < dev_count; ++c) 
    607     { 
    608         PJ_LOG(4, (THIS_FILE, " dev_id %d: %s  (in=%d, out=%d)",  
    609             c, 
    610             dev_info[c].info.name, 
    611             dev_info[c].info.input_count, 
    612             dev_info[c].info.output_count)); 
    613     } 
    614     return PJ_SUCCESS; 
    615 } 
    616  
    617 /* 
    618  * Deinitialize sound library. 
    619  */ 
    620 PJ_DEF(pj_status_t) pjmedia_snd_deinit(void) 
    621 { 
    622     snd_initialized = PJ_FALSE; 
    623     return PJ_SUCCESS; 
    624 } 
    625  
    626 /* 
    627  * Get device count. 
    628  */ 
    629 PJ_DEF(int) pjmedia_snd_get_dev_count(void) 
    630 { 
    631     return dev_count; 
    632 } 
    633  
    634 /* 
    635  * Get device info. 
    636  */ 
    637 PJ_DEF(const pjmedia_snd_dev_info*) pjmedia_snd_get_dev_info(unsigned index) 
    638 { 
    639     if (index == (unsigned)-1)  
    640         index = 0; 
    641  
    642     PJ_ASSERT_RETURN(index < dev_count, NULL); 
    643  
    644     return &dev_info[index].info; 
    645 } 
    646  
    647  
    648 /* 
    649  * Open stream. 
    650  */ 
    651 static pj_status_t open_stream(pjmedia_dir dir, 
    652                                int rec_id, 
    653                                int play_id, 
    654                                unsigned clock_rate, 
    655                                unsigned channel_count, 
    656                                unsigned samples_per_frame, 
    657                                unsigned bits_per_sample, 
    658                                pjmedia_snd_rec_cb rec_cb, 
    659                                pjmedia_snd_play_cb play_cb, 
    660                                void *user_data, 
    661                                pjmedia_snd_stream **p_snd_strm) 
    662 { 
    663     pj_pool_t *pool; 
    664     pjmedia_snd_stream *strm; 
    665     pj_status_t status; 
    666  
    667  
    668     /* Make sure sound subsystem has been initialized with 
    669      * pjmedia_snd_init() 
    670      */ 
    671     PJ_ASSERT_RETURN(pool_factory != NULL, PJ_EINVALIDOP); 
    672  
    673  
    674     /* Can only support 16bits per sample */ 
    675     PJ_ASSERT_RETURN(bits_per_sample == BITS_PER_SAMPLE, PJ_EINVAL); 
    676  
    677     /* Create and Initialize stream descriptor */ 
    678     pool = pj_pool_create(pool_factory, "wmme-dev", 1000, 1000, NULL); 
    679     PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); 
    680  
    681     strm = pj_pool_zalloc(pool, sizeof(pjmedia_snd_stream)); 
    682     strm->dir = dir; 
    683     strm->play_id = play_id; 
    684     strm->rec_id = rec_id; 
    685     strm->pool = pool; 
    686     strm->rec_cb = rec_cb; 
    687     strm->play_cb = play_cb; 
    688     strm->user_data = user_data; 
    689     strm->clock_rate = clock_rate; 
    690     strm->samples_per_frame = samples_per_frame; 
    691     strm->bits_per_sample = bits_per_sample; 
    692     strm->channel_count = channel_count; 
    693     strm->buffer = pj_pool_alloc(pool, samples_per_frame * BYTES_PER_SAMPLE); 
    694     if (!strm->buffer) 
    695     { 
    696         pj_pool_release(pool); 
    697         return PJ_ENOMEM; 
    698     } 
    699  
    700     /* Create player stream */ 
    701     if (dir & PJMEDIA_DIR_PLAYBACK) 
    702     { 
    703         unsigned buf_count; 
    704  
    705         buf_count = snd_output_latency * clock_rate * channel_count /  
    706                     samples_per_frame / 1000; 
    707  
    708         status = init_player_stream(strm->pool, 
    709                                     &strm->play_strm, 
    710                                     play_id, 
    711                                     clock_rate, 
    712                                     channel_count, 
    713                                     samples_per_frame, 
    714                                     buf_count); 
    715  
    716         if (status != PJ_SUCCESS) 
    717         { 
    718             pjmedia_snd_stream_close(strm); 
    719             return status; 
    720         } 
    721     } 
    722  
    723     /* Create capture stream */ 
    724     if (dir & PJMEDIA_DIR_CAPTURE) 
    725     { 
    726         unsigned buf_count; 
    727  
    728         buf_count = snd_input_latency * clock_rate * channel_count /  
    729                     samples_per_frame / 1000; 
    730  
    731         status = init_capture_stream(strm->pool, 
    732                                      &strm->rec_strm, 
    733                                      rec_id, 
    734                                      clock_rate, 
    735                                      channel_count, 
    736                                      samples_per_frame, 
    737                                      buf_count); 
    738  
    739         if (status != PJ_SUCCESS) 
    740         { 
    741             pjmedia_snd_stream_close(strm); 
    742             return status; 
    743         } 
    744     } 
    745  
    746     /* Create the stop event */ 
    747     strm->thread_quit_event = CreateEvent(NULL, FALSE, FALSE, NULL); 
    748     if (strm->thread_quit_event == NULL) 
    749         return pj_get_os_error(); 
    750  
    751     /* Create and start the thread */ 
    752     status = pj_thread_create(pool, "wmme", &wmme_dev_thread, strm, 0, 0,  
    753                               &strm->thread); 
    754     if (status != PJ_SUCCESS) 
    755     { 
    756         pjmedia_snd_stream_close(strm); 
    757         return status; 
    758     } 
    759  
    760     *p_snd_strm = strm; 
    761  
    762     return PJ_SUCCESS; 
    763 } 
    764  
    765 /* 
    766  * Open stream. 
    767  */ 
    768 PJ_DEF(pj_status_t) pjmedia_snd_open_rec(int index, 
    769                                          unsigned clock_rate, 
    770                                          unsigned channel_count, 
    771                                          unsigned samples_per_frame, 
    772                                          unsigned bits_per_sample, 
    773                                          pjmedia_snd_rec_cb rec_cb, 
    774                                          void *user_data, 
    775                                          pjmedia_snd_stream **p_snd_strm) 
    776 { 
    777     PJ_ASSERT_RETURN(rec_cb && p_snd_strm, PJ_EINVAL); 
    778  
    779     return open_stream( PJMEDIA_DIR_CAPTURE, 
    780                         index, 
    781                         -1, 
    782                         clock_rate, 
    783                         channel_count, 
    784                         samples_per_frame, 
    785                         bits_per_sample, 
    786                         rec_cb, 
    787                         NULL, 
    788                         user_data, 
    789                         p_snd_strm); 
    790 } 
    791  
    792 PJ_DEF(pj_status_t) pjmedia_snd_open_player(int index, 
    793                                             unsigned clock_rate, 
    794                                             unsigned channel_count, 
    795                                             unsigned samples_per_frame, 
    796                                             unsigned bits_per_sample, 
    797                                             pjmedia_snd_play_cb play_cb, 
    798                                             void *user_data, 
    799                                             pjmedia_snd_stream **p_snd_strm) 
    800 { 
    801     PJ_ASSERT_RETURN(play_cb && p_snd_strm, PJ_EINVAL); 
    802  
    803     return open_stream( PJMEDIA_DIR_PLAYBACK, 
    804                         -1, 
    805                         index, 
    806                         clock_rate, 
    807                         channel_count, 
    808                         samples_per_frame, 
    809                         bits_per_sample, 
    810                         NULL, 
    811                         play_cb, 
    812                         user_data, 
    813                         p_snd_strm); 
    814 } 
    815  
    816 /* 
    817  * Open both player and recorder. 
    818  */ 
    819 PJ_DEF(pj_status_t) pjmedia_snd_open(int rec_id, 
    820                                      int play_id, 
    821                                      unsigned clock_rate, 
    822                                      unsigned channel_count, 
    823                                      unsigned samples_per_frame, 
    824                                      unsigned bits_per_sample, 
    825                                      pjmedia_snd_rec_cb rec_cb, 
    826                                      pjmedia_snd_play_cb play_cb, 
    827                                      void *user_data, 
    828                                      pjmedia_snd_stream **p_snd_strm) 
    829 { 
    830     PJ_ASSERT_RETURN(rec_cb && play_cb && p_snd_strm, PJ_EINVAL); 
    831  
    832     return open_stream( PJMEDIA_DIR_CAPTURE_PLAYBACK, 
    833                         rec_id, 
    834                         play_id, 
    835                         clock_rate, 
    836                         channel_count, 
    837                         samples_per_frame, 
    838                         bits_per_sample, 
    839                         rec_cb, 
    840                         play_cb, 
    841                         user_data, 
    842                         p_snd_strm); 
    843 } 
    844  
    845 /* 
    846  * Get stream info. 
    847  */ 
    848 PJ_DEF(pj_status_t) pjmedia_snd_stream_get_info(pjmedia_snd_stream *strm,  
    849                                                 pjmedia_snd_stream_info *pi) 
    850 { 
    851     PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL); 
    852  
    853     pj_bzero(pi, sizeof(*pi)); 
    854     pi->dir = strm->dir; 
    855     pi->play_id = strm->play_id; 
    856     pi->rec_id = strm->rec_id; 
    857     pi->clock_rate = strm->clock_rate; 
    858     pi->channel_count = strm->channel_count; 
    859     pi->samples_per_frame = strm->samples_per_frame; 
    860     pi->bits_per_sample = strm->bits_per_sample; 
    861     pi->rec_latency = snd_input_latency * strm->clock_rate *  
    862                       strm->channel_count / 1000; 
    863     pi->play_latency = snd_output_latency * strm->clock_rate *  
    864                        strm->channel_count / 1000; 
    865  
    866     return PJ_SUCCESS; 
    867 } 
    868  
    869  
    870 /* 
    871 * Start stream. 
    872 */ 
    873 PJ_DEF(pj_status_t) pjmedia_snd_stream_start(pjmedia_snd_stream *stream) 
    874 { 
    875     MMRESULT mr; 
    876  
    877     PJ_UNUSED_ARG(stream); 
    878  
    879     if (stream->play_strm.hWave.Out != NULL) 
    880     { 
    881         mr = waveOutRestart(stream->play_strm.hWave.Out); 
    882         if (mr != MMSYSERR_NOERROR) 
    883             /* TODO: This macro is supposed to be used for HRESULT, fix. */ 
    884             PJ_RETURN_OS_ERROR(mr); 
    885         PJ_LOG(5,(THIS_FILE, "WMME playback stream started")); 
    886     } 
    887  
    888     if (stream->rec_strm.hWave.In != NULL) 
    889     { 
    890         mr = waveInStart(stream->rec_strm.hWave.In); 
    891         if (mr != MMSYSERR_NOERROR) 
    892             /* TODO: This macro is supposed to be used for HRESULT, fix. */ 
    893             PJ_RETURN_OS_ERROR(mr); 
    894         PJ_LOG(5,(THIS_FILE, "WMME capture stream started")); 
    895     } 
    896  
    897     return PJ_SUCCESS; 
    898 } 
    899  
    900 /* 
    901  * Stop stream. 
    902  */ 
    903 PJ_DEF(pj_status_t) pjmedia_snd_stream_stop(pjmedia_snd_stream *stream) 
    904 { 
    905     MMRESULT mr; 
    906  
    907     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 
    908  
    909     if (stream->play_strm.hWave.Out != NULL) 
    910     { 
    911         mr = waveOutPause(stream->play_strm.hWave.Out); 
    912         if (mr != MMSYSERR_NOERROR) 
    913             /* TODO: This macro is supposed to be used for HRESULT, fix. */ 
    914             PJ_RETURN_OS_ERROR(mr); 
    915         PJ_LOG(5,(THIS_FILE, "Stopped WMME playback stream")); 
    916     } 
    917  
    918     if (stream->rec_strm.hWave.In != NULL) 
    919     { 
    920         mr = waveInStop(stream->rec_strm.hWave.In); 
    921         if (mr != MMSYSERR_NOERROR) 
    922             /* TODO: This macro is supposed to be used for HRESULT, fix. */ 
    923             PJ_RETURN_OS_ERROR(mr); 
    924         PJ_LOG(5,(THIS_FILE, "Stopped WMME capture stream")); 
    925     } 
    926  
    927     return PJ_SUCCESS; 
    928 } 
    929  
    930  
    931 /* 
    932  * Destroy stream. 
    933  */ 
    934 PJ_DEF(pj_status_t) pjmedia_snd_stream_close(pjmedia_snd_stream *stream) 
    935 { 
    936     unsigned i; 
    937  
    938     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL); 
    939  
    940     pjmedia_snd_stream_stop(stream); 
    941  
    942     if (stream->thread) 
    943     { 
    944         SetEvent(stream->thread_quit_event); 
    945         pj_thread_join(stream->thread); 
    946         pj_thread_destroy(stream->thread); 
    947         stream->thread = NULL; 
    948     } 
    949  
    950     /* Unprepare the headers and close the play device */ 
    951     if (stream->play_strm.hWave.Out) 
    952     { 
    953         waveOutReset(stream->play_strm.hWave.Out); 
    954         for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i) 
    955             waveOutUnprepareHeader(stream->play_strm.hWave.Out,  
    956                                    &(stream->play_strm.WaveHdr[i]), 
    957                                    sizeof(WAVEHDR)); 
    958         waveOutClose(stream->play_strm.hWave.Out); 
    959         stream->play_strm.hWave.Out = NULL; 
    960     } 
    961  
    962     /* Close the play event */ 
    963     if (stream->play_strm.hEvent) 
    964     { 
    965         CloseHandle(stream->play_strm.hEvent); 
    966         stream->play_strm.hEvent = NULL; 
    967     } 
    968  
    969     /* Unprepare the headers and close the record device */ 
    970     if (stream->rec_strm.hWave.In) 
    971     { 
    972         waveInReset(stream->rec_strm.hWave.In); 
    973         for (i = 0; i < stream->play_strm.dwMaxBufIdx; ++i) 
    974             waveInUnprepareHeader(stream->rec_strm.hWave.In,  
    975                                   &(stream->rec_strm.WaveHdr[i]), 
    976                                   sizeof(WAVEHDR)); 
    977         waveInClose(stream->rec_strm.hWave.In); 
    978         stream->rec_strm.hWave.In = NULL; 
    979     } 
    980  
    981     /* Close the record event */ 
    982     if (stream->rec_strm.hEvent) 
    983     { 
    984         CloseHandle(stream->rec_strm.hEvent); 
    985         stream->rec_strm.hEvent = NULL; 
    986     } 
    987  
    988     pj_pool_release(stream->pool); 
    989  
    990     return PJ_SUCCESS; 
    991 } 
    992  
    993 /* 
    994  * Set sound latency. 
    995  */ 
    996 PJ_DEF(pj_status_t) pjmedia_snd_set_latency(unsigned input_latency,  
    997                                             unsigned output_latency) 
    998 { 
    999     snd_input_latency  = (input_latency == 0)?  
    1000                           PJMEDIA_SND_DEFAULT_REC_LATENCY : input_latency; 
    1001     snd_output_latency = (output_latency == 0)?  
    1002                          PJMEDIA_SND_DEFAULT_PLAY_LATENCY : output_latency; 
    1003  
    1004     return PJ_SUCCESS; 
    1005 } 
    1006  
    1007 #endif  /* PJMEDIA_SOUND_IMPLEMENTATION */ 
    1008  
Note: See TracChangeset for help on using the changeset viewer.