Audio Synthesis

Mac OSX specific forum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Audio Synthesis

Post by wilbert »

The code below creates a simple audio stream for audio synthesis.
What it does is already possible using PortAudio but this is a native approach not requiring anything else.

Include file

Code: Select all

; AudioStream.pbi

#AUDIOBUFFER_SIZE = 16384
#AUDIOBUFFER_SAMPLES = #AUDIOBUFFER_SIZE >> 3

Structure AudioStreamBasicDescription
  mSampleRate.d
  mFormatID.l
  mFormatFlags.l
  mBytesPerPacket.l
  mFramesPerPacket.l
  mBytesPerFrame.l
  mChannelsPerFrame.l
  mBitsPerChannel.l
  mReserved.l
EndStructure

Structure AudioQueueBuffer
  mAudioDataBytesCapacity.i
  mAudioData.i
  mAudioDataByteSize.i
  mUserData.i
  mPacketDescriptionCapacity.i
  mPacketDescriptions.i
  mPacketDescriptionCount.i
EndStructure

Structure AudioStreamStereoSample
  l.f
  r.f
EndStructure

Structure AudioStreamSamples
  StructureUnion
    s.d[0]
    ss.AudioStreamStereoSample[0]
  EndStructureUnion
EndStructure

ImportC "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"
  AudioQueueAllocateBuffer(inAQ, inBufferByteSize, *outBuffer)
  AudioQueueDispose(inAQ, inImmediate = #True)
  AudioQueueEnqueueBuffer(inAQ, inBuffer, inNumPacketDescs = 0, *inPacketDescs = #Null)
  AudioQueueNewOutput(*inFormat, inCallbackProc, *inUserData, inCallbackRunLoop, inCallbackRunLoopMode, inFlags, *outAQ)
  AudioQueueSetParameter(inAQ, inParamID, inValue.f)
  AudioQueueStart(inAQ, *inStartTime = #Null)
  AudioQueueStop(inAQ, inImmediate = #True)
EndImport

Prototype AudioStream_Callback_Proto(*SampleData.Float)
Global AudioStream_Callback.AudioStream_Callback_Proto

Global *AudioStream_Buf1.AudioQueueBuffer, *AudioStream_Buf2.AudioQueueBuffer
Global AudioStream_Queue = 0

ProcedureC AudioStream_Callback_(*ptr, queue, *buf.AudioQueueBuffer)
  AudioStream_Callback(*buf\mAudioData)
  AudioQueueEnqueueBuffer(queue, *buf)
EndProcedure

Procedure AudioStream_Init(*Callback, Stereo = #False)
  AudioStream_Callback = *Callback
  Protected fmt.AudioStreamBasicDescription
  fmt\mSampleRate = 44100
  fmt\mFormatID = $6C70636D; kAudioFormatLinearPCM
  fmt\mFormatFlags = 1; kAudioFormatFlagIsFloat
  fmt\mFramesPerPacket = 1
  fmt\mBytesPerFrame = 8
  fmt\mBytesPerPacket = 8
  If Stereo
    fmt\mChannelsPerFrame = 2
    fmt\mBitsPerChannel = 32
  Else
    fmt\mChannelsPerFrame = 1
    fmt\mBitsPerChannel = 64
  EndIf
  If AudioQueueNewOutput(@fmt, @AudioStream_Callback_(), #Null, #Null, #Null, 0, @AudioStream_Queue) = 0
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf1)
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf2)
    *AudioStream_Buf1\mAudioDataByteSize = #AUDIOBUFFER_SIZE
    *AudioStream_Buf2\mAudioDataByteSize = #AUDIOBUFFER_SIZE
  EndIf
EndProcedure

Procedure AudioStream_SetVolume(volume.f)
  If AudioStream_Queue
    AudioQueueSetParameter(AudioStream_Queue, 1, volume); 1 = kAudioQueueParam_Volume
  EndIf
EndProcedure

Procedure AudioStream_Start()
  If AudioStream_Queue
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf1)
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf2)
    AudioQueueStart(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Stop()
  If AudioStream_Queue
    AudioQueueStop(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Dispose()
  If AudioStream_Queue
    AudioQueueDispose(AudioStream_Queue)
    AudioStream_Queue = 0  
  EndIf
EndProcedure
Last edited by wilbert on Tue Feb 26, 2013 11:12 am, edited 4 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

Example code (mono)

Code: Select all

EnableExplicit

XIncludeFile "AudioStream.pbi"

Global Phase.d

Procedure SampleCallback(*out.AudioStreamSamples)
  Protected i = 0
  While i < #AUDIOBUFFER_SAMPLES
    *out\s[i] = Sin(Phase) / 6
    Phase + 0.06
    i + 1
  Wend
EndProcedure

AudioStream_Init(@SampleCallback())
AudioStream_SetVolume(0.7)
AudioStream_Start()

MessageRequester("Audio Synthesis Demo", "Playing audio")

AudioStream_Dispose()
Example code (stereo)

Code: Select all

EnableExplicit

XIncludeFile "AudioStream.pbi"

Global Phase.d

Procedure SampleCallback(*out.AudioStreamSamples)
  Protected i = 0
  While i < #AUDIOBUFFER_SAMPLES
    *out\ss[i]\l = Sin(Phase) / 6
    *out\ss[i]\r = Sin(Phase * 2) / 6
    Phase + 0.06
    i + 1
  Wend
EndProcedure

AudioStream_Init(@SampleCallback(), #True)
AudioStream_SetVolume(0.7)
AudioStream_Start()

MessageRequester("Audio Synthesis Demo", "Playing audio")

AudioStream_Dispose()
Last edited by wilbert on Tue Feb 26, 2013 11:13 am, edited 2 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
WilliamL
Addict
Addict
Posts: 1215
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Audio Synthesis

Post by WilliamL »

Thanks wilbert!

It works fine. Now just to figure out what to do with it... :)
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Audio Synthesis

Post by J. Baker »

Jiminy Cricket!!!

What ever you do, don't do "*output + 2" with headphones on.

Nice work wilbert! But I'll wait till day, when I don't need headphones on to fiddle around with it. ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

J. Baker wrote:What ever you do, don't do "*output + 2" with headphones on.
I made a similar mistake with speakers on; wasn't nice :shock:

Anyway, I changed the include file and example above.
I changed it so mono is 1 x 64-bit float and stereo is 2 x 32-bit float.
This way the number of samples is the same for both and with a structure union it is easier to access the samples (no need to increase a memory pointer).

@William,
Something like this would be nice http://tonematrix.audiotool.com
It's created by someone else using Flash but I suppose it should be possible to create something similar using the canvas gadget.
Windows (x64)
Raspberry Pi OS (Arm64)
WilliamL
Addict
Addict
Posts: 1215
Joined: Mon Aug 04, 2008 10:56 pm
Location: Seattle, USA

Re: Audio Synthesis

Post by WilliamL »

wilbert,

tonematrix is a lot of fun and the site is a real time-waster :) I agree it looks like a canvas gadget waiting to happen.

I was listening to http://www.audiotool.com/track/yesterday-pa8gus/ ...enviro-synthesis (just made that up)
MacBook Pro-M1 (2021), Sonoma 14.3.1 (CLT 15.3), PB 6.10b7 M1
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Audio Synthesis

Post by J. Baker »

wilbert wrote:
J. Baker wrote:What ever you do, don't do "*output + 2" with headphones on.
I made a similar mistake with speakers on; wasn't nice :shock:

Anyway, I changed the include file and example above.
I changed it so mono is 1 x 64-bit float and stereo is 2 x 32-bit float.
This way the number of samples is the same for both and with a structure union it is easier to access the samples (no need to increase a memory pointer).
Cool. ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Audio Synthesis

Post by J. Baker »

Hello? Who is it? :D

Code: Select all

EnableExplicit

XIncludeFile "AudioStream.pbi"

Global Phase.d

Procedure SampleCallback(*out.AudioStreamSamples)
  Protected i = 0
  While i < #AUDIOBUFFER_SAMPLES
    *out\s[i] = Sin(Phase) / 3
     If i / 500
       Phase + 0.35
     Else
       Phase + 0.25
     EndIf
    i + 1
  Wend
EndProcedure

Define Ring = 0

AudioStream_Init(@SampleCallback())
AudioStream_SetVolume(0.7)

Repeat
  
 AudioStream_Start()

  Delay(1000)

   AudioStream_Stop()

  Delay(100)

 Ring + 1

Until Ring = 2

AudioStream_Dispose()
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

@Joe, was that your phone ringing ? :wink:

@William, you are right, it is fun that tone matrix. Don't know if it would be hard to recreate using PB.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

For MacroTune we used the hardware abstraction layer which is more low-level.

Code: Select all

ImportC ""
  AudioUnitSetVolume__(Unit, ID, Scope, Element, Value.f, BufferOffsetInFrames) As "_AudioUnitSetParameter"
EndImport

#kAudioDevicePropertyBufferFrameSize  = $6673697a; 'fsiz'
#kAudioFormatFlagIsPacked             = 8
#kAudioFormatFlagIsSignedInteger      = 4
#kAudioFormatLinearPCM                = $6C70636D; 'lpcm'
#kAudioUnitManufacturer_Apple         = $6170706C; 'appl'
#kAudioUnitProperty_SetRenderCallback = 23
#kAudioUnitProperty_StreamFormat      = 8
#kAudioUnitScope_Input                = 1
#kAudioUnitSubType_DefaultOutput      = $64656620; 'def '
#kAudioUnitType_Output                = $61756F75; 'auou'
#kHALOutputParam_Volume               = 14

Structure AudioComponentDescription
  componentType.l
  componentSubType.l
  componentManufacturer.l
  componentFlags.l
  componentFlagsMask.l
EndStructure

Structure AudioStreamBasicDescription
  mSampleRate.d
  mFormatID.l
  mFormatFlags.l
  mBytesPerPacket.l
  mFramesPerPacket.l
  mBytesPerFrame.l
  mChannelsPerFrame.l
  mBitsPerChannel.l
  mReserved.l
EndStructure

Structure AURenderCallbackStruct
  *inputProc
  inputProcRefCon.i
EndStructure


Global Audio_Unit.i

Procedure Audio_SetVolume(Volume); 0 - 100
  Protected factor.f
  If Volume
    factor = Pow(10, 40 * (Volume - 100) * 0.0005)
  EndIf
  AudioUnitSetVolume__(Audio_Unit, #kHALOutputParam_Volume, #kAudioUnitScope_Input, 0, factor, 0)
EndProcedure

Procedure Audio_Init(*CallbackFunction, BufferSize.l = 2048)
  
  Protected ACD.AudioComponentDescription
  Protected ASBD.AudioStreamBasicDescription
  Protected CallbackStruct.AURenderCallbackStruct
  
  ACD\componentType         = #kAudioUnitType_Output
  ACD\componentSubType      = #kAudioUnitSubType_DefaultOutput
  ACD\componentManufacturer = #kAudioUnitManufacturer_Apple
  
  ASBD\mSampleRate        = 44100
  ASBD\mFormatID          = #kAudioFormatLinearPCM
  ASBD\mFormatFlags       = #kAudioFormatFlagIsPacked | #kAudioFormatFlagIsSignedInteger
  ASBD\mBytesPerPacket    = 4
  ASBD\mFramesPerPacket   = 1
  ASBD\mBytesPerFrame     = 4
  ASBD\mChannelsPerFrame  = 2
  ASBD\mBitsPerChannel    = 16
  
  CallbackStruct\inputProc = *CallbackFunction

  AudioComponentInstanceNew_(AudioComponentFindNext_(#Null, @ACD), @Audio_Unit)
  AudioUnitSetProperty_(Audio_Unit, #kAudioUnitProperty_SetRenderCallback, #kAudioUnitScope_Input, 0, @CallbackStruct, SizeOf(AURenderCallbackStruct))
  AudioUnitSetProperty_(Audio_Unit, #kAudioUnitProperty_StreamFormat, #kAudioUnitScope_Input, 0, @ASBD, SizeOf(AudioStreamBasicDescription))
  AudioUnitSetProperty_(Audio_Unit, #kAudioDevicePropertyBufferFrameSize, #kAudioUnitScope_Input, 0, @BufferSize, SizeOf(Long))
  AudioUnitInitialize_(Audio_Unit)
  
EndProcedure

Procedure Audio_Start()
  ProcedureReturn Bool(AudioOutputUnitStart_(Audio_Unit) = 0)
EndProcedure

Procedure Audio_Stop()
  AudioOutputUnitStop_(Audio_Unit)
EndProcedure

Procedure Audio_Terminate()
  If Audio_Unit
    AudioOutputUnitStop_(Audio_Unit)
    AudioUnitUninitialize_(Audio_Unit)
    AudioComponentInstanceDispose_(Audio_Unit)
    Audio_Unit = 0
  EndIf
EndProcedure


; *** Audio test ***

Global Phase.f = 0
Global Phase_add.f = 0.1
Global Sample.w

ProcedureC Callback(*inRefcon, *ioActionFlags, *inTimeStamp, inBusNumber.l, inNumberFrames.l, *ioData)
  Protected i.i, *out.Word = PeekI(*ioData + SizeOf(Integer) + 8)
  While i < inNumberFrames
    Phase + Phase_add
    If Phase >= 2*#PI : Phase - 2*#PI : EndIf
    Sample = Sin(Phase) * 32767
    *out\w = Sample : *out + 2
    *out\w = Sample : *out + 2
    i + 1
  Wend
  ProcedureReturn 0
EndProcedure

Audio_Init(@Callback())
Audio_SetVolume(50)

If Audio_Start()
  MessageRequester("", "Click to close application")
  Audio_Terminate()
EndIf
Last edited by wilbert on Fri Aug 09, 2019 3:23 pm, edited 8 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Audio Synthesis

Post by Wolfram »

Hi Wilbert,

thanks for these example.
I try to use it to play back a WAVE file but it crashes.
Do you have any idea why?

Code: Select all

EnableExplicit
;-AudioStream.pbi

#AUDIOBUFFER_SIZE = 16384
#AUDIOBUFFER_SAMPLES = #AUDIOBUFFER_SIZE >> 3



    #kAudioFormatFlagIsFloat                     = (1 << 0)     ;// 0x1
    #kAudioFormatFlagIsBigEndian                 = (1 << 1)     ;// 0x2
    #kAudioFormatFlagIsSignedInteger             = (1 << 2)     ;// 0x4
    #kAudioFormatFlagIsPacked                    = (1 << 3)     ;// 0x8
    #kAudioFormatFlagIsAlignedHigh               = (1 << 4)     ;// 0x10
    #kAudioFormatFlagIsNonInterleaved            = (1 << 5)     ;// 0x20
    #kAudioFormatFlagIsNonMixable                = (1 << 6)     ;// 0x40
    #kAudioFormatFlagsAreAllClear                = (1 << 31)
    
    #kLinearPCMFormatFlagIsFloat                 = #kAudioFormatFlagIsFloat
    #kLinearPCMFormatFlagIsBigEndian             = #kAudioFormatFlagIsBigEndian
    #kLinearPCMFormatFlagIsSignedInteger         = #kAudioFormatFlagIsSignedInteger
    #kLinearPCMFormatFlagIsPacked                = #kAudioFormatFlagIsPacked
    #kLinearPCMFormatFlagIsAlignedHigh           = #kAudioFormatFlagIsAlignedHigh
    #kLinearPCMFormatFlagIsNonInterleaved        = #kAudioFormatFlagIsNonInterleaved
    #kLinearPCMFormatFlagIsNonMixable            = #kAudioFormatFlagIsNonMixable
    #kLinearPCMFormatFlagsSampleFractionShift    = 7
    #kLinearPCMFormatFlagsSampleFractionMask     = ($3F << #kLinearPCMFormatFlagsSampleFractionShift)
    #kLinearPCMFormatFlagsAreAllClear            = #kAudioFormatFlagsAreAllClear
    
    #kAppleLosslessFormatFlag_16BitSourceData    = 1
    #kAppleLosslessFormatFlag_20BitSourceData    = 2
    #kAppleLosslessFormatFlag_24BitSourceData    = 3
    #kAppleLosslessFormatFlag_32BitSourceData    = 4

    
Structure AudioStreamBasicDescription
  mSampleRate.d
  mFormatID.l
  mFormatFlags.l
  mBytesPerPacket.l
  mFramesPerPacket.l
  mBytesPerFrame.l
  mChannelsPerFrame.l
  mBitsPerChannel.l
  mReserved.l
EndStructure

Structure AudioQueueBuffer
  mAudioDataBytesCapacity.i
  mAudioData.i
  mAudioDataByteSize.i
  mUserData.i
  mPacketDescriptionCapacity.i
  mPacketDescriptions.i
  mPacketDescriptionCount.i
EndStructure

Structure AudioStreamStereoSample
  l.f
  r.f
EndStructure

Structure AudioStreamSamples
  StructureUnion
    s.d[0]
    ss.AudioStreamStereoSample[0]
  EndStructureUnion
EndStructure

ImportC "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"
  AudioQueueAllocateBuffer(inAQ, inBufferByteSize, *outBuffer)
  AudioQueueDispose(inAQ, inImmediate = #True)
  AudioQueueEnqueueBuffer(inAQ, inBuffer, inNumPacketDescs = 0, *inPacketDescs = #Null)
  AudioQueueNewOutput(*inFormat, inCallbackProc, *inUserData, inCallbackRunLoop, inCallbackRunLoopMode, inFlags, *outAQ)
  AudioQueueSetParameter(inAQ, inParamID, inValue.f)
  AudioQueueStart(inAQ, *inStartTime = #Null)
  AudioQueueStop(inAQ, inImmediate = #True)
EndImport

Prototype AudioStream_Callback_Proto(*SampleData.Float)
Global AudioStream_Callback.AudioStream_Callback_Proto

Global *AudioStream_Buf1.AudioQueueBuffer, *AudioStream_Buf2.AudioQueueBuffer
Global AudioStream_Queue = 0

ProcedureC AudioStream_Callback_(*ptr, queue, *buf.AudioQueueBuffer)
  AudioStream_Callback(*buf\mAudioData)
  AudioQueueEnqueueBuffer(queue, *buf)
EndProcedure

Procedure AudioStream_Init(*Callback, Stereo = #False)
  AudioStream_Callback = *Callback
  Protected fmt.AudioStreamBasicDescription
  fmt\mSampleRate = 44100
  fmt\mFormatID = $6C70636D; kAudioFormatLinearPCM
  fmt\mFormatFlags = #kAudioFormatFlagIsBigEndian ; #kAudioFormatFlagIsFloat
  fmt\mFramesPerPacket = 1
  fmt\mBytesPerFrame = 8
  fmt\mBytesPerPacket = 8
  fmt\
  If Stereo
    fmt\mChannelsPerFrame = 2
    fmt\mBitsPerChannel = 16
  Else
    fmt\mChannelsPerFrame = 1
    fmt\mBitsPerChannel = 64
  EndIf
  If AudioQueueNewOutput(@fmt, @AudioStream_Callback_(), #Null, #Null, #Null, 0, @AudioStream_Queue) = 0
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf1)
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf2)
    *AudioStream_Buf1\mAudioDataByteSize = #AUDIOBUFFER_SIZE
    *AudioStream_Buf2\mAudioDataByteSize = #AUDIOBUFFER_SIZE
  EndIf
EndProcedure

Procedure AudioStream_SetVolume(volume.f)
  If AudioStream_Queue
    AudioQueueSetParameter(AudioStream_Queue, 1, volume); 1 = kAudioQueueParam_Volume
  EndIf
EndProcedure

Procedure AudioStream_Start()
  If AudioStream_Queue
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf1)
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf2)
    AudioQueueStart(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Stop()
  If AudioStream_Queue
    AudioQueueStop(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Dispose()
  If AudioStream_Queue
    AudioQueueDispose(AudioStream_Queue)
    AudioStream_Queue = 0  
  EndIf
EndProcedure

;-Main


; XIncludeFile "AudioStream.pbi"
Define FileName.s = "/Users/YourNmae/Desktop/16bit_44100WAVE_Stereo.wav"
If OpenFile(1, FileName)
  Define FileSize.i = Lof(1)
  Global Dim WaveBuffer.w(FileSize)
  Global WaveLength =30000
  ReadData(1, @WaveBuffer(), FileSize)
  
  CloseFile(1)
EndIf


Procedure SampleCallback(*out.AudioStreamSamples)
  Protected i = 0
  Protected L = 128 ;Start of the Wave Samples

  While L = < WaveLength
    While i < #AUDIOBUFFER_SAMPLES
      *out\ss[i]\l = WaveBuffer(L )
      *out\ss[i]\r = WaveBuffer(L  +1)
      
      L +1
      i + 1
    Wend
    i = 0
    L +1
  Wend

EndProcedure

AudioStream_Init(@SampleCallback(), #True)
AudioStream_SetVolume(0.5)
AudioStream_Start()

MessageRequester("Audio Synthesis Demo", "Playing audio")

AudioStream_Dispose()
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

@Wolfram,
There were multiple things wrong with your code.
Here's my attempt.

Code: Select all

EnableExplicit
;-AudioStream.pbi

#AUDIOBUFFER_SIZE = 8192
#AUDIOBUFFER_SAMPLES = #AUDIOBUFFER_SIZE >> 2



    #kAudioFormatFlagIsFloat                     = (1 << 0)     ;// 0x1
    #kAudioFormatFlagIsBigEndian                 = (1 << 1)     ;// 0x2
    #kAudioFormatFlagIsSignedInteger             = (1 << 2)     ;// 0x4
    #kAudioFormatFlagIsPacked                    = (1 << 3)     ;// 0x8
    #kAudioFormatFlagIsAlignedHigh               = (1 << 4)     ;// 0x10
    #kAudioFormatFlagIsNonInterleaved            = (1 << 5)     ;// 0x20
    #kAudioFormatFlagIsNonMixable                = (1 << 6)     ;// 0x40
    #kAudioFormatFlagsAreAllClear                = (1 << 31)
    
    #kLinearPCMFormatFlagIsFloat                 = #kAudioFormatFlagIsFloat
    #kLinearPCMFormatFlagIsBigEndian             = #kAudioFormatFlagIsBigEndian
    #kLinearPCMFormatFlagIsSignedInteger         = #kAudioFormatFlagIsSignedInteger
    #kLinearPCMFormatFlagIsPacked                = #kAudioFormatFlagIsPacked
    #kLinearPCMFormatFlagIsAlignedHigh           = #kAudioFormatFlagIsAlignedHigh
    #kLinearPCMFormatFlagIsNonInterleaved        = #kAudioFormatFlagIsNonInterleaved
    #kLinearPCMFormatFlagIsNonMixable            = #kAudioFormatFlagIsNonMixable
    #kLinearPCMFormatFlagsSampleFractionShift    = 7
    #kLinearPCMFormatFlagsSampleFractionMask     = ($3F << #kLinearPCMFormatFlagsSampleFractionShift)
    #kLinearPCMFormatFlagsAreAllClear            = #kAudioFormatFlagsAreAllClear
    
    #kAppleLosslessFormatFlag_16BitSourceData    = 1
    #kAppleLosslessFormatFlag_20BitSourceData    = 2
    #kAppleLosslessFormatFlag_24BitSourceData    = 3
    #kAppleLosslessFormatFlag_32BitSourceData    = 4

    
Structure AudioStreamBasicDescription
  mSampleRate.d
  mFormatID.l
  mFormatFlags.l
  mBytesPerPacket.l
  mFramesPerPacket.l
  mBytesPerFrame.l
  mChannelsPerFrame.l
  mBitsPerChannel.l
  mReserved.l
EndStructure

Structure AudioQueueBuffer
  mAudioDataBytesCapacity.i
  mAudioData.i
  mAudioDataByteSize.i
  mUserData.i
  mPacketDescriptionCapacity.i
  mPacketDescriptions.i
  mPacketDescriptionCount.i
EndStructure

Structure AudioStreamStereoSample
  l.w
  r.w
EndStructure

Structure AudioStreamSamples
  ss.AudioStreamStereoSample[0]
EndStructure

ImportC "-framework AudioToolbox"
  AudioQueueAllocateBuffer(inAQ, inBufferByteSize, *outBuffer)
  AudioQueueDispose(inAQ, inImmediate = #True)
  AudioQueueEnqueueBuffer(inAQ, inBuffer, inNumPacketDescs = 0, *inPacketDescs = #Null)
  AudioQueueNewOutput(*inFormat, inCallbackProc, *inUserData, inCallbackRunLoop, inCallbackRunLoopMode, inFlags, *outAQ)
  AudioQueueSetParameter(inAQ, inParamID, inValue.f)
  AudioQueueStart(inAQ, *inStartTime = #Null)
  AudioQueueStop(inAQ, inImmediate = #True)
EndImport

Prototype AudioStream_Callback_Proto(*SampleData.Float)
Global AudioStream_Callback.AudioStream_Callback_Proto

Global *AudioStream_Buf1.AudioQueueBuffer, *AudioStream_Buf2.AudioQueueBuffer
Global AudioStream_Queue = 0

ProcedureC AudioStream_Callback_(*ptr, queue, *buf.AudioQueueBuffer)
  AudioStream_Callback(*buf\mAudioData)
  AudioQueueEnqueueBuffer(queue, *buf)
EndProcedure

Procedure AudioStream_Init(*Callback)
  AudioStream_Callback = *Callback
  Protected fmt.AudioStreamBasicDescription
  fmt\mSampleRate = 44100
  fmt\mFormatID = $6C70636D; kAudioFormatLinearPCM
  fmt\mFormatFlags = #kAudioFormatFlagIsPacked | #kLinearPCMFormatFlagIsSignedInteger
  fmt\mFramesPerPacket = 1
  fmt\mBytesPerFrame = 4
  fmt\mBytesPerPacket = 4
  fmt\mChannelsPerFrame = 2
  fmt\mBitsPerChannel = 16
  If AudioQueueNewOutput(@fmt, @AudioStream_Callback_(), #Null, #Null, #Null, 0, @AudioStream_Queue) = 0
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf1)
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf2)
    *AudioStream_Buf1\mAudioDataByteSize = #AUDIOBUFFER_SIZE
    *AudioStream_Buf2\mAudioDataByteSize = #AUDIOBUFFER_SIZE
  EndIf
EndProcedure

Procedure AudioStream_SetVolume(volume.f)
  If AudioStream_Queue
    AudioQueueSetParameter(AudioStream_Queue, 1, volume); 1 = kAudioQueueParam_Volume
  EndIf
EndProcedure

Procedure AudioStream_Start()
  If AudioStream_Queue
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf1)
    AudioStream_Callback_(#Null, AudioStream_Queue, *AudioStream_Buf2)
    AudioQueueStart(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Stop()
  If AudioStream_Queue
    AudioQueueStop(AudioStream_Queue)
  EndIf
EndProcedure

Procedure AudioStream_Dispose()
  If AudioStream_Queue
    AudioQueueDispose(AudioStream_Queue)
    AudioStream_Queue = 0  
  EndIf
EndProcedure

;-Main


; XIncludeFile "AudioStream.pbi"
Define FileName.s = "/Users/YourNmae/Desktop/16bit_44100WAVE_Stereo.wav"
If OpenFile(1, FileName)
  Define FileSize.i = Lof(1)
  Global SamplePos = 44
  Global NumSamples = FileSize / SizeOf(AudioStreamStereoSample)
  Global Dim WaveBuffer.AudioStreamStereoSample(NumSamples)
  ReadData(1, @WaveBuffer(), FileSize)
  
  CloseFile(1)
EndIf


Procedure SampleCallback(*out.AudioStreamSamples)
  Protected i = 0
  While i < #AUDIOBUFFER_SAMPLES
    If SamplePos < NumSamples
      *out\ss[i]\l = WaveBuffer(SamplePos)\l
      *out\ss[i]\r = WaveBuffer(SamplePos)\r
      SamplePos + 1      
    Else
      *out\ss[i]\l = 0
      *out\ss[i]\r = 0
    EndIf
    i + 1
  Wend

EndProcedure

AudioStream_Init(@SampleCallback())
AudioStream_SetVolume(0.5)
AudioStream_Start()

MessageRequester("Audio Synthesis Demo", "Playing audio")

AudioStream_Dispose()
AUHAL version

Code: Select all

ImportC ""
  AudioUnitSetVolume__(Unit, ID, Scope, Element, Value.f, BufferOffsetInFrames) As "_AudioUnitSetParameter"
EndImport

DataSection
  dOut:
  Data.l $61756f75,$64656620,$6170706c,0,0
  PCM16Bit:
  Data.l 0,$40E58880,$6C70636D,12,4,1,4,2,16,0
  PCM32BitFloat:
  Data.l 0,$40E58880,$6C70636D,1,8,1,8,2,32,0
EndDataSection

Global Audio_Unit.i

Procedure Audio_SetVolume(Volume); 0 - 100
  Protected factor.f
  If Volume
    factor = Pow(10, 40 * (Volume - 100) * 0.0005)
  EndIf
  AudioUnitSetVolume__(Audio_Unit, 14, 1, 0, factor, 0)
EndProcedure

Procedure Audio_Init(*CallbackFunction, BufferSize.l = 2048)
  Protected Dim CallbackStruct.i(1) : CallbackStruct(0) = *CallbackFunction
  AudioComponentInstanceNew_(AudioComponentFindNext_(#Null, ?dOut), @Audio_Unit)
  AudioUnitSetProperty_(Audio_Unit, 23, 1, 0, @CallbackStruct(), SizeOf(Integer) << 1)
  AudioUnitSetProperty_(Audio_Unit, 8, 1, 0, ?PCM16Bit, 40)
  AudioUnitSetProperty_(Audio_Unit, $6673697a, 1, 0, @BufferSize, 4)
  AudioUnitInitialize_(Audio_Unit)
EndProcedure

Procedure Audio_Start()
  ProcedureReturn Bool(AudioOutputUnitStart_(Audio_Unit) = 0)
EndProcedure

Procedure Audio_Stop()
  AudioOutputUnitStop_(Audio_Unit)
EndProcedure

Procedure Audio_Terminate()
  If Audio_Unit
    AudioOutputUnitStop_(Audio_Unit)
    AudioUnitUninitialize_(Audio_Unit)
    AudioComponentInstanceDispose_(Audio_Unit)
    Audio_Unit = 0
  EndIf
EndProcedure


;-Main

Structure AudioStreamStereoSample
  l.w
  r.w
EndStructure

Structure AudioStreamSamples
  ss.AudioStreamStereoSample[0]
EndStructure

Define FileName.s = "/Users/YourNmae/Desktop/16bit_44100WAVE_Stereo.wav"
If OpenFile(1, FileName)
  Define FileSize.i = Lof(1)
  Global SamplePos = 44
  Global NumSamples = FileSize / SizeOf(AudioStreamStereoSample)
  Global Dim WaveBuffer.AudioStreamStereoSample(NumSamples)
  ReadData(1, @WaveBuffer(), FileSize)
  
  CloseFile(1)
EndIf


ProcedureC SampleCallback(*inRefcon, *ioActionFlags, *inTimeStamp, inBusNumber.l, inNumberFrames.l, *ioData)
  Protected i.i, *out.AudioStreamSamples = PeekI(*ioData + SizeOf(Integer) + 8)
  While i < inNumberFrames
    If SamplePos < NumSamples
      *out\ss[i]\l = WaveBuffer(SamplePos)\l
      *out\ss[i]\r = WaveBuffer(SamplePos)\r
      SamplePos + 1      
    Else
      *out\ss[i]\l = 0
      *out\ss[i]\r = 0
    EndIf
    i + 1
  Wend

EndProcedure

Audio_Init(@SampleCallback())
Audio_SetVolume(50)
Audio_Start()

MessageRequester("Audio Synthesis Demo", "Playing audio")

Audio_Terminate()
Last edited by wilbert on Thu Mar 25, 2021 2:58 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Audio Synthesis

Post by Wolfram »

Hello,

I'm trying to play a 16 bit mono WAVE file by using this code, but there is something wrong.
The WAVE file plays to slow. This is not happened if I use 4 for BytesPerFrame and mBytesPerPacket and 2 per ChannelsPerFrame

Code: Select all

Protected fmt.AudioStreamBasicDescription
  fmt\mSampleRate = SampleRate
  fmt\mFormatID = $6C70636D; kAudioFormatLinearPCM
  fmt\mFormatFlags = #kAudioFormatFlagIsPacked | #kLinearPCMFormatFlagIsSignedInteger
  fmt\mFramesPerPacket = 1
  fmt\mBytesPerFrame = 2
  fmt\mBytesPerPacket = 2
  fmt\mChannelsPerFrame = 1
  fmt\mBitsPerChannel = 16
  If AudioQueueNewOutput(@fmt, @AudioStream_Callback_(), #Null, #Null, #Null, 0, @AudioStream_Queue) = 0
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf1)
    AudioQueueAllocateBuffer(AudioStream_Queue, #AUDIOBUFFER_SIZE, @*AudioStream_Buf2)
    *AudioStream_Buf1\mAudioDataByteSize = #AUDIOBUFFER_SIZE
    *AudioStream_Buf2\mAudioDataByteSize = #AUDIOBUFFER_SIZE
  EndIf
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Audio Synthesis

Post by wilbert »

Wolfram wrote:I'm trying to play a 16 bit mono WAVE file by using this code, but there is something wrong.
The WAVE file plays to slow. This is not happened if I use 4 for BytesPerFrame and mBytesPerPacket and 2 per ChannelsPerFrame
I'm not sure why this is happening. You can try adding #kAudioFormatFlagIsNonInterleaved to the format flags (value of the constant is 32).
If it doesn't work, maybe the AUHAL version (see above) works.
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Audio Synthesis

Post by Wolfram »

Hi Wilbert,

I already tried that.
Do you get the same effect on your system?
macOS Catalina 10.15.7
Post Reply