Hello All,
Is it possible to avoid clicks during DSD playback?
I mean the clicks at the beginning and at the end of playback (so PCM->DSD and DSD-PCM transition).
Clicks are presents as in DoP, as also in Native mode.
I've tried to use mute line, to short the DAC's output to ground, but click appeared before the mute is activsted, so this did not help.
Thank you.
USB Audio Reference Design - click in DSD
-
- XCore Addict
- Posts: 150
- Joined: Sun Feb 23, 2014 11:30 am
-
Verified
- XCore Legend
- Posts: 1156
- Joined: Thu May 27, 2010 10:08 am
It can be done, but it needs a little thought and work.
Native DSD is easier becasue the host signals a change in interface (USB interface) which allows the firmware to identify stream start/stop. This means mute code can be applied when changing modes outside of streaming. There should be hooks for this alread (UserAudioStreamStart/UserAudioStreamStop)
DoP is trickier. Of course DoP is transported as a PCM stream. It's only at the I2S driver layer that the DoP markers are detected. When 32 consequtive markers are detected, then I2S changes the DAC mode and starts extracting and transferring DSD to the DAC. This means track or stream starts of DoP will always have 32 PCM samples followed by PCM to DSD mode change. You may also get a pop at the end, especially if the host decides to stream 0s after playing the track, which of course are not DoP samples.
The key is to assert hardware mute before any mode changes happen. This can be done in the following code at the point where dsdMode = ... is executed.
Ultimately, this will all depend on your hardware (how well/quickly it can assert mute) and where you apply the mute code. If you do not have mute hardware it is harder. It can be done, but I know that certain DACs (eg. ESS) are a lot easier to make behave quietly on mode changes than others.
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
/* Check for DSD - note we only move into DoP mode if valid DoP Freq */
/* Currently we only check on channel 0 - we get all 0's on channels without data */
if((dsdMode == DSD_MODE_OFF) && (curSamFreq > 96000))
{
if((DSD_MASK(samplesOut[0]) == dsdMarker) && (DSD_MASK(samplesOut[1]) == dsdMarker))
{
dsdCount++;
dsdMarker ^= DSD_MARKER_XOR;
if(dsdCount == DSD_EN_THRESH)
{
dsdMode = DSD_MODE_DOP;
dsdCount = 0;
dsdMarker = DSD_MARKER_2;
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
p_dsd_clk <: 0;
return 0;
}
}
else
{
dsdCount = 0;
dsdMarker = DSD_MARKER_2;
}
}
else if(dsdMode == DSD_MODE_DOP) // DSD DoP Mode
{
/* If we are running in DOP mode, check if we need to come out */
if((DSD_MASK(samplesOut[0]) != DSD_MARKER_1) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_1))
{
if((DSD_MASK(samplesOut[0]) != DSD_MARKER_2) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_2))
{
dsdMode = DSD_MODE_OFF;
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
p_dsd_clk <: 0;
return 0;
}
}
}
#endif
Native DSD is easier becasue the host signals a change in interface (USB interface) which allows the firmware to identify stream start/stop. This means mute code can be applied when changing modes outside of streaming. There should be hooks for this alread (UserAudioStreamStart/UserAudioStreamStop)
DoP is trickier. Of course DoP is transported as a PCM stream. It's only at the I2S driver layer that the DoP markers are detected. When 32 consequtive markers are detected, then I2S changes the DAC mode and starts extracting and transferring DSD to the DAC. This means track or stream starts of DoP will always have 32 PCM samples followed by PCM to DSD mode change. You may also get a pop at the end, especially if the host decides to stream 0s after playing the track, which of course are not DoP samples.
The key is to assert hardware mute before any mode changes happen. This can be done in the following code at the point where dsdMode = ... is executed.
Ultimately, this will all depend on your hardware (how well/quickly it can assert mute) and where you apply the mute code. If you do not have mute hardware it is harder. It can be done, but I know that certain DACs (eg. ESS) are a lot easier to make behave quietly on mode changes than others.
#if (DSD_CHANS_DAC != 0) && (NUM_USB_CHAN_OUT > 0)
/* Check for DSD - note we only move into DoP mode if valid DoP Freq */
/* Currently we only check on channel 0 - we get all 0's on channels without data */
if((dsdMode == DSD_MODE_OFF) && (curSamFreq > 96000))
{
if((DSD_MASK(samplesOut[0]) == dsdMarker) && (DSD_MASK(samplesOut[1]) == dsdMarker))
{
dsdCount++;
dsdMarker ^= DSD_MARKER_XOR;
if(dsdCount == DSD_EN_THRESH)
{
dsdMode = DSD_MODE_DOP;
dsdCount = 0;
dsdMarker = DSD_MARKER_2;
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
p_dsd_clk <: 0;
return 0;
}
}
else
{
dsdCount = 0;
dsdMarker = DSD_MARKER_2;
}
}
else if(dsdMode == DSD_MODE_DOP) // DSD DoP Mode
{
/* If we are running in DOP mode, check if we need to come out */
if((DSD_MASK(samplesOut[0]) != DSD_MARKER_1) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_1))
{
if((DSD_MASK(samplesOut[0]) != DSD_MARKER_2) && (DSD_MASK(samplesOut[1]) != DSD_MARKER_2))
{
dsdMode = DSD_MODE_OFF;
// Set clocks low
p_lrclk <: 0;
p_bclk <: 0;
p_dsd_clk <: 0;
return 0;
}
}
}
#endif
Engineer at XMOS
-
- XCore Addict
- Posts: 150
- Joined: Sun Feb 23, 2014 11:30 am
Thank you for the reply.
I'm making the "mute" at audiostream.xc:
void UserAudioStreamStart(void)
{
MuteAudio(0);
}
void UserAudioStreamStop(void)
{
MuteAudio(1);
}
also at audiohw.xc I make:
void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode,
unsigned samRes_DAC, unsigned samRes_ADC)
{
timer t;
unsigned time;
unsigned tmp;
unsigned freq;
/* Put codec in reset and set master clock select appropriately */
/* Read current port output */
PORT32A_PEEK(tmp);
tmp &= (~ ( P32A_F0 | P32A_F1)); // clear these bits
MuteAudio(1);
if(dsdMode!=DSD_MODE_OFF){ // DSD Mode -------------------------------------------------------------------------
DSD_PIN <: DSD_ON;
if(dsdMode == DSD_MODE_NATIVE){
tmp |=P32A_F1; // Native Mode
if (samFreq > 6000000) tmp |=P32A_F0; // // DSD128
}else{
if (samFreq > 3000000) tmp |=P32A_F0; // > 3MHz e.g. 2.2822400 MHz */) // DSD128
}
}else{// PCM Mode -------------------------------------------------------------------------
DSD_PIN <:DSD_OFF;
if ((samFreq % 22050) == 0){/* Frequency select low for 441000 etc */
tmp &= (~P32A_CLK_SEL);
freq= samFreq /44100;
}else{ //if((samFreq % 24000) == 0) /* Frequency select high for 48000 etc */
tmp |= P32A_CLK_SEL;
freq= samFreq /48000;
}
PORT32A_OUT(tmp);
/* Hold in reset for MIN_RESET_TIME */
t :> time;
time += MIN_RESET_TIME;
t when timerafter(time) :> int _;
if(dsdMode!=DSD_MODE_OFF){ // tmp: For DSD set Mute Off here, for PCM - at UserAudioStreamStart() in audiostream.xc
MuteAudio(0);
}
}
But I get a huge click at the beginning and at the end of DSD playback.
I'm making the "mute" at audiostream.xc:
void UserAudioStreamStart(void)
{
MuteAudio(0);
}
void UserAudioStreamStop(void)
{
MuteAudio(1);
}
also at audiohw.xc I make:
void AudioHwConfig(unsigned samFreq, unsigned mClk, chanend ?c_codec, unsigned dsdMode,
unsigned samRes_DAC, unsigned samRes_ADC)
{
timer t;
unsigned time;
unsigned tmp;
unsigned freq;
/* Put codec in reset and set master clock select appropriately */
/* Read current port output */
PORT32A_PEEK(tmp);
tmp &= (~ ( P32A_F0 | P32A_F1)); // clear these bits
MuteAudio(1);
if(dsdMode!=DSD_MODE_OFF){ // DSD Mode -------------------------------------------------------------------------
DSD_PIN <: DSD_ON;
if(dsdMode == DSD_MODE_NATIVE){
tmp |=P32A_F1; // Native Mode
if (samFreq > 6000000) tmp |=P32A_F0; // // DSD128
}else{
if (samFreq > 3000000) tmp |=P32A_F0; // > 3MHz e.g. 2.2822400 MHz */) // DSD128
}
}else{// PCM Mode -------------------------------------------------------------------------
DSD_PIN <:DSD_OFF;
if ((samFreq % 22050) == 0){/* Frequency select low for 441000 etc */
tmp &= (~P32A_CLK_SEL);
freq= samFreq /44100;
}else{ //if((samFreq % 24000) == 0) /* Frequency select high for 48000 etc */
tmp |= P32A_CLK_SEL;
freq= samFreq /48000;
}
PORT32A_OUT(tmp);
/* Hold in reset for MIN_RESET_TIME */
t :> time;
time += MIN_RESET_TIME;
t when timerafter(time) :> int _;
if(dsdMode!=DSD_MODE_OFF){ // tmp: For DSD set Mute Off here, for PCM - at UserAudioStreamStart() in audiostream.xc
MuteAudio(0);
}
}
But I get a huge click at the beginning and at the end of DSD playback.