Skip to content

Commit

Permalink
Merge pull request #233 from saybur/port-output-bug
Browse files Browse the repository at this point in the history
CDROM: Add support for enabling/disabling channels within the audio ports.
  • Loading branch information
aperezbios committed Jun 3, 2023
2 parents d360c42 + 5cdd1e1 commit 995f043
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 5 deletions.
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

0 comments on commit 995f043

Please sign in to comment.