macOS Audio Player

From Free Pascal wiki
macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

Overview

The Apple AVFoundation framework combines four major technology areas that together encompass a wide range of tasks for capturing, processing, synthesising, controlling, importing and exporting audiovisual media on Apple platforms. It is available from macOS 10.7 (Lion). The framework provides essential services for working with time-based audiovisual media.

Supported Audio File and Data Formats

File Format Data Formats
AAC (.aac, .adts) 'aac '
AC3 (.ac3) 'ac-3'
AIFC (.aif, .aiff,.aifc) BEI8, BEI16, BEI24, BEI32, BEF32, BEF64, 'ulaw', 'alaw', 'MAC3', 'MAC6', 'ima4' , 'QDMC', 'QDM2', 'Qclp', 'agsm'
AIFF (.aiff) BEI8, BEI16, BEI24, BEI32
Apple Core Audio Format (.caf) '.mp3', 'MAC3', 'MAC6', 'QDM2', 'QDMC', 'Qclp', 'Qclq', 'aac ', 'agsm', 'alac', 'alaw', 'drms', 'dvi ', 'ima4', 'lpc ', BEI8, BEI16, BEI24, BEI32, BEF32, BEF64, LEI16, LEI24, LEI32, LEF32, LEF64, 'ms\x00\x02', 'ms\x00\x11', 'ms\x001', 'ms\x00U', 'ms \x00', 'samr', 'ulaw'
MPEG Layer 3 (.mp3) '.mp3'
MPEG 4 Audio (.mp4) 'aac '
MPEG 4 Audio (.m4a) 'aac ', alac'
NeXT/Sun Audio (.snd, .au) BEI8, BEI16, BEI24, BEI32, BEF32, BEF64, 'ulaw'
Sound Designer II (.sd2) BEI8, BEI16, BEI24, BEI32
WAVE (.wav) LEUI8, LEI16, LEI24, LEI32, LEF32, LEF64, 'ulaw', 'alaw'

AVAudioPlayer

The AVAudioPlayer class lets you play sound in any audio format available in macOS from a file or memory. The properties of this class are used for managing information about a sound such as the playback point within the sound’s timeline and for accessing playback options such as volume and looping.

Using an audio player you can:

  • Play sounds of any duration (unlike System Sound Services that has a 30 second limit);
  • Play sounds from files or memory buffers;
  • Play sounds when your application is in the background;
  • Loop sounds (including indefinitely);
  • Play multiple sounds simultaneously, one sound per audio player, with precise synchronisation;
  • Control relative playback level, stereo positioning, and playback rate for each sound you are playing;
  • Seek to a particular point in a sound file, which supports such application features as fast forward and rewind; and
  • Obtain data you can use for playback-level metering.

Example Code

Note-icon.png

Note: Not every method in the AVAudioPlayer class has been implemented in the code below. Most methods have been implemented: play audio from a file, pause playback, resume playback, stop playback, set/get volume, set/get number of loops, set/get current time (ie forward, rewind) and set/get rate (ie speed) of playback. Check the Apple documentation for the rest. The implemented methods are described in detail in the code below.

//
// Note: Free Pascal v3.3.1 (trunk), revision r42640, was used 27-Dec-2019
//

unit unit1;

{$mode objfpc}{$H+}
{$modeswitch objectivec1}
{$linkframework AVFoundation}

interface

uses                          
...
  MacOSAll,
  CocoaAll,
  LCLType,
...

type

  { TForm1 }

  AVAudioPlayer = objcclass external(NSObject)
  public
    {Initializes and returns an audio player for playing a designated sound file.
     url: a URL identifying the sound file to play. The audio data must be in a
     format supported by Core Audio.
     outError:  If an error occurs, on return the NSError object describes the
     error. Set to NULL to ignore errors.}
     function initWithContentsOfURL_error (url: NSURL; outError: NSErrorPtr): id;
                            message 'initWithContentsOfURL:error:';   
    {Plays a sound asynchronously. Calling this method implicitly calls the
     prepareToPlay() method if the audio player is not already prepared to play.}
    function play: Boolean; message 'play';

    {A value of 0.0 indicates silence; a value of 1.0 (the default) indicates
     full volume for the audio player instance. Use this property to control an
     audio player’s volume relative to other audio output.}
    function volume: Single; message 'volume';
    procedure setVolume(newValue: Single); message 'setVolume:';

    {A value of 0, which is the default, means to play the sound once.
     Set a positive integer value to specify the number of times to return
     to the start and play again. For example, specifying a value of 1
     results in a total of two plays of the sound. Set any negative
     integer value to loop the sound indefinitely until you call the
     stop() method.}
    function  numberOfLoops: Integer; message 'numberOfLoops';
    procedure setNumberOfLoops(newValue: Integer); message 'setNumberOfLoops:';

    {A Boolean value that indicates whether the audio player is playing (true)
     or not (false).}
    function isPlaying: Boolean; message 'isPlaying';

    {Calling this method preloads buffers and acquires the audio hardware
     needed for playback, which minimises the lag between calling the play()
     method and the start of sound output.}
    function prepareToPlay: Boolean; message 'prepareToPlay';

    {Pauses playback; sound remains ready to resume playback from where
    it left off. It does not release the audio hardware that was acquired
    on calling play() or prepareToPlay().}
    procedure pause; message 'pause';

    {The stop method does not reset the value of the currentTime property to 0.
     If you call stop during playback and then call play(), playback resumes
     at the point where it left off. Callikng this method undoes the setup
     performed on calling the play() or prepareToPlay() methods.}
    procedure stop; message 'stop';

    {The total duration, in seconds, of the sound associated with the audio
    player.}
    function duration: Double; message 'duration';

    {If the sound is playing, currentTime is the offset of the current playback
    position, measured in seconds from the start of the sound. If the sound is
    not playing, currentTime is the offset of where playing starts upon calling
    the play() method, measured in seconds from the start of the sound.
    By setting this property you can seek to a specific point in a sound file or
    implement audio fast-forward and rewind functions.}
    function currentTime: Double; message 'currentTime';
    procedure setCurrentTime(newValue: Double); message 'setCurrentTime:'; 

    {To enable adjustable playback rate for an audio player, set this property
     to true after you initialize the player and before you call the
     prepareToPlay() instance method for the player.}
    function enableRate: Boolean; message 'enableRate';
    procedure setEnableRate(newValue:Boolean); message 'setEnableRate:';

    {This property’s default value of 1.0 provides normal playback rate. The
     available range is from 0.5 for half-speed playback through 2.0 for
     double-speed playback.
     To set an audio player’s playback rate, you must first enable rate
     adjustment as described in the enableRate property description.}
    function rate: Single; message 'rate';
    procedure setRate(newValue: Single); message 'setRate:';

    {The UID of the current audio player.}
    function currentDevice: NSString; message 'currentDevice';

    {Number of audio channels in the sound associated with the audio player.}
    function numberOfChannels: Integer; message 'numberOfChannels';

    {The default value for the meteringEnabled property is off (Boolean false).
     Before using metering for an audio player, you need to enable it by
     setting this property to true.}
    function isMeteringEnabled: Boolean; message 'isMeteringEnabled';
    procedure setMeteringEnabled(newValue:Boolean); message 'setMeteringEnabled:';

    {To obtain current audio power values, you must call this method before
     calling averagePowerForChannel or peakPowerForChannel}
    procedure updateMeters; message 'updateMeters';

    {Returns the average power for a given channel, in decibels, for the sound
    being played.
    Channel numbers are zero-indexed. A monaural signal, or the left channel
    of a stereo signal, has channel number 0.
    A return value of 0 dB indicates full scale, or maximum power; a return
    value of –160 dB indicates minimum power (that is, near silence). If the
    signal provided to the audio player exceeds ±full scale, then the return
    value may exceed 0 (that is, it may enter the positive range).}
    function avgPwrForChannel(newValue:LongWord): Double; message 'averagePowerForChannel:';

    {Returns the peak power for a given channel, in decibels, for the sound
    being played.
    Channel numbers are zero-indexed. A monaural signal, or the left channel
    of a stereo signal, has channel number 0.
    A return value of 0 dB indicates full scale, or maximum power; a return
    value of –160 dB indicates minimum power (that is, near silence). If the
    signal provided to the audio player exceeds ±full scale, then the return
    value may exceed 0 (that is, it may enter the positive range).}
    function peakPwrForChannel(newValue:LongWord): Double; message 'peakPowerForChannel:'; 
  end;
     
  TForm1 = class(TForm)

  ...
  
  private

  public

  end; 

var
  Form1: TForm1;
  MyAudioPlayer : AVAudioPlayer = Nil;

implementation 

...

// Convert NSStrings function 
function NSStringToString(ns: NSString): String;
var
  pathStr: shortstring;
begin
  CFStringGetPascalString(CFStringRef(ns),@pathStr,255,CFStringGetSystemEncoding());
  Result := pathStr;
end;


// Play audio procedure
procedure PlayAudio(audioFileName : NSString; numberOfLoops: Integer);
var
  path: NSString;
  url : NSURL;
  err : NSError;
begin
  // Path to your application bundle's resource directory
  // with the mp3 filename appended
  path := NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent(audioFileName);
  url  := NSURL.fileURLWithPath(path);

  MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);

  if Assigned(MyAudioPlayer) then
    begin
      MyAudioPlayer.setNumberOfLoops(numberOfLoops);
      MyAudioPlayer.play
    end
  else
    // Use the Applications > Utilities > Console application to find error messages
    NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);
end;

...

// Play audio file located in your application bundle Resources directory
// This particular demo file can be found on your Mac at: 
// /Applications/iPhoto.app/Contents/Resources/Music/Minuet in G.mp3
// and should be copied to your application bundle Resources directory.
procedure TForm1.MenuItem23Click(Sender: TObject);
begin
  PlayAudio(NSStr('Minuet in G.mp3'), 0);
end;


// Pause audio
procedure TForm1.MenuItem24Click(Sender: TObject);
begin
  MyAudioPlayer.pause;
end;

// Resume audio
procedure TForm1.MenuItem25Click(Sender: TObject);
begin
  // Note: This will restart the audio where it left off after a pause 
  //       unless audio has been reset to zero
  // Do not create yet another audio player by calling PlayAudio() again :-)
  if(MyAudioPlayer.currentTime <> 0) then
    MyAudioPlayer.play;
end;

// Stop audio
procedure TForm1.MenuItem26Click(Sender: TObject);
begin
  MyAudioPlayer.stop;               // audio can still be resumed, so...
  MyAudioPlayer.setCurrentTime(0);  // reset audio to start
end;

...

finalization

If (Assigned (MyAudioPlayer)) then
  begin
    MyAudioPlayer.Release;
    MyAudioPlayer := Nil;
  end;

end.

See also

External links