MODULE i810Sound;
IMPORT SYSTEM, PCI, Machine, Kernel, Objects, Modules, Plugins,
SoundDevices, KernelLog;
CONST
PACRegAC97CmdData = 0060H;
PACRegAC97CmdAddress = 0062H;
PACRegAC97StatusData = 0064H;
PACRegAC97StatusAddress = 0066H;
MasterVolume = 225;
InitVolume = 128;
BufferSize = 08FF0H;
NofMixerChannels = 6;
Boost = TRUE;
InsecureMode = TRUE;
StatePlaying = 1;
StateRecording = 2;
StatePaused = 3;
StateStoped = 4;
StateClosed = 5;
BMRInBase = 00H;
BMROutBase = 10H;
BMRBufferDescriptorBaseAddress = 00H;
BMRCurrentIndexReg = 04H;
BMRLastValidIndexReg = 05H;
BMRStatusReg = 06H;
BMRCtrlReg = 0BH;
BMRGlobalStatusReg = 30H;
AC97MasterVolReg = 02H;
AC97AuxVolReg = 04H;
AC97MonoVolReg = 06H;
AC97MicVolReg = 0EH;
AC97LineInVolReg = 10H;
AC97CDVolReg = 12H;
AC97PCMOutVolReg = 18H;
AC97RecordGainReg = 1CH;
AC97ResetReg = 0;
AC97RecordSelectReg = 1AH;
AC97DacRateReg = 2CH;
AC97AdcRateReg = 32H;
AC97StatCtrlReg = 2AH;
DebugBaseAddress = 0;
DebugDMAAddess = 1;
TraceOpenChannel = 2;
DebugTraceListeners = 3;
DebugInit = 4;
Debug = {DebugBaseAddress, DebugDMAAddess, TraceOpenChannel, DebugTraceListeners, DebugInit };
TYPE
PlayChannelList = POINTER TO RECORD
channel : PlayChannel;
next : PlayChannelList;
END;
RecChannelList = POINTER TO RECORD
channel : RecChannel;
next : RecChannelList;
END;
MixerChangedProcList = POINTER TO RECORD
mixerChangedProc : SoundDevices.MixerChangedProc;
next : MixerChangedProcList;
END;
BufferList = POINTER TO RECORD
buffer : SoundDevices.Buffer;
next : BufferList;
END;
Sample = ARRAY 4 OF CHAR;
DriverList = POINTER TO RECORD
driver : Driver;
next : DriverList;
END;
DMABufferDescriptorList = POINTER TO ARRAY 32 OF RECORD
addr : LONGINT;
CmdAndLength : LONGINT;
END;
PCIAudioControl = OBJECT
VAR
base: LONGINT;
PROCEDURE &Constr*(base : LONGINT);
BEGIN
SELF.base := base
END Constr;
PROCEDURE RegRead8(offset : LONGINT) : SHORTINT;
VAR
res : CHAR;
BEGIN
Machine.Portin8(base + offset, res);
RETURN SYSTEM.VAL(SHORTINT, res)
END RegRead8;
PROCEDURE RegRead16(offset : LONGINT) : INTEGER;
VAR
res : INTEGER;
BEGIN
Machine.Portin16(base + offset, res);
RETURN SYSTEM.VAL(INTEGER, res)
END RegRead16;
PROCEDURE RegRead32(offset : LONGINT) : LONGINT;
VAR
res : LONGINT;
BEGIN
Machine.Portin32(base + offset, res);
RETURN res
END RegRead32;
PROCEDURE RegWrite8(offset : LONGINT; val : LONGINT);
BEGIN
Machine.Portout8(base + offset, SYSTEM.VAL(CHAR, val))
END RegWrite8;
PROCEDURE RegWrite16(offset : LONGINT; val : LONGINT);
BEGIN
Machine.Portout16(base + offset, SYSTEM.VAL(INTEGER, val))
END RegWrite16;
PROCEDURE RegWrite32(offset : LONGINT; val : LONGINT);
BEGIN
Machine.Portout32(base + offset, val)
END RegWrite32;
END PCIAudioControl;
AC97Control = OBJECT
VAR
NAMBAR, NABMBAR: PCIAudioControl;
PROCEDURE &Constr*(NAMBAR, NABMBAR : PCIAudioControl);
BEGIN
SELF.NAMBAR := NAMBAR;
SELF.NABMBAR := NABMBAR
END Constr;
PROCEDURE RegRead16(offset : LONGINT): LONGINT;
BEGIN{EXCLUSIVE}
ASSERT(BusyWait());
RETURN NAMBAR.RegRead16(offset)
END RegRead16;
PROCEDURE RegWrite16(offset, value : LONGINT);
BEGIN{EXCLUSIVE}
ASSERT(BusyWait());
NAMBAR.RegWrite16(offset, value)
END RegWrite16;
PROCEDURE BusyWait() : BOOLEAN;
VAR t: Kernel.MilliTimer;
BEGIN
Kernel.SetTimer(t, 2);
WHILE ~Kernel.Expired(t) DO
IF ~(0 IN SYSTEM.VAL(SET, NABMBAR.RegRead8(34H))) THEN
RETURN TRUE
END
END;
RETURN FALSE
END BusyWait;
PROCEDURE Reset*;
BEGIN
RegWrite16(AC97ResetReg, 1);
END Reset;
PROCEDURE WriteCodec*;
BEGIN
RegWrite16(AC97ResetReg, 0001H);
RegWrite16(AC97MasterVolReg, MasterVolume);
RegWrite16(AC97AuxVolReg, 8000H);
RegWrite16(AC97MonoVolReg, 8000H);
RegWrite16(AC97PCMOutVolReg, 1818H);
RegWrite16(AC97StatCtrlReg, 0001H)
END WriteCodec;
PROCEDURE SetDacRate*(rate : LONGINT);
BEGIN
RegWrite16(AC97DacRateReg, rate)
END SetDacRate;
PROCEDURE SetAdcRate*(rate : LONGINT);
BEGIN
RegWrite16(AC97AdcRateReg, rate)
END SetAdcRate;
END AC97Control;
TYPE
Driver = OBJECT(SoundDevices.Driver)
VAR
base : LONGINT;
irq : LONGINT;
AC97 : AC97Control;
NAMBAR, NABMBAR : PCIAudioControl;
DMAIn, DMAOut : DMABufferDescriptorList;
firstPlayChannel : PlayChannelList;
firstRecChannel : RecChannelList;
mixerChannelList : ARRAY NofMixerChannels OF MixerChannel;
firstMixerChangedProc : MixerChangedProcList;
playCurrentBufferIndex, recCurrentBufferIndex : LONGINT;
playLastValidIndex, recLastValidIndex : LONGINT;
playBuffer, recBuffer, silentBuffer : SoundDevices.Buffer;
playSamplingRate : ARRAY 32 OF LONGINT;
recSamplingRate : LONGINT;
playCurrentSamplingRate : LONGINT;
playBufferList, recBufferList : ARRAY 32 OF SoundDevices.Buffer;
playInterrupt, recInterrupt : BOOLEAN;
PROCEDURE &Constr*(nambar, nabmbar, irq : LONGINT);
VAR
res : LONGINT;
DMAInPhysAdr, DMAOutPhysAdr: LONGINT;
i : LONGINT;
BEGIN
SELF.base := base;
SELF.irq := irq;
NEW(NAMBAR, nambar);
NEW(NABMBAR, nabmbar);
NEW(AC97, NAMBAR, NABMBAR);
NABMBAR.RegWrite8(BMRInBase + BMRCtrlReg, 02H);
NABMBAR.RegWrite8(BMROutBase + BMRCtrlReg, 02H);
AC97.Reset;
AC97.WriteCodec;
NEW(DMAIn);
NEW(DMAOut);
DMAInPhysAdr := GetPhysicalAdr(SYSTEM.ADR(DMAIn[0]), 256);
DMAOutPhysAdr := GetPhysicalAdr(SYSTEM.ADR(DMAOut[0]), 256);
NABMBAR.RegWrite32(BMRInBase + BMRBufferDescriptorBaseAddress, DMAInPhysAdr);
NABMBAR.RegWrite32(BMROutBase + BMRBufferDescriptorBaseAddress, DMAOutPhysAdr);
IF DebugDMAAddess IN Debug THEN
KernelLog.String("DMA In Physical Address: "); KernelLog.Int(DMAInPhysAdr, 5); KernelLog.Ln;
KernelLog.String("DMA Out Physical Address: "); KernelLog.Int(DMAOutPhysAdr, 5); KernelLog.Ln
END;
FOR i := 0 TO 31 DO
NEW(playBufferList[i]); NEW(recBufferList[i]);
NEW(playBufferList[i].data, BufferSize); NEW(recBufferList[i].data, BufferSize);
playBufferList[i].len := BufferSize; recBufferList[i].len := BufferSize;
DMAOut[i].addr := GetPhysicalAdr(
SYSTEM.ADR(playBufferList[i].data[0]), BufferSize);
DMAIn[i].addr := GetPhysicalAdr(
SYSTEM.ADR(recBufferList[i].data[0]), BufferSize);
DMAOut[i].CmdAndLength := SYSTEM.LSH(BufferSize, -1);
SetBit(DMAOut[i].CmdAndLength, 31);
DMAIn[i].CmdAndLength := SYSTEM.LSH(BufferSize, -1);
SetBit(DMAIn[i].CmdAndLength, 31)
END;
NEW(silentBuffer);
NEW(silentBuffer.data, BufferSize);
FOR i := 0 TO BufferSize - 1 DO silentBuffer.data[i] := silentData END;
silentBuffer.len := BufferSize;
FOR i := 0 TO 31 DO playSamplingRate[i] := 48000 END;
playCurrentSamplingRate := 48000;
AC97.RegWrite16(AC97RecordSelectReg, 0000H);
NEW(mixerChannelList[0], SELF, AC97MasterVolReg, FALSE, "MasterOut", "Master output mixer channel");
NEW(mixerChannelList[1], SELF, AC97RecordGainReg, FALSE, "MasterIn", "Master input mixer channel");
NEW(mixerChannelList[2], SELF, AC97LineInVolReg, TRUE, "LineIn", "LineIn mixer channel");
NEW(mixerChannelList[3], SELF, AC97PCMOutVolReg, FALSE, "PCMOut", "PCM out mixer channel");
NEW(mixerChannelList[4], SELF, AC97CDVolReg, TRUE, "CD", "CD input mixer channel");
NEW(mixerChannelList[5], SELF, AC97MicVolReg, FALSE, "Mic", "Microphone mixer channel");
mixerChannelList[5].SetVolume(0);
mixerChannelList[0].SetVolume(MasterVolume);
Objects.InstallHandler(HandleInterrupt, Machine.IRQ0+irq);
SoundDevices.devices.Add(SELF, res);
ASSERT(res = Plugins.Ok);
SoundDevices.devices.GetAll(DriverTab);
Enable
END Constr;
PROCEDURE Finalize;
BEGIN
Disable;
AC97.Reset;
Objects.RemoveHandler(HandleInterrupt, Machine.IRQ0+irq);
SoundDevices.devices.Remove(SELF);
SoundDevices.devices.GetAll(DriverTab)
END Finalize;
PROCEDURE HandleInterrupt;
VAR
status : LONGINT;
interrupt : LONGINT;
BEGIN
interrupt := NABMBAR.RegRead32(BMRGlobalStatusReg);
IF (6 IN SYSTEM.VAL(SET, interrupt)) THEN
status := NABMBAR.RegRead32(BMROutBase + BMRCurrentIndexReg);
IF (19 IN SYSTEM.VAL(SET, status)) THEN
BEGIN {EXCLUSIVE}
playLastValidIndex := (SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, status)*{0..7}) + 2) MOD 32;
playInterrupt := TRUE;
END;
END;
NABMBAR.RegWrite16(BMROutBase + BMRStatusReg, 0FFH);
END;
IF (5 IN SYSTEM.VAL(SET, interrupt)) THEN
status := NABMBAR.RegRead32(BMRInBase + BMRCurrentIndexReg);
IF (19 IN SYSTEM.VAL(SET, status)) THEN
BEGIN {EXCLUSIVE}
recLastValidIndex := (SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, status)*{0..7}) + 2) MOD 32;
recInterrupt := TRUE;
END;
END;
NABMBAR.RegWrite16(BMRInBase + BMRStatusReg, 0FFH)
END;
END HandleInterrupt;
PROCEDURE QueueBuffer*;
VAR
noOfSamples : LONGINT;
BEGIN
MixBuffers;
DMAOut[playLastValidIndex].addr := GetPhysicalAdr(
SYSTEM.ADR(playBufferList[playCurrentBufferIndex].data[0]), BufferSize);
noOfSamples := SYSTEM.LSH(playBufferList[playCurrentBufferIndex].len, -1);
DMAOut[playLastValidIndex].CmdAndLength := noOfSamples;
SetBit(DMAOut[playLastValidIndex].CmdAndLength, 31);
NABMBAR.RegWrite8(BMROutBase + BMRLastValidIndexReg, playLastValidIndex)
END QueueBuffer;
PROCEDURE MixBuffers;
VAR
item : PlayChannelList;
buffer : SoundDevices.Buffer;
silent : BOOLEAN;
BEGIN
playCurrentBufferIndex := playLastValidIndex;
playBuffer := NIL;
playSamplingRate[playCurrentBufferIndex] := 1;
silent := TRUE;
item := firstPlayChannel;
WHILE item # NIL DO
IF item.channel.state = StatePlaying THEN
playSamplingRate[playCurrentBufferIndex] :=
Max(playSamplingRate[playCurrentBufferIndex], item.channel.samplingRate);
silent := FALSE
END;
item := item.next
END;
item := firstPlayChannel;
WHILE item # NIL DO
IF item.channel.state = StatePlaying THEN
item.channel.PrepareData(playSamplingRate[playCurrentBufferIndex]);
buffer := item.channel.ReturnBuffer();
MixTwo(playBuffer, buffer);
END;
item := item.next
END;
IF silent THEN
playSamplingRate[playCurrentBufferIndex] := 48000;
playBuffer := silentBuffer
END;
IF playSamplingRate[(playCurrentBufferIndex-2) MOD 32] # playCurrentSamplingRate THEN
playCurrentSamplingRate := playSamplingRate[(playCurrentBufferIndex-2) MOD 32];
AC97.SetDacRate(playCurrentSamplingRate)
END;
SYSTEM.MOVE(
SYSTEM.ADR(playBuffer.data[0]),
SYSTEM.ADR(playBufferList[playCurrentBufferIndex].data[0]),
BufferSize);
playBufferList[playCurrentBufferIndex].len := playBuffer.len;
END MixBuffers;
PROCEDURE MixTwo(VAR buffer : SoundDevices.Buffer; buffer2 : SoundDevices.Buffer);
VAR
mix, sample1, sample2 : LONGINT;
i : LONGINT;
BEGIN
IF buffer = NIL THEN
buffer := buffer2;
RETURN;
END;
ASSERT(buffer.len = buffer2.len);
FOR i := 0 TO buffer.len-1 DO
sample1 := SYSTEM.VAL(INTEGER, buffer.data[i]);
sample2 := SYSTEM.VAL(INTEGER, buffer2.data[i]);
mix := sample1+sample2;
IF mix > MAX(INTEGER) THEN
mix := MAX(INTEGER);
END;
IF mix < MIN(INTEGER) THEN
mix := MIN(INTEGER);
END;
buffer.data[i] := CHR(mix);
buffer.data[i+1] := CHR(SYSTEM.LSH(mix, -8));
INC(i)
END
END MixTwo;
PROCEDURE ReturnBuffers*;
VAR
item : RecChannelList;
BEGIN
recCurrentBufferIndex := (recLastValidIndex - 3) MOD 32;
item := firstRecChannel;
WHILE item # NIL DO
IF item.channel.state = StateRecording THEN
item.channel.PrepareData(recBufferList[recCurrentBufferIndex], recSamplingRate)
END;
item := item.next
END;
NABMBAR.RegWrite8(BMRInBase + BMRLastValidIndexReg, recLastValidIndex)
END ReturnBuffers;
PROCEDURE Init*;
BEGIN
SetName("i810 Sound driver");
desc := "Sound driver for the Intel 810 chips"
END Init;
PROCEDURE Enable*;
BEGIN
IF TestBit(NABMBAR.RegRead16(BMROutBase + BMRStatusReg), 0) THEN
NABMBAR.RegWrite8(BMROutBase + BMRCtrlReg, 11H)
END;
IF TestBit(NABMBAR.RegRead16(BMRInBase + BMRStatusReg), 0) THEN
NABMBAR.RegWrite8(BMRInBase + BMRCtrlReg, 11H)
END
END Enable;
PROCEDURE Disable*;
BEGIN
NABMBAR.RegWrite16(BMROutBase + BMRStatusReg, 0002H);
NABMBAR.RegWrite8(BMROutBase + BMRCtrlReg, 00H);
NABMBAR.RegWrite16(BMRInBase + BMRStatusReg, 0002H);
NABMBAR.RegWrite8(BMRInBase + BMRCtrlReg, 00H)
END Disable;
PROCEDURE NofNativeFrequences() : LONGINT;
BEGIN
RETURN 0;
END NofNativeFrequences;
PROCEDURE GetNativeFrequency*(nr : LONGINT) : LONGINT;
BEGIN
RETURN 48000;
END GetNativeFrequency;
PROCEDURE NofSamplingResolutions*() : LONGINT;
BEGIN
RETURN 2
END NofSamplingResolutions;
PROCEDURE GetSamplingResolution(nr : LONGINT) : LONGINT;
BEGIN
ASSERT((nr >= 0) & (nr <2));
IF nr = 0 THEN
RETURN 16
ELSE
RETURN 8
END;
END GetSamplingResolution;
PROCEDURE NofSubChannelSettings*() : LONGINT;
BEGIN
RETURN 2
END NofSubChannelSettings;
PROCEDURE GetSubChannelSetting*(nr : LONGINT) : LONGINT;
BEGIN
ASSERT((nr >= 0) & (nr <= 2));
IF nr = 0 THEN
RETURN 1
ELSE
RETURN 2
END
END GetSubChannelSetting;
PROCEDURE NofWaveFormats*() : LONGINT;
BEGIN
RETURN 1
END NofWaveFormats;
PROCEDURE OpenPlayChannel*(VAR channel : SoundDevices.Channel; samplingRate, samplingResolution,
nofSubChannels, format : LONGINT; VAR res : LONGINT);
VAR
playChannel : PlayChannel;
sResolution : LONGINT;
BEGIN {EXCLUSIVE}
channel := NIL;
IF ~((samplingRate > 0) & (samplingRate <= 48000)) THEN
res := SoundDevices.ResUnsupportedFrequency;
RETURN
END;
IF samplingResolution = 16 THEN
sResolution := 0
ELSIF samplingResolution = 8 THEN
sResolution := 1
ELSE
res := SoundDevices.ResUnsupportedSamplingRes;
RETURN
END;
IF (nofSubChannels # 1) & (nofSubChannels # 2) THEN
res := SoundDevices.ResUnsupportedSubChannels;
RETURN
END;
IF format # SoundDevices.FormatPCM THEN
res := SoundDevices.ResUnsupportedFormat;
RETURN
END;
NEW(playChannel, SELF, samplingRate, sResolution, nofSubChannels);
channel := playChannel;
AddPlayChannel(playChannel);
IF TraceOpenChannel IN Debug THEN
KernelLog.String("Open PlayChannel: "); KernelLog.Int(samplingRate, 1); KernelLog.String("Hz, ");
KernelLog.Int(samplingResolution, 1); KernelLog.String("bit, ");
KernelLog.Int(nofSubChannels, 1); KernelLog.String(" channel(s)"); KernelLog.Ln
END;
res := SoundDevices.ResOK
END OpenPlayChannel;
PROCEDURE RemovePlayChannel(channel : PlayChannel);
VAR
item : PlayChannelList;
BEGIN
item := firstPlayChannel;
IF item # NIL THEN
IF item.channel = channel THEN
firstPlayChannel := item.next;
RETURN
END
END;
WHILE item.next # NIL DO
IF item.next.channel = channel THEN
item.next := item.next.next;
RETURN
END;
item := item.next
END
END RemovePlayChannel;
PROCEDURE AddPlayChannel(channel : PlayChannel);
VAR
item : PlayChannelList;
BEGIN
NEW(item);
item.channel := channel;
item.next := firstPlayChannel;
firstPlayChannel := item
END AddPlayChannel;
PROCEDURE OpenRecordChannel*(VAR channel : SoundDevices.Channel; samplingRate, samplingResolution,
nofSubChannels, format : LONGINT; VAR res : LONGINT);
VAR
recChannel : RecChannel;
item : RecChannelList;
sResolution : LONGINT;
BEGIN {EXCLUSIVE}
channel := NIL;
IF ~((samplingRate > 0) & (samplingRate <= 48000)) THEN
res := SoundDevices.ResUnsupportedFrequency;
RETURN
END;
IF samplingResolution = 16 THEN
sResolution := 0
ELSIF samplingResolution = 8 THEN
sResolution := 1
ELSE
res := SoundDevices.ResUnsupportedSamplingRes;
RETURN
END;
IF (nofSubChannels # 1) & (nofSubChannels # 2) THEN
res := SoundDevices.ResUnsupportedSubChannels;
RETURN
END;
IF format # SoundDevices.FormatPCM THEN
res := SoundDevices.ResUnsupportedFormat;
RETURN;
END;
NEW(recChannel, SELF, samplingRate, sResolution, nofSubChannels);
channel := recChannel;
AddRecChannel(recChannel);
IF TraceOpenChannel IN Debug THEN
KernelLog.String("Open RecChannel: "); KernelLog.Int(samplingRate, 1); KernelLog.String("Hz, ");
KernelLog.Int(samplingResolution, 1); KernelLog.String("bit, ");
KernelLog.Int(nofSubChannels, 1); KernelLog.String(" channel(s)"); KernelLog.Ln
END;
recSamplingRate := 1;
item := firstRecChannel;
WHILE item # NIL DO
recSamplingRate := Max(recSamplingRate, item.channel.samplingRate);
item := item.next;
END;
recSamplingRate := samplingRate;
AC97.SetAdcRate(recSamplingRate);
res := SoundDevices.ResOK
END OpenRecordChannel;
PROCEDURE RemoveRecChannel(channel : RecChannel);
VAR
item : RecChannelList;
BEGIN
item := firstRecChannel;
IF item # NIL THEN
IF item.channel = channel THEN
firstRecChannel := item.next;
RETURN
END
END;
WHILE item.next # NIL DO
IF item.next.channel = channel THEN
item.next := item.next.next;
RETURN
END;
item := item.next
END;
END RemoveRecChannel;
PROCEDURE AddRecChannel(channel : RecChannel);
VAR
item : RecChannelList;
BEGIN
NEW(item);
item.channel := channel;
item.next := firstRecChannel;
firstRecChannel := item
END AddRecChannel;
PROCEDURE RegisterMixerChangeListener*(mixChangedProc : SoundDevices.MixerChangedProc);
VAR
item : MixerChangedProcList;
BEGIN {EXCLUSIVE}
ASSERT(mixChangedProc # NIL);
NEW(item);
item.mixerChangedProc := mixChangedProc;
item.next := firstMixerChangedProc;
firstMixerChangedProc := item
END RegisterMixerChangeListener;
PROCEDURE UnregisterMixerChangeListener*(mixChangeProc : SoundDevices.MixerChangedProc);
VAR
item : MixerChangedProcList;
BEGIN {EXCLUSIVE}
item := firstMixerChangedProc;
IF item = NIL THEN
RETURN;
END;
IF item.mixerChangedProc = mixChangeProc THEN
firstMixerChangedProc := item.next;
IF DebugTraceListeners IN Debug THEN KernelLog.String("Removed mixerChangedProc"); KernelLog.Ln END;
RETURN;
END;
WHILE (item.next # NIL) & (item.next.mixerChangedProc # mixChangeProc) DO
item := item.next
END;
IF item.next # NIL THEN
item.next := item.next.next;
IF DebugTraceListeners IN Debug THEN KernelLog.String("Removed mixerChangedProc"); KernelLog.Ln END
ELSE
IF DebugTraceListeners IN Debug THEN KernelLog.String("Could not remove mixerChangeProc"); KernelLog.Ln END
END;
END UnregisterMixerChangeListener;
PROCEDURE GetMixerChannel*(channelNr : LONGINT; VAR channel : SoundDevices.MixerChannel);
BEGIN
IF (channelNr >= 0) & (channelNr < NofMixerChannels) THEN
channel := mixerChannelList[channelNr]
ELSE
channel := NIL
END
END GetMixerChannel;
PROCEDURE GetNofMixerChannels*() : LONGINT;
BEGIN
RETURN NofMixerChannels
END GetNofMixerChannels;
BEGIN {ACTIVE}
playInterrupt := FALSE;
recInterrupt := FALSE;
WHILE (TRUE) DO
BEGIN {EXCLUSIVE}
AWAIT(playInterrupt OR recInterrupt);
END;
IF playInterrupt THEN
QueueBuffer;
playInterrupt := FALSE;
ELSIF recInterrupt THEN
ReturnBuffers;
recInterrupt := FALSE;
END;
END;
END Driver;
MixerChannel = OBJECT(SoundDevices.MixerChannel)
VAR
driver : Driver;
name, desc : POINTER TO ARRAY OF CHAR;
volumeReg : LONGINT;
volume, muteVol : LONGINT;
volBits : LONGINT;
mute : BOOLEAN;
PROCEDURE &Constr*(driver : Driver; volumeReg : LONGINT; mute : BOOLEAN; name, desc : ARRAY OF CHAR);
VAR
i : LONGINT;
BEGIN
SELF.driver := driver;
SELF.volumeReg := volumeReg;
SELF.mute := mute;
NEW(SELF.name, LEN(name));
FOR i := 0 TO LEN(name) - 1 DO
SELF.name^[i] := name[i];
END;
NEW(SELF.desc, LEN(desc));
FOR i := 0 TO LEN(desc) - 1 DO
SELF.desc^[i] := desc[i]
END;
driver.AC97.RegWrite16(volumeReg, 803FH);
volume := driver.AC97.RegRead16(volumeReg);
volBits := 0;
WHILE ODD(volume) DO
volume := SYSTEM.LSH(volume, -1);
INC(volBits)
END;
SetVolume(InitVolume)
END Constr;
PROCEDURE GetName*(VAR name : ARRAY OF CHAR);
BEGIN
COPY(SELF.name^, name)
END GetName;
PROCEDURE GetDesc*(VAR desc : ARRAY OF CHAR);
BEGIN
COPY(SELF.desc^, desc)
END GetDesc;
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN {EXCLUSIVE}
ASSERT((volume >= 0) & (volume <= 255));
SELF.volume := volume;
volume := 255 - volume;
volume := SYSTEM.LSH(volume, volBits - 8);
volume := volume + SYSTEM.LSH(volume, 8);
IF SELF.mute THEN SetBit(volume, 15) END;
IF Boost THEN SetBit(volume, 6) END;
driver.AC97.RegWrite16(volumeReg, volume);
CallMixerListeners
END SetVolume;
PROCEDURE GetVolume*() : LONGINT;
BEGIN
RETURN SELF.volume
END GetVolume;
PROCEDURE SetMute*(mute : BOOLEAN);
VAR
volume : LONGINT;
BEGIN {EXCLUSIVE}
SELF.mute := mute;
IF mute THEN muteVol := volume; volume := 0
ELSE volume := muteVol END;
CallMixerListeners
END SetMute;
PROCEDURE GetIsMute*() : BOOLEAN;
BEGIN
RETURN SELF.mute;
END GetIsMute;
PROCEDURE CallMixerListeners;
VAR
item : MixerChangedProcList;
BEGIN
item := driver.firstMixerChangedProc;
WHILE item # NIL DO
item.mixerChangedProc(SELF);
item := item.next
END
END CallMixerListeners;
END MixerChannel;
Channel = OBJECT(SoundDevices.Channel)
VAR
driver : Driver;
samplingRate, samplingResolution, nofSubChannels : LONGINT;
bufferListener : SoundDevices.BufferListener;
firstBuffer, lastBuffer : BufferList;
playRecBuffer : SoundDevices.Buffer;
firstBufferPos : LONGINT;
volume : LONGINT;
state : LONGINT;
PROCEDURE &Constr*(driver : Driver; samplingRate, samplingResolution, nofSubChannels : LONGINT);
BEGIN
SELF.driver := driver;
SELF.samplingRate := samplingRate;
SELF.samplingResolution := samplingResolution;
SELF.nofSubChannels := nofSubChannels;
NEW(playRecBuffer);
NEW(playRecBuffer.data, BufferSize);
playRecBuffer.len := BufferSize;
SetVolume(255)
END Constr;
PROCEDURE GetChannelKind*() : LONGINT;
BEGIN
HALT(99)
END GetChannelKind;
PROCEDURE SetVolume*(volume : LONGINT);
BEGIN
SELF.volume := volume
END SetVolume;
PROCEDURE SampleSetVolume(VAR sample : Sample);
VAR
left, right : LONGINT;
volume : LONGINT;
BEGIN
volume := SELF.volume;
IF (volume < 0) THEN volume := 0 END;
IF (volume > 255) THEN volume := 255 END;
left := SYSTEM.VAL(INTEGER, sample);
right := SYSTEM.VAL(INTEGER, sample[2]);
left := left * volume DIV 256;
right := right * volume DIV 256;
sample[0] := CHR(left);
sample[1] := CHR(SYSTEM.LSH(left, -8));
sample[2] := CHR(right);
sample[3] := CHR(SYSTEM.LSH(right, -8))
END SampleSetVolume;
PROCEDURE GetVolume*() : LONGINT;
BEGIN
RETURN SELF.volume
END GetVolume;
PROCEDURE GetPosition*() : LONGINT;
BEGIN
RETURN firstBufferPos
END GetPosition;
PROCEDURE RegisterBufferListener*(bufferListener : SoundDevices.BufferListener);
BEGIN {EXCLUSIVE}
SELF.bufferListener := bufferListener
END RegisterBufferListener;
PROCEDURE QueueBuffer*(x : SoundDevices.Buffer);
VAR
bufferListItem : BufferList;
BEGIN
NEW(bufferListItem);
bufferListItem.buffer := x;
IF firstBuffer = NIL THEN
firstBuffer := bufferListItem
ELSE
lastBuffer.next := bufferListItem
END;
lastBuffer := bufferListItem
END QueueBuffer;
PROCEDURE ReturnBuffer*() : SoundDevices.Buffer;
BEGIN
RETURN playRecBuffer
END ReturnBuffer;
PROCEDURE Start*;
BEGIN
HALT(99)
END Start;
PROCEDURE Pause*;
BEGIN
state := StatePaused
END Pause;
PROCEDURE Stop*;
BEGIN
state := StateStoped;
ReturnAllBuffers;
END Stop;
PROCEDURE Close*;
BEGIN
HALT(99)
END Close;
PROCEDURE CallBufferListener() : BOOLEAN;
BEGIN
IF bufferListener = NIL THEN
IF firstBuffer # NIL THEN
firstBuffer := firstBuffer.next
END;
firstBufferPos := 0;
RETURN FALSE
END;
bufferListener(firstBuffer.buffer);
firstBuffer := firstBuffer.next;
firstBufferPos := 0;
RETURN TRUE
END CallBufferListener;
PROCEDURE ReturnAllBuffers;
VAR
item : BufferList;
BEGIN {EXCLUSIVE}
IF bufferListener = NIL THEN RETURN END;
item := firstBuffer;
WHILE item # NIL DO
bufferListener(item.buffer);
item := item.next
END;
firstBuffer := NIL
END ReturnAllBuffers;
END Channel;
PlayChannel = OBJECT(Channel);
PROCEDURE &ConstrPlay*(driver : Driver; samplingRate, samplingResolution, nofSubChannels : LONGINT);
BEGIN
Constr(driver, samplingRate, samplingResolution, nofSubChannels);
END ConstrPlay;
PROCEDURE Start*;
BEGIN
ASSERT(state # StateClosed);
state := StatePlaying;
driver.Enable;
END Start;
PROCEDURE Close*;
BEGIN
Stop;
state := StateClosed;
driver.RemovePlayChannel(SELF)
END Close;
PROCEDURE PrepareData*(toRate : LONGINT);
VAR
playRecBufferPos : LONGINT;
sample : Sample;
srate : LONGINT;
BEGIN {EXCLUSIVE}
srate := 0;
WHILE (firstBuffer # NIL) & (playRecBufferPos < BufferSize) DO
srate := srate + samplingRate;
IF (srate >= toRate) THEN
srate := srate MOD toRate;
IF (samplingResolution = 0) & (nofSubChannels = 2) THEN
sample[0] := firstBuffer.buffer.data[firstBufferPos];
sample[1] := firstBuffer.buffer.data[firstBufferPos+1];
sample[2] := firstBuffer.buffer.data[firstBufferPos+2];
sample[3] := firstBuffer.buffer.data[firstBufferPos+3];
INC(firstBufferPos, 4);
ELSIF (samplingResolution = 0) & (nofSubChannels = 1) THEN
sample[0] := firstBuffer.buffer.data[firstBufferPos];
sample[1] := firstBuffer.buffer.data[firstBufferPos+1];
MonoToStereo(sample);
INC(firstBufferPos, 2);
ELSIF (samplingResolution = 1) & (nofSubChannels = 2) THEN
sample[0] := firstBuffer.buffer.data[firstBufferPos];
sample[1] := firstBuffer.buffer.data[firstBufferPos+1];
EightToSixten(sample);
INC(firstBufferPos, 2);
ELSIF (samplingResolution = 1) & (nofSubChannels = 1) THEN
sample[0] := firstBuffer.buffer.data[firstBufferPos];
EightToSixten(sample);
MonoToStereo(sample);
INC(firstBufferPos);
ELSE
KernelLog.Int(samplingResolution, 1); KernelLog.String(" bit, ");
KernelLog.Int(nofSubChannels, 1); KernelLog.String(" channels are not supported");
HALT(99)
END;
SampleSetVolume(sample);
playRecBuffer.data[playRecBufferPos] := sample[0];
playRecBuffer.data[playRecBufferPos+1] := sample[1];
playRecBuffer.data[playRecBufferPos+2] := sample[2];
playRecBuffer.data[playRecBufferPos+3] := sample[3];
INC(playRecBufferPos, 4);
IF firstBufferPos >= firstBuffer.buffer.len THEN
IF ~CallBufferListener() THEN
OutputSilentData(playRecBufferPos);
RETURN
END;
END;
ELSE
playRecBuffer.data[playRecBufferPos] := CHR(0);
playRecBuffer.data[playRecBufferPos+1] := CHR(0);
playRecBuffer.data[playRecBufferPos+2] := CHR(0);
playRecBuffer.data[playRecBufferPos+3] := CHR(0);
INC(playRecBufferPos, 4);
END;
END;
OutputSilentData(playRecBufferPos)
END PrepareData;
PROCEDURE EightToSixten(VAR sample : Sample);
BEGIN
sample[3] := CHR(ORD(sample[1])-128);
sample[2] := CHR(0);
sample[1] := CHR(ORD(sample[0])-128);
sample[0] := CHR(0)
END EightToSixten;
PROCEDURE MonoToStereo(VAR sample : Sample);
BEGIN
sample[2] := sample[0];
sample[3] := sample[1]
END MonoToStereo;
PROCEDURE OutputSilentData(playRecBufferPos : LONGINT);
BEGIN
WHILE (playRecBufferPos < BufferSize) DO
playRecBuffer.data[playRecBufferPos] := silentData;
INC(playRecBufferPos, 1)
END
END OutputSilentData;
END PlayChannel;
RecChannel = OBJECT(Channel)
PROCEDURE Start*;
BEGIN
ASSERT(state # StateClosed);
state := StateRecording;
driver.Enable
END Start;
PROCEDURE Close*;
BEGIN
Stop;
state := StateClosed;
driver.RemoveRecChannel(SELF);
END Close;
PROCEDURE PrepareData(buffer : SoundDevices.Buffer; toRate : LONGINT);
VAR
bufferPos : LONGINT;
sample : Sample;
sampleSize : SHORTINT;
srate : LONGINT;
BEGIN
srate := 0;
bufferPos := 0;
WHILE (firstBuffer # NIL) & (bufferPos < buffer.len) DO
srate := srate + toRate;
IF (srate >= samplingRate) THEN
srate := srate MOD samplingRate;
sample[0] := buffer.data[bufferPos];
sample[1] := buffer.data[bufferPos+1];
sample[2] := buffer.data[bufferPos+2];
sample[3] := buffer.data[bufferPos+3];
INC(bufferPos, 4);
IF (samplingResolution = 0) & (nofSubChannels = 2) THEN
sampleSize := 4
ELSIF (samplingResolution = 0) & (nofSubChannels = 1) THEN
StereoToMono(sample);
sampleSize := 2
ELSIF (samplingResolution = 1) & (nofSubChannels = 2) THEN
SixtenToEight(sample);
sampleSize := 2
ELSIF (samplingResolution = 1) & (nofSubChannels = 1) THEN
StereoToMono(sample);
SixtenToEight(sample);
sampleSize := 1
ELSE
KernelLog.Int(samplingResolution, 1); KernelLog.String(" bit, ");
KernelLog.Int(nofSubChannels, 1); KernelLog.String(" channels are not supported");
HALT(99)
END;
SYSTEM.MOVE(
SYSTEM.ADR(sample[0]),
SYSTEM.ADR(firstBuffer.buffer.data[0]) + firstBufferPos,
sampleSize);
INC(firstBufferPos, sampleSize);
IF firstBufferPos >= firstBuffer.buffer.len THEN
IF ~CallBufferListener() THEN
RETURN
END;
END;
ELSE
INC(bufferPos, 4);
END;
END;
END PrepareData;
PROCEDURE StereoToMono(VAR sample : Sample);
VAR
mix, left, right : LONGINT;
BEGIN
left := SYSTEM.VAL(INTEGER, sample);
right := SYSTEM.VAL(INTEGER, sample[2]);
mix := SYSTEM.LSH(left + right, -1);
sample[0] := CHR(mix);
sample[1] := CHR(SYSTEM.LSH(mix, -8))
END StereoToMono;
PROCEDURE SixtenToEight(VAR sample : Sample);
BEGIN
sample[0] := CHR(ORD(sample[1]) - 128);
sample[1] := CHR(ORD(sample[3]) - 128)
END SixtenToEight;
END RecChannel;
VAR
silentData : CHAR;
firstDriver : DriverList;
DriverTab : Plugins.Table;
installedDriver : BOOLEAN;
PROCEDURE SetBit(VAR a : LONGINT; n : LONGINT);
BEGIN
ASSERT((n >= 0) & (n < 32));
INCL(SYSTEM.VAL(SET, a), n)
END SetBit;
PROCEDURE TestBit(a, n : LONGINT) : BOOLEAN;
BEGIN
RETURN n IN SYSTEM.VAL(SET, a);
END TestBit;
PROCEDURE Max(i, j : LONGINT) : LONGINT;
BEGIN
IF i > j THEN RETURN i ELSE RETURN j END
END Max;
PROCEDURE GetPhysicalAdr(adr: SYSTEM.ADDRESS; size : SYSTEM.SIZE) : Machine.Address32;
VAR
physadr : Machine.Address32;
BEGIN
physadr := Machine.Ensure32BitAddress (Machine.PhysicalAdr(adr, size));
ASSERT(physadr # Machine.NilAdr);
ASSERT(physadr MOD 4 = 0);
RETURN physadr
END GetPhysicalAdr;
PROCEDURE ScanPCI(vendid, devid : LONGINT);
VAR
index, res, i : LONGINT;
CmdReg : LONGINT;
bus, dev, fkt : LONGINT;
nambar, nabmbar, irq : LONGINT;
driver : Driver;
driverList : DriverList;
BEGIN
IF DebugInit IN Debug THEN KernelLog.String("Search for PCI card"); KernelLog.Ln END;
index := 0;
WHILE (index < 10) &
(PCI.FindPCIDevice(devid, vendid, index, bus, dev, fkt) = PCI.Done) DO
res := PCI.ReadConfigWord(bus, dev, fkt, PCI.CmdReg, i);
IF DebugInit IN Debug THEN
KernelLog.String("Vendor ID: "); KernelLog.Hex(vendid, 4); KernelLog.String("h"); KernelLog.Ln;
KernelLog.String("Device ID: "); KernelLog.Hex(devid, 4); KernelLog.String("h"); KernelLog.Ln;
KernelLog.String("Bus: "); KernelLog.Int(bus, 1); KernelLog.Ln;
KernelLog.String("Device: "); KernelLog.Int(dev, 1); KernelLog.Ln;
KernelLog.String("Function: "); KernelLog.Int(fkt, 1); KernelLog.Ln;
KernelLog.String("Command: "); KernelLog.Hex(i, 4); KernelLog.String("h"); KernelLog.Ln
END;
res := PCI.ReadConfigDword(bus, dev, fkt, PCI.Adr0Reg, nambar);
ASSERT(res = PCI.Done);
ASSERT(ODD(nambar));
DEC(nambar, nambar MOD 16);
IF DebugInit IN Debug THEN
KernelLog.String("Native Audio Mastering Base Address (NAMABR): "); KernelLog.Int(nambar, 5); KernelLog.Ln
END;
res := PCI.ReadConfigDword(bus, dev, fkt, PCI.Adr1Reg, nabmbar);
ASSERT(res = PCI.Done);
ASSERT(ODD(nabmbar));
DEC(nabmbar, nabmbar MOD 16);
IF DebugInit IN Debug THEN
KernelLog.String("Native Audio Bus Mastering Base Address (NABMABR): "); KernelLog.Int(nabmbar, 5); KernelLog.Ln
END;
res := PCI.ReadConfigByte(bus, dev, fkt, PCI.IntlReg, irq);
ASSERT(res = PCI.Done);
IF DebugInit IN Debug THEN
KernelLog.String("IRQ: "); KernelLog.Int(irq, 2); KernelLog.Ln
END;
res := PCI.ReadConfigWord(bus, dev, fkt, PCI.CmdReg, CmdReg);
ASSERT(res = PCI.Done);
SetBit(CmdReg, 0);
res := PCI.WriteConfigWord(bus, dev, fkt, PCI.CmdReg, CmdReg);
ASSERT(res = PCI.Done);
NEW(driver, nambar, nabmbar, irq);
KernelLog.String("Intel 810 sound driver installed."); KernelLog.Ln;
NEW(driverList);
driverList.driver := driver;
driverList.next := firstDriver;
firstDriver := driverList;
INC(index)
END
END ScanPCI;
PROCEDURE Install*;
BEGIN {EXCLUSIVE}
IF ~installedDriver THEN
ScanPCI(8086H,2485H);
ScanPCI(8086H, 24C5H);
ScanPCI(8086H, 266EH);
IF (InsecureMode = TRUE) THEN
ScanPCI(8086H, 2415H);
ScanPCI(8086H, 2425H);
ScanPCI(8086H, 2445H);
ScanPCI(8086H, 24D5H);
ScanPCI(8086H, 7195H);
END;
installedDriver := TRUE
END;
END Install;
PROCEDURE Enable*;
VAR
item : DriverList;
BEGIN
item := firstDriver;
WHILE item # NIL DO
item.driver.Enable;
item := item.next
END;
END Enable;
PROCEDURE Disable*;
VAR
item : DriverList;
BEGIN
item := firstDriver;
WHILE item # NIL DO
item.driver.Disable;
item := item.next
END;
END Disable;
PROCEDURE Cleanup;
VAR
item : DriverList;
BEGIN
item := firstDriver;
WHILE item # NIL DO
item.driver.Finalize;
item := item.next
END
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup)
END i810Sound.
PC.Compile * \s ~
System.Free i810Sound ~ (* Make sure module is unloaded *)
Aos.Call i810Sound.Install ~
Aos.Call i810Sound.Disable ~ (* pause all *)
Aos.Call i810Sound.Enable ~
Aos.Call PlayRecWave.Play x.wav ~
Aos.Call MP3Player.Play DATA:/SomeMp3File.Mp3 ~
System.Free i810Sound ~
SystemTools.Free i810Sound~