Author Topic: Swift not recognizing BASS commands  (Read 746 times)

brom43

  • Posts: 18
Swift not recognizing BASS commands
« on: 31 May '23 - 20:45 »
Ian, I am finding that Xcode with Swift does not recognize several BASS commands - even though they are defined in the bridged bass.h file.  For example, when I use STREAMPROC_PUSH within the BASS_StreamCreate() command, Xcode shows "Cannot find 'STREAMPROC_PUSH' in scope".  What am I doing wrong?

Ian @ un4seen

  • Administrator
  • Posts: 26108
Re: Swift not recognizing BASS commands
« Reply #1 on: 1 Jun '23 - 14:19 »
Indeed, it looks like the STREAMPROC_PUSH/DUMMY/DEVICE constants aren't getting bridged to Swift. I guess it's because they're pointers. I'm not sure if there's a better way to handle it, but one solution is to add a wrapper function in the bridging header with an intptr_t "proc" parameter and change the constants accordingly, like this:

Code: [Select]
#include "bass.h"

// Special STREAMPROCs
#define STREAMPROC_DUMMY (intptr_t)0 // "dummy" stream
#define STREAMPROC_PUSH (intptr_t)-1 // push stream
#define STREAMPROC_DEVICE (intptr_t)-2 // device mix stream
#define STREAMPROC_DEVICE_3D (intptr_t)-3 // device 3D mix stream

static inline HSTREAM BASS_StreamCreateSpecial(DWORD freq, DWORD chans, DWORD flags, intptr_t proc)
{
return BASS_StreamCreate(freq, chans, flags, (STREAMPROC*)proc, 0);
}

And then use that instead of BASS_StreamCreate.

Keith Bromley

  • Guest
Swift not recognizing BASS commands
« Reply #2 on: 1 Jun '23 - 17:48 »
Ian, that trick worked, and Xcode doesn't complain anymore when I use STREAMPROC_PUSH.  But that leads me to a slightly different problem.  What I think I want to accomplish is the following Swift code:

// Create a BASS stream from this outputAudioBuffer:
outputStream = BASS_StreamCreate(
    44100,                        // freq: the default sample rate
    1,                               // chans: number of audio channels, 1 = mono
    0,                               // flags:
    myOutputProc)            // callback proc:

func myOutputProc(_: HRECORD, _: UnsafeRawPointer?, _: DWORD, _: UnsafeMutableRawPointer?) -> BOOL32{
    BASS_StreamPutData( outputStream,                        // handle: HSTREAM
                                      &outputAudioBuffer,                // *buffer:
                                      UInt32(samplesPerFrame)*4 ) // length (in bytes):
    return BOOL32(truncating: true)                                // continue
}

BASS_ChannelPlay( outputStream, -1 )

But, when I use this code in my app, Xcode complains that "Type of expression is ambiguous without more context."  after the BASS_StreamCreate line.  I gather that this error message generally means that I've put some value into a field that the compiler wasn't expecting.  Can you spot what I'm doing wrong?  Thanks again for all your help.

Ian @ un4seen

  • Administrator
  • Posts: 26108
Re: Swift not recognizing BASS commands
« Reply #3 on: 2 Jun '23 - 16:38 »
That BASS_StreamCreate call is missing a "user" parameter; try putting "nil" there. The "myOutputProc" function also appears to be a RECORDPROC rather than a STREAMPROC? The main difference between them is the return value (DWORD vs BOOL).

brom43

  • Posts: 18
Re: Swift not recognizing BASS commands
« Reply #4 on: 2 Jun '23 - 20:35 »
Ian,  I am confused as to where I should put the BASS_StreamPutData() in my app. Let me re-phrase my previous question:

I have Swift code that puts computed audio samples into an array called outputAudioBuffer.  I then want to create a stream from this array so that I can play it through my speakers.  So I think the BASS code I want is:

// Create a BASS stream from this outputAudioBuffer:
outputStream = BASS_StreamCreateSpecial( 44100,                                      // freq: the default sample rate
                                                              1,                                             // chans: number of audio channels, 1 = mono
                                                         UInt32(BASS_SAMPLE_FLOAT),   // flags:
                                                         STREAMPROC_PUSH )                // proc:

BASS_StreamPutData( outputStream,                    // handle: HSTREAM
                                 &outputAudioBuffer,            // *buffer:   &outputAudioBuffer,
                                 UInt32(samplesPerFrame)*4 )    // length (in bytes):

BASS_ChannelPlay( outputStream, -1 )

Xcode does not complain about this code.  My app runs, but I do not hear any audio output.  I am not sure that I am using BASS_StreamPutData() in the correct way.  Do you spot anything that I am doing wrong?

Ian @ un4seen

  • Administrator
  • Posts: 26108
Re: Swift not recognizing BASS commands
« Reply #5 on: 5 Jun '23 - 11:48 »
Try changing the BASS_ChannelPlay "restart" parameter to 0 or false. A non-0 value will clear the buffers, ie. remove the data that you just passed to BASS_StreamPutData.

brom43

  • Posts: 18
Re: Swift not recognizing BASS commands
« Reply #6 on: 5 Jun '23 - 20:58 »
Ian, thank you for your help so far.  I am using BASS in a Swift app to read audio from a file, generate it's spectrum, performs some manipulations on the spectrum, re-generate an audio waveform from these spectral manipulations, and finally to play the re-generated audio. The simplified BASS calls in my code are:
   BASS_Init()
   inputStream = BASS_StreamCreateFile()
    BASS_ChannelPlay(inputStream, -1)

   outputStream = BASS_StreamCreate()

   Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true) { _ in
            BASS_ChannelGetData(inputStream, &spectrum, BASS_DATA_FFT16384)

             // code to manipulate spectrum, re-generate an audio waveform, and put it into an outputAudioBuffer
         
             BASS_StreamPutData( outputStream,
                                                  outputAudioBuffer,
                                                  UInt32(samplesPerFrame*4) )
        } // end of Timer()

       BASS_ChannelPlay( outputStream, 0 )

My problem is that this code sends two audio streams to my computer speakers.  I would like to just hear the output (and not the input).  But I need to use the BASS_ChannelPlay(inputStream) command to send the audio to the BASS_ChannelGetData(inputStream) to get my spectral values.  How do I silence this BASS_ChannelPlay((inputStream), so that I can hear only the desired BASS_ChannelPlay(outputStream) ?

Ian @ un4seen

  • Administrator
  • Posts: 26108
Re: Swift not recognizing BASS commands
« Reply #7 on: 6 Jun '23 - 14:37 »
That looks like "inputStream" should be a decoding channel, ie. have the BASS_STREAM_DECODE flag set in the BASS_StreamCreateFile call. You can then also remove the BASS_ChannelPlay call on it (which would be failing).

brom43

  • Posts: 18
Re: Swift not recognizing BASS commands
« Reply #8 on: 6 Jun '23 - 18:14 »
Ian,  I did as you suggested.  It did prevent the audio playback - which is good.  But it also changed the time synchronization of my entire app - which is bad.  It hurridly processes the input stream as fast as my computer allows - which ruins my attempt to extract the spectrum at a 60 frames-per-second rate.  Do you see a solution to silence the audio output while maintaining the time synchronization?

Ian @ un4seen

  • Administrator
  • Posts: 26108
Re: Swift not recognizing BASS commands
« Reply #9 on: 7 Jun '23 - 12:42 »
Are you using the same timer for processing audio and refreshing a display at 60Hz? If so, you should probably separate those 2 things.

The audio processing could perhaps go in a DSPPROC callback (via BASS_ChannelSetDSP) instead of a timer, and use the BASS_ATTRIB_GRANULE option (via BASS_ChannelSetAttribute) to request a mulitple of 16384 samples per call if that it what the processing requires. Note you would only need a single stream in that case (not separate input/output streams). If you prefer to keep your current input+output streams method then you can use the BASS_StreamPutData return value to check how much data is queued and limit the processing accordingly, eg. don't process more while there is already data queued. The display refreshing should use the output stream handle (ie. outputStream not inputStream) in its BASS_ChannelGetData calls.