20 Jun '13 - 07:38 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: BASSASIO Input to BassMix Mixer with Bass.NET  (Read 987 times)
C Del
Posts: 10


« on: 15 Aug '12 - 23:25 »
Reply with quoteQuote

Hello,

Sorry if this problem is a stupid one, but I'm at my wit's end here and extensive searching didn't seem to solve the problem.  I'm using Bass to get input from an ASIO device (up to 14 ins, 4 outs); I'd like to be able to mix an arbitrary number of input streams into a stereo stream to be recorded to a WAV file.  Here is my code (C#.NET):

        private void StartAudioRecordingWithMixer()
        {
            HandleBassError(BassAsio.BASS_ASIO_Free());
            HandleBassError(BassAsio.BASS_ASIO_Init(Info.AsioDevice, BASSASIOInit.BASS_ASIO_DEFAULT));
            m_AsioProcessor = new ASIOPROC(AsioProcessor);
            m_MixerHandle = BassMix.BASS_Mixer_StreamCreate(48000, 2, BASSFlag.BASS_SAMPLE_FLOAT | BASSFlag.BASS_STREAM_DECODE);
            if (m_MixerHandle == 0)
                throw new Exception("Could not create mixer: " + Bass.BASS_ErrorGetCode());
            List<int> channelStreams = new List<int>(1);
            foreach (int i in Info.InputChannels)
            {
                int channelStream = Bass.BASS_StreamCreatePush(48000, 1, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero);
                HandleBassError(BassAsio.BASS_ASIO_ChannelEnable(true, Info.InputChannels[i], m_AsioProcessor, new IntPtr(channelStream)));
                HandleBassError(BassAsio.BASS_ASIO_ChannelSetFormat(true, Info.InputChannels[i], BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT));
                HandleBassError(BassAsio.BASS_ASIO_ChannelSetRate(true, Info.InputChannels[i], 48000));
                channelStreams.Add(channelStream);
            }
            foreach (int i in channelStreams)
                HandleBassError(BassMix.BASS_Mixer_StreamAddChannel(m_MixerHandle, i, BASSFlag.BASS_DEFAULT));

            HandleBassError(BassAsio.BASS_ASIO_Start(0));

            m_WavEncoder = new EncoderWAV(m_MixerHandle);
            m_AudioPath = m_WavEncoder.OutputFile = Path.Combine(".\\Audio", Info.GetNextFilename("test{0}.wav", new DirectoryInfo(".\\Audio")));
            HandleBassError(m_WavEncoder.Start(null, IntPtr.Zero, false));
        }

        private int AsioProcessor(bool input, int channel, IntPtr buffer, int length, IntPtr user)
        {
            // Get the ASIO data and copy it to the associated stream
            // Note: 'user' is not actually a pointer in this instance, it's being used to store an Int32 value
            return Bass.BASS_StreamPutData(user.ToInt32(), buffer, length);
        }

I hope I'm not way off.  From what I can tell, I'm getting the ASIO input into the push streams, but the push streams are not feeding into the mixer (I was hoping the mixer would pull sample data from them).  I also have no idea whether assigning an EncoderWAV DSP to the mixer output is valid.  Since I have little to no idea how the mixer/streams work internally, I'm having quite a bit of trouble figuring out what I'm doing wrong.

Thanks,
C Del
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #1 on: 16 Aug '12 - 15:13 »
Reply with quoteQuote

I think the problem there is that the data isn't being processed by the mixer, and so the data will just be piling up in the "push" streams. To have the mixer process the data, you would use BASS_ChannelGetData, after all of the ASIO inputs have provided data to the push streams. For example, the ASIOPROC function could look something like this...

DWORD CALLBACK AsioProc(BOOL input, DWORD channel, void *buffer, DWORD length, void *user)
{
BASS_StreamPutData((DWORD)user, buffer, length); // feed the data to the associated push stream (handle in "user")
if (channel==lastchannel) { // all of the input channels have provided data
BYTE temp[20000]; // processing buffer
while (BASS_ChannelGetData(mixer, temp, sizeof(temp))) ; // process the mix
}
return 0;
}
Logged
C Del
Posts: 10


« Reply #2 on: 16 Aug '12 - 21:46 »
Reply with quoteQuote

*sigh* Hard to believe it was that simple, thanks.
Logged
C Del
Posts: 10


« Reply #3 on: 20 Aug '12 - 19:31 »
Reply with quoteQuote

Ok. Now I'm having trouble getting ASIO output. Here's my ASIOPROC callback at the moment:

        private int AsioProcessor(bool input, int channel, IntPtr buffer, int length, IntPtr user)
        {
            if (input)
            {
                // Get the ASIO data and copy it to the associated stream
                // Note: 'user' is not actually a pointer in this instance, it's being used to store an Int32 value
                Bass.BASS_StreamPutData(user.ToInt32(), buffer, length);
                if (channel == Info.InputChannels[Info.InputChannels.Count - 1]) // All channels processed for this cycle
                {
                    // Pull data from the mixer to force processing (because these are all decoder streams)
                    //byte[] temp = new byte[20000];
                    //Bass.BASS_ChannelGetData(m_MixerHandle, temp, temp.Length);
                }
            }
            else
            {
                if (Bass.BASS_ChannelGetData(m_MixerHandle, buffer, length) < 1)
                    Info.WriteDebug("Bass Error: " + Bass.BASS_ErrorGetCode());
            }
            return 0;
        }

Is it incorrect to attempt to copy the mixer output stream to the ASIO output?  The mixer output stream is stereo and the ASIO output is two-channel, created like so:

            // Enable ASIO output(s)
            HandleBassAsioError(BassAsio.BASS_ASIO_ChannelEnable(false, 0, m_AsioProcessor, new IntPtr(m_MixerHandle)));
            HandleBassAsioError(BassAsio.BASS_ASIO_ChannelJoin(false, 1, 0));
            HandleBassAsioError(BassAsio.BASS_ASIO_ChannelSetFormat(false, 0, BASSASIOFormat.BASS_ASIO_FORMAT_FLOAT));
            HandleBassAsioError(BassAsio.BASS_ASIO_ChannelSetRate(false, 0, Info.SampleRate));
            HandleBassAsioError(BassAsio.BASS_ASIO_ChannelSetVolume(false, 0, 1f));

I've tried changing the order of everything and changing sample formats to no avail.  I can see that the ChannelGetData call is actually being made to transfer data from the mixer stream to the ASIO output buffer, and it returns the correct number of bytes.  I do notice the ASIOPROC is being called with length = 48 samples.  The ASIO buffer should be 192 samples, is this normal?  Also, if I have the ASIO channels set to a format the device doesn't support (i.e. 32-bit float), will it be resampled before output or just break?
Logged
C Del
Posts: 10


« Reply #4 on: 25 Aug '12 - 18:22 »
Reply with quoteQuote

Still can't figure out the problem, and I need to get this thing running rather urgently.  Can anyone at least offer ideas on what to check for?  I can see that the data is being copied to the ASIO output buffer, so I don't know where to look after that.
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #5 on: 27 Aug '12 - 14:16 »
Reply with quoteQuote

I've tried changing the order of everything and changing sample formats to no avail.  I can see that the ChannelGetData call is actually being made to transfer data from the mixer stream to the ASIO output buffer, and it returns the correct number of bytes.  I do notice the ASIOPROC is being called with length = 48 samples.  The ASIO buffer should be 192 samples, is this normal?

That looks like there is some resampling involved, ie. the ASIO channel's rate isn't the same as the device's rate (BASS_ASIO_ChannelGetRate != BASS_ASIO_GetRate). If you are sending the ASIO input back to the ASIO output, then I would recommend leaving both the input and output channels running at the device's rate, which is the default unless BASS_ASIO_ChannelSetRate is used.

Also, if I have the ASIO channels set to a format the device doesn't support (i.e. 32-bit float), will it be resampled before output or just break?

BASSASIO will convert the data between the user-chosen (via BASS_ASIO_ChannelSetFormat) format and the device's format. Unless the device happens to be using some unexpected/unsupported sample format, I don't think the sample format will be the issue. BASS_ASIO_ChannelGetInfo can be used to check the device's format (see the "format" value).

If you are still having trouble with this, please expand on what the problem is, eg. do you hear no output, or is it distorted, or something else? If it's silent, and the ASIOPROC appears is be working fine, are you sure that the enabled ASIO channels are correct, eg. are the ASIO inputs providing non-0 sample data?

For comparison, is ASIO output working with the pre-compiled examples in the C\BIN directory?
Logged
C Del
Posts: 10


« Reply #6 on: 28 Aug '12 - 20:27 »
Reply with quoteQuote

I compiled the "contest" sample (had to compile it because I'm on an x64 sytem) and it ran perfectly. 

In my own code, I tried dumping the ASIO output buffer to file after each BASS_ChannelGetData call and I can see that non-zero data is being successfully copied to the buffer.  During execution, I am getting nothing but silence on the outputs of the interface when my code runs.  I know the data is coming in and mixing correctly, as the .wav file I'm recording to is being written correctly and plays back as it should.  It almost seems like the output channels aren't running/enabled, but the check I run against (BassAsio.BASS_ASIO_IsStarted() && BassAsio.BASS_ASIO_ChannelIsActive(false, 0) == BASSASIOActive.BASS_ASIO_ACTIVE_ENABLED) passes every time.
Logged
C Del
Posts: 10


« Reply #7 on: 28 Aug '12 - 20:44 »
Reply with quoteQuote

Also, I see that the ASIOPROC callback is actually being called with the correct length.  I must have been pretty tired when I calculated what the length should have been the first time - I was thinking in bits instead of bytes.
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #8 on: 29 Aug '12 - 17:49 »
Reply with quoteQuote

That sounds strange. If you write the sample data to file in the output channel's ASIOPROC and load that into a sample editor, does it look/sound OK? Also, are you using BASS_ASIO_ChannelSetVolume at all? If so, perhaps the output channels are being muted?
Logged
C Del
Posts: 10


« Reply #9 on: 29 Aug '12 - 19:14 »
Reply with quoteQuote

Oy.... I feel like an idiot.  The problem was that I was returning 0 from the ASIOPROC unconditionally.  I think I had copied an ASIO input example in which this was done because the return value is ignored for input, and I had failed to realize that this will not work for ASIO output (which is why my input worked, but no output).  It seems so obvious now that BassAsio would need that information to write to the ASIO driver buffer.

The moral of the story: return the number of bytes you copied to the ASIO output buffer!

Thanks for your help.
Logged
Ian @ un4seen
Administrator
Posts: 15366


« Reply #10 on: 30 Aug '12 - 14:15 »
Reply with quoteQuote

The problem was that I was returning 0 from the ASIOPROC unconditionally.

Oops, I somehow missed that myself in the code you posted above.
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines