Difference between revisions of "macOS Audio Recorder"

From Lazarus wiki
Jump to navigationJump to search
m (→‎AVAudioRecorder: Minor update)
(→‎Example Code: Added code example)
Line 23: Line 23:
 
== Example Code ==
 
== Example Code ==
  
t/c
+
{{Warning|This code compiles and creates the appropriate audio file, but until I get a microphone for my Mac mini the code should be considered "untested" :-)}}
 +
 
 +
{{Note|Not every method in the AVAudioRecorder class has been implemented in the code below. Most methods have been implemented. Check the Apple documentation for the rest. The implemented methods are described in detail in the code below.}}
 +
 
 +
<syntaxhighlight lang="pascal">
 +
//
 +
// Note: Free Pascal v3.3.1 (trunk), revision r42644, was used 6-Jan-2019
 +
//
 +
 
 +
unit unit1;
 +
 
 +
{$mode objfpc}{$H+}
 +
{$modeswitch objectivec1}
 +
{$linkframework AVFoundation}
 +
 
 +
interface
 +
 
 +
uses                         
 +
...
 +
  MacOSAll,
 +
  CocoaAll,
 +
  LCLType,
 +
...
 +
 
 +
type
 +
 
 +
  { TForm1 }
 +
 
 +
  AVAudioRecorder = objcclass external(NSObject)
 +
  public
 +
    {Initializes and returns an audio recorder.
 +
    url: File system location to record to. File type to record to is inferred
 +
    from the file extension included in this parameter’s value.
 +
    settings: Settings for the recording session.
 +
    outError: Returns, by-reference, a description of the error, if an error
 +
    occurs. Pass nil to ignore the error.}
 +
    function initWithURL_settings_error (url: NSURL; settings: NSDictionary; outError: NSErrorPtr):id;
 +
                            message 'initWithURL:settings:error:';
 +
 
 +
    {Calling this method implicitly calls prepareToRecord(), which creates (or
 +
    erases) an audio file and prepares the system for recording.}
 +
    function avRecord: Boolean; message 'record';
 +
 
 +
    {Creates an audio file at the location specified by the url parameter in the
 +
    init() method. If a file already exists at that location, this method
 +
    overwrites it. The preparation invoked by this method takes place
 +
    automatically when you call record(). Use prepareToRecord when you want
 +
    recording to start as quickly as possible upon calling record().}
 +
    function prepareToRecord: Boolean; message 'prepareToRecord';
 +
 
 +
    {Pauses a recording. Call record() to resume recording.}
 +
    procedure pause; message 'pause';
 +
 
 +
    {Stops recording and closes the audio file.}
 +
    procedure stop; message 'stop';
 +
 
 +
    {Deletes a recorded audio file. The audio recorder must be stopped before
 +
    you call this method.}
 +
    function deleteRecording: Boolean; message 'deleteRecording';
 +
 
 +
    {A Boolean value that indicates whether the audio recorder is recording.}
 +
    function isRecording: Boolean; message 'isRecording';
 +
 
 +
    {Audio recorder settings are in effect only after you explicitly call the
 +
    prepareToRecord() method, or after you call it implicitly by starting
 +
    recording.}
 +
    function settings: NSDictionary; message 'settings';
 +
 
 +
    {The time, in seconds, since the beginning of the recording.}
 +
    function currentTime: NSTimeInterval; message 'currentTime';
 +
 
 +
    {By default, audio level metering is off for an audio recorder. Because
 +
    metering uses computing resources, turn it on only if you intend to use it.}
 +
    procedure setMeteringEnabled(newValue: ObjCBOOL); message 'setMeteringEnabled:';
 +
 
 +
    {A Boolean value that indicates whether audio-level metering is enabled.}
 +
    function isMeteringEnabled: ObjCBOOL; message 'isMeteringEnabled';
 +
 
 +
    {To obtain current audio power values, you must call this method before you
 +
    call averagePower(forChannel:) or peakPower(forChannel:).}
 +
    procedure updateMeters; message 'updateMeters';
 +
 
 +
    {The current peak power, in decibels, for the sound being recorded. 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 recorder exceeds ±full scale, then the return value
 +
    may exceed 0 (ie enter the positive range).}
 +
    function peakPowerForChannel (channelNumber: NSUInteger): single; message 'peakPowerForChannel:';
 +
 
 +
    {The current average power, in decibels, for the sound being recorded. 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 recorder exceeds ±full scale, then the return value
 +
    may exceed 0 (ie, it may enter the positive range).}
 +
    function averagePowerForChannel (channelNumber: NSUInteger): single; message 'averagePowerForChannel:';
 +
  end;
 +
 
 +
    TForm1 = class(TForm)
 +
 
 +
  ...
 +
 
 +
  private
 +
 
 +
  public
 +
 
 +
  end;
 +
 
 +
var
 +
  Form1: TForm1;
 +
  MyAudioRecorder : AVAudioRecorder = Nil;
 +
 
 +
 
 +
implementation
 +
 
 +
{$R *.lfm}   
 +
 
 +
...
 +
 
 +
// Convert NSStrings function
 +
function NSStringToString(ns: NSString): String;
 +
var
 +
  pathStr: shortstring;
 +
begin
 +
  CFStringGetPascalString(CFStringRef(ns),@pathStr,255,CFStringGetSystemEncoding());
 +
  Result := pathStr;
 +
end;
 +
 
 +
// Record audio to file
 +
procedure RecordAudio(audioFileName : NSString);
 +
var
 +
  path : NSString;
 +
  url : NSURL;
 +
  settings: NSDictionary;
 +
  err : NSError;
 +
begin
 +
  path := NSTemporaryDirectory.stringByAppendingPathComponent(audioFileName);
 +
  url := NSURL.fileURLWithPath(path);
 +
  settings := Nil; 
 +
 
 +
  MyAudioRecorder := AVAudioRecorder.alloc.initWithURL_settings_error(url, settings, @err);
 +
 
 +
  if Assigned(MyAudioRecorder) then
 +
      MyAudioRecorder.avrecord
 +
  else
 +
    NSLog(NSStr('Error in procedure RecordAudio(): %@'), err);
 +
end;
 +
 
 +
...
 +
 
 +
// Record audio - format mp4a inferred from filename
 +
procedure TForm1.MenuItem27Click(Sender: TObject);
 +
begin
 +
  RecordAudio(NSStr('testaudio.mp4a'));
 +
end;
 +
 
 +
// Pause audio recording
 +
procedure TForm1.MenuItem28Click(Sender: TObject);
 +
begin
 +
  MyAudioRecorder.Pause;
 +
end;
 +
 
 +
// Resume audio recording
 +
procedure TForm1.MenuItem29Click(Sender: TObject);
 +
begin
 +
  MyAudioRecorder.avRecord;
 +
end;
 +
 
 +
// Stop audio recording and close file
 +
procedure TForm1.MenuItem30Click(Sender: TObject);
 +
begin
 +
  MyAudioRecorder.Stop;
 +
end; 
 +
 
 +
...
 +
 
 +
finalization
 +
 
 +
If (Assigned (MyAudioRecorder)) then
 +
  begin
 +
    MyAudioRecorder.Release;
 +
    MyAudioRecorder := Nil;
 +
  end;
 +
 
 +
end.
 +
 
 +
</syntaxhighlight>
  
 
== See also ==
 
== See also ==

Revision as of 00:49, 6 January 2020

macOSlogo.png

This article applies to macOS only.

See also: Multiplatform Programming Guide

Light bulb  Note: This page is under construction

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.

AVAudioRecorder

The AVAudioRecorder class is intended to allow you to make audio recordings straight to a file with very little programming overhead. Using audio recorder, you can:

  • Record until the user stops the recording;
  • Record for a specified duration;
  • Pause and resume a recording;
  • Obtain input audio-level data that you can use to provide level metering.

In macOS, the audio comes from the system’s default audio input device as set by a user in System Preferences. To configure a recording, including options such as bit depth, bit rate, and sample rate conversion quality, configure the audio recorder’s settings dictionary.

Example Code

Warning-icon.png

Warning: This code compiles and creates the appropriate audio file, but until I get a microphone for my Mac mini the code should be considered "untested" :-)

Light bulb  Note: Not every method in the AVAudioRecorder class has been implemented in the code below. Most methods have been implemented. 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 r42644, was used 6-Jan-2019
//

unit unit1;

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

interface

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

type

  { TForm1 }

  AVAudioRecorder = objcclass external(NSObject)
  public
    {Initializes and returns an audio recorder.
    url: File system location to record to. File type to record to is inferred
    from the file extension included in this parameter’s value.
    settings: Settings for the recording session.
    outError: Returns, by-reference, a description of the error, if an error
    occurs. Pass nil to ignore the error.}
    function initWithURL_settings_error (url: NSURL; settings: NSDictionary; outError: NSErrorPtr):id;
                             message 'initWithURL:settings:error:';

    {Calling this method implicitly calls prepareToRecord(), which creates (or
    erases) an audio file and prepares the system for recording.}
    function avRecord: Boolean; message 'record';

    {Creates an audio file at the location specified by the url parameter in the
    init() method. If a file already exists at that location, this method
    overwrites it. The preparation invoked by this method takes place
    automatically when you call record(). Use prepareToRecord when you want
    recording to start as quickly as possible upon calling record().}
    function prepareToRecord: Boolean; message 'prepareToRecord';

    {Pauses a recording. Call record() to resume recording.}
    procedure pause; message 'pause';

    {Stops recording and closes the audio file.}
    procedure stop; message 'stop';

    {Deletes a recorded audio file. The audio recorder must be stopped before
    you call this method.}
    function deleteRecording: Boolean; message 'deleteRecording';

    {A Boolean value that indicates whether the audio recorder is recording.}
    function isRecording: Boolean; message 'isRecording';

    {Audio recorder settings are in effect only after you explicitly call the
    prepareToRecord() method, or after you call it implicitly by starting
    recording.}
    function settings: NSDictionary; message 'settings';

    {The time, in seconds, since the beginning of the recording.}
    function currentTime: NSTimeInterval; message 'currentTime';

    {By default, audio level metering is off for an audio recorder. Because
    metering uses computing resources, turn it on only if you intend to use it.}
    procedure setMeteringEnabled(newValue: ObjCBOOL); message 'setMeteringEnabled:';

    {A Boolean value that indicates whether audio-level metering is enabled.}
    function isMeteringEnabled: ObjCBOOL; message 'isMeteringEnabled';

    {To obtain current audio power values, you must call this method before you
    call averagePower(forChannel:) or peakPower(forChannel:).}
    procedure updateMeters; message 'updateMeters';

    {The current peak power, in decibels, for the sound being recorded. 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 recorder exceeds ±full scale, then the return value
    may exceed 0 (ie enter the positive range).}
    function peakPowerForChannel (channelNumber: NSUInteger): single; message 'peakPowerForChannel:';

    {The current average power, in decibels, for the sound being recorded. 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 recorder exceeds ±full scale, then the return value
    may exceed 0 (ie, it may enter the positive range).}
    function averagePowerForChannel (channelNumber: NSUInteger): single; message 'averagePowerForChannel:';
  end;

    TForm1 = class(TForm)

  ...
  
  private

  public

  end; 

var
  Form1: TForm1;
  MyAudioRecorder : AVAudioRecorder = Nil;


implementation

{$R *.lfm}    

...

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

// Record audio to file
procedure RecordAudio(audioFileName : NSString);
var
  path : NSString;
  url : NSURL;
  settings: NSDictionary;
  err : NSError;
begin
  path := NSTemporaryDirectory.stringByAppendingPathComponent(audioFileName);
  url := NSURL.fileURLWithPath(path);
  settings := Nil;   

  MyAudioRecorder := AVAudioRecorder.alloc.initWithURL_settings_error(url, settings, @err);

  if Assigned(MyAudioRecorder) then
      MyAudioRecorder.avrecord
  else
    NSLog(NSStr('Error in procedure RecordAudio(): %@'), err);
end;

...

// Record audio - format mp4a inferred from filename
procedure TForm1.MenuItem27Click(Sender: TObject);
begin
  RecordAudio(NSStr('testaudio.mp4a'));
end;

// Pause audio recording
procedure TForm1.MenuItem28Click(Sender: TObject);
begin
   MyAudioRecorder.Pause;
end;

// Resume audio recording
procedure TForm1.MenuItem29Click(Sender: TObject);
begin
   MyAudioRecorder.avRecord;
end; 

// Stop audio recording and close file
procedure TForm1.MenuItem30Click(Sender: TObject);
begin
  MyAudioRecorder.Stop;
end;   

...

finalization

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

end.

See also

External links