예제 #1
0
static void InitHQPanning(ALCdevice *device, const AmbDecConf *conf, const ALuint speakermap[MAX_OUTPUT_CHANNELS])
{
    const char *devname;
    int decflags = 0;
    size_t count;
    ALuint i;

    devname = al_string_get_cstr(device->DeviceName);
    if(GetConfigValueBool(devname, "decoder", "distance-comp", 1))
        decflags |= BFDF_DistanceComp;

    if((conf->ChanMask&AMBI_PERIPHONIC_MASK))
    {
        count = (conf->ChanMask > 0x1ff) ? 16 :
                (conf->ChanMask > 0xf) ? 9 : 4;
        for(i = 0;i < count;i++)
        {
            device->Dry.Ambi.Map[i].Scale = 1.0f;
            device->Dry.Ambi.Map[i].Index = i;
        }
    }
    else
    {
        static int map[MAX_AMBI_COEFFS] = { 0, 1, 3, 4, 8, 9, 15 };

        count = (conf->ChanMask > 0x1ff) ? 7 :
                (conf->ChanMask > 0xf) ? 5 : 3;
        for(i = 0;i < count;i++)
        {
            device->Dry.Ambi.Map[i].Scale = 1.0f;
            device->Dry.Ambi.Map[i].Index = map[i];
        }
    }
    device->Dry.CoeffCount = 0;
    device->Dry.NumChannels = count;

    TRACE("Enabling %s-band %s-order%s ambisonic decoder\n",
        (conf->FreqBands == 1) ? "single" : "dual",
        (conf->ChanMask > 0xf) ? (conf->ChanMask > 0x1ff) ? "third" : "second" : "first",
        (conf->ChanMask&AMBI_PERIPHONIC_MASK) ? " periphonic" : ""
    );
    bformatdec_reset(device->AmbiDecoder, conf, count, device->Frequency,
                     speakermap, decflags);

    if(bformatdec_getOrder(device->AmbiDecoder) < 2)
    {
        memcpy(&device->FOAOut.Ambi, &device->Dry.Ambi, sizeof(device->FOAOut.Ambi));
        device->FOAOut.CoeffCount = device->Dry.CoeffCount;
    }
    else
    {
        memset(&device->FOAOut.Ambi, 0, sizeof(device->FOAOut.Ambi));
        for(i = 0;i < 4;i++)
        {
            device->FOAOut.Ambi.Map[i].Scale = 1.0f;
            device->FOAOut.Ambi.Map[i].Index = i;
        }
        device->FOAOut.CoeffCount = 0;
    }
}
예제 #2
0
void alc_pulse_init(BackendFuncs *func_list) //{{{
{
    *func_list = pulse_funcs;

    pulse_ctx_flags = 0;
    if(!GetConfigValueBool("pulse", "spawn-server", 0))
        pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
} //}}}
예제 #3
0
ALCboolean alc_pulse_init(BackendFuncs *func_list) //{{{
{
    if(!pulse_load())
        return ALC_FALSE;

    *func_list = pulse_funcs;

    pulse_ctx_flags = 0;
    if(!GetConfigValueBool("pulse", "spawn-server", 0))
        pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;

    return ALC_TRUE;
} //}}}
예제 #4
0
ALCboolean alc_pulse_init(BackendFuncs *func_list)
{
    ALCboolean ret = ALC_FALSE;

    if(pulse_load())
    {
        pa_threaded_mainloop *loop;

        pulse_ctx_flags = 0;
        if(!GetConfigValueBool("pulse", "spawn-server", 0))
            pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;

        if((loop=pa_threaded_mainloop_new()) &&
           pa_threaded_mainloop_start(loop) >= 0)
        {
            pa_context *context;

            pa_threaded_mainloop_lock(loop);
            context = connect_context(loop, AL_TRUE);
            if(context)
            {
                *func_list = pulse_funcs;
                ret = ALC_TRUE;

                /* Some libraries (Phonon, Qt) set some pulseaudio properties
                 * through environment variables, which causes all streams in
                 * the process to inherit them. This attempts to filter those
                 * properties out by setting them to 0-length data. */
                prop_filter = pa_proplist_new();
                pa_proplist_set(prop_filter, PA_PROP_MEDIA_ROLE, NULL, 0);
                pa_proplist_set(prop_filter, "phonon.streamid", NULL, 0);

                pa_context_disconnect(context);
                pa_context_unref(context);
            }
            pa_threaded_mainloop_unlock(loop);
            pa_threaded_mainloop_stop(loop);
        }
        if(loop)
            pa_threaded_mainloop_free(loop);
    }

    return ret;
}
예제 #5
0
static ALCboolean alsa_reset_playback(ALCdevice *device)
{
    alsa_data *data = (alsa_data*)device->ExtraData;
    snd_pcm_uframes_t periodSizeInFrames;
    snd_pcm_sw_params_t *sp = NULL;
    snd_pcm_hw_params_t *p = NULL;
    snd_pcm_access_t access;
    unsigned int periods;
    unsigned int rate;
    int allowmmap;
    char *err;
    int i;


    switch(aluBytesFromFormat(device->Format))
    {
        case 1:
            data->format = SND_PCM_FORMAT_U8;
            break;
        case 2:
            data->format = SND_PCM_FORMAT_S16;
            break;
        case 4:
            data->format = SND_PCM_FORMAT_FLOAT;
            break;
        default:
            AL_PRINT("Unknown format: 0x%x\n", device->Format);
            return ALC_FALSE;
    }

    allowmmap = GetConfigValueBool("alsa", "mmap", 1);
    periods = device->NumUpdates;
    periodSizeInFrames = device->UpdateSize;
    rate = device->Frequency;

    err = NULL;
    psnd_pcm_hw_params_malloc(&p);

    if((i=psnd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
        err = "any";
    /* set interleaved access */
    if(err == NULL && (!allowmmap || (i=psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
    {
        if(periods > 2) periods--;
        if((i=psnd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
            err = "set access";
    }
    /* set format (implicitly sets sample bits) */
    if(err == NULL && (i=psnd_pcm_hw_params_set_format(data->pcmHandle, p, data->format)) < 0)
        err = "set format";
    /* set channels (implicitly sets frame bits) */
    if(err == NULL && (i=psnd_pcm_hw_params_set_channels(data->pcmHandle, p, aluChannelsFromFormat(device->Format))) < 0)
        err = "set channels";
    /* set periods (implicitly constrains period/buffer parameters) */
    if(err == NULL && (i=psnd_pcm_hw_params_set_periods_near(data->pcmHandle, p, &periods, NULL)) < 0)
        err = "set periods near";
    /* set rate (implicitly constrains period/buffer parameters) */
    if(err == NULL && (i=psnd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
        err = "set rate near";
    /* set period size in frame units (implicitly sets buffer size/bytes/time and period time/bytes) */
    if(err == NULL && (i=psnd_pcm_hw_params_set_period_size_near(data->pcmHandle, p, &periodSizeInFrames, NULL)) < 0)
        err = "set period size near";
    /* install and prepare hardware configuration */
    if(err == NULL && (i=psnd_pcm_hw_params(data->pcmHandle, p)) < 0)
        err = "set params";
    if(err == NULL && (i=psnd_pcm_hw_params_get_access(p, &access)) < 0)
        err = "get access";
    if(err == NULL && (i=psnd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
        err = "get period size";
    if(err == NULL && (i=psnd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
        err = "get periods";
    if(err != NULL)
    {
        AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
        psnd_pcm_hw_params_free(p);
        return ALC_FALSE;
    }

    psnd_pcm_hw_params_free(p);

    err = NULL;
    psnd_pcm_sw_params_malloc(&sp);

    if((i=psnd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
        err = "sw current";
    if(err == NULL && (i=psnd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
        err = "sw set avail min";
    if(err == NULL && (i=psnd_pcm_sw_params(data->pcmHandle, sp)) != 0)
        err = "sw set params";
    if(err != NULL)
    {
        AL_PRINT("%s failed: %s\n", err, psnd_strerror(i));
        psnd_pcm_sw_params_free(sp);
        return ALC_FALSE;
    }

    psnd_pcm_sw_params_free(sp);

    data->size = psnd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
    if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
    {
        /* Increase periods by one, since the temp buffer counts as an extra
         * period */
        periods++;
        data->buffer = malloc(data->size);
        if(!data->buffer)
        {
            AL_PRINT("buffer malloc failed\n");
            return ALC_FALSE;
        }
        data->thread = StartThread(ALSANoMMapProc, device);
    }
    else
    {
        i = psnd_pcm_prepare(data->pcmHandle);
        if(i < 0)
        {
            AL_PRINT("prepare error: %s\n", psnd_strerror(i));
            free(data->buffer);
            data->buffer = NULL;
            return ALC_FALSE;
        }
        data->thread = StartThread(ALSAProc, device);
    }
    if(data->thread == NULL)
    {
        AL_PRINT("Could not create playback thread\n");
        free(data->buffer);
        data->buffer = NULL;
        return ALC_FALSE;
    }

    device->UpdateSize = periodSizeInFrames;
    device->NumUpdates = periods;
    device->Frequency = rate;

    return ALC_TRUE;
}
예제 #6
0
void aluInitRenderer(ALCdevice *device, ALint hrtf_id, enum HrtfRequestMode hrtf_appreq, enum HrtfRequestMode hrtf_userreq)
{
    const char *mode;
    bool headphones;
    int bs2blevel;
    size_t i;

    device->Hrtf = NULL;
    al_string_clear(&device->Hrtf_Name);
    device->Render_Mode = NormalRender;

    memset(&device->Dry.Ambi, 0, sizeof(device->Dry.Ambi));
    device->Dry.CoeffCount = 0;
    device->Dry.NumChannels = 0;

    if(device->FmtChans != DevFmtStereo)
    {
        ALuint speakermap[MAX_OUTPUT_CHANNELS];
        const char *devname, *layout = NULL;
        AmbDecConf conf, *pconf = NULL;

        if(hrtf_appreq == Hrtf_Enable)
            device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;

        ambdec_init(&conf);

        devname = al_string_get_cstr(device->DeviceName);
        switch(device->FmtChans)
        {
            case DevFmtQuad: layout = "quad"; break;
            case DevFmtX51: layout = "surround51"; break;
            case DevFmtX51Rear: layout = "surround51rear"; break;
            case DevFmtX61: layout = "surround61"; break;
            case DevFmtX71: layout = "surround71"; break;
            /* Mono, Stereo, and B-Fornat output don't use custom decoders. */
            case DevFmtMono:
            case DevFmtStereo:
            case DevFmtBFormat3D:
                break;
        }
        if(layout)
        {
            const char *fname;
            if(ConfigValueStr(devname, "decoder", layout, &fname))
            {
                if(!ambdec_load(&conf, fname))
                    ERR("Failed to load layout file %s\n", fname);
                else
                {
                    if(conf.ChanMask > 0xffff)
                        ERR("Unsupported channel mask 0x%04x (max 0xffff)\n", conf.ChanMask);
                    else
                    {
                        if(MakeSpeakerMap(device, &conf, speakermap))
                            pconf = &conf;
                    }
                }
            }
        }

        if(pconf && GetConfigValueBool(devname, "decoder", "hq-mode", 0))
        {
            if(!device->AmbiDecoder)
                device->AmbiDecoder = bformatdec_alloc();
        }
        else
        {
            bformatdec_free(device->AmbiDecoder);
            device->AmbiDecoder = NULL;
        }

        if(!pconf)
            InitPanning(device);
        else if(device->AmbiDecoder)
            InitHQPanning(device, pconf, speakermap);
        else
            InitCustomPanning(device, pconf, speakermap);

        ambdec_deinit(&conf);
        return;
    }

    bformatdec_free(device->AmbiDecoder);
    device->AmbiDecoder = NULL;

    headphones = device->IsHeadphones;
    if(device->Type != Loopback)
    {
        const char *mode;
        if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-mode", &mode))
        {
            if(strcasecmp(mode, "headphones") == 0)
                headphones = true;
            else if(strcasecmp(mode, "speakers") == 0)
                headphones = false;
            else if(strcasecmp(mode, "auto") != 0)
                ERR("Unexpected stereo-mode: %s\n", mode);
        }
    }

    if(hrtf_userreq == Hrtf_Default)
    {
        bool usehrtf = (headphones && hrtf_appreq != Hrtf_Disable) ||
                       (hrtf_appreq == Hrtf_Enable);
        if(!usehrtf) goto no_hrtf;

        device->Hrtf_Status = ALC_HRTF_ENABLED_SOFT;
        if(headphones && hrtf_appreq != Hrtf_Disable)
            device->Hrtf_Status = ALC_HRTF_HEADPHONES_DETECTED_SOFT;
    }
    else
    {
        if(hrtf_userreq != Hrtf_Enable)
        {
            if(hrtf_appreq == Hrtf_Enable)
                device->Hrtf_Status = ALC_HRTF_DENIED_SOFT;
            goto no_hrtf;
        }
        device->Hrtf_Status = ALC_HRTF_REQUIRED_SOFT;
    }

    if(VECTOR_SIZE(device->Hrtf_List) == 0)
    {
        VECTOR_DEINIT(device->Hrtf_List);
        device->Hrtf_List = EnumerateHrtf(device->DeviceName);
    }

    if(hrtf_id >= 0 && (size_t)hrtf_id < VECTOR_SIZE(device->Hrtf_List))
    {
        const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, hrtf_id);
        if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
        {
            device->Hrtf = entry->hrtf;
            al_string_copy(&device->Hrtf_Name, entry->name);
        }
    }

    for(i = 0;!device->Hrtf && i < VECTOR_SIZE(device->Hrtf_List);i++)
    {
        const HrtfEntry *entry = &VECTOR_ELEM(device->Hrtf_List, i);
        if(GetHrtfSampleRate(entry->hrtf) == device->Frequency)
        {
            device->Hrtf = entry->hrtf;
            al_string_copy(&device->Hrtf_Name, entry->name);
        }
    }

    if(device->Hrtf)
    {
        device->Render_Mode = HrtfRender;
        if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "hrtf-mode", &mode))
        {
            if(strcasecmp(mode, "full") == 0)
                device->Render_Mode = HrtfRender;
            else if(strcasecmp(mode, "basic") == 0)
                device->Render_Mode = NormalRender;
            else
                ERR("Unexpected hrtf-mode: %s\n", mode);
        }

        TRACE("HRTF enabled, \"%s\"\n", al_string_get_cstr(device->Hrtf_Name));
        InitHrtfPanning(device);
        return;
    }
    device->Hrtf_Status = ALC_HRTF_UNSUPPORTED_FORMAT_SOFT;

no_hrtf:
    TRACE("HRTF disabled\n");

    bs2blevel = ((headphones && hrtf_appreq != Hrtf_Disable) ||
                 (hrtf_appreq == Hrtf_Enable)) ? 5 : 0;
    if(device->Type != Loopback)
        ConfigValueInt(al_string_get_cstr(device->DeviceName), NULL, "cf_level", &bs2blevel);
    if(bs2blevel > 0 && bs2blevel <= 6)
    {
        device->Bs2b = al_calloc(16, sizeof(*device->Bs2b));
        bs2b_set_params(device->Bs2b, bs2blevel, device->Frequency);
        device->Render_Mode = StereoPair;
        TRACE("BS2B enabled\n");
        InitPanning(device);
        return;
    }

    TRACE("BS2B disabled\n");

    device->Render_Mode = NormalRender;
    if(ConfigValueStr(al_string_get_cstr(device->DeviceName), NULL, "stereo-panning", &mode))
    {
        if(strcasecmp(mode, "paired") == 0)
            device->Render_Mode = StereoPair;
        else if(strcasecmp(mode, "uhj") != 0)
            ERR("Unexpected stereo-panning: %s\n", mode);
    }
    if(device->Render_Mode == NormalRender)
    {
        device->Uhj_Encoder = al_calloc(16, sizeof(Uhj2Encoder));
        TRACE("UHJ enabled\n");
        InitUhjPanning(device);
        return;
    }

    TRACE("UHJ disabled\n");
    InitPanning(device);
}
예제 #7
0
파일: alsa.c 프로젝트: LighFusion/surreal
static ALCboolean alsa_reset_playback(ALCdevice *device)
{
    alsa_data *data = (alsa_data*)device->ExtraData;
    snd_pcm_uframes_t periodSizeInFrames;
    unsigned int periodLen, bufferLen;
    snd_pcm_sw_params_t *sp = NULL;
    snd_pcm_hw_params_t *hp = NULL;
    snd_pcm_access_t access;
    snd_pcm_format_t format;
    unsigned int periods;
    unsigned int rate;
    const char *funcerr;
    int allowmmap;
    int err;

    format = -1;
    switch(device->FmtType)
    {
        case DevFmtByte:
            format = SND_PCM_FORMAT_S8;
            break;
        case DevFmtUByte:
            format = SND_PCM_FORMAT_U8;
            break;
        case DevFmtShort:
            format = SND_PCM_FORMAT_S16;
            break;
        case DevFmtUShort:
            format = SND_PCM_FORMAT_U16;
            break;
        case DevFmtInt:
            format = SND_PCM_FORMAT_S32;
            break;
        case DevFmtUInt:
            format = SND_PCM_FORMAT_U32;
            break;
        case DevFmtFloat:
            format = SND_PCM_FORMAT_FLOAT;
            break;
    }

    allowmmap = GetConfigValueBool("alsa", "mmap", 1);
    periods = device->NumUpdates;
    periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
    bufferLen = periodLen * periods;
    rate = device->Frequency;

    snd_pcm_hw_params_malloc(&hp);
#define CHECK(x) if((funcerr=#x),(err=(x)) < 0) goto error
    CHECK(snd_pcm_hw_params_any(data->pcmHandle, hp));
    /* set interleaved access */
    if(!allowmmap || snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
    {
        if(periods > 2)
        {
            periods--;
            bufferLen = periodLen * periods;
        }
        CHECK(snd_pcm_hw_params_set_access(data->pcmHandle, hp, SND_PCM_ACCESS_RW_INTERLEAVED));
    }
    /* test and set format (implicitly sets sample bits) */
    if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) < 0)
    {
        static const struct {
            snd_pcm_format_t format;
            enum DevFmtType fmttype;
        } formatlist[] = {
            { SND_PCM_FORMAT_FLOAT, DevFmtFloat  },
            { SND_PCM_FORMAT_S32,   DevFmtInt    },
            { SND_PCM_FORMAT_U32,   DevFmtUInt   },
            { SND_PCM_FORMAT_S16,   DevFmtShort  },
            { SND_PCM_FORMAT_U16,   DevFmtUShort },
            { SND_PCM_FORMAT_S8,    DevFmtByte   },
            { SND_PCM_FORMAT_U8,    DevFmtUByte  },
        };
        size_t k;

        for(k = 0;k < COUNTOF(formatlist);k++)
        {
            format = formatlist[k].format;
            if(snd_pcm_hw_params_test_format(data->pcmHandle, hp, format) >= 0)
            {
                device->FmtType = formatlist[k].fmttype;
                break;
            }
        }
    }
    CHECK(snd_pcm_hw_params_set_format(data->pcmHandle, hp, format));
    /* test and set channels (implicitly sets frame bits) */
    if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)) < 0)
    {
        static const enum DevFmtChannels channellist[] = {
            DevFmtStereo,
            DevFmtQuad,
            DevFmtX51,
            DevFmtX71,
            DevFmtMono,
        };
        size_t k;

        for(k = 0;k < COUNTOF(channellist);k++)
        {
            if(snd_pcm_hw_params_test_channels(data->pcmHandle, hp, ChannelsFromDevFmt(channellist[k])) >= 0)
            {
                device->FmtChans = channellist[k];
                break;
            }
        }
    }
    CHECK(snd_pcm_hw_params_set_channels(data->pcmHandle, hp, ChannelsFromDevFmt(device->FmtChans)));
    /* set rate (implicitly constrains period/buffer parameters) */
    if(snd_pcm_hw_params_set_rate_resample(data->pcmHandle, hp, 0) < 0)
        ERR("Failed to disable ALSA resampler\n");
    CHECK(snd_pcm_hw_params_set_rate_near(data->pcmHandle, hp, &rate, NULL));
    /* set buffer time (implicitly constrains period/buffer parameters) */
    CHECK(snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, hp, &bufferLen, NULL));
    /* set period time (implicitly sets buffer size/bytes/time and period size/bytes) */
    CHECK(snd_pcm_hw_params_set_period_time_near(data->pcmHandle, hp, &periodLen, NULL));
    /* install and prepare hardware configuration */
    CHECK(snd_pcm_hw_params(data->pcmHandle, hp));
    /* retrieve configuration info */
    CHECK(snd_pcm_hw_params_get_access(hp, &access));
    CHECK(snd_pcm_hw_params_get_period_size(hp, &periodSizeInFrames, NULL));
    CHECK(snd_pcm_hw_params_get_periods(hp, &periods, NULL));

    snd_pcm_hw_params_free(hp);
    hp = NULL;
    snd_pcm_sw_params_malloc(&sp);

    CHECK(snd_pcm_sw_params_current(data->pcmHandle, sp));
    CHECK(snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames));
    CHECK(snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods));
    CHECK(snd_pcm_sw_params(data->pcmHandle, sp));
#undef CHECK
    snd_pcm_sw_params_free(sp);
    sp = NULL;

    /* Increase periods by one, since the temp buffer counts as an extra
     * period */
    if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
        device->NumUpdates = periods+1;
    else
        device->NumUpdates = periods;
    device->UpdateSize = periodSizeInFrames;
    device->Frequency = rate;

    SetDefaultChannelOrder(device);

    return ALC_TRUE;

error:
    ERR("%s failed: %s\n", funcerr, snd_strerror(err));
    if(hp) snd_pcm_hw_params_free(hp);
    if(sp) snd_pcm_sw_params_free(sp);
    return ALC_FALSE;
}
예제 #8
0
int MOB_File_GetConfigValueBool(const char *blockName, const char *keyName, int def)
{
	char buffer[ 64 ];
	return GetConfigValueBool( ConvertBlockName( blockName ), ConvertKeyName( buffer, keyName ), def );
}
예제 #9
0
ALvoid aluInitPanning(ALCdevice *Device)
{
    ALfloat SpeakerAngle[MAXCHANNELS];
    ALfloat (*Matrix)[MAXCHANNELS];
    Channel *Speaker2Chan;
    ALfloat Alpha, Theta;
    ALfloat *PanningLUT;
    ALint pos, offset;
    ALuint s, s2;

    for(s = 0;s < MAXCHANNELS;s++)
    {
        for(s2 = 0;s2 < MAXCHANNELS;s2++)
            Device->ChannelMatrix[s][s2] = ((s==s2) ? 1.0f : 0.0f);
    }

    Speaker2Chan = Device->Speaker2Chan;
    Matrix = Device->ChannelMatrix;
    switch(Device->FmtChans)
    {
        case DevFmtMono:
            Matrix[FRONT_LEFT][FRONT_CENTER]  = aluSqrt(0.5);
            Matrix[FRONT_RIGHT][FRONT_CENTER] = aluSqrt(0.5);
            Matrix[SIDE_LEFT][FRONT_CENTER]   = aluSqrt(0.5);
            Matrix[SIDE_RIGHT][FRONT_CENTER]  = aluSqrt(0.5);
            Matrix[BACK_LEFT][FRONT_CENTER]   = aluSqrt(0.5);
            Matrix[BACK_RIGHT][FRONT_CENTER]  = aluSqrt(0.5);
            Matrix[BACK_CENTER][FRONT_CENTER] = 1.0f;
            Device->NumChan = 1;
            Speaker2Chan[0] = FRONT_CENTER;
            SpeakerAngle[0] = 0.0f * M_PI/180.0f;
            break;

        case DevFmtStereo:
            Matrix[FRONT_CENTER][FRONT_LEFT]  = aluSqrt(0.5);
            Matrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
            Matrix[SIDE_LEFT][FRONT_LEFT]     = 1.0f;
            Matrix[SIDE_RIGHT][FRONT_RIGHT]   = 1.0f;
            Matrix[BACK_LEFT][FRONT_LEFT]     = 1.0f;
            Matrix[BACK_RIGHT][FRONT_RIGHT]   = 1.0f;
            Matrix[BACK_CENTER][FRONT_LEFT]   = aluSqrt(0.5);
            Matrix[BACK_CENTER][FRONT_RIGHT]  = aluSqrt(0.5);
            Device->NumChan = 2;
            Speaker2Chan[0] = FRONT_LEFT;
            Speaker2Chan[1] = FRONT_RIGHT;
            SpeakerAngle[0] = -90.0f * M_PI/180.0f;
            SpeakerAngle[1] =  90.0f * M_PI/180.0f;
            SetSpeakerArrangement("layout_STEREO", SpeakerAngle, Speaker2Chan, Device->NumChan);
            break;

        case DevFmtQuad:
            Matrix[FRONT_CENTER][FRONT_LEFT]  = aluSqrt(0.5);
            Matrix[FRONT_CENTER][FRONT_RIGHT] = aluSqrt(0.5);
            Matrix[SIDE_LEFT][FRONT_LEFT]     = aluSqrt(0.5);
            Matrix[SIDE_LEFT][BACK_LEFT]      = aluSqrt(0.5);
            Matrix[SIDE_RIGHT][FRONT_RIGHT]   = aluSqrt(0.5);
            Matrix[SIDE_RIGHT][BACK_RIGHT]    = aluSqrt(0.5);
            Matrix[BACK_CENTER][BACK_LEFT]    = aluSqrt(0.5);
            Matrix[BACK_CENTER][BACK_RIGHT]   = aluSqrt(0.5);
            Device->NumChan = 4;
            Speaker2Chan[0] = BACK_LEFT;
            Speaker2Chan[1] = FRONT_LEFT;
            Speaker2Chan[2] = FRONT_RIGHT;
            Speaker2Chan[3] = BACK_RIGHT;
            SpeakerAngle[0] = -135.0f * M_PI/180.0f;
            SpeakerAngle[1] =  -45.0f * M_PI/180.0f;
            SpeakerAngle[2] =   45.0f * M_PI/180.0f;
            SpeakerAngle[3] =  135.0f * M_PI/180.0f;
            SetSpeakerArrangement("layout_QUAD", SpeakerAngle, Speaker2Chan, Device->NumChan);
            break;

        case DevFmtX51:
            Matrix[SIDE_LEFT][FRONT_LEFT]   = aluSqrt(0.5);
            Matrix[SIDE_LEFT][BACK_LEFT]    = aluSqrt(0.5);
            Matrix[SIDE_RIGHT][FRONT_RIGHT] = aluSqrt(0.5);
            Matrix[SIDE_RIGHT][BACK_RIGHT]  = aluSqrt(0.5);
            Matrix[BACK_CENTER][BACK_LEFT]  = aluSqrt(0.5);
            Matrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
            Device->NumChan = 5;
            Speaker2Chan[0] = BACK_LEFT;
            Speaker2Chan[1] = FRONT_LEFT;
            Speaker2Chan[2] = FRONT_CENTER;
            Speaker2Chan[3] = FRONT_RIGHT;
            Speaker2Chan[4] = BACK_RIGHT;
            SpeakerAngle[0] = -110.0f * M_PI/180.0f;
            SpeakerAngle[1] =  -30.0f * M_PI/180.0f;
            SpeakerAngle[2] =    0.0f * M_PI/180.0f;
            SpeakerAngle[3] =   30.0f * M_PI/180.0f;
            SpeakerAngle[4] =  110.0f * M_PI/180.0f;
            SetSpeakerArrangement("layout_51CHN", SpeakerAngle, Speaker2Chan, Device->NumChan);
            break;

        case DevFmtX61:
            Matrix[BACK_LEFT][BACK_CENTER]  = aluSqrt(0.5);
            Matrix[BACK_LEFT][SIDE_LEFT]    = aluSqrt(0.5);
            Matrix[BACK_RIGHT][BACK_CENTER] = aluSqrt(0.5);
            Matrix[BACK_RIGHT][SIDE_RIGHT]  = aluSqrt(0.5);
            Device->NumChan = 6;
            Speaker2Chan[0] = SIDE_LEFT;
            Speaker2Chan[1] = FRONT_LEFT;
            Speaker2Chan[2] = FRONT_CENTER;
            Speaker2Chan[3] = FRONT_RIGHT;
            Speaker2Chan[4] = SIDE_RIGHT;
            Speaker2Chan[5] = BACK_CENTER;
            SpeakerAngle[0] = -90.0f * M_PI/180.0f;
            SpeakerAngle[1] = -30.0f * M_PI/180.0f;
            SpeakerAngle[2] =   0.0f * M_PI/180.0f;
            SpeakerAngle[3] =  30.0f * M_PI/180.0f;
            SpeakerAngle[4] =  90.0f * M_PI/180.0f;
            SpeakerAngle[5] = 180.0f * M_PI/180.0f;
            SetSpeakerArrangement("layout_61CHN", SpeakerAngle, Speaker2Chan, Device->NumChan);
            break;

        case DevFmtX71:
            Matrix[BACK_CENTER][BACK_LEFT]  = aluSqrt(0.5);
            Matrix[BACK_CENTER][BACK_RIGHT] = aluSqrt(0.5);
            Device->NumChan = 7;
            Speaker2Chan[0] = BACK_LEFT;
            Speaker2Chan[1] = SIDE_LEFT;
            Speaker2Chan[2] = FRONT_LEFT;
            Speaker2Chan[3] = FRONT_CENTER;
            Speaker2Chan[4] = FRONT_RIGHT;
            Speaker2Chan[5] = SIDE_RIGHT;
            Speaker2Chan[6] = BACK_RIGHT;
            SpeakerAngle[0] = -150.0f * M_PI/180.0f;
            SpeakerAngle[1] =  -90.0f * M_PI/180.0f;
            SpeakerAngle[2] =  -30.0f * M_PI/180.0f;
            SpeakerAngle[3] =    0.0f * M_PI/180.0f;
            SpeakerAngle[4] =   30.0f * M_PI/180.0f;
            SpeakerAngle[5] =   90.0f * M_PI/180.0f;
            SpeakerAngle[6] =  150.0f * M_PI/180.0f;
            SetSpeakerArrangement("layout_71CHN", SpeakerAngle, Speaker2Chan, Device->NumChan);
            break;
    }

    if(GetConfigValueBool(NULL, "scalemix", 0))
    {
        ALfloat maxout = 1.0f;
        for(s = 0;s < MAXCHANNELS;s++)
        {
            ALfloat out = 0.0f;
            for(s2 = 0;s2 < MAXCHANNELS;s2++)
                out += Device->ChannelMatrix[s2][s];
            maxout = __max(maxout, out);
        }

        maxout = 1.0f/maxout;
        for(s = 0;s < MAXCHANNELS;s++)
        {
            for(s2 = 0;s2 < MAXCHANNELS;s2++)
                Device->ChannelMatrix[s2][s] *= maxout;
        }
    }

    PanningLUT = Device->PanningLUT;
    for(pos = 0; pos < LUT_NUM; pos++)
    {
        /* clear all values */
        offset = MAXCHANNELS * pos;
        for(s = 0; s < MAXCHANNELS; s++)
            PanningLUT[offset+s] = 0.0f;

        if(Device->NumChan == 1)
        {
            PanningLUT[offset + Speaker2Chan[0]] = 1.0f;
            continue;
        }

        /* source angle */
        Theta = aluLUTpos2Angle(pos);

        /* set panning values */
        for(s = 0; s < Device->NumChan - 1; s++)
        {
            if(Theta >= SpeakerAngle[s] && Theta < SpeakerAngle[s+1])
            {
                /* source between speaker s and speaker s+1 */
                Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) /
                                 (SpeakerAngle[s+1]-SpeakerAngle[s]);
                PanningLUT[offset + Speaker2Chan[s]]   = cos(Alpha);
                PanningLUT[offset + Speaker2Chan[s+1]] = sin(Alpha);
                break;
            }
        }
        if(s == Device->NumChan - 1)
        {
            /* source between last and first speaker */
            if(Theta < SpeakerAngle[0])
                Theta += 2.0f * M_PI;
            Alpha = M_PI_2 * (Theta-SpeakerAngle[s]) /
                             (2.0f * M_PI + SpeakerAngle[0]-SpeakerAngle[s]);
            PanningLUT[offset + Speaker2Chan[s]] = cos(Alpha);
            PanningLUT[offset + Speaker2Chan[0]] = sin(Alpha);
        }
    }
}
예제 #10
0
static void alc_init(void)
{
    int i;
    const char *devs, *str;

    InitializeCriticalSection(&g_csMutex);
    ALTHUNK_INIT();
    ReadALConfig();

    tls_create(&LocalContext);

    devs = GetConfigValue(NULL, "drivers", "");
    if(devs[0])
    {
        int n;
        size_t len;
        const char *next = devs;

        i = 0;
        do {
            devs = next;
            next = strchr(devs, ',');

            if(!devs[0] || devs[0] == ',')
                continue;

            len = (next ? ((size_t)(next-devs)) : strlen(devs));
            for(n = i;BackendList[n].Init;n++)
            {
                if(len == strlen(BackendList[n].name) &&
                   strncmp(BackendList[n].name, devs, len) == 0)
                {
                    BackendInfo Bkp = BackendList[i];
                    BackendList[i] = BackendList[n];
                    BackendList[n] = Bkp;

                    i++;
                }
            }
        } while(next++);

        BackendList[i].name = NULL;
        BackendList[i].Init = NULL;
        BackendList[i].Deinit = NULL;
        BackendList[i].Probe = NULL;
    }

    for(i = 0;BackendList[i].Init;i++)
    {
        BackendList[i].Init(&BackendList[i].Funcs);

        BackendList[i].Probe(DEVICE_PROBE);
        BackendList[i].Probe(ALL_DEVICE_PROBE);
        BackendList[i].Probe(CAPTURE_DEVICE_PROBE);
    }

    DuplicateStereo = GetConfigValueBool(NULL, "stereodup", 0);

    str = GetConfigValue(NULL, "excludefx", "");
    if(str[0])
    {
        const struct {
            const char *name;
            int type;
        } EffectList[] = {
            { "eaxreverb", EAXREVERB },
            { "reverb", REVERB },
            { "echo", ECHO },
            { NULL, 0 }
        };
        int n;
        size_t len;
        const char *next = str;

        do {
            str = next;
            next = strchr(str, ',');

            if(!str[0] || next == str)
                continue;

            len = (next ? ((size_t)(next-str)) : strlen(str));
            for(n = 0;EffectList[n].name;n++)
            {
                if(len == strlen(EffectList[n].name) &&
                   strncmp(EffectList[n].name, str, len) == 0)
                    DisabledEffects[EffectList[n].type] = AL_TRUE;
            }
        } while(next++);
    }
}
예제 #11
0
/* OpenAL */
static ALCenum pulse_open_playback(ALCdevice *device, const ALCchar *device_name)
{
    const char *pulse_name = NULL;
    pa_stream_flags_t flags;
    pa_sample_spec spec;
    pulse_data *data;
    pa_operation *o;

    if(device_name)
    {
        ALuint i;

        if(!allDevNameMap)
            probe_devices(AL_FALSE);

        for(i = 0; i < numDevNames; i++)
        {
            if(strcmp(device_name, allDevNameMap[i].name) == 0)
            {
                pulse_name = allDevNameMap[i].device_name;
                break;
            }
        }
        if(i == numDevNames)
            return ALC_INVALID_VALUE;
    }

    if(pulse_open(device) == ALC_FALSE)
        return ALC_INVALID_VALUE;

    data = device->ExtraData;
    pa_threaded_mainloop_lock(data->loop);

    flags = PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
            PA_STREAM_FIX_CHANNELS;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    spec.format = PA_SAMPLE_S16NE;
    spec.rate = 44100;
    spec.channels = 2;

    data->stream = connect_playback_stream(pulse_name, data->loop, data->context,
                                           flags, NULL, &spec, NULL);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        pulse_close(device);
        return ALC_INVALID_VALUE;
    }

    data->device_name = strdup(pa_stream_get_device_name(data->stream));
    o = pa_context_get_sink_info_by_name(data->context, data->device_name,
                                         sink_name_callback, device);
    wait_for_operation(o, data->loop);

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);

    pa_threaded_mainloop_unlock(data->loop);

    return ALC_NO_ERROR;
}
예제 #12
0
static ALCenum pulse_open_capture(ALCdevice *device, const ALCchar *device_name)
{
    const char *pulse_name = NULL;
    pa_stream_flags_t flags = 0;
    pa_channel_map chanmap;
    pulse_data *data;
    pa_operation *o;
    ALuint samples;

    if(device_name)
    {
        ALuint i;

        if(!allCaptureDevNameMap)
            probe_devices(AL_TRUE);

        for(i = 0; i < numCaptureDevNames; i++)
        {
            if(strcmp(device_name, allCaptureDevNameMap[i].name) == 0)
            {
                pulse_name = allCaptureDevNameMap[i].device_name;
                break;
            }
        }
        if(i == numCaptureDevNames)
            return ALC_INVALID_VALUE;
    }

    if(pulse_open(device) == ALC_FALSE)
        return ALC_INVALID_VALUE;

    data = device->ExtraData;
    pa_threaded_mainloop_lock(data->loop);

    data->spec.rate = device->Frequency;
    data->spec.channels = ChannelsFromDevFmt(device->FmtChans);

    switch(device->FmtType)
    {
    case DevFmtUByte:
        data->spec.format = PA_SAMPLE_U8;
        break;
    case DevFmtShort:
        data->spec.format = PA_SAMPLE_S16NE;
        break;
    case DevFmtInt:
        data->spec.format = PA_SAMPLE_S32NE;
        break;
    case DevFmtFloat:
        data->spec.format = PA_SAMPLE_FLOAT32NE;
        break;
    case DevFmtByte:
    case DevFmtUShort:
    case DevFmtUInt:
        ERR("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    if(pa_sample_spec_valid(&data->spec) == 0)
    {
        ERR("Invalid sample format\n");
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
    {
        ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }

    samples = device->UpdateSize * device->NumUpdates;
    samples = maxu(samples, 100 * device->Frequency / 1000);

    data->attr.minreq = -1;
    data->attr.prebuf = -1;
    data->attr.maxlength = samples * pa_frame_size(&data->spec);
    data->attr.tlength = -1;
    data->attr.fragsize = minu(samples, 50*device->Frequency/1000) *
                          pa_frame_size(&data->spec);

    flags |= PA_STREAM_START_CORKED|PA_STREAM_ADJUST_LATENCY;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    data->stream = connect_record_stream(pulse_name, data->loop, data->context,
                                         flags, &data->attr, &data->spec,
                                         &chanmap);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        goto fail;
    }
    pa_stream_set_state_callback(data->stream, stream_state_callback2, device);

    data->device_name = strdup(pa_stream_get_device_name(data->stream));
    o = pa_context_get_source_info_by_name(data->context, data->device_name,
                                           source_name_callback, device);
    wait_for_operation(o, data->loop);

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);

    pa_threaded_mainloop_unlock(data->loop);
    return ALC_NO_ERROR;

fail:
    pulse_close(device);
    return ALC_INVALID_VALUE;
}
예제 #13
0
static ALCboolean pulse_reset_playback(ALCdevice *device)
{
    pulse_data *data = device->ExtraData;
    pa_stream_flags_t flags = 0;
    pa_channel_map chanmap;
    ALuint len;

    pa_threaded_mainloop_lock(data->loop);

    if(data->stream)
    {
        pa_stream_set_moved_callback(data->stream, NULL, NULL);
#if PA_CHECK_VERSION(0,9,15)
        if(pa_stream_set_buffer_attr_callback)
            pa_stream_set_buffer_attr_callback(data->stream, NULL, NULL);
#endif
        pa_stream_disconnect(data->stream);
        pa_stream_unref(data->stream);
        data->stream = NULL;
    }

    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
    {
        pa_operation *o;
        o = pa_context_get_sink_info_by_name(data->context, data->device_name, sink_info_callback, device);
        wait_for_operation(o, data->loop);
    }
    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
        flags |= PA_STREAM_FIX_RATE;

    flags |= PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE;
    flags |= PA_STREAM_ADJUST_LATENCY;
    flags |= PA_STREAM_START_CORKED;
    if(!GetConfigValueBool("pulse", "allow-moves", 0))
        flags |= PA_STREAM_DONT_MOVE;

    switch(device->FmtType)
    {
    case DevFmtByte:
        device->FmtType = DevFmtUByte;
    /* fall-through */
    case DevFmtUByte:
        data->spec.format = PA_SAMPLE_U8;
        break;
    case DevFmtUShort:
        device->FmtType = DevFmtShort;
    /* fall-through */
    case DevFmtShort:
        data->spec.format = PA_SAMPLE_S16NE;
        break;
    case DevFmtUInt:
        device->FmtType = DevFmtInt;
    /* fall-through */
    case DevFmtInt:
        data->spec.format = PA_SAMPLE_S32NE;
        break;
    case DevFmtFloat:
        data->spec.format = PA_SAMPLE_FLOAT32NE;
        break;
    }
    data->spec.rate = device->Frequency;
    data->spec.channels = ChannelsFromDevFmt(device->FmtChans);

    if(pa_sample_spec_valid(&data->spec) == 0)
    {
        ERR("Invalid sample format\n");
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }

    if(!pa_channel_map_init_auto(&chanmap, data->spec.channels, PA_CHANNEL_MAP_WAVEEX))
    {
        ERR("Couldn't build map for channel count (%d)!\n", data->spec.channels);
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }
    SetDefaultWFXChannelOrder(device);

    data->attr.fragsize = -1;
    data->attr.prebuf = 0;
    data->attr.minreq = device->UpdateSize * pa_frame_size(&data->spec);
    data->attr.tlength = data->attr.minreq * maxu(device->NumUpdates, 2);
    data->attr.maxlength = -1;

    data->stream = connect_playback_stream(data->device_name, data->loop,
                                           data->context, flags, &data->attr,
                                           &data->spec, &chanmap);
    if(!data->stream)
    {
        pa_threaded_mainloop_unlock(data->loop);
        return ALC_FALSE;
    }
    pa_stream_set_state_callback(data->stream, stream_state_callback2, device);

    data->spec = *(pa_stream_get_sample_spec(data->stream));
    if(device->Frequency != data->spec.rate)
    {
        pa_operation *o;

        /* Server updated our playback rate, so modify the buffer attribs
         * accordingly. */
        device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates / device->Frequency *
                                      data->spec.rate + 0.5);
        data->attr.minreq  = device->UpdateSize * pa_frame_size(&data->spec);
        data->attr.tlength = data->attr.minreq * clampu(device->NumUpdates, 2, 16);
        data->attr.maxlength = -1;
        data->attr.prebuf  = 0;

        o = pa_stream_set_buffer_attr(data->stream, &data->attr,
                                      stream_success_callback, device);
        wait_for_operation(o, data->loop);

        device->Frequency = data->spec.rate;
    }

    pa_stream_set_moved_callback(data->stream, stream_moved_callback, device);
#if PA_CHECK_VERSION(0,9,15)
    if(pa_stream_set_buffer_attr_callback)
        pa_stream_set_buffer_attr_callback(data->stream, stream_buffer_attr_callback, device);
#endif
    stream_buffer_attr_callback(data->stream, device);

    len = data->attr.minreq / pa_frame_size(&data->spec);
    if((CPUCapFlags&CPU_CAP_SSE))
        len = (len+3)&~3;
    device->NumUpdates = (ALuint)((ALdouble)device->NumUpdates/len*device->UpdateSize + 0.5);
    device->NumUpdates = clampu(device->NumUpdates, 2, 16);
    device->UpdateSize = len;

    pa_threaded_mainloop_unlock(data->loop);
    return ALC_TRUE;
}
예제 #14
0
파일: alsa.c 프로젝트: 9heart/DT3
static ALCboolean alsa_reset_playback(ALCdevice *device)
{
    alsa_data *data = (alsa_data*)device->ExtraData;
    snd_pcm_uframes_t periodSizeInFrames;
    unsigned int periodLen, bufferLen;
    snd_pcm_sw_params_t *sp = NULL;
    snd_pcm_hw_params_t *p = NULL;
    snd_pcm_access_t access;
    snd_pcm_format_t format;
    unsigned int periods;
    unsigned int rate;
    int allowmmap;
    char *err;
    int i;


    format = -1;
    switch(device->FmtType)
    {
        case DevFmtByte:
            format = SND_PCM_FORMAT_S8;
            break;
        case DevFmtUByte:
            format = SND_PCM_FORMAT_U8;
            break;
        case DevFmtShort:
            format = SND_PCM_FORMAT_S16;
            break;
        case DevFmtUShort:
            format = SND_PCM_FORMAT_U16;
            break;
        case DevFmtFloat:
            format = SND_PCM_FORMAT_FLOAT;
            break;
    }

    allowmmap = GetConfigValueBool("alsa", "mmap", 1);
    periods = device->NumUpdates;
    periodLen = (ALuint64)device->UpdateSize * 1000000 / device->Frequency;
    bufferLen = periodLen * periods;
    rate = device->Frequency;

    err = NULL;
    snd_pcm_hw_params_malloc(&p);

    if((i=snd_pcm_hw_params_any(data->pcmHandle, p)) < 0)
        err = "any";
    /* set interleaved access */
    if(i >= 0 && (!allowmmap || (i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0))
    {
        if(periods > 2)
        {
            periods--;
            bufferLen = periodLen * periods;
        }
        if((i=snd_pcm_hw_params_set_access(data->pcmHandle, p, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
            err = "set access";
    }
    /* set format (implicitly sets sample bits) */
    if(i >= 0 && (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, format)) < 0)
    {
        device->FmtType = DevFmtFloat;
        if(format == SND_PCM_FORMAT_FLOAT ||
           (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_FLOAT)) < 0)
        {
            device->FmtType = DevFmtShort;
            if(format == SND_PCM_FORMAT_S16 ||
               (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_S16)) < 0)
            {
                device->FmtType = DevFmtUByte;
                if(format == SND_PCM_FORMAT_U8 ||
                   (i=snd_pcm_hw_params_set_format(data->pcmHandle, p, SND_PCM_FORMAT_U8)) < 0)
                    err = "set format";
            }
        }
    }
    /* set channels (implicitly sets frame bits) */
    if(i >= 0 && (i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, ChannelsFromDevFmt(device->FmtChans))) < 0)
    {
        if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 2)) < 0)
        {
            if((i=snd_pcm_hw_params_set_channels(data->pcmHandle, p, 1)) < 0)
                err = "set channels";
            else
                device->FmtChans = DevFmtMono;
        }
        else
            device->FmtChans = DevFmtStereo;
    }
    if(i >= 0 && (i=snd_pcm_hw_params_set_rate_resample(data->pcmHandle, p, 0)) < 0)
    {
        ERR("Failed to disable ALSA resampler\n");
        i = 0;
    }
    /* set rate (implicitly constrains period/buffer parameters) */
    if(i >= 0 && (i=snd_pcm_hw_params_set_rate_near(data->pcmHandle, p, &rate, NULL)) < 0)
        err = "set rate near";
    /* set buffer time (implicitly constrains period/buffer parameters) */
    if(i >= 0 && (i=snd_pcm_hw_params_set_buffer_time_near(data->pcmHandle, p, &bufferLen, NULL)) < 0)
        err = "set buffer time near";
    /* set period time in frame units (implicitly sets buffer size/bytes/time and period size/bytes) */
    if(i >= 0 && (i=snd_pcm_hw_params_set_period_time_near(data->pcmHandle, p, &periodLen, NULL)) < 0)
        err = "set period time near";
    /* install and prepare hardware configuration */
    if(i >= 0 && (i=snd_pcm_hw_params(data->pcmHandle, p)) < 0)
        err = "set params";
    if(i >= 0 && (i=snd_pcm_hw_params_get_access(p, &access)) < 0)
        err = "get access";
    if(i >= 0 && (i=snd_pcm_hw_params_get_period_size(p, &periodSizeInFrames, NULL)) < 0)
        err = "get period size";
    if(i >= 0 && (i=snd_pcm_hw_params_get_periods(p, &periods, NULL)) < 0)
        err = "get periods";
    if(i < 0)
    {
        ERR("%s failed: %s\n", err, snd_strerror(i));
        snd_pcm_hw_params_free(p);
        return ALC_FALSE;
    }

    snd_pcm_hw_params_free(p);

    err = NULL;
    snd_pcm_sw_params_malloc(&sp);

    if((i=snd_pcm_sw_params_current(data->pcmHandle, sp)) != 0)
        err = "sw current";
    if(i == 0 && (i=snd_pcm_sw_params_set_avail_min(data->pcmHandle, sp, periodSizeInFrames)) != 0)
        err = "sw set avail min";
    if(i == 0 && (i=snd_pcm_sw_params_set_stop_threshold(data->pcmHandle, sp, periodSizeInFrames*periods)) != 0)
        err = "sw set stop threshold";
    if(i == 0 && (i=snd_pcm_sw_params(data->pcmHandle, sp)) != 0)
        err = "sw set params";
    if(i != 0)
    {
        ERR("%s failed: %s\n", err, snd_strerror(i));
        snd_pcm_sw_params_free(sp);
        return ALC_FALSE;
    }

    snd_pcm_sw_params_free(sp);

    device->Frequency = rate;
    SetDefaultChannelOrder(device);

    data->size = snd_pcm_frames_to_bytes(data->pcmHandle, periodSizeInFrames);
    if(access == SND_PCM_ACCESS_RW_INTERLEAVED)
    {
        /* Increase periods by one, since the temp buffer counts as an extra
         * period */
        periods++;
        data->buffer = malloc(data->size);
        if(!data->buffer)
        {
            ERR("buffer malloc failed\n");
            return ALC_FALSE;
        }
        device->UpdateSize = periodSizeInFrames;
        device->NumUpdates = periods;
        data->thread = StartThread(ALSANoMMapProc, device);
    }
    else
    {
        i = snd_pcm_prepare(data->pcmHandle);
        if(i < 0)
        {
            ERR("prepare error: %s\n", snd_strerror(i));
            return ALC_FALSE;
        }
        device->UpdateSize = periodSizeInFrames;
        device->NumUpdates = periods;
        data->thread = StartThread(ALSAProc, device);
    }
    if(data->thread == NULL)
    {
        ERR("Could not create playback thread\n");
        free(data->buffer);
        data->buffer = NULL;
        return ALC_FALSE;
    }

    return ALC_TRUE;
}