Decoding mp3 / m4a into sample data

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

Decoding mp3 / m4a into sample data

Post by wilbert »

An example of how to decode mp3 / m4a into 44100 Hz, 16 bit audio samples.
Maybe someone finds it useful :)

Code: Select all

;- Constants

#kAudioFormatFlagIsPacked        = 8
#kAudioFormatFlagIsSignedInteger = 4
#kAudioFormatLinearPCM           = $6c70636d; 'lpcm'

#kExtAudioFileProperty_ClientDataFormat = $63666d74; 'cfmt'
#kExtAudioFileProperty_FileDataFormat   = $66666d74; 'ffmt'
#kExtAudioFileProperty_FileLengthFrames = $2366726d; '#frm'

#noErr = 0

;- Structures

Structure AudioBuffer Align #PB_Structure_AlignC
  mNumberChannels.l
  mDataByteSize.l
  *mData
EndStructure

Structure AudioBufferList Align #PB_Structure_AlignC
  mNumberBuffers.l
  mBuffers.AudioBuffer[1]
EndStructure

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

Structure Sample
  l.w
  r.w
EndStructure

Structure Samples
  sample.Sample[0]
EndStructure

;- Imports

ImportC "-framework AudioToolbox"
  ExtAudioFileDispose(inExtAudioFile)
  ExtAudioFileGetProperty(inExtAudioFile, inPropertyID, *ioPropertyDataSize.Long, *outPropertyData)
  ExtAudioFileOpenURL(inURL, *outExtAudioFile)
  ExtAudioFileRead(inExtAudioFile, *ioNumberFrames.Long, *ioData.AudioBufferList)
  ExtAudioFileSeek(inExtAudioFile, inFrameOffset.q)
  ExtAudioFileSetProperty(inExtAudioFile, inPropertyID, inPropertyDataSize.l, *inPropertyData)
EndImport



;- Example

; Open file

FileName.s = "Test.mp3"
If ExtAudioFileOpenURL(CocoaMessage(0, 0, "NSURL fileURLWithPath:$", @FileName), @ExtAudioFile) = #noErr
  
  ; Get file format and length in frames
  
  FileFormat.AudioStreamBasicDescription
  Size = SizeOf(FileFormat)
  ExtAudioFileGetProperty(ExtAudioFile, #kExtAudioFileProperty_FileDataFormat, @Size, @FileFormat)
  
  Size = SizeOf(Quad)
  ExtAudioFileGetProperty(ExtAudioFile, #kExtAudioFileProperty_FileLengthFrames, @Size, @FrameCount.q)
  
  Debug "Source format id : " + PeekS(@FileFormat\mFormatID, 4, #PB_Ascii)
  Debug "Source length in seconds : " + StrD(FrameCount / FileFormat\mSampleRate, 3)
  
  ; Set client format to 44100 Hz, 16 bit stereo
  
  ClientFormat.AudioStreamBasicDescription
  ClientFormat\mSampleRate = 44100
  ClientFormat\mFormatID = #kAudioFormatLinearPCM
  ClientFormat\mFormatFlags = #kAudioFormatFlagIsPacked | #kAudioFormatFlagIsSignedInteger
  ClientFormat\mBytesPerPacket = 4
  ClientFormat\mFramesPerPacket = 1
  ClientFormat\mBytesPerFrame = 4
  ClientFormat\mChannelsPerFrame = 2
  ClientFormat\mBitsPerChannel = 16
  
  Size = SizeOf(ClientFormat)
  ExtAudioFileSetProperty(ExtAudioFile, #kExtAudioFileProperty_ClientDataFormat, @Size, @ClientFormat)
  
  ; Setup buffer and AudioBufferList
  
  ClientFrameCount.q = FrameCount * ClientFormat\mSampleRate / FileFormat\mSampleRate
  BufferSize = ClientFrameCount * ClientFormat\mBytesPerFrame
  *Buffer.Samples = AllocateMemory(BufferSize)
  
  AudioBufferList.AudioBufferList
  AudioBufferList\mNumberBuffers = 1
  AudioBufferList\mBuffers[0]\mNumberChannels = ClientFormat\mChannelsPerFrame
  AudioBufferList\mBuffers[0]\mDataByteSize = BufferSize
  AudioBufferList\mBuffers[0]\mData = *Buffer
  
  NumberFrames.l = ClientFrameCount
  If ExtAudioFileRead(ExtAudioFile, @NumberFrames, @AudioBufferList) = #noErr
    Debug "Total client frames : " + Str(ClientFrameCount)
    Debug "Frames read : " + Str(NumberFrames)
    
    Debug "First 10 samples left channel : "
    For i = 0 To 9
      Debug *Buffer\sample[i]\l
    Next
    
  EndIf

  ExtAudioFileDispose(ExtAudioFile)
  FreeMemory(*Buffer)
  
EndIf
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Decoding mp3 / m4a into sample data

Post by Wolfram »

Thanks Wilbert,

but here on OSX 10.7 the Buffer is empty.
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Decoding mp3 / m4a into sample data

Post by wilbert »

Wolfram wrote: here on OSX 10.7 the Buffer is empty.
I don’t have 10.7 but the functions I used should be available on 10.7 :?
Do you get any debug output ?
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Decoding mp3 / m4a into sample data

Post by Wolfram »

Yes I get the debugger values and they seams to be right, but showmemory(*Buffer, 1000) shows an empty buffer.
And of cause the first 10 samples in the debugger are also 0.
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Decoding mp3 / m4a into sample data

Post by wilbert »

There are 4 bytes per stereo sample so 1000 bytes = 250 samples.
This is less than 6 milliseconds.
So there is a possibility if the audio starts with a small silence, that you have to look a bit further into the buffer.
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 567
Joined: Thu May 30, 2013 4:39 pm

Re: Decoding mp3 / m4a into sample data

Post by Wolfram »

I didn't expect so much silent at the beginning.
In my test I get the first Sound after 2500 Bytes.

Thanks!
Last edited by Wolfram on Tue Aug 14, 2018 1:48 pm, edited 1 time in total.
macOS Catalina 10.15.7
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Decoding mp3 / m4a into sample data

Post by wilbert »

Thanks for letting me know that it is working after all. :)
Maybe I should have given a more extensive example like drawing the waveform or playing the loaded sound.
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply