Beispiel #1
0
static ALenum AddEffectSlotArray(ALCcontext *context, ALeffectslot **start, ALsizei count)
{
    ALenum err = AL_NO_ERROR;

    LockContext(context);
    if(!VECTOR_INSERT(context->ActiveAuxSlots, VECTOR_END(context->ActiveAuxSlots), start, start+count))
        err = AL_OUT_OF_MEMORY;
    UnlockContext(context);

    return err;
}
Beispiel #2
0
static ALCenum qsa_open_playback(ALCdevice* device, const ALCchar* deviceName)
{
    qsa_data *data;
    int card, dev;
    int status;

    data = (qsa_data*)calloc(1, sizeof(qsa_data));
    if(data == NULL)
        return ALC_OUT_OF_MEMORY;

    if(!deviceName)
        deviceName = qsaDevice;

    if(strcmp(deviceName, qsaDevice) == 0)
        status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_PLAYBACK);
    else
    {
        const DevMap *iter;

        if(VECTOR_SIZE(DeviceNameMap) == 0)
            deviceList(SND_PCM_CHANNEL_PLAYBACK, &DeviceNameMap);

#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
        VECTOR_FIND_IF(iter, const DevMap, DeviceNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
        if(iter == VECTOR_END(DeviceNameMap))
        {
            free(data);
            return ALC_INVALID_DEVICE;
        }

        status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_PLAYBACK);
    }

    if(status < 0)
    {
        free(data);
        return ALC_INVALID_DEVICE;
    }

    data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
    if(data->audio_fd < 0)
    {
        snd_pcm_close(data->pcmHandle);
        free(data);
        return ALC_INVALID_DEVICE;
    }

    al_string_copy_cstr(&device->DeviceName, deviceName);
    device->ExtraData = data;

    return ALC_NO_ERROR;
}
Beispiel #3
0
static void RemoveEffectSlotArray(ALCcontext *context, const ALeffectslot *slot)
{
    ALeffectslot **iter;

    LockContext(context);
#define MATCH_SLOT(_i)  (slot == *(_i))
    VECTOR_FIND_IF(iter, ALeffectslot*, context->ActiveAuxSlots, MATCH_SLOT);
    if(iter != VECTOR_END(context->ActiveAuxSlots))
    {
        *iter = VECTOR_BACK(context->ActiveAuxSlots);
        VECTOR_POP_BACK(context->ActiveAuxSlots);
    }
#undef MATCH_SLOT
    UnlockContext(context);
}
Beispiel #4
0
static BOOL CALLBACK DSoundEnumDevices(GUID *guid, const WCHAR *desc, const WCHAR* UNUSED(drvname), void *data)
{
    vector_DevMap *devices = data;
    OLECHAR *guidstr = NULL;
    DevMap entry;
    HRESULT hr;
    int count;

    if(!guid)
        return TRUE;

    AL_STRING_INIT(entry.name);

    count = 0;
    while(1)
    {
        const DevMap *iter;

        al_string_copy_cstr(&entry.name, DEVNAME_HEAD);
        al_string_append_wcstr(&entry.name, desc);
        if(count != 0)
        {
            char str[64];
            snprintf(str, sizeof(str), " #%d", count+1);
            al_string_append_cstr(&entry.name, str);
        }

#define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
        VECTOR_FIND_IF(iter, const DevMap, *devices, MATCH_ENTRY);
        if(iter == VECTOR_END(*devices)) break;
#undef MATCH_ENTRY
        count++;
    }
    entry.guid = *guid;

    hr = StringFromCLSID(guid, &guidstr);
    if(SUCCEEDED(hr))
    {
        TRACE("Got device \"%s\", GUID \"%ls\"\n", al_string_get_cstr(entry.name), guidstr);
        CoTaskMemFree(guidstr);
    }

    VECTOR_PUSH_BACK(*devices, entry);

    return TRUE;
}
Beispiel #5
0
static void ProbeCaptureDevices(void)
{
    ALuint numdevs;
    ALuint i;

    clear_devlist(&CaptureDevices);

    numdevs = waveInGetNumDevs();
    VECTOR_RESIZE(CaptureDevices, 0, numdevs);
    for(i = 0;i < numdevs;i++)
    {
        WAVEINCAPSW WaveCaps;
        const al_string *iter;
        al_string dname;

        AL_STRING_INIT(dname);
        if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
        {
            ALuint count = 0;
            while(1)
            {
                alstr_copy_cstr(&dname, DEVNAME_HEAD);
                alstr_append_wcstr(&dname, WaveCaps.szPname);
                if(count != 0)
                {
                    char str[64];
                    snprintf(str, sizeof(str), " #%d", count+1);
                    alstr_append_cstr(&dname, str);
                }
                count++;

#define MATCH_ENTRY(i) (alstr_cmp(dname, *(i)) == 0)
                VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_ENTRY);
                if(iter == VECTOR_END(CaptureDevices)) break;
#undef MATCH_ENTRY
            }

            TRACE("Got device \"%s\", ID %u\n", alstr_get_cstr(dname), i);
        }
        VECTOR_PUSH_BACK(CaptureDevices, dname);
    }
}
Beispiel #6
0
ALvoid ReleaseALAuxiliaryEffectSlots(ALCcontext *context)
{
    ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
    ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList);
    size_t leftover = 0;

    for(;iter != end;iter++)
    {
        ALeffectslot *slot = *iter;
        if(!slot) continue;
        *iter = NULL;

        DeinitEffectSlot(slot);

        memset(slot, 0, sizeof(*slot));
        al_free(slot);
        ++leftover;
    }
    if(leftover > 0)
        WARN("(%p) Deleted "SZFMT" AuxiliaryEffectSlot%s\n", context, leftover, (leftover==1)?"":"s");
}
Beispiel #7
0
void ReleaseALEffects(ALCdevice *device)
{
    EffectSubList *sublist = VECTOR_BEGIN(device->EffectList);
    EffectSubList *subend = VECTOR_END(device->EffectList);
    size_t leftover = 0;
    for(;sublist != subend;++sublist)
    {
        ALuint64 usemask = ~sublist->FreeMask;
        while(usemask)
        {
            ALsizei idx = CTZ64(usemask);
            ALeffect *effect = sublist->Effects + idx;

            memset(effect, 0, sizeof(*effect));
            ++leftover;

            usemask &= ~(U64(1) << idx);
        }
        sublist->FreeMask = ~usemask;
    }
    if(leftover > 0)
        WARN("(%p) Deleted "SZFMT" Effect%s\n", device, leftover, (leftover==1)?"":"s");
}
Beispiel #8
0
void ReleaseALFilters(ALCdevice *device)
{
    FilterSubList *sublist = VECTOR_BEGIN(device->FilterList);
    FilterSubList *subend = VECTOR_END(device->FilterList);
    size_t leftover = 0;
    for(;sublist != subend;++sublist)
    {
        ALuint64 usemask = ~sublist->FreeMask;
        while(usemask)
        {
            ALsizei idx = CTZ64(usemask);
            ALfilter *filter = sublist->Filters + idx;

            memset(filter, 0, sizeof(*filter));
            ++leftover;

            usemask &= ~(U64(1) << idx);
        }
        sublist->FreeMask = ~usemask;
    }
    if(leftover > 0)
        WARN("(%p) Deleted "SZFMT" Filter%s\n", device, leftover, (leftover==1)?"":"s");
}
Beispiel #9
0
static ALeffect *AllocEffect(ALCcontext *context)
{
    ALCdevice *device = context->Device;
    EffectSubList *sublist, *subend;
    ALeffect *effect = NULL;
    ALsizei lidx = 0;
    ALsizei slidx;

    almtx_lock(&device->EffectLock);
    sublist = VECTOR_BEGIN(device->EffectList);
    subend = VECTOR_END(device->EffectList);
    for(;sublist != subend;++sublist)
    {
        if(sublist->FreeMask)
        {
            slidx = CTZ64(sublist->FreeMask);
            effect = sublist->Effects + slidx;
            break;
        }
        ++lidx;
    }
    if(UNLIKELY(!effect))
    {
        const EffectSubList empty_sublist = { 0, NULL };
        /* Don't allocate so many list entries that the 32-bit ID could
         * overflow...
         */
        if(UNLIKELY(VECTOR_SIZE(device->EffectList) >= 1<<25))
        {
            almtx_unlock(&device->EffectLock);
            alSetError(context, AL_OUT_OF_MEMORY, "Too many effects allocated");
            return NULL;
        }
        lidx = (ALsizei)VECTOR_SIZE(device->EffectList);
        VECTOR_PUSH_BACK(device->EffectList, empty_sublist);
        sublist = &VECTOR_BACK(device->EffectList);
        sublist->FreeMask = ~U64(0);
        sublist->Effects = al_calloc(16, sizeof(ALeffect)*64);
        if(UNLIKELY(!sublist->Effects))
        {
            VECTOR_POP_BACK(device->EffectList);
            almtx_unlock(&device->EffectLock);
            alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate effect batch");
            return NULL;
        }

        slidx = 0;
        effect = sublist->Effects + slidx;
    }

    memset(effect, 0, sizeof(*effect));
    InitEffectParams(effect, AL_EFFECT_NULL);

    /* Add 1 to avoid effect ID 0. */
    effect->id = ((lidx<<6) | slidx) + 1;

    sublist->FreeMask &= ~(U64(1)<<slidx);
    almtx_unlock(&device->EffectLock);

    return effect;
}
Beispiel #10
0
AL_API ALvoid AL_APIENTRY alGenAuxiliaryEffectSlots(ALsizei n, ALuint *effectslots)
{
    ALCdevice *device;
    ALCcontext *context;
    ALsizei cur;

    context = GetContextRef();
    if(!context) return;

    if(n < 0)
        SETERR_GOTO(context, AL_INVALID_VALUE, done, "Generating %d effect slots", n);
    if(n == 0) goto done;

    LockEffectSlotList(context);
    device = context->Device;
    for(cur = 0;cur < n;cur++)
    {
        ALeffectslotPtr *iter = VECTOR_BEGIN(context->EffectSlotList);
        ALeffectslotPtr *end = VECTOR_END(context->EffectSlotList);
        ALeffectslot *slot = NULL;
        ALenum err = AL_OUT_OF_MEMORY;

        for(;iter != end;iter++)
        {
            if(!*iter)
                break;
        }
        if(iter == end)
        {
            if(device->AuxiliaryEffectSlotMax == VECTOR_SIZE(context->EffectSlotList))
            {
                UnlockEffectSlotList(context);
                alDeleteAuxiliaryEffectSlots(cur, effectslots);
                SETERR_GOTO(context, AL_OUT_OF_MEMORY, done,
                    "Exceeding %u auxiliary effect slot limit", device->AuxiliaryEffectSlotMax);
            }
            VECTOR_PUSH_BACK(context->EffectSlotList, NULL);
            iter = &VECTOR_BACK(context->EffectSlotList);
        }
        slot = al_calloc(16, sizeof(ALeffectslot));
        if(!slot || (err=InitEffectSlot(slot)) != AL_NO_ERROR)
        {
            al_free(slot);
            UnlockEffectSlotList(context);

            alDeleteAuxiliaryEffectSlots(cur, effectslots);
            SETERR_GOTO(context, err, done, "Effect slot object allocation failed");
        }
        aluInitEffectPanning(slot);

        slot->id = (iter - VECTOR_BEGIN(context->EffectSlotList)) + 1;
        *iter = slot;

        effectslots[cur] = slot->id;
    }
    AddActiveEffectSlots(effectslots, n, context);
    UnlockEffectSlotList(context);

done:
    ALCcontext_DecRef(context);
}
Beispiel #11
0
static ALfilter *AllocFilter(ALCcontext *context)
{
    ALCdevice *device = context->Device;
    FilterSubList *sublist, *subend;
    ALfilter *filter = NULL;
    ALsizei lidx = 0;
    ALsizei slidx;

    almtx_lock(&device->FilterLock);
    sublist = VECTOR_BEGIN(device->FilterList);
    subend = VECTOR_END(device->FilterList);
    for(;sublist != subend;++sublist)
    {
        if(sublist->FreeMask)
        {
            slidx = CTZ64(sublist->FreeMask);
            filter = sublist->Filters + slidx;
            break;
        }
        ++lidx;
    }
    if(UNLIKELY(!filter))
    {
        const FilterSubList empty_sublist = { 0, NULL };
        /* Don't allocate so many list entries that the 32-bit ID could
         * overflow...
         */
        if(UNLIKELY(VECTOR_SIZE(device->FilterList) >= 1<<25))
        {
            almtx_unlock(&device->FilterLock);
            alSetError(context, AL_OUT_OF_MEMORY, "Too many filters allocated");
            return NULL;
        }
        lidx = (ALsizei)VECTOR_SIZE(device->FilterList);
        VECTOR_PUSH_BACK(device->FilterList, empty_sublist);
        sublist = &VECTOR_BACK(device->FilterList);
        sublist->FreeMask = ~U64(0);
        sublist->Filters = al_calloc(16, sizeof(ALfilter)*64);
        if(UNLIKELY(!sublist->Filters))
        {
            VECTOR_POP_BACK(device->FilterList);
            almtx_unlock(&device->FilterLock);
            alSetError(context, AL_OUT_OF_MEMORY, "Failed to allocate filter batch");
            return NULL;
        }

        slidx = 0;
        filter = sublist->Filters + slidx;
    }

    memset(filter, 0, sizeof(*filter));
    InitFilterParams(filter, AL_FILTER_NULL);

    /* Add 1 to avoid filter ID 0. */
    filter->id = ((lidx<<6) | slidx) + 1;

    sublist->FreeMask &= ~(U64(1)<<slidx);
    almtx_unlock(&device->FilterLock);

    return filter;
}
Beispiel #12
0
static ALCenum qsa_open_capture(ALCdevice* device, const ALCchar* deviceName)
{
    qsa_data *data;
    int card, dev;
    int format=-1;
    int status;

    data=(qsa_data*)calloc(1, sizeof(qsa_data));
    if (data==NULL)
    {
        return ALC_OUT_OF_MEMORY;
    }

    if(!deviceName)
        deviceName = qsaDevice;

    if(strcmp(deviceName, qsaDevice) == 0)
        status = snd_pcm_open_preferred(&data->pcmHandle, &card, &dev, SND_PCM_OPEN_CAPTURE);
    else
    {
        const DevMap *iter;

        if(VECTOR_SIZE(CaptureNameMap) == 0)
            deviceList(SND_PCM_CHANNEL_CAPTURE, &CaptureNameMap);

#define MATCH_DEVNAME(iter) ((iter)->name && strcmp(deviceName, (iter)->name)==0)
        VECTOR_FIND_IF(iter, const DevMap, CaptureNameMap, MATCH_DEVNAME);
#undef MATCH_DEVNAME
        if(iter == VECTOR_END(CaptureNameMap))
        {
            free(data);
            return ALC_INVALID_DEVICE;
        }

        status = snd_pcm_open(&data->pcmHandle, iter->card, iter->dev, SND_PCM_OPEN_CAPTURE);
    }

    if(status < 0)
    {
        free(data);
        return ALC_INVALID_DEVICE;
    }

    data->audio_fd = snd_pcm_file_descriptor(data->pcmHandle, SND_PCM_CHANNEL_CAPTURE);
    if(data->audio_fd < 0)
    {
        snd_pcm_close(data->pcmHandle);
        free(data);
        return ALC_INVALID_DEVICE;
    }

    al_string_copy_cstr(&device->DeviceName, deviceName);
    device->ExtraData = data;

    switch (device->FmtType)
    {
        case DevFmtByte:
             format=SND_PCM_SFMT_S8;
             break;
        case DevFmtUByte:
             format=SND_PCM_SFMT_U8;
             break;
        case DevFmtShort:
             format=SND_PCM_SFMT_S16_LE;
             break;
        case DevFmtUShort:
             format=SND_PCM_SFMT_U16_LE;
             break;
        case DevFmtInt:
             format=SND_PCM_SFMT_S32_LE;
             break;
        case DevFmtUInt:
             format=SND_PCM_SFMT_U32_LE;
             break;
        case DevFmtFloat:
             format=SND_PCM_SFMT_FLOAT_LE;
             break;
    }

    /* we actually don't want to block on reads */
    snd_pcm_nonblock_mode(data->pcmHandle, 1);
    /* Disable mmap to control data transfer to the audio device */
    snd_pcm_plugin_set_disable(data->pcmHandle, PLUGIN_DISABLE_MMAP);

    /* configure a sound channel */
    memset(&data->cparams, 0, sizeof(data->cparams));
    data->cparams.mode=SND_PCM_MODE_BLOCK;
    data->cparams.channel=SND_PCM_CHANNEL_CAPTURE;
    data->cparams.start_mode=SND_PCM_START_GO;
    data->cparams.stop_mode=SND_PCM_STOP_STOP;

    data->cparams.buf.block.frag_size=device->UpdateSize*
        ChannelsFromDevFmt(device->FmtChans)*BytesFromDevFmt(device->FmtType);
    data->cparams.buf.block.frags_max=device->NumUpdates;
    data->cparams.buf.block.frags_min=device->NumUpdates;

    data->cparams.format.interleave=1;
    data->cparams.format.rate=device->Frequency;
    data->cparams.format.voices=ChannelsFromDevFmt(device->FmtChans);
    data->cparams.format.format=format;

    if(snd_pcm_plugin_params(data->pcmHandle, &data->cparams) < 0)
    {
        snd_pcm_close(data->pcmHandle);
        free(data);
        device->ExtraData=NULL;

        return ALC_INVALID_VALUE;
    }

    return ALC_NO_ERROR;
}
Beispiel #13
0
static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
{
    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
    const al_string *iter;
    ALbyte *BufferData = NULL;
    DWORD CapturedDataSize;
    ALint BufferSize;
    UINT DeviceID;
    MMRESULT res;
    ALuint i;

    if(VECTOR_SIZE(CaptureDevices) == 0)
        ProbeCaptureDevices();

    // Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && (!name || alstr_cmp_cstr(*iter, name) == 0))
    VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
    if(iter == VECTOR_END(CaptureDevices))
        return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME

    DeviceID = (UINT)(iter - VECTOR_BEGIN(CaptureDevices));

    switch(device->FmtChans)
    {
        case DevFmtMono:
        case DevFmtStereo:
            break;

        case DevFmtQuad:
        case DevFmtX51:
        case DevFmtX51Rear:
        case DevFmtX61:
        case DevFmtX71:
        case DevFmtAmbi3D:
            return ALC_INVALID_ENUM;
    }

    switch(device->FmtType)
    {
        case DevFmtUByte:
        case DevFmtShort:
        case DevFmtInt:
        case DevFmtFloat:
            break;

        case DevFmtByte:
        case DevFmtUShort:
        case DevFmtUInt:
            return ALC_INVALID_ENUM;
    }

    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
    self->Format.wFormatTag = ((device->FmtType == DevFmtFloat) ?
                               WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
    self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans, device->AmbiOrder);
    self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
    self->Format.nBlockAlign = self->Format.wBitsPerSample *
                               self->Format.nChannels / 8;
    self->Format.nSamplesPerSec = device->Frequency;
    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
                                   self->Format.nBlockAlign;
    self->Format.cbSize = 0;

    if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
    {
        ERR("waveInOpen failed: %u\n", res);
        goto failure;
    }

    // Allocate circular memory buffer for the captured audio
    CapturedDataSize = device->UpdateSize*device->NumUpdates;

    // Make sure circular buffer is at least 100ms in size
    if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
        CapturedDataSize = self->Format.nSamplesPerSec / 10;

    self->Ring = ll_ringbuffer_create(CapturedDataSize, self->Format.nBlockAlign, false);
    if(!self->Ring) goto failure;

    InitRef(&self->WaveBuffersCommitted, 0);

    // Create 4 Buffers of 50ms each
    BufferSize = self->Format.nAvgBytesPerSec / 20;
    BufferSize -= (BufferSize % self->Format.nBlockAlign);

    BufferData = calloc(4, BufferSize);
    if(!BufferData) goto failure;

    for(i = 0;i < 4;i++)
    {
        memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
        self->WaveBuffer[i].dwBufferLength = BufferSize;
        self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
                                      (self->WaveBuffer[i-1].lpData +
                                       self->WaveBuffer[i-1].dwBufferLength));
        self->WaveBuffer[i].dwFlags = 0;
        self->WaveBuffer[i].dwLoops = 0;
        waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
        waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
        IncrementRef(&self->WaveBuffersCommitted);
    }

    ATOMIC_STORE(&self->killNow, AL_FALSE, almemory_order_release);
    if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
        goto failure;

    alstr_copy(&device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
    return ALC_NO_ERROR;

failure:
    if(BufferData)
    {
        for(i = 0;i < 4;i++)
            waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
        free(BufferData);
    }

    ll_ringbuffer_free(self->Ring);
    self->Ring = NULL;

    if(self->InHdl)
        waveInClose(self->InHdl);
    self->InHdl = NULL;

    return ALC_INVALID_VALUE;
}
Beispiel #14
0
static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
{
    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
    const al_string *iter;
    UINT DeviceID;
    MMRESULT res;

    if(VECTOR_SIZE(PlaybackDevices) == 0)
        ProbePlaybackDevices();

    // Find the Device ID matching the deviceName if valid
#define MATCH_DEVNAME(iter) (!alstr_empty(*(iter)) && \
                             (!deviceName || alstr_cmp_cstr(*(iter), deviceName) == 0))
    VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
    if(iter == VECTOR_END(PlaybackDevices))
        return ALC_INVALID_VALUE;
#undef MATCH_DEVNAME

    DeviceID = (UINT)(iter - VECTOR_BEGIN(PlaybackDevices));

retry_open:
    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
    if(device->FmtType == DevFmtFloat)
    {
        self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
        self->Format.wBitsPerSample = 32;
    }
    else
    {
        self->Format.wFormatTag = WAVE_FORMAT_PCM;
        if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
            self->Format.wBitsPerSample = 8;
        else
            self->Format.wBitsPerSample = 16;
    }
    self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
    self->Format.nBlockAlign = self->Format.wBitsPerSample *
                               self->Format.nChannels / 8;
    self->Format.nSamplesPerSec = device->Frequency;
    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
                                   self->Format.nBlockAlign;
    self->Format.cbSize = 0;

    if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
    {
        if(device->FmtType == DevFmtFloat)
        {
            device->FmtType = DevFmtShort;
            goto retry_open;
        }
        ERR("waveOutOpen failed: %u\n", res);
        goto failure;
    }

    alstr_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
    return ALC_NO_ERROR;

failure:
    if(self->OutHdl)
        waveOutClose(self->OutHdl);
    self->OutHdl = NULL;

    return ALC_INVALID_VALUE;
}