Thema: Eventrecorder

Einzelnen Beitrag anzeigen

hadschi92

Registriert seit: 25. Okt 2006
83 Beiträge
 
Delphi XE3 Professional
 
#2

AW: Eventrecorder

  Alt 8. Apr 2013, 15:37
Mein Stand der Neuprogrammierung ist folgender:
In der Unit AudioControl sollen alle Aufnahmefunktionen gesammelt werden.
Die Aufnahme wird mit den New Audio Components realisiert.
Delphi-Quellcode:
unit AudioControl;

interface

uses
  ACS_Streams, ACS_DXAudio, ACS_LAME, ACS_Wave, NewACIndicators,
  SysUtils, Windows, Classes, Contnrs;

type
  TAudioControlState = (Initialised, Recording, Paused, Stopped);

  TAudioSaveFormat = class
  private
    FFormat: String;
    FBitrate: Integer;
  public
     procedure SetToMP3(Bitrate: Integer);
     procedure SetToWave;
     function GetFormat: String;
     function GetBitrate: Integer;
  end;

  TTrack = class
  strict private
    FAlbum: String;
    FTitle: String;
    FInterpret: String;
    FComposer: String;
    FGenre: String;
    FCategory: String;
    FDescription: String;
    FStartTime: Cardinal;
    FLengthInMilliseconds: Cardinal;
    FStreamPath: String;
    FStream: TFileStream;
    class var FNumberOfRecordedStreams: Integer;
  private
    class property NumberOfRecordedStreams: Integer read FNumberOfRecordedStreams write FNumberOfRecordedStreams;
  public
    constructor Create(Directory: String);
    destructor Destroy; override;
    property Album: String read FAlbum write FAlbum;
    property Title: String read FTitle write FTitle;
    property Interpret: String read FInterpret write FInterpret;
    property Composer: String read FComposer write FComposer;
    property Genre: String read FGenre write FGenre;
    property Category: String read FCategory write FCategory;
    property Description: String read FDescription write FDescription;
    property StartTime: Cardinal read FStartTime write FStartTime;
    property LengthInMilliseconds: Cardinal read FLengthInMilliseconds write FLengthInMilliseconds;
    function GetLengthAsString: String;
    property Stream: TFileStream read FStream write FStream;
    property StreamPath: String read FStreamPath write FStreamPath;
  end;

  TTrackList = class
  private
    FTrackList: TObjectList;
    FTemporaryDirectory: String;
    FNumberOfChannels: Cardinal;
    FBitsPerSample: Cardinal;
    FSampleRate: Cardinal;
    function GetTrack(TrackNumber: Integer): TTrack;
  public
    constructor Create(TemporaryDirectory: String);
    destructor Destroy; override;
    procedure AddTrack;
    procedure MoveTrack(StartTrackNumber, EndTrackNumber: Integer);
    procedure DeleteTrack(TrackNumber: Integer);
    procedure DeleteAllTracks;
    property NumberOfChannels: Cardinal read FNumberOfChannels;
    property BitsPerSample: Cardinal read FBitsPerSample;
    property SampleRate: Cardinal read FSampleRate;
    property Track[TrackNumber: Integer]: TTrack read GetTrack; default;
    function Count: Integer;
    function High: Integer;
    function GetLengthOfAllTracksInMilliseconds: Cardinal;
  end;

  TAudioControl = class(TComponent)
  private
    FAudioSource: TDXAudioIn;
    FAudioGain: TGainIndicator;
    FAudioStream: TStreamOut;
    FTrackList: TTrackList;
    FWaveOut: TWaveOut;
    FMP3Out: TMP3Out;
    FState: TAudioControlState;
    procedure ProcessSavingThreadAsWave(AOwner: TComponent; SaveDirectory: String; TrackNumber: Integer);
    procedure RunStream;
    procedure PauseStream;
    procedure ResumeStream;
    procedure StopStream;
  public
    constructor Create(AOwner: TComponent;
      DeviceNumber, Channels, BitsPerSample, SampleRate: Integer; Directory: String);
    destructor Destroy; override;
    procedure CreateTrackList(Directory: String);
    procedure StartRecording;
    procedure StartRecordingWithNewTrack;
    procedure PauseRecording;
    procedure StopRecording;
    property TrackList: TTrackList read FTrackList;
    property AudioGain: TGainIndicator read FAudioGain;
    property State: TAudioControlState read FState;
    procedure StartSavingThreadAsWave(AOwner: TComponent; SaveDirectory: String);
  end;

implementation

//------------------------------ TAudioControl -------------------------------//

constructor TAudioControl.Create(AOwner: TComponent;
  DeviceNumber, Channels, BitsPerSample, SampleRate: Integer; Directory: String);
begin
  Inherited Create(AOwner);
  FAudioSource := TDXAudioIn.Create(AOwner);
  FAudioSource.DeviceNumber := DeviceNumber;
  FAudioSource.InChannels := Channels;
  FAudioSource.InBitsPerSample := BitsPerSample;
  FAudioSource.InSampleRate := SampleRate;
  FAudioGain := TGainIndicator.Create(AOwner);
  FAudioGain.Input := FAudioSource;
  FAudioStream := TStreamOut.Create(AOwner);
  FAudioStream.Input := FAudioGain;
  FTrackList := TTrackList.Create(Directory);
  FState := Initialised;
end;

destructor TAudioControl.Destroy;
begin
  StopRecording;
  FTrackList.Free;
  FTrackList := nil;
  FAudioSource.Free;
  FAudioSource := nil;
  FAudioGain.Free;
  FAudioGain := nil;
  FAudioStream.Free;
  FAudioStream := nil;
  Inherited Destroy;
end;

procedure TAudioControl.CreateTrackList(Directory: String);
begin
  FTrackList := TTrackList.Create(Directory);
  FState := Initialised;
end;

procedure TAudioControl.StartRecording;
begin
  if FState = Initialised then begin
    RunStream;
  end else if FState = Recording then begin
    //
  end else if FState = Paused then begin
    ResumeStream;
  end else if FState = Stopped then begin
    RunStream;
  end else begin
    // Fehlerrückgabe
  end;
end;

procedure TAudioControl.StartRecordingWithNewTrack;
begin
  if FState = Initialised then begin
    RunStream;
  end else if FState = Recording then begin
    StopStream;
    RunStream;
  end else if FState = Paused then begin
    StopStream;
    RunStream;
  end else if FState = Stopped then begin
    RunStream;
  end else begin
    // Fehlerrückgabe
  end;
end;

procedure TAudioControl.PauseRecording;
begin
  if FState = Initialised then begin
    //
  end else if FState = Recording then begin
    PauseStream;
  end else if FState = Paused then begin
    //
  end else if FState = Stopped then begin
    //
  end else begin
    // Fehlerrückgabe
  end;
end;

procedure TAudioControl.StopRecording;
begin
  if FState = Initialised then begin
    //
  end else if FState = Recording then begin
    StopStream;
  end else if FState = Paused then begin
    StopStream;
  end else if FState = Stopped then begin
    //
  end else begin
    // Fehlerrückgabe
  end;
end;

procedure TAudioControl.StartSavingThreadAsWave(AOwner: TComponent; SaveDirectory: String);
var
  InputStream: TStreamIn;
begin
  if 1 <= (TrackList.High + 1) then begin
    FWaveOut := TWaveOut.Create(AOwner);
    InputStream := TStreamIn.Create(AOwner);
    FWaveOut.Input := InputStream;

    InputStream.InChannels := TrackList.FNumberOfChannels;
    InputStream.InBitsPerSample := TrackList.FBitsPerSample;
    InputStream.InSampleRate := TrackList.SampleRate;
    InputStream.Stream := TFileStream.Create(TrackList[0].StreamPath, fmOpenRead);

    FWaveOut.FileName := SaveDirectory + '1.wav';
    //FWaveOut.OnDone := ProcessSavingThreadAsWave(SaveDirectory, 2);
    FWaveOut.Run;
  end else begin
    FWaveOut.Free;
  end;
end;

procedure TAudioControl.ProcessSavingThreadAsWave(AOwner: TComponent; SaveDirectory: String; TrackNumber: Integer);
begin
if TrackNumber <= (TrackList.High + 1) then begin
    FWaveOut.FileName := SaveDirectory + '/' + IntToStr(TrackNumber) + '.wav';
    Inc(TrackNumber);
    //FWaveOut.OnDone := ProcessSavingThreadAsWave(self, SaveDirectory, TrackNumber);
    FWaveOut.Run;
  end else begin
    FWaveOut.Free;
  end;
end;

procedure TAudioControl.RunStream;
begin
  FTrackList.AddTrack();
  FAudioStream.Stream := (FTrackList[FTrackList.High] as TTrack).Stream;
  FAudioStream.Run;
  (FTrackList[FTrackList.High] as TTrack).StartTime := GetTickCount;
  FState := Recording;
end;

procedure TAudioControl.PauseStream;
begin
  with (FTrackList[FTrackList.High] as TTrack) do begin
    LengthInMilliseconds := LengthInMilliseconds + (GetTickCount - StartTime);
  end;
  FAudioStream.Pause;
  FState := Paused;
end;

procedure TAudioControl.ResumeStream;
begin
  FAudioStream.Resume;
  (FTrackList[FTrackList.High] as TTrack).StartTime := GetTickCount;
  FState := Recording;
end;

procedure TAudioControl.StopStream;
begin
  with (FTrackList[FTrackList.High] as TTrack) do begin
    LengthInMilliseconds := LengthInMilliseconds + (GetTickCount - StartTime);
  end;
  FAudioStream.Stop(false);

{  SavingStream := TFileStream.Create(FTrackList[FTrackList.High].StreamPath, fmCreate);
  SavingStream.CopyFrom()
  ms := TMemoryStream.Create;
  ms.LoadFromFile('C:\Download\Source.txt');
  fs := TFileStream.Create('C:\Download\Dest.txt', fmOpenReadWrite);
  fs.CopyFrom(ms, ms.Size);
  fs.Free;
   ms.Free;  }

  FTrackList[FTrackList.High].Stream.Free;
  FState := Stopped;
end;

//---------------------------------- TTrack ----------------------------------//

constructor TTrack.Create(Directory: String);
var test: TFileStream;
begin
  FAlbum := '';
  FTitle := '';
  FInterpret := '';
  FComposer := '';
  FGenre := '';
  FCategory := '';
  FDescription := '';
  FLengthInMilliseconds := 0;
  FStreamPath := Directory + IntToStr(NumberOfRecordedStreams) + '.dat';
  FStream := TFileStream.Create(FStreamPath, fmCreate);
end;

destructor TTrack.Destroy;
begin
  SysUtils.DeleteFile(FStreamPath);
  Inherited;
end;

function TTrack.GetLengthAsString: String;
begin
  Result := FormatDateTime( 'hh:mm:ss.zzz',
          (LengthInMilliseconds / (24*60*60*1000)));
end;

//------------------------------ TTrackList ----------------------------------//

constructor TTrackList.Create(TemporaryDirectory: String);
begin
  FTrackList := TObjectList.Create;
  TTrack.NumberOfRecordedStreams := 0;
  FTemporaryDirectory := TemporaryDirectory;
  FNumberOfChannels := 2;
  FBitsPerSample := 16;
  FSampleRate := 44000;
end;

destructor TTrackList.Destroy;
var
  i: Integer;
begin
  for i := 0 to High do begin
    (FTrackList[i] as TTrack).Free;
  end;
  Inherited;
end;

function TTrackList.GetTrack(TrackNumber: Integer): TTrack;
begin
  Result := (FTrackList[TrackNumber] as TTrack);
end;

procedure TTrackList.AddTrack;
begin
  FTrackList.Add(TTrack.Create(FTemporaryDirectory));
  TTrack.NumberOfRecordedStreams := TTrack.NumberOfRecordedStreams + 1;
end;

procedure TTrackList.MoveTrack(StartTrackNumber, EndTrackNumber: Integer);
begin
  FTrackList.Move(StartTrackNumber, EndTrackNumber);
end;

procedure TTrackList.DeleteTrack(TrackNumber: Integer);
begin
  FTrackList.Delete(TrackNumber);
end;

procedure TTrackList.DeleteAllTracks;
var
  i: Integer;
begin
  for i := 0 to FTrackList.Count - 1 do begin
    FTrackList.Delete(i);
  end;
end;

function TTrackList.Count: Integer;
begin
  Result := FTrackList.Count;
end;

function TTrackList.High: Integer;
begin
  Result := FTrackList.Count - 1;
end;

function TTrackList.GetLengthOfAllTracksInMilliseconds: Cardinal;
var
  i: Integer;
  LengthOfAllTracksInMilliseconds: Cardinal;
begin
  LengthOfAllTracksInMilliseconds := 0;
  for i := 0 to FTrackList.Count - 1 do begin
    LengthOfAllTracksInMilliseconds :=
      LengthOfAllTracksInMilliseconds + (FTrackList[i] as TTrack).LengthInMilliseconds;
  end;
  Result := LengthOfAllTracksInMilliseconds;
end;

//------------------------------ TAudioSaveFormat ----------------------------//

procedure TAudioSaveFormat.SetToMP3(Bitrate: Integer);
begin
  FFormat := 'mp3';
  FBitrate := Bitrate;
end;

procedure TAudioSaveFormat.SetToWave;
begin
  FFormat := 'wav';
  FBitrate := 0;
end;

function TAudioSaveFormat.GetFormat: String;
begin
  Result := FFormat;
end;

function TAudioSaveFormat.GetBitrate: Integer;
begin
  Result := FBitrate;
end;

end.
Die AudioControl Unit funktioniert bereits ganz gut. Ein paar Probleme sind noch vorhanden:
  • Mein ThinkPad parkt bei Erschütterungen die Festplatte. Bei der Verwendung von FileStream kann während der Erschütterung nicht auf die Festplatte geschrieben werden, deshalb gehen Audiodaten verloren. Ein MemoryStream ist auch nicht die perfekte Lösung, da eine Aufnahme sehr groß werden kann und der Arbeitsspeicher damit sehr gefüllt würde. Optimal wäre ein gepufferter FileStream, muss ich diesen selbst programmieren oder gibt es da schon Komponenten?
  • Die New Audio Components haben Events auf die ich reagieren kann. Jedoch weiß ich nicht, wie ich darauf reagieren kann und wie ich sie als neues Ereignis in meiner selbst erstellten Komponente weitergeben kann.
  • Außerdem soll mein Programmcode möglichst sauber und wartungsfreundlich sein, da benötige ich Anregungen von euch.

Vielen Dank für eure Mithilfe!
  Mit Zitat antworten Zitat