Igor Kovalenko <kovalenko@home.com> wrote:
Okay I have PCM part of Maestro working too, more or less.
Now I really need to know what to do with subchannels. Do I have to
create ‘subchannel mixers’ if I allow multiple acquires? Docs say that
‘volume set’ and ‘mute set’ functions of ‘subchannel mixer’ are
optional, what is purpose of the subchannel mixer then? What do I get by
creating it? How does it appear on user side (with and without those
optional funcs)?
Subchannel mixers are simply mixer groups that are associated directly with
a particular subchannel. They are visible to the user through the
snd_pcm_channel_info() call in the mixer_gid member. Note: You must call
snd_pcm_channel_info() after you have configured the channel with a call
to snd_pcm_channel_params(). If you call it before, the subchannel has
not been allocated yet, so you get the next best thing, the PCM mixer
group. This is expected to be the most commonly accessed control in the
mixer (from an API point of view). Most application writers are writing
players that want a single control for their application only. That is
what the subchannel mixer group is intended for. It allows independent
controls for each application.
Implementation of subchannel mixers is recommended, but completely
optional. If they do not exist, the lib will pass on the next best
thing instead.
Here is a short section of code demonstrating how to setup a subchannel
mixer. It is for a fictional card called “Zone” (name changed to protect
the guilty . Note that the volume and mute functions have the same
prototype. That is by design. You can have a single function, or two
as demonstrated here.
static void
zone_subchn_volume_set (HW_CONTEXT_T * zone,
PCM_SUBCHN_CONTEXT_T * zsc,
int32_t * volumes,
int32_t mute,
ado_pcm_subchn_mixer_config_t * config)
{
zone_voice_t *leftvoice, *rightvoice;
uint32_t value;
leftvoice = zsc->voice[0];
if (zsc->config->format.voices == 1)
rightvoice = zsc->voice[0];
else
rightvoice = zsc->voice[1];
zone_readvalue (zone, leftvoice->num, VOL, &value);
value &= 0xff00;
zone_writevalue (zone,
leftvoice->num, VOL, volumes[0] | value);
zone_readvalue (zone, rightvoice->num, RIGHT_VOL, &value);
value &= 0xff00;
zone_writevalue (zone,
rightvoice->num, VOL, volumes[1] | value);
}
static void
zone_subchn_mute_set ( HW_CONTEXT_T * zone,
PCM_SUBCHN_CONTEXT_T * zsc,
int32_t * volumes,
int32_t mute,
ado_pcm_subchn_mixer_config_t * config)
{
zone_voice_t *leftvoice, *rightvoice;
uint32_t value;
leftvoice = zsc->voice[0];
if (zsc->config->format.voices == 1)
rightvoice = zsc->voice[0];
else
rightvoice = zsc->voice[1];
zone_readvalue (zone, leftvoice->num, VOL, &value);
value &= 0x00ff;
zone_writevalue (zone,
leftvoice->num, VOL, (mute & (1 << 0)) ? 0x8000 : 0x0000 | value);
zone_readvalue (zone, rightvoice->num, RIGHT_VOL, &value);
value &= 0x00ff;
zone_writevalue (zone,
rightvoice->num, VOL, (mute & (1 << 1)) ? 0x8000 : 0x0000 | value);
}
int32_t
zone_playback_aquire (HW_CONTEXT_T * zone,
PCM_SUBCHN_CONTEXT_T ** zsc,
ado_pcm_config_t * config,
ado_pcm_subchn_t * subchn,
uint32_t * why_failed)
{
ado_pcm_subchn_mixer_config_t zsc_mix_cfg;
ado_mutex_lock (&zone->hw_lock);
…
memset (&zsc_mix_cfg, 0, sizeof (zsc_mix_cfg));
zsc_mix_cfg.hw_context = zone;
zsc_mix_cfg.pcm_sc_context = *zsc;
zsc_mix_cfg.channel_mask = SND_MIXER_CHN_MASK_STEREO;
zsc_mix_cfg.volume_range.min = 0;
zsc_mix_cfg.volume_range.max = 0xFF;
zsc_mix_cfg.volume_range.min_dB = -10235;
zsc_mix_cfg.volume_range.max_dB = 0;
zsc_mix_cfg.volume_set = zone_subchn_volume_set;
zsc_mix_cfg.mute_set = zone_subchn_mute_set;
if (((*zsc)->scmix =
ado_pcm_subchn_mixer_create (subchn,
zone->mixer,
&zsc_mix_cfg)) == NULL)
{
…
ado_mutex_unlock (&zone->hw_lock);
return (ENOMEM);
}
ado_mutex_unlock (&zone->hw_lock);
return (EOK);
}
Also, template driver suggests to use ado_mutex_lock in the interrupt
handler, but ado_mutex_lock is wrapper for pthread_mutex_lock which is
nointerrupt-safe. I tried and easily got myself a MUTEX deadlock between
2 threads of io-audio.I guess I should use InterruprLock, right?
Yes, if the template used a real ISR directly then you would need to use
InterruptLock/Unlock().
It does not use a real ISR.
ado_attach_interrupt() uses InterruptAttachEvent(), not InterruptAttach().
template_interrupt() is run in a standard thread, not an ISR. So the
interrupt-safety rules do not apply.
Do a pidin while running a driver and you can see the thread waiting on
interrupts. It has a STATE of INTR. Which means it has called
InterruptWait() and not received an interrupt signal yet.
If you want us to take a peek at your code and offer suggestions, let
use know.