Author Topic: BASSWASAPI beta  (Read 138674 times)

morcibacsi

  • Posts: 11
Re: BASSWASAPI beta
« Reply #150 on: 1 Aug '11 - 12:30 »
Well, there was an BASS_WASAPI_Init call on application startup, but with device id 0. Maybe that was one of the problems. Another problem was, that I've passed null as the wasapiproc argument but as I understand from the help it has to be implemented, so I've fixed that too. Unfortunately these didn't fix the problem. Both BassWasapiHandler's Init methods return false, and Bass.BASS_ErrorGetCode() says BASS_ERROR_ALREADY. SetFullDuplex returns false too.

Now the code looks like this (I've included the full code, it is just a quick sample application):
Code: [Select]
using System;
using System.Windows.Forms;
using Un4seen.Bass;
using Un4seen.Bass.AddOn.Enc;
using Un4seen.Bass.Misc;
using Un4seen.Bass.AddOn.Mix;
using Un4seen.BassWasapi;

namespace WindowsFormsApplication4
{
    public partial class Form1 : Form
    {
        private EncoderLAME encoder;
        private int stereo_mixer;
        private int microphone;
        private int mixer;
        private BassWasapiHandler stereoHandler;
        private BassWasapiHandler micHandler;
        private WASAPIPROC smWasapiProc;
        private WASAPIPROC micWasapiProc;

        public Form1()
        {
            InitializeComponent();
        }

        public void log(string msg)
        {
            richTextBox1.Text += String.Format("{0}\n", msg);
        }

        public int MyWasapiProc(IntPtr buffer, int length, IntPtr user)
        {
            return 1;
        }

        private void btStartWASAPI_Click(object sender, EventArgs e)
        {
            Bass.BASS_Init(-1, 48000, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            smWasapiProc = new WASAPIPROC(MyWasapiProc);           
            bool res = BassWasapi.BASS_WASAPI_Init(int.Parse(textBox1.Text), 0, 2, BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT, 0, 0, smWasapiProc, IntPtr.Zero);
            log(string.Format("smWASAPI_Init: {0} ({1})", res, Bass.BASS_ErrorGetCode()));
            stereoHandler = new BassWasapiHandler(int.Parse(textBox1.Text), false, 48000, 2, 0, 0);
            log(string.Format("smInit: {0}", stereoHandler.Init()));
            log(Bass.BASS_ErrorGetCode().ToString());
            log(string.Format("stereo mixer isInput: {0}", stereoHandler.IsInput));
            stereoHandler.DeviceVolume = 1;
            stereoHandler.Volume = 1;

            res = stereoHandler.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE, false);
            log(string.Format("stereo mixer fullduplex: {0} ({1})", res, Bass.BASS_ErrorGetCode()));

            int stereo_mixer = stereoHandler.OutputChannel;
            log(string.Format("stereo mixer: {0} ({1})", stereo_mixer, Bass.BASS_ErrorGetCode()));

            // double check, that the device is not muted externally
            if (stereoHandler.DeviceMute)
                stereoHandler.DeviceMute = false;

            log(string.Format("smSTART: {0}", stereoHandler.Start()));
            log(string.Format("stereo mixer: {0} ({1})", stereo_mixer, Bass.BASS_ErrorGetCode()));
//---------------------------------------------------------------------------------------------------------
            micWasapiProc = new WASAPIPROC(MyWasapiProc);
            res = BassWasapi.BASS_WASAPI_Init(int.Parse(textBox2.Text), 0, 2, BASSWASAPIInit.BASS_WASAPI_AUTOFORMAT, 50, 50, micWasapiProc, IntPtr.Zero);
            log(string.Format("micWASAPI_Init: {0} ({1})", res, Bass.BASS_ErrorGetCode()));
            micHandler = new BassWasapiHandler(int.Parse(textBox2.Text), false, 48000, 2, 0, 0);
            log(string.Format("micInit: {0}", micHandler.Init()));
            log(Bass.BASS_ErrorGetCode().ToString());
            log(string.Format("microphone isInput: {0}", micHandler.IsInput));
            micHandler.DeviceVolume = 1;
            micHandler.Volume = 1;

            res = micHandler.SetFullDuplex(0, BASSFlag.BASS_STREAM_DECODE, false);
            log(string.Format("microphone fullduplex: {0} ({1})", res, Bass.BASS_ErrorGetCode()));
           
            int microphone = micHandler.OutputChannel;
            log(string.Format("microphone: {0} ({1})", microphone, Bass.BASS_ErrorGetCode()));
           
            // double check, that the device is not muted externally
            if (micHandler.DeviceMute)
                micHandler.DeviceMute = false;
            log(string.Format("micSTART: {0}", micHandler.Start()));
            log(string.Format("microphone: {0} ({1})", microphone, Bass.BASS_ErrorGetCode()));


            mixer = BassMix.BASS_Mixer_StreamCreate(48000, 2, BASSFlag.BASS_SAMPLE_FLOAT);
            log(string.Format("Mixer: {0} ({1})", mixer, Bass.BASS_ErrorGetCode()));

            res = BassMix.BASS_Mixer_StreamAddChannel(mixer, stereo_mixer, BASSFlag.BASS_MIXER_END);//  BASSFlag.BASS_MIXER_DOWNMIX | BASSFlag.BASS_MIXER_FILTER);
            log(string.Format("Channel1: {0} ({1})", res, Bass.BASS_ErrorGetCode()));

            res = BassMix.BASS_Mixer_StreamAddChannel(mixer, microphone, BASSFlag.BASS_MIXER_END);// BASS_MIXER_DOWNMIX | BASSFlag.BASS_MIXER_FILTER);
            log(string.Format("Channel2: {0} ({1})", res, Bass.BASS_ErrorGetCode()));

            // mute the mixer, so that you don't hear it in the output to avoid echos!
            res = Bass.BASS_ChannelSetAttribute(mixer, BASSAttribute.BASS_ATTRIB_VOL, 0f);
            log(string.Format("ChannelSet: {0} ({1})", res, Bass.BASS_ErrorGetCode()));

            encoder = new EncoderLAME(mixer);
            encoder.InputFile = null;
            encoder.OutputFile = "out.mp3";
            encoder.LAME_Bitrate = (int)EncoderLAME.BITRATE.kbps_192;
            encoder.LAME_Mode = EncoderLAME.LAMEMode.Default;
            encoder.LAME_TargetSampleRate = (int)EncoderLAME.SAMPLERATE.Hz_44100;
            encoder.LAME_Quality = EncoderLAME.LAMEQuality.Quality;

            res = encoder.Start(null, IntPtr.Zero, false); //new version
            log(string.Format("encodeStart: {0} ({1})", res, Bass.BASS_ErrorGetCode()));

            // start the mixer
            Bass.BASS_ChannelPlay(mixer, false);
            log(string.Format("ChannelPlay: {0} ({1})", res, Bass.BASS_ErrorGetCode()));
        }

        private void btStopWASAPI_Click(object sender, EventArgs e)
        {
            encoder.Stop();
            Bass.BASS_ChannelStop(mixer);
            stereoHandler.Stop();
            micHandler.Stop();
        }
    }
}

Program output is:
Code: [Select]
smWASAPI_Init: True (BASS_OK)
smInit: False
BASS_ERROR_ALREADY
stereo mixer isInput: True
stereo mixer fullduplex: False (BASS_OK)
stereo mixer: 0 (BASS_OK)
smSTART: True
stereo mixer: 0 (BASS_OK)
micWASAPI_Init: True (BASS_OK)
micInit: False
BASS_ERROR_ALREADY
microphone isInput: True
microphone fullduplex: False (BASS_OK)
microphone: 0 (BASS_OK)
micSTART: True
microphone: 0 (BASS_OK)
Mixer: -1342177277 (BASS_OK)
Channel1: False (BASS_ERROR_HANDLE)
Channel2: False (BASS_ERROR_HANDLE)
ChannelSet: True (BASS_OK)
encodeStart: True (BASS_OK)
ChannelPlay: True (BASS_OK)

Now I'm a little desperate, usually my codes are working  :-[

radio42

  • Posts: 4438
Re: BASSWASAPI beta
« Reply #151 on: 1 Aug '11 - 13:25 »
No, maybe there was a missunderstanding - I ment the "BassWasapiHandler.Init()" method!
There is no need to provide your own WASAPI proc - as the BassWasapiHandler handler implements them internally!

So here is the sequence for using the BassWasapiHandler handler:
1. call BASS_Init(...)
2. create an instance of the BassWasapiHandler handler (new BassWasapiHandler(...))
3. Init the handler (call BassWasapiHandler.Init())
4. Set the full-duplex option (call SetFullDuplex(...))
5. Start the handler (call BassWasapiHandler.Start())

morcibacsi

  • Posts: 11
Re: BASSWASAPI beta
« Reply #152 on: 1 Aug '11 - 20:10 »
Well, then I can't figure out what could be the problem. The calls are in that order, what you suggested. The .Init methods are there, in logging lines. I've just tried the application on an another computer, but it does not work. (I suspected something is wrong with my PC)

Modified the code to not to use the WasapiHandler. This way I got no error messages, everything returns true, and whatsoever, but the resulting file is only 1879 bytes long. I'm trying everything but really got stuck now.  :-\

Code: [Select]
        private EncoderLAME encoder;
        private int stereo_mixer;
        private int microphone;
        private int mixer;
        private WASAPIPROC smWasapiProc;
        private WASAPIPROC micWasapiProc;
...
        public int MyMicWasapiProc(IntPtr buffer, int length, IntPtr user)
        {
            return 1;
        }

        public int MySMWasapiProc(IntPtr buffer, int length, IntPtr user)
        {
            return 1;
        }

        private void btStartWASAPI_Click(object sender, EventArgs e)
        {
            Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            mixer = BassMix.BASS_Mixer_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT);         

            smWasapiProc = new WASAPIPROC(MySMWasapiProc);
            bool res = BassWasapi.BASS_WASAPI_Init(int.Parse(textBox1.Text), 0, 0, 0, 1, 0, smWasapiProc, IntPtr.Zero);
            stereo_mixer = Bass.BASS_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE,
                                   BASSStreamProc.STREAMPROC_PUSH);
            res = BassMix.BASS_Mixer_StreamAddChannel(mixer, stereo_mixer, BASSFlag.BASS_MIXER_END);//  BASSFlag.BASS_MIXER_DOWNMIX | BASSFlag.BASS_MIXER_FILTER);
 
            micWasapiProc = new WASAPIPROC(MyMicWasapiProc);
            res = BassWasapi.BASS_WASAPI_Init(int.Parse(textBox2.Text), 0, 0, 0, 1, 0, smWasapiProc, IntPtr.Zero);           
            microphone = Bass.BASS_StreamCreate(44100, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE,
                                   BASSStreamProc.STREAMPROC_PUSH);
            res = BassMix.BASS_Mixer_StreamAddChannel(mixer, microphone, BASSFlag.BASS_MIXER_END);

            BassWasapi.BASS_WASAPI_SetDevice(int.Parse(textBox1.Text));
            res = BassWasapi.BASS_WASAPI_Start();
         
            BassWasapi.BASS_WASAPI_SetDevice(int.Parse(textBox2.Text));
            res = BassWasapi.BASS_WASAPI_Start();

            // mute the mixer, so that you don't hear it in the output to avoid echos!
            res = Bass.BASS_ChannelSetAttribute(mixer, BASSAttribute.BASS_ATTRIB_VOL, 0f);
           
            res = Bass.BASS_ChannelPlay(mixer, false);

            encoder = new EncoderLAME(mixer);
            encoder.InputFile = null;
            encoder.OutputFile = "out.mp3";
            encoder.LAME_Bitrate = (int)EncoderLAME.BITRATE.kbps_192;
            encoder.LAME_Mode = EncoderLAME.LAMEMode.Default;
            encoder.LAME_TargetSampleRate = (int)EncoderLAME.SAMPLERATE.Hz_44100;
            encoder.LAME_Quality = EncoderLAME.LAMEQuality.Quality;

            res = encoder.Start(null, IntPtr.Zero, false);
        }

        private void btStopWASAPI_Click(object sender, EventArgs e)
        {
            encoder.Stop();
            Bass.BASS_ChannelStop(mixer);
            Bass.BASS_ChannelStop(stereo_mixer);
            Bass.BASS_ChannelStop(microphone);
        }
« Last Edit: 2 Aug '11 - 15:23 by morcibacsi »

tigera

  • Posts: 4
Re: BASSWASAPI beta
« Reply #153 on: 7 Aug '11 - 11:54 »
I am trying use BASSWASAPI, but BASS_ErrorGetCode() return error 5000. Why?

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #154 on: 8 Aug '11 - 13:40 »
Error code 5000 is BASS_ERROR_WASAPI, which indicates that WASAPI is not available. What Windows version are you using? Please note that WASAPI was introduced with Vista and it isn't available on older Windows versions.

tigera

  • Posts: 4
Re: BASSWASAPI beta
« Reply #155 on: 9 Aug '11 - 16:19 »
My OS is WinXP  :(

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #156 on: 9 Aug '11 - 17:49 »
OK. Unfortunately, WASAPI won't be available to you then, but perhaps you could ASIO instead (via BASSASIO). ASIO has basically the same capabilities as WASAPI.

Led

  • Posts: 40
Re: BASSWASAPI beta
« Reply #157 on: 14 Aug '11 - 21:22 »
How do I use Bass x64 with Wasapi in C# ?

I've added the Basswasapi.dll (x64 version from the Winx64 folder) to my project.
Build Action=Content, Copy Always.

If I build my app and I go to the \bin\x64\Debug folder where the executable is compiled, I see that the basswasapi.dll is copied there.
But when I run my executable, it's still a no-go :
+$exception{"Unable to load DLL 'basswasapi.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)"}   System.Exception {System.DllNotFoundException}

The dll is in the same place the executable is in. Somehow my executable either can't load the dll, or is looking somewhere else entirely.
How can i figure out what's going wrong ?

WACKA

  • Posts: 14
Re: BASSWASAPI beta
« Reply #158 on: 24 Aug '11 - 14:27 »
Hi,

I am currently using BassWasapi and it is working very well, many thanks!!

I am having a minor problem with volume control when the sound is going over HDMI.
I am outputting to a HDMI tv from my PC but the BASS_WASAPI_SetVolume has no affect on the volume of the playback.

However, if i output to speakers or a usb headset, the volume control works fine.

Has anyone else experienced this? Does anyone have a workaround?

Thanks,

Conor

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #159 on: 24 Aug '11 - 14:41 »
It might be that the device doesn't have a volume control. To confirm whether that is the case, you can check in the Sound control panel, eg. does the device have a "Levels" tab?

If you're using a mixer (BASSmix) to feed the WASAPI output, you can control the volume of the sources via BASS_ChannelSetAttribute (BASS_ATTRIB_VOL). Otherwise, you could use the BASS_FX add-on's BASS_FX_BFX_VOLUME effect to do it.

WACKA

  • Posts: 14
Re: BASSWASAPI beta
« Reply #160 on: 24 Aug '11 - 15:18 »
Hi Ian,

Many thanks for the speedy reply.

I can confirm there is a levels tab for the hdmi output.

However, thanks for the mixer tip, I will use this to set the volume.

Many thanks,

Conor

yosato

  • Posts: 6
Re: BASSWASAPI beta
« Reply #161 on: 30 Aug '11 - 22:42 »
Code: [Select]

        public bool start(string outFile, int freq, int devNum)
        {
            int wasapiId, bassId;

            if (devNum == -1)
            {
                wasapiId = defaultWasapiNum;
                bassId = defaultBassNum;
            }
            else
            {
                wasapiId = wasapiNums[devNum];
                bassId = bassNums[devNum];
            }

            Bass.BASS_Init(bassId, freq, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero);

            _wasapiHandler = new BassWasapiHandler(wasapiId, true, freq, 2, 2f, 0.5f);
            if (_wasapiHandler.Init())
            {
                _recordStream = _wasapiHandler.InputChannel;

                // double check, that the device is not muted externally
                if (_wasapiHandler.DeviceMute)
                    _wasapiHandler.DeviceMute = false;
                // check, that the master volume is not 0
                if (_wasapiHandler.DeviceVolume == 0f)
                    _wasapiHandler.DeviceVolume = 1f;

                _myDSPProc = new DSPPROC(MyRecording);
                Bass.BASS_ChannelSetDSP(_recordStream, _myDSPProc, IntPtr.Zero, 0);

                bw = new BinaryWriter(File.Open(outFile, FileMode.Create));


                // start WASAPI
                _wasapiHandler.Start();
                return true;
            }
            else
                return false;

        }

        bool writing = false;

        private void MyRecording(int handle, int channel, IntPtr buffer, int length, IntPtr user)
        {
            if (length > 0 && buffer != IntPtr.Zero && bw != null)
            {
                writing = true;
                for (int i = 0; i < length; i++)
                    bw.Write(Marshal.ReadByte(buffer, i));
                writing = false;
            }
        }


Hi Ian.

This code works very well usually.
But once in 20-30 hours, recording suddenly skips.
How do you think about this trouble?

fmcoder

  • Posts: 423
Re: BASSWASAPI beta
« Reply #162 on: 31 Aug '11 - 11:00 »
Seems I found a bug. If I make a call to BASS_WASAPI_Init with negative device number, like -5, the call is successful and BASS_ErrorGetCode is 0, but soon an "Access Violation" error occurs.

yosato

  • Posts: 6
Re: BASSWASAPI beta
« Reply #163 on: 31 Aug '11 - 12:47 »
Thank you for your quick reply.
But wasapi device number seems positive or zero.

Code: [Select]
        public void setDeviceList(BassRec bassRec)
        {
            recDevCnt = bassRec.recDevCnt();
            wasapiNums = new int[recDevCnt];
            bassNums = new int[recDevCnt];
            devNames = new string[recDevCnt];

            BASS_WASAPI_DEVICEINFO info = new BASS_WASAPI_DEVICEINFO();

            for (int i = 0; BassWasapi.BASS_WASAPI_GetDeviceInfo(i, info); i++)
            {
                if (info.IsEnabled && info.IsInput && !info.IsLoopback)
                {
                    if (info.IsDefault)
                        defaultWasapiNum = i;

                    for (int recNum = 0; recNum < recDevCnt; recNum++)
                    {
                        if (bassRec.getDevName(recNum) == info.name)
                        {
                            wasapiNums[recNum] = i;
                            devNames[recNum] = info.name;
                        }
                    }
                }
            }

            BASS_DEVICEINFO devInfo = new BASS_DEVICEINFO();

            for (int i = 0; Bass.BASS_GetDeviceInfo(i, devInfo); i++)
            {
                for (int recNum = 0; recNum < recDevCnt; recNum++)
                {
                    if (bassRec.getDevName(recNum) == info.name)
                    {
                        bassNums[recNum] = i;
                        if (info.IsDefault)
                            defaultBassNum = i;
                    }
                }
            }

        }

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #164 on: 31 Aug '11 - 15:57 »
This code works very well usually.
But once in 20-30 hours, recording suddenly skips.

Is it just a single skip and then it continues as normal, or does it continue skipping? If you add some logging to the "MyRecording" function (eg. the entry/exit and parameters), do you notice anything different when the skipping occurs, eg. is it called less/more frequently or does it take longer between entry & exit, or does the "length" parameter change?

Also, to eliminate possible causes, please try removing your DSP (the BASS_ChannelSetDSP call), ie. do nothing more than recording.

Seems I found a bug. If I make a call to BASS_WASAPI_Init with negative device number, like -5, the call is successful and BASS_ErrorGetCode is 0, but soon an "Access Violation" error occurs.

BASS_WASAPI_Init currently treats any negative device number other than -2 as -1, ie. -2 = default input device, any other negative number = default output device. Does the AV not occur if you use -1 instead of -5? If the AV does still occur, please provide any details that you have on it.

yosato

  • Posts: 6
Re: BASSWASAPI beta
« Reply #165 on: 31 Aug '11 - 23:18 »
Thank you for your reply.

Skipping occurs a few times in one minute and then it continues as normal for 20-30 hours.

I will monitor "length" parameter.
But it may be difficult.
To detect skipping, I must listen to music for a long time.

After I remove BASS_ChannelSetDSP call,
I have no idea to define the callback function which is necessary for recording.

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #166 on: 5 Sep '11 - 14:47 »
After I remove BASS_ChannelSetDSP call,
I have no idea to define the callback function which is necessary for recording.

Yep, you should actually keep that DSP function (and add the logging to it). I wasn't initially aware that a DSP function is used for input with BASS.Net's WASAPI handler.

fmcoder

  • Posts: 423
Re: BASSWASAPI beta
« Reply #167 on: 25 Sep '11 - 23:18 »
There's bassmix.dll file in the basswadsp package, what is it for? Should I use it, or IU may use the regular bassmix?

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #168 on: 26 Sep '11 - 17:00 »
That sounds like you may have an old BASSWASAPI package? There was a BASSmix update included initially to provide the BASS_MIXER_POSEX/BASS_Mixer_ChannelGetPositionEx option, but it was no longer needed once BASSmix 2.4.5 was released including that option.

fmcoder

  • Posts: 423
Re: BASSWASAPI beta
« Reply #169 on: 2 Oct '11 - 09:45 »
Ahh... Yes, I unpacked new packages to the same folder, and there were files left from previous packages.

fmcoder

  • Posts: 423
Re: BASSWASAPI beta
« Reply #170 on: 4 Oct '11 - 20:42 »
New question.
I have 2 apps which use the same input device.
Problem is that 2nd app doesn't receive any data in the WASAPI proc.
Why's that?

Is it some kind of limitation, that only one app can use the input device at a time?

fmcoder

  • Posts: 423
Re: BASSWASAPI beta
« Reply #171 on: 4 Oct '11 - 21:09 »
My bad. Those programs initizlized different input devices. Seems that it's sound card limitation - it can not record from 2 different sources simultaneously (records only from the first initialized, second doesn't get any data to the WASAPIProc). Sorry :)

Smoov

  • Posts: 13
Re: BASSWASAPI beta
« Reply #172 on: 5 Oct '11 - 13:14 »
So Ian,

Do you fore see a new version of BASSWASAPI being released? Is it time to take it out of beta. I know it is working very well for us.

Ian @ un4seen

  • Administrator
  • Posts: 19194
Re: BASSWASAPI beta
« Reply #173 on: 5 Oct '11 - 14:12 »
My bad. Those programs initizlized different input devices. Seems that it's sound card limitation - it can not record from 2 different sources simultaneously (records only from the first initialized, second doesn't get any data to the WASAPIProc). Sorry :)

Yep, most soundcards won't allow recording from multiple inputs (on the card) at the same time.

Do you fore see a new version of BASSWASAPI being released? Is it time to take it out of beta. I know it is working very well for us.

No, there aren't any changes planned, and there haven't been any problems reported recently, so you're right, it probably is about time to take it out of beta. If anyone has any requests or problems, now is the time to give them :)

AndyMK

  • Posts: 171
Re: BASSWASAPI beta
« Reply #174 on: 14 Oct '11 - 20:27 »
Hi guy's, just tried this lib and i'm finding the minimum latency a bit high but it sounds just as responsive as asio. The minimum buffer length returned for shared mode is 10584 and for exclusive mode 28224 (both using 0.03 for the buffer parameter as anything lower does not change the returned buffer size and exclusive mode returns a higher minimum for some reason) (10584/44100)*1000=240ms latency .. it sounds much lower than that. I tried this on a 1$ realtek chip and a Novation NIO 2|4 usb pro audio unit.