Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CDROM: Add support for enabling/disabling channels within the audio ports. #233

Merged
merged 1 commit into from Jun 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 26 additions & 3 deletions lib/ZuluSCSI_platform_RP2040/audio.cpp
Expand Up @@ -145,6 +145,10 @@ static volatile uint16_t volumes[8] = {
DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL,
DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL, DEFAULT_VOLUME_LEVEL
};
static volatile uint16_t channels[8] = {
AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK,
AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK, AUDIO_CHANNEL_ENABLE_MASK
};

// mechanism for cleanly stopping DMA units
static volatile bool audio_stopping = false;
Expand All @@ -165,10 +169,17 @@ static uint8_t invert = 0; // biphase encode help: set if last wire bit was '1'
*/
static void snd_encode(uint8_t* samples, uint16_t* wire_patterns, uint16_t len, uint8_t swap) {
uint16_t wvol = volumes[audio_owner & 7];
uint8_t vol = ((wvol >> 8) + (wvol & 0xFF)) >> 1; // average of both values
uint8_t lvol = ((wvol >> 8) + (wvol & 0xFF)) >> 1; // average of both values
// limit maximum volume; with my DACs I've had persistent issues
// with signal clipping when sending data in the highest bit position
vol = vol >> 2;
lvol = lvol >> 2;
uint8_t rvol = lvol;
// enable or disable based on the channel information for both output
// ports, where the high byte and mask control the right channel, and
// the low control the left channel
uint16_t chn = channels[audio_owner & 7] & AUDIO_CHANNEL_ENABLE_MASK;
if (!(chn >> 8)) rvol = 0;
if (!(chn & 0xFF)) lvol = 0;

uint16_t widx = 0;
for (uint16_t i = 0; i < len; i += 2) {
Expand All @@ -182,7 +193,11 @@ static void snd_encode(uint8_t* samples, uint16_t* wire_patterns, uint16_t len,
rsamp = (int16_t)(samples[i] + (samples[i + 1] << 8));
}
// linear scale to requested audio value
rsamp *= vol;
if (i & 2) {
rsamp *= rvol;
} else {
rsamp *= lvol;
}
// use 20 bits of value only, which allows ignoring the lowest 8
// bits during biphase conversion (after including sample shift)
sample = ((uint32_t)rsamp) & 0xFFFFF0;
Expand Down Expand Up @@ -562,4 +577,12 @@ void audio_set_volume(uint8_t id, uint16_t vol) {
volumes[id & 7] = vol;
}

uint16_t audio_get_channel(uint8_t id) {
return channels[id & 7];
}

void audio_set_channel(uint8_t id, uint16_t chn) {
channels[id & 7] = chn;
}

#endif // ENABLE_AUDIO_OUTPUT
27 changes: 27 additions & 0 deletions src/ZuluSCSI_audio.h
Expand Up @@ -34,6 +34,13 @@
* for port 0. The two values are averaged to determine final volume level.
*/
#define DEFAULT_VOLUME_LEVEL 0x3F3F
/*
* Defines the 'enable' masks for the two audio output ports of each device.
* If this mask is matched with audio_get_channel() the relevant port will
* have audio output to it, otherwise it will be muted, regardless of the
* volume level.
*/
#define AUDIO_CHANNEL_ENABLE_MASK 0x0201

/*
* Status codes for audio playback, matching the SCSI 'audio status codes'.
Expand Down Expand Up @@ -117,3 +124,23 @@ uint16_t audio_get_volume(uint8_t id);
* \param vol The new volume level.
*/
void audio_set_volume(uint8_t id, uint16_t vol);

/**
* Gets the 0x0E channel information for both audio ports. The high byte
* corresponds to port 1 and the low byte to port 0. If the bits defined in
* AUDIO_CHANNEL_ENABLE_MASK are not set for the respective ports, that
* output will be muted, regardless of volume set.
*
* \param id SCSI ID to provide channel information for.
* \return The channel information.
*/
uint16_t audio_get_channel(uint8_t id);

/**
* Sets the 0x0E channel information for a target, as above. See 0x0E mode
* page for more.
*
* \param id SCSI ID to set channel information for.
* \param chn The new channel information.
*/
void audio_set_channel(uint8_t id, uint16_t chn);
13 changes: 11 additions & 2 deletions src/ZuluSCSI_mode.cpp
Expand Up @@ -107,23 +107,30 @@ int modeSenseCDAudioControlPage(int pc, int idx, int pageCode, int* pageFound)
sizeof(CDROMAudioControlParametersPage));
if (pc == 0x00)
{
// report current volume level
// report current port assignments and volume level
uint16_t chn = audio_get_channel(scsiDev.target->targetId);
uint16_t vol = audio_get_volume(scsiDev.target->targetId);
scsiDev.data[idx+8] = chn & 0xFF;
scsiDev.data[idx+9] = vol & 0xFF;
scsiDev.data[idx+10] = chn >> 8;
scsiDev.data[idx+11] = vol >> 8;
}
else if (pc == 0x01)
{
// report bits that can be set
scsiDev.data[idx+8] = 0xFF;
scsiDev.data[idx+9] = 0xFF;
scsiDev.data[idx+10] = 0xFF;
scsiDev.data[idx+11] = 0xFF;
}
else
{
// report defaults for 0x02
// also report same for 0x03, though we are actually supposed
// to terminate with CHECK CONDITION and SAVING PARAMETERS NOT SUPPORTED
scsiDev.data[idx+8] = AUDIO_CHANNEL_ENABLE_MASK & 0xFF;
scsiDev.data[idx+9] = DEFAULT_VOLUME_LEVEL & 0xFF;
scsiDev.data[idx+10] = AUDIO_CHANNEL_ENABLE_MASK >> 8;
scsiDev.data[idx+11] = DEFAULT_VOLUME_LEVEL >> 8;
}
return sizeof(CDROMAudioControlParametersPage);
Expand All @@ -144,8 +151,10 @@ int modeSelectCDAudioControlPage(int pageLen, int idx)
if (scsiDev.target->cfg->deviceType == S2S_CFG_OPTICAL)
{
if (pageLen != 0x0E) return 0;
uint16_t chn = (scsiDev.data[idx+10] << 8) + scsiDev.data[idx+8];
uint16_t vol = (scsiDev.data[idx+11] << 8) + scsiDev.data[idx+9];
dbgmsg("------ CD audio control page volume (", vol, ")");
dbgmsg("------ CD audio control page channels (", chn, "), volume (", vol, ")");
audio_set_channel(scsiDev.target->targetId, chn);
audio_set_volume(scsiDev.target->targetId, vol);
return 1;
}
Expand Down