好的,最后一个答案并不是真正的答案 - 这是一个更好的尝试 -
为了让其他人免于我过去几天的头痛,
这是更正后的“pwfx”waveOutOpen 参数:
const WAVEFORMATEXTENSIBLE
_wex_ext_ =
{ .Format =
{ .wFormatTag = WAVE_FORMAT_EXTENSIBLE
, .nChannels = 2
, .nSamplesPerSec = 8000
, .nAvgBytesPerSec= 32000
, .nBlockAlign = 4
, .wBitsPerSample = 16
, .cbSize = sizeof(WAVEFORMATEXTENSIBLE) -
sizeof(WAVEFORMATEX)
}
, .Samples = {16}
, .dwChannelMask = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
, .SubFormat = DEFINE_WAVEFORMATEX_GUID(WAVE_FORMAT_PCM)
};
因此,如果将其用作 'pwfx' 指向的结构:
switch
( r =
waveOutOpen
( &aoh // Audio Output Handle
, WAVE_MAPPER
, ((WAVEFORMATEX*)&_wex_ext_)
, ((DWORD_PTR)waveOutProc) // Callback function on WOM_DONE
, ((DWORD_PTR)0UL) // no user paramet
, WAVE_FORMAT_DIRECT | CALLBACK_FUNCTION // do not modify audio data
)
)
{case MMSYSERR_NOERROR:
break;
case MMSYSERR_ALLOCATED:
ok = false;
emsg = "Specified resource is already allocated.";
break;
case MMSYSERR_BADDEVICEID:
ok = false;
emsg = "Specified device identifier is out of range.";
break;
case MMSYSERR_NODRIVER:
ok = false;
emsg = "No device driver is present.";
break;
case MMSYSERR_NOMEM:
ok = false;
emsg = "Unable to allocate or lock memory.";
break;
case WAVERR_BADFORMAT:
ok = false;
emsg = "Attempted to open with an unsupported waveform-audio format.";
break;
case WAVERR_SYNC:
ok = false;
emsg = "The device is synchronous but waveOutOpen was called without using the WAVE_ALLOWSYNC flag.";
break;
default:
ok = false;
if ( 0 == waveOutGetErrorText(r, ((u8_t*) &pcm[0].l[0]), 1024 ))
{ u16_t len = 1024;
s8_t *str = ((s8_t*)&pcm[0].l[0]);
utf8str( ((u16_t*)&pcm[0].l[0]), wcslen((U16_t*)&pcm[0].l[0]), &str, &len);
emsg = ((const char*) &pcm[0].l[0]);
}
break;
}
那么2个进程播放的两种SINGLE CHANNEL格式之一:
Bit:15
Bit: 31: | Bit: 0:
<16-bit LE PCM L>| 0 ... 0 # all zeros, OR:
Bit:15
Bit: 31: | Bit: 0:
0 ... 0| <16-bit LE PCM R> |
工作正常,混合后“PCM L”在左声道播放,PCM R
在右频道播放,
但我没有成功使用单个进程玩这个:
Bit:15
Bit: 31: | Bit: 0:
<1| <16-bit LE PCM L> | <16-bit LE PCM R>
在 Linux 上使用 ALSA (alsa-lib) 播放流时也会发生同样的情况 -
所以它不是 Windows 的东西。
在 Linux 上,使用 ALSA 的 S16_LE 格式,
我曾尝试在一个过程中播放相同的流,例如:
Bit:15
Bit: 31: | Bit: 0:
<16-bit LE PCM L>| 0 ... 0 # all zeros
0 ... 0| <16-bit LE PCM R>
我也试过了:
Bit:15
Bit: 31: | Bit: 0:
0 ... 0| <16-bit LE PCM L>|
0 ... 0| <16-bit LE PCM R>|
和:
Bit:15
Bit: 31: | Bit: 0:
<16-bit LE PCM L>| 0 ... 0|
<16-bit LE PCM R>| 0 ... 0|
但是这些也不起作用(流中充满了点击和故障
并且信号似乎在左右交替之间几乎没有
听得见)。当然,如果我指定了
S16_LE 格式,需要 16 位帧,我正在写 32 位
帧?
所以,我真的不知道确切的交错字节格式是什么
应该是。
在 Windows 上,执行交错的代码如下所示:
typedef struct pcm_frame_s
{ U16_t l[320], r[320], lr[640];
} PCMFrame_t;
static
PCMFrame_t pcm[16] = {0};
...
// pcmA & pcmB are pointers to pcm[i].l[0] & pcm[i].r[0] :
if ( pcmA && pcmB )
{ register U8_t
np;
register U16_t
*ppcm =&pcm[i].lr[0];
for( np = 0
, pcmA=&pcm[i].l[0]
, pcmB=&pcm[i].r[0]
; np < 160
; ++np, ++ppcm, ++pcmA, ++pcmB
)
{ *ppcm = *pcmA ? *pcmA : pcm_slnc[np];
ppcm += 1;
*ppcm = *pcmB ? *pcmB : pcm_slnc[np];
} // pcm_slnc is a pre-computed "Comfort Noise" block
}else
{ register U8_t
np;
register U16_t
*ppcm = &pcm[i].lr[0]
, *pspcm = pcmA ? pcmA : pcmB;
if(left) // user has chosen to put 1st stream on left channel
{
for(np=0; np < 160; ++np, ++ppcm, ++pspcm)
{ *ppcm = *pspcm;
ppcm += 1;
*ppcm = pcm_slnc[np];
}
} else
{
for(np=0; np < 160; ++np, ++ppcm, ++pspcm)
{ *ppcm = pcm_slnc[np];
ppcm += 1;
*ppcm=*pspcm;
}
}
}
while ( (WHDR_PREPARED | WHDR_DONE)
!= (wh[i].dwFlags & (WHDR_PREPARED | WHDR_DONE))
)
{ if (!WaitOnAddress
( &(wh[i].dwFlags)
, &playingDwFlags
, sizeof(u32_t)
, INFINITE
)
)
{ say(FMT("WaitOnAddress failed in INFINITE mode."));
ok = false;
break;
}
}
wh[i].dwFlags &= ~WHDR_DONE;
switch
( r = waveOutWrite(aoh, &wh[i], sizeof(wh[i])) )
{case MMSYSERR_NOERROR:
...
我认为我必须指定每个通道 32 位,并使用 32 位帧大小?
所以我无法将两个 16 位左交错 |右声道
PCM 值彼此相邻而不使用 32 位流格式?
我即将放弃,只使用 2 个进程和
混合器,奇怪的是它尊重“|0”(左进程)和
"0|"(右处理)(左|右)通道格式
并按预期将它们发送到左/右扬声器。
但是有没有办法在一个 32 位字中指定两个 16 位通道?
奇怪。