program dmr_replay; {$MODE Delphi} uses {$IFDEF UNIX}cthreads,{$ENDIF} Classes, SysUtils, DMRDecoder; type TBurstCounter = class public Count: QWord; procedure BurstReady(const Dibits: array of Byte; DibitCount: Integer); end; procedure TBurstCounter.BurstReady(const Dibits: array of Byte; DibitCount: Integer); begin if DibitCount = DMR_BURST_DIBITS then Inc(Count); end; procedure Usage; begin WriteLn('Usage: dmr_replay [--invert]'); Halt(2); end; var Stream: TFileStream; Decoder: TDMRDecoder; Counter: TBurstCounter; Samples, Right: array[0..2047] of Single; Status: TDMRStatus; BytesRead, N, i: Integer; TotalSamples: QWord; Invert: Boolean; begin if ParamCount < 1 then Usage; Invert := (ParamCount >= 2) and (ParamStr(2) = '--invert'); Stream := TFileStream.Create(ParamStr(1), fmOpenRead or fmShareDenyNone); Counter := TBurstCounter.Create; Decoder := TDMRDecoder.Create; try Decoder.OnBurst := Counter.BurstReady; Decoder.SetInverted(Invert); Decoder.SetEnabled(True); TotalSamples := 0; repeat BytesRead := Stream.Read(Samples[0], SizeOf(Samples)); N := BytesRead div SizeOf(Single); if N <= 0 then Break; for i := 0 to N - 1 do Right[i] := Samples[i]; Decoder.FeedAudio(Samples, Right, N); Inc(TotalSamples, N); // Keep the bounded realtime ring from becoming the replay bottleneck. Sleep(2); until BytesRead < SizeOf(Samples); Sleep(500); Decoder.GetStatus(Status); WriteLn('file=', ParamStr(1)); WriteLn('seconds=', FormatFloat('0.000', TotalSamples / DMR_INPUT_RATE)); WriteLn('invert=', Invert); WriteLn('rms=', FormatFloat('0.000000', Status.SignalRMS)); WriteLn('sync_count=', Status.SyncCount); WriteLn('best_sync_distance=', Status.BestSyncDistance); WriteLn('best_sync_phase=', Status.BestSyncPhase); WriteLn('best_candidate=', TDMRDecoder.SyncKindName(Status.BestCandidateKind)); WriteLn('symbol_outer_level=', FormatFloat('0.000000', Status.SymbolOuterLevel)); WriteLn('dc_mean=', FormatFloat('0.000000', Status.SignalMean)); WriteLn('last_sync=', TDMRDecoder.SyncKindName(Status.SyncKind)); WriteLn('bursts=', Counter.Count); WriteLn('dropped_samples=', Status.DroppedSamples); WriteLn('selected_slot=', Status.SelectedSlot + 1); WriteLn('voice_frames=', Status.VoiceFrameCount); WriteLn('voice_errors=', Status.VoiceErrorCount); WriteLn('vocoder_available=', Status.VocoderAvailable); if Status.CACHValid then WriteLn('timeslot=', Status.Slot + 1); if Status.SlotTypeValid then WriteLn('color_code=', Status.ColorCode, ' data_type=', Status.DataType); if Status.LinkControlValid then WriteLn('lc_slot=', Status.LCSlot + 1, ' target=', Status.TargetID, ' source=', Status.SourceID, ' service_options=', Status.ServiceOptions); finally Decoder.Free; Counter.Free; Stream.Free; end; end.