Laden...

Forenbeiträge von elli Ingesamt 26 Beiträge

17.10.2009 - 22:40 Uhr

Hallo Tom,

Mann Du bastelst ja immer noch an der CAPI rum ...

DTMF erkennen wird über den Görtzel Algo gemacht. Vor Jahren hatte ich mir mal
aus den erhältlichen Quellen eine Klasse zusammengebastelt. Ich poste sie mal hier
(siehe unten).

Soweit ich in Erinnerung habe, berücksichtigen die Tabellen schon die inverse ISDN Darstellung der ISDN B-Kanal Daten. Meist haben ja die ISDN Karten den DTMF Kram in Form eines Hardware Codecs. Der Code ist nicht super optimiert, kann man
besser machen.

Das Codeplex Projekt kenne ich. Viel zu kompliziert programmiert für meinen Geschmack ...

Wenn Du fragen hast poste sie.

elli

typedef void (CALLBACK * PDTMFSignalProc)(
                                          PVOID pvContext,
                                          char  ch
                                         );


typedef struct
 {
  int freq;
  int grp;        /* low/high group     */
  int k;          /* k                  */
  int k2;         /* k fuer 2. harmonic */
 } TDTMF;

typedef struct
 {
  int freq1;
  int freq2;
 } TFrequenceDesc;


#define MAX_DTMF_TONES     8  + 1
#define NCOEFF             16 + 2     // number of frequencies to be analyzed

class CDTMFCodec
 {
  public:
   CDTMFCodec();

   void init              (
                           DWORD dwNumberSamples,
                           DWORD dwSampleRate = 8000
                          );


   void    setSignalProc(
                         PDTMFSignalProc signalProc,
                         PVOID           signalContext
                        );


   void    scanAlaw          (PBYTE pvBuffer, int iLength );
   void    scan              (PWORD pvBuffer, int iLength );
   BOOL    generateTone      (PWORD buffer, DWORD dwNumOfSamples, char ch);
   void    setCNGDetection   (BOOL bFlag);

  protected:
   virtual void   signalChar (char ch);
           void   scanTones  (int * pvBuffer);
           void   goertzel   (int * pvBuffer);

  private:
    static int            cos2pik[NCOEFF];
    static TDTMF          tones[MAX_DTMF_TONES];
    static char           chMatrix[4][4];
    static TFrequenceDesc freqTones[NCOEFF];

    PDTMFSignalProc       dtmfSignalProc;
    PVOID                 dtmfSignalContext;
    char                  lastChar;
    int                   iIndex;
    int                   pcmBuffer [2048];         // we use int for faster 32 bit access
    int                   tempBuffer[NCOEFF];
    BOOL                  bCNGDetection;
 };


typedef CDTMFCodec * PDTMFCodec;

inline void
CDTMFCodec::signalChar (char ch)
 {
  if ( dtmfSignalProc )
   {
    dtmfSignalProc( dtmfSignalContext, ch );
   }
 }


inline void
CDTMFCodec::setSignalProc(
                          PDTMFSignalProc signalProc,
                          PVOID           signalContext
                         )
 {
  dtmfSignalProc    = signalProc;
  dtmfSignalContext = signalContext;
 }


inline void
CDTMFCodec::setCNGDetection(BOOL bFlag)
 {
  bCNGDetection = bFlag;
 }


//#define DTMF_TRESH   100000   // above this is dtmf
//#define H2_TRESH      20000   // 2nd harmonic

#define DTMF_TRESH     50000     // above this is dtmf
#define H2_TRESH       10000     // 2nd harmonic
#define SILENCE_TRESH    100     // below this is silence

#define AMP_BITS           9     // bits per sample, reduced to avoid overflow
#define DTMF_NPOINTS     205     // Number of samples for DTMF recognition

#define LOGRP              0
#define HIGRP              1
#define MAX_N            384

#define PI            3.1415

#pragma warning (disable : 4309) // Verkuerzung eines konstanten Wertes
#pragma warning (disable : 4305) // Verkuerzung von 'const int' in 'short'

//---------------------------------------------------------------------------
// alaw -> signed 16-bit
//---------------------------------------------------------------------------
static short isdnAlawTo16BitPCM[] =
 {
  0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
  0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
  0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
  0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
  0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
  0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
  0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
  0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
  0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
  0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
  0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
  0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
  0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
  0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
  0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
  0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
  0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
  0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
  0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
  0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
  0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
  0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
  0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
  0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
  0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
  0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
  0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
  0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
  0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
  0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
  0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
  0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
 };


//---------------------------------------------------------------------------
// For dtmf recognition: * 2 * cos(2 * PI * k / N) precalculated for ISDN
// (8000 Hz) and all k
//---------------------------------------------------------------------------
int CDTMFCodec::cos2pik[NCOEFF] =
 {
  55812,  29528, 53603,  24032, 51193,  14443, 48590,   6517,
  38113, -21204, 33057, -32186, 25889, -45081, 18332, -55279, 42450, -10225
 };


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
TDTMF CDTMFCodec::tones[MAX_DTMF_TONES] =
 {
  { 697, LOGRP, 0,  1 },
  { 770, LOGRP, 2,  3 },
  { 852, LOGRP, 4,  5 },
  { 941, LOGRP, 6,  7 },
  {1209, HIGRP, 8,  9 },
  {1336, HIGRP, 10, 11 },
  {1477, HIGRP, 12, 13 },
  {1633, HIGRP, 14, 15 },
  {1100, HIGRP, 16, 17 }   // FAX CNG detection
 };

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
char CDTMFCodec::chMatrix[4][4] =
 {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
 };


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
TFrequenceDesc CDTMFCodec::freqTones[NCOEFF] =
 {
  {697, 1209}, {697, 1336}, {697, 1477}, {697, 1633},
  {770, 1209}, {770, 1336}, {770, 1477}, {770, 1633},
  {852, 1209}, {852, 1336}, {852, 1477}, {852, 1633},
  {941, 1209}, {941, 1336}, {941, 1477}, {941, 1633}, {1100,1100}
 };


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
CDTMFCodec::CDTMFCodec()
 {
  iIndex            = 0;
  lastChar          = ' ';
  dtmfSignalProc    = NULL;
  dtmfSignalContext = NULL;
  bCNGDetection     = FALSE;
 }


//---------------------------------------------------------------------------
// Der Goertzel-Algorithmus:
// Ist ein genauso schlichtes, wie geniales numerisches Verfahren (!) und
// beinhaltet im Groben "folgendes":
//
// Sog. "Feedback-Phase":
// Ein Schritt bezieht sich immer auf einen Block von 205 Samples
// (also n=0, ... 204) bei 8kHz.
// DTMF hat 8 Frequenzen: 697, 770, 852, 941, 1209, 1336, 1477, 1633 Hz
// Jede Frequenz hat einen eigenen Koeffizienten coef.
// coef = 2 cos(2 * PI * k / N), mit N=205, k=(2N/samplingrate) * DTMF frequenz .
// 4 Parameter sind dabei im Spiel: Q[n] = coef * Q[n-1] - Q[n-2] + Sample[n] .
// Q[n-1] & Q[n-2] werden am anfang gleich Null gesetzt.
// Sog. "Feedforward-Phase":
// Wurden alle 205 Samples verrechnet tritt die sog "Feedforward-Phase" ein.
// "quadrierte Werte": Y=(Q[n-1]) ^2 - (q[n-2])^2 - Q[n-1] * Q[n-2] * coef .
// Y representiert den "Energie-Gehalt" der gesuchten Frequenz.
// Weiter mit dem nächsten Block.
// Dabei kommen idR sehr grosse Werte heraus, so das es Sinn macht, die
// Samples auf doubles zwischen 0 ... 1 zu skalieren
//---------------------------------------------------------------------------
void
CDTMFCodec::goertzel(int * pvBuffer)
 {
  int tempCos2Pik;
  int sk;
  int sk1;
  int sk2;
  int k;
  int n;
  int * result;
  int maxCoefficients;

  if ( bCNGDetection )
   maxCoefficients = NCOEFF + 2;
  else
   maxCoefficients = NCOEFF;

  tempCos2Pik = 0;
  result      = tempBuffer;

  for (k = 0; k < maxCoefficients ; k++)
   {
    tempCos2Pik = cos2pik[k];
    sk          = 0;
    sk1         = 0;
    sk2         = 0;

    for (n = 0; n < DTMF_NPOINTS; n++)
     {
      sk  = pvBuffer[n] + ((tempCos2Pik * sk1) >> 15) - sk2;
      sk2 = sk1;
      sk1 = sk;
     }

    result[k] = ((sk * sk) >> AMP_BITS) -
                (((( tempCos2Pik * sk) >> 15) * sk2) >> AMP_BITS) +
               ((sk2 * sk2) >> AMP_BITS);
   }

  scanTones( tempBuffer );
 }

//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void
CDTMFCodec::scanAlaw( PBYTE pvBuffer, int iLength )
 {
  int i;
  int c;

  while ( iLength )
   {
    c = min(iLength, (DTMF_NPOINTS - iIndex));
    if (c <= 0) break;

    for (i = 0; i < c; i++)
     {
      pcmBuffer[ iIndex++ ] = isdnAlawTo16BitPCM[*pvBuffer++] >> (15 - AMP_BITS);
     }

    if (iIndex == DTMF_NPOINTS)
     {
      goertzel(pcmBuffer);
      iIndex = 0;
      }

    iLength -= c;
   }
 }


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void
CDTMFCodec::scan( PWORD pvBuffer, int iLength )
 {
  int i;
  int c;

  while ( iLength )
   {
    c = min(iLength, (DTMF_NPOINTS - iIndex));
    if (c <= 0) break;

    for (i = 0; i < c; i++)
     {
      pcmBuffer[ iIndex++ ] = *pvBuffer++; // >> (15 - AMP_BITS);
     }

    if (iIndex == DTMF_NPOINTS)
     {
      goertzel(pcmBuffer);
      iIndex = 0;
     }

    iLength -= c;
   }
 }



//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
BOOL
CDTMFCodec::generateTone(PWORD pvBuffer, DWORD dwNumOfSamples, char ch)
 {
  int col, row, idx;
  double f1, f2;
  double value  = 12000.0;
  int i1;
  int i2;

  idx  = -1;
  i1   = 0;
  i2   = 0;
  for (row = 0; row < 4; row++)
   {
    for (col = 0; col < 4; col++)
     {
      if (ch == chMatrix[row][col])
       {
        idx = row * 4 + col;
       }
     }
   }

  if (idx < 0)
   {
    return FALSE;
   }

  f1 = (((double) freqTones[idx].freq1) * PI );
  f2 = (((double) freqTones[idx].freq2) * PI );

  for (idx = 0; idx < (int) dwNumOfSamples; idx++)
   {
    pvBuffer[idx]  = (WORD)(value * sin((double)i1 * f1/4000));
    pvBuffer[idx] = pvBuffer[idx] + (WORD)(value * sin((double)i2 * f2/4000));

    i1++;
    i2++;
   }

  return TRUE;
 }


//---------------------------------------------------------------------------
//
//---------------------------------------------------------------------------
void
CDTMFCodec::scanTones(int * pvBuffer )
 {
  int *result;
  int silence;
  int i;
  int grp[2];
  char what;

  result     = pvBuffer;
  grp[LOGRP] = -2;
  grp[HIGRP] = -2;
  silence    = 0;

  for (i = 0; i < 8; i++)
   {
    if ( (result[ tones[i].k ] > DTMF_TRESH) && (result[tones[i].k2] < H2_TRESH))
     {
      grp[tones[i].grp] = (grp[tones[i].grp] == -2) ? i : -1;
     }
    else if ((result[tones[i].k] < SILENCE_TRESH) && (result[tones[i].k2] < SILENCE_TRESH))
     {
      silence++;
     }
   }

  if (silence == 8)
   {
    what = ' ';
    if ( bCNGDetection )
     {
      if ( (result[ tones[9].k ] > DTMF_TRESH) && (result[tones[9].k2] < H2_TRESH))
       {
        // start CNG timer

        signalChar( 'F' );
       }
      else
       {
        // stop CNG Timer,  calculate elapsed time
       }
     }
   }
  else
   {
    if ( (grp[LOGRP] >= 0) && (grp[HIGRP] >= 0) )
     {
      what = chMatrix[grp[LOGRP]] [grp[HIGRP] - 4];

      if ( (lastChar != ' ') && (lastChar != '.'))
       {
        lastChar = what;        //  min. 1 non-DTMF between DTMF
       }
     }
    else
     {
      what = '.';
     }
   }

  if ( (what != lastChar) && (what != ' ') && (what != '.'))
   {
    signalChar( what );
   }

  lastChar = what;
 }
25.09.2009 - 23:58 Uhr

Hi,

Ich habe das Projekt ins Leben gerufen, damit ich mich in das neue .NET 3.5

Cool zu hören. Ich bin noch am überlegen auf welches ich umstellen will, denn ich will
V 2.4 benutzen wegen den GAIN Tags. Den Weg den MP3Gain geht, alle MP3 Frame Header zur verdändern gefällt mir nicht.

Allgeimein habe ich gemerkt, dass es sehr schwierig ist eine "saubere" ID3 Kodierung zu finden.

Jopp, da hast Du ja das Grundproblem von Standards schon mitbekommen. Ich plage mich mit sowas schon seit 20 Jahren rum. Man muß es allen recht machen. Pochen auf Standards und die Regeln nützt nichts. Tolerant sein und alles unterstützen 😃., schlicht kompatibel sein.

elli

12.09.2009 - 00:15 Uhr

hi,

es gibt 2 fette ID3 Tag Projekte. Es lohnt sich nicht sowas selber zu machen.

http://id3tag.codeplex.com/

wahrscheinlich noch besser das vom Mono Project, weil es auch mehr als
MP3 kann.

EDIT von winSharp93: Link zur TagLibSharp Wiki entfernt, da Inhalt nun anstößig (möglicherweise durch gehackten Server)

Allerdings hat die Seite gerade Probleme. Hier sind ein paar Beispiele:

http://developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples

elli

13.10.2008 - 23:07 Uhr

Hallo!

@maxibk:

meinst du per CAPI einen Ruf annehmen dann einen zweiten aufbauen und die
die B-Kanäle quais verbinden ?

Zuerst sollte Du die Export Funktionen von accapi20.dll prüfen., ob es überhaupt eine
CAPI 2.0 Implementierung ist.

Muß je eine sehr alte HiPath sein, denn die Software der letzte 6 Jahre kenne ich dachte ich.

>Gibts von dem c# Wrapper

c# ist nicht immer der eleganteste Weg 🙂

elli

29.06.2008 - 22:58 Uhr

Hallo,

lustig das es weiter geht.

Prinzipiell sollte man bei CAPI-Anwendungen den Empfang von Nachrichten in einen eigenen Prozess auslagern, welcher bei neuen Nachrichten ein Event auslöst. In diesem können die "erwarteten" Nachrichten entsprechend behandelt werden.

Thread ist schon mal OK. In dem kann Du CAPI_WAIT_FOR_SIGNAL machen.
Es gibt keine erwarteten Nachrichten ! Prinzipiel müßen die State Machine alles
abfangen und je nach dem was Du als CAPI Nachricht gesendet hast und was Du erreichen willst entsprechend reagieren. Das hat bei mir auch etliche Zeit gedauert
und es gibt diverse unterschiedliche Implementierungen. Dann kann es durch Race Conditions auch noch zu lustigsten Effekten kommen. Erst heute würde ich sagen ich fange eigentlich alles ab was so kommt.

Aus Sicht einer API gibt es wirklich nur eine Reihe von Events die eine Applikation interessieren.

Frage ruhig, ich stehe bereit 🙂

elli

12.02.2008 - 19:28 Uhr

Hallo,

Microsoft hat ein ACM Wrapper interface. Das kann auch so was ...
Habe mal ein uraltes Sample aus meiner Sammlung rangehangen.

elli

12.02.2008 - 19:21 Uhr

Hallo,

ich nehme mal an Du hast einen Slider 'audioVolume'. Der kann ein Event auslösen wenn der Wert sich ändert

   this.audioVolume.ValueChanged += new System.EventHandler(this.audioVolumeChanged);


  private void audioVolumeChanged(object sender, EventArgs e)
   {
    Bass.BASS_SetVolume(value);
   }

Wenn Du da komische VB Interface für Bass benutzt. Ich halte nix vom dem Ding weil es eine blöde Abbildung einer DLL ist und mitnichten irgendeinen OOP Ansatz verfolgt.

Das sollte reichen als Anstoß denke ich.

elli

28.01.2007 - 18:37 Uhr

hallo,

soviel wie ich sehen kann ist diese Listbox konkret in einer Form verankert, also nicht unbedingt wiederverwendbar. Besser wäre daraus ein einzelnes Control zu machen, welches in einer Form eingebunden werden kann.

        // Da mein Fenster eine feste Größe hat, kann ich hier ganz einfach ermitten, auf was der User geklickt hat.  
        // sind immer 16 Pixel Abstand

diese Annahme hat auch nicht unbedingt allgemeingültigen Character.

elli

25.01.2007 - 22:23 Uhr

Hallo,

sieht nach vielen verschiedenen Eingaben aus, auch zu Laufzeit. Kennst Du das

SynBackSE ?

Habe ich mir endlich gekauft, und macht alles von alleine im Hintergrund.

elli

04.01.2007 - 22:07 Uhr

Hallo.

hier scheint Synchronisation möglich ...
CSharp Twain Project

sprich eine Assembly zur Fernsteuerung (WCF?) der anderen Komponente.

elli

04.01.2007 - 21:59 Uhr

Hallo Tom.

Die TWAIN-Komponente soll letztendlich nahezu GUI-unabhängig sein

sie muß sogar, Du weisst doch Eingabe, Verarbeitung, Ausgabe. Nicht so wie viele tolle Sachen wo die Vearbeitung mit einer Fom verwuselt ist.

elli

PS. Ich bin gespannt

04.01.2007 - 21:16 Uhr

Hallo Tom,

EDIT: Was genau fehlt dir persönlich für ein Digitalisierungsprogramm?

Tja wenn ich das so genau benennen könnte ... Wenn man sich den Ablauf (Workflow) vorstellt, den man hat wenn z.B. persönliche Dokumente einscannt, kommt man an den Punkt wo man ja mehrere Scannergebnisse hat die logisch zusammengehören.

Alle einzelnen Images laufen durch die gleiche Filtergruppe (Predefines für S/W , Gray)
und können als Einzelbilder und/oder als ein PDF mit mehreren Seiten abgelegt werden.

Denkbar wäre noch eine Zusammenfassung der einzelnen Bilder auf einer Html Seite.
Jeder Vorgang in ein Directory. Das alles möglichst bequem mit den Eingaben.
Also Button für nächsten Scanvorgang oder Fertig ...

Anderer Vorgang wenn man z.B. Dias/Negative scannt ...

Anderer Vorgang wenn man nur Kopien für Drucker macht ...

Hier noch eine Idee für die Thumbnails - die Größe frei angebbar.

elli

03.01.2007 - 22:43 Uhr

hallo tom,

bei mir funktioniert es. 🙂

Ich habe einen Canon 4200F. Ist schon mal ein recht schöner Anfang. Ich will auch irgendwann mal meine persönlichen Unterlagen digitalisieren und das ist zumindest die
Assembly ein erster Schritt. Das was ich machen will deckt das Programm erst mal so nicht ab.

Ein paar Ideen ...

  • Im Context Menü eines Thumbnails noch Open mit Default Programm 1..n
  • Speichern aller Thumbnails in eine PDF Datei (mehrere Seiten)
  • Save as PDF, Default Dateiname vergebbar in Optionen.
  • Im Grafikpreviewmode des Thumbnail digitale Filter (Plugins) ...

Bugs
Contextmenu des Thumbnail Tabs,(wenn kein Thumbnail selektiert ist) darf nicht
angezeigt werden, bzw ein anderes (z.B save all as ...)

Mehrfach Selektion von Thumbnails führt nicht zu erwarteten Aktion.

File Save All darf nicht selektierbar sein wenn noch nichts gemacht wurde.

OK, sehr guter Ansatz. Gibt es auch mal den Source Code ?. Immer Refl.. ist langweilig...

elli

20.12.2006 - 18:18 Uhr

@hulkstar

hallo,

der plci ist eine Resource die beim D-Kanal Verbindungsaubau, sprich beim Connect Request oder Connect Indication vergeben wird. Wenn noch kein Verbindungaufbau erfolgte, muß ein PLCI immer zu Verfügung stehen. Ist das nicht der Fall, kann es sich nur um ein Problem der Capi Implmentierung handeln oder wie bei Dir anscheinend um ein Konfigurationsproblem des Capi Servers.

Da Du anscheinend schon eine Verbindung augebaut hattest, schreibe mal die Sequence der Capi Befehle auf die Du beim Verbindungsabbau machst.

elli

ps. ein nicht erfolgter disconnect response auf ein disconnect indication oder dabei eine fasche Angabe verhindern das Freigeben des PLCIs.

17.12.2006 - 22:14 Uhr

hallo,

eine mögliche Ursache könnte an gelöschten INF Files liegen. Typischerweise
liegn unter \windows\inf folgende Files:

23.08.2001 13:00 40.950 usb.inf
25.08.2006 17:00 43.968 usb.PNF
03.08.2004 23:51 47.648 usbport.inf
28.11.2005 17:20 51.832 usbport.PNF
18.08.2001 12:00 3.354 usbprint.inf
03.01.2006 21:17 5.492 usbprint.PNF
18.08.2001 12:00 29.652 usbstor.inf
23.12.2005 20:38 37.952 usbstor.PNF
03.08.2004 23:51 24.042 usbvideo.inf
28.11.2005 15:16 24.992 usbvideo.PNF

Ich hatte mal was ähnliches und da fehlte usb.inf. (Kein Ahnung wie und warum).
Anhand dieser INF Files kennt Windows so die meisten Standard Geräte.

elli

16.12.2006 - 22:11 Uhr

Hallo,

also nochmal die Logik. Jedes Byte von der ISDN Leitung (sprich CAPI) bzw. muß erst
über z.b. eine Tabelle bit inevrtiert werden. Anbei die Tabelle, bzw. wie man sie
aufbaut (geht besser).

 BYTE  lookUpTable[256];

  int  b;
  BYTE o;

  for ( b = 0; b <= 255; b++ )
   {
    o = 0;
    if ( b & 0x01) o |= 0x80;
    if ( b & 0x02) o |= 0x40;
    if ( b & 0x04) o |= 0x20;
    if ( b & 0x08) o |= 0x10;
    if ( b & 0x10) o |= 0x08;
    if ( b & 0x20) o |= 0x04;
    if ( b & 0x40) o |= 0x02;
    if ( b & 0x80) o |= 0x01;

    lookUpTable[b] = o;
   }

Also für jedes BYTE von DATA_B3_IND genannt b ist i = lookupTable**.
i wird dann von A-Law nach Wave konvertiert.

Diese Konvertierung kann durch eine Tabelle erfolgen da wir nur 255 Werte haben.

Umgekehrt Senden von PCM Daten, erfolgt zuerst die PCM -> A-LAW konvertierung.
Jedes BYTE b das da rauskommt wid jetzt zu j = lookupTables**.
j geht dann als Byte beim DATA_B3_REQ raus.

Das reine Anwenden des G.711 Algos kann nie zu einem hörbaren Erlebnis bei ISDN werden. Es gibt Linux Sourcen die das besser verdeutlichen.

elli

//EDIT: c#-code-tags ergänzt

10.12.2006 - 22:02 Uhr

Hallo,

@balaban_s

Also Codeproject hast Du ja schon entdeckt, dann hätte Dir ein wenig mehr weitersuchen auch geholfen. Der PCM -> A-Law Codec muß nun einen Dynamic
Bereich von 0xFFFF Werten zu 255 Werten abbilden. Kann man natürlich mit
Tabellen machen, aber der Artikel G.711
wird Dir da sicherlich helfen.

Wenn Du also ankommende WAVE IN Daten nach A-Law Konvertiert hast, müßen sie noch mit der Tabelle die ich schon mal gepostet hatte (oder nicht ?) bitinvertiert werden. Dann können sie raus mit DATA B3 REQUEST.

Das ruckeln beim Abbspielen bekommst Du nur weg wenn Du ein wenig Latency und
damit Echo erzeugst und das abspielen erst anfängst wenn Du den 2. oder 3. Block empfangen hast. Beide Datenströme arbeiten mit 8Kz sind aber nicht synchronisiert. Mit diesem kleinen Vorlaufbuffer kannst Du eventuelle Differenzen überleben. Andere Methode wäre einen kleinen Silent Block abzuspielen. Das kann Dich aber alles nicht gegen Windows schützen. Da es kein Realtime System ist, kann es passieren das Du für 100ms und mehr im Usermode keine Rechenzeit bekommst. Dann ist nicht mehr mit einem kontinuirlichen Datenstrom. (Ich habe es erlebt als ich 8 ISDN Kanäle gebündelt haben und eine Applikation alle 10 ms die entsprechenden Daten liefern muß).

Je kleiner die Datenblockgröße wird, umso geringer ist das Echo das Du auf der Leitung hast.

Ich hoffe das hilft weiter. Wenn nicht, noch mal nachfragen.

elli

05.12.2006 - 23:25 Uhr

Hallo,

@hulkstar
die Nummer mit der ihr verbunden seit wird bei CON_ACT_IND angezeigt. Von außen
kommt man aber nicht mehr ran an eine CAPI. Hast Du die Kontrolle über die Verbindung ?, wenn nicht mußt Du sowiso eine neue Verbindung aufbauen. Male mal
eine kleine Skizze, denn so ganz habe ich die Architektur nicht kapiert.

Kleiner Tip am Rande, ich würde nie bei jeder DATA_B3_IND einen neuen Buffer
mit new allozieren. Baut euch eine Resource Queue mit der Anzahl von freien
Buffern die Ihr bei CAPI_REGISTER angegeben habt. Die maximale Größe eines dieser Buffer habt ihr bereits bei CAPI_REGISTER festgesetzt.

Wenn ein DATA_B3_IND Nachricht bearbeitet wird, freien Buffer Block ausqueuen.
Daten kopieren und zur Verarbeitung schicken. Wenn DATA_B3_RESP gemacht wird
diese Buffer wieder zurück an die Resourcequeue.

Bei Kommunikation treten viele asynchrone Ereignisse auf. Ich halte es für einen schlechten Stil und da es führt zu nicht deterministischen Laufzeitverhalten wenn
immer fleißig mit new gearbeitet wird.

@Balaban_S

if (command == CapiCommand.cmDataB3Indication)  
{  
    IntPtr DataRaw =Marshal.AllocHGlobal(256);  
    /* byte DataLenght =1; word CapiCommand= 2, dword Datahandle = 4  
  also: 1+2+4=7*/  
    DataRaw = Marshal.ReadIntPtr(msg, 7);//&lt;--7  
    Marshal.Copy(DataRaw, b3Data, 0, 256);  
    Marshal.FreeHGlobal( DataRaw);

Die Datenlänge sind immer 2 Byte.
und mit diese Länge dann das Copy.

Du hast bereits einen unmanged Pointer, also ist MarshallhGlobal() falsch und überflüßig. Das braucst Du beim DATA_B3_REQ.

Der Code von hulkstar ist fast korrekt, man braucht keine 4096 Byte allozieren,
sondern nur von der Größe 'len'.

Viel Spaß weiterhin 🙂

elli

22.11.2006 - 18:38 Uhr

@hulkstar

hallo,

ch hatte vergessen, auf die DATA_B3_INDICATIONs mit DATA_B3_RESPONSEs zu antworten.

Naja ein paar mal kann man das machen, zumindest einmal weniger als man bei
CAPI_REGISTER und B3 Block Count angegeben hat. Dann noch ein Block und dann ist Sense 🙂

Aber ein DISC_B3_IND bzw. DISC_IND hat damit nix zu tun. Das muß auch so kommen. Das würde mich mal interessieren was passiert wenn Du wieder keine
DATA_B3_RESP schickst und dann den ISDN Stecker ziehst.

elli

21.11.2006 - 19:45 Uhr

@hulkstar,

nur mal so, CAPI_WAIT_FOR_SIGNAL würde ich ständig in einem Thread laufen lassen
und wenn WAIT_FOR_SIGNAL beendet ist 2 mal CAPI_GET_MESSAGE aufrufen.
Der zweite Aufruf falls noch was da ist. (in der Regel ja nicht). Da wenn eine Nachricht
da ist , dispatcht Du sie. Es können ja nur Indications oder Confirmations kommen.

Wenn Du alles beendet machst Du ja CAPI_RELEASE, das beendet normalerweise
WAIT_FOR_SIGNAL und Du kommts sauber raus aus dem Thread.

Anwendung aufhängen weil von der CAPI nichts mehr kommt ist Quatsch.
CAPI ist Kommunikation und damit asynchron, ankommende Messages immer in einem Thread.

Wenn Du laut Deinem Protokoll jeztzt nach dem Senden einer Nachricht an einem SMS Server eine Antwort erwartest, muß ja ein DATA_B3_INDICATION kommen.

Kann es sein das der Server ein anderes "Protokoll" spricht ?. Ich habe mich um SMS nie gekümmert.

Was mich dabei erst richtig verwirrt ist, dass der Server nach ein paar Sekunden inaktivität die Verbindung beendet aber ich auch keine Disconnect_(B3_)Indication oder sonstwas bekomme...

Das geht nicht. Wenn der Server die Verbindung abbaut, spricht auf dem D-Kanal muß die CAPI Implementation Dir zuerst ein DICONNECT B3 INDICATION und danach ein DISCONNECT INDICATION senden. Alles andere verstößt gegen die Regeln. Da die meisten eine Fritz CARD haben ist das auch so. Wenn Du in diesem Zustand die ISDN Leitung mal abziehst mußt Du auch einen DISC_B3_IND und dann ein DISC_IND mit Grund 0x3301 bekommen.

Als erstes würde ich Dir empfehlen Dein Programmlogik zu ändern. Mann kann keine Nachricht an die CAPI senden und dann mal auf eine Anwort der CAPI warten, das ist schlechte Programmierung an dieser Stelle. Alles wird durch States und Ereignisse gesteuert. Ein Ereignis ist hier, das eine neue CAPI Nachricht da ist, die abhängig vom Verbindungszustand eine neue Aktion bzw. Zustandswechsel bewirkt. Hast Du eine Verbindung löst der Inhalt der Daten einen neuen Zustandsautomaten deiner Anwendung aus.

Wenn Du Fritzt hast, hat da nicht mal sowas wie ein CAPI Trace existiert ? Ich hatte auch mal eine Löungs da habe ich eine Trace DLL in eine Fremd CAPI injeziert. Weiß aber nicht mehr ob das noch geht.

elli

PS. Stück Source Code habe ich noch gefunden. Es ist AS IS, denn ich habe den C# Pfad nicht weiterverfolgt ....

17.11.2006 - 14:14 Uhr

Hallo,

Töne aus dem Lautsprecher, das ist so eine Sache. Eine Wave Komponente hast
Du hoffentlich. Die Daten der CAPI kommen praktisch als 8Khz Stream.

Wenn man jetzt z.b. Hören und Sprechen will hat man ein gewisse Verzögerung, abhängig von der Blockgröße die man bei der CAPI Applikation einstellt.
256 Byte ist ein guter Wert wenn man Voice macht. (Je geringer desto besser, aber
mehr Overhead im Gesamtsystem).

Die Daten kommen dann so ca. alle 32ms. Anfangen mit dem Abspielen sollte man nicht unbedingt gleich, weil man ja 2 synchrone Datenströme hat (von ISDN, zum Audio Device) die nicht untereinander synchronisiert sind. Also kann ein kleiner Zwischenbuffer von Vorteil sein.

Das Hauptproblem sind die Daten an sich. Sie liegen im G.711 Format vor. Bei uns in
Mitteleuropa in G.711 ALaw. Diese müßen jetzt nach PCM Wave Mono Khz 16Bit gewandelt werden. Die sie aus dem Chips noch bitinvertiert rauskommen ist zusätzlich noch eine Tabelle vorzusetzen.

G.711 Codecs sind im Windows System vorhanden. Das ACM System dafür ist aber relativ umständlich (halt Microsoft) zu bedienen und eigentlich zu oversized dafür.
Mann kann das durch simple Tabellen machen, dabei werden beide notwendigen Transformationen zu einer zusammengefaßt.

Ich habe sowas irgendwo rumzuliegen, war aber bis lang zu faul es anzuwenden weil mein ACM Code seit Jahren läuft.

Ich werde mal rumsuchen, um was zum posten bereitzustellen.

elli

14.11.2006 - 19:18 Uhr

@Balaban_S

Es gibt doch das CAPI-ADK von AVM (Supermächtig das Teil, da von AVM), welches frei zur Verfügung steht.
Könnte man das nicht per C++/CLI wrappen ?

Sollte kein Problem sein. Allerdings ist es ja C-Code und Du musst daraus erst
mal eine Art Obekt Modell machen. Aber nicht in der Art ich mache ein .NET Low Level CAPI Schnittstelle. Davon halte ich überhaupt nichts. Wir haben es hier immer mit
Verbindungen zu tun und man sollte da eine andere Sicht haben. So in der Art

IsdnConnection
Connect
Disconnect
AcceptCall
RejectCall
IgnoreCall
Transmit
DataResponse

und Ereignisse der Art
OnConnectionEstablished
OnConnectionFailed
OnIncomingCall
OnDataReceived
OnDataTransmitted

...

mit dieser Art des herangehens (im Grund mehr oder weniger OSI-ISO - Schichtenmodell) kann man jede Art von Kommunilationsmodell abbilden.

Sei es ISDN, Bluetooth, TCPIP ...

elli

02.11.2006 - 20:51 Uhr

hallo,

vergesse immer reinzuschauen 🙂

Selbst wenn die CIP etc. nicht stimmt, muß der Ruf mit einem Disconnect Indication terminieren und anzeigen das der PLCI vom Connect Confirmation nicht mehr gültig ist.

Gerade bei den Sprachen und Voice CIP kann es lustig werden...

elli

28.10.2006 - 22:33 Uhr

Hallo,

habe vergessen mal wieder vorbeizuschauen, bzw. bin total dicht und arbeite 14h am Tag ...
Anyway, ich schicke die mal meinen ganzen Code. Ist extrem unvollständig, eine Linie
müßte aber zu erkennen sein (ein Ansatz wie ma programmieren könnte) und wenn ich mich nicht irre konnten ankommende Rufe gemonitort werden. Da ich inzwischen wie gesagt alles in C++ CLI habe brauche ich die C# Linie nicht weiter zu verfolgen. Ein C++ Kernel hat den schönen Vorteil das ich ihn überall verwenden kann.

Mal sehen ob ich den Anhang schaffe...

elli

25.10.2006 - 21:59 Uhr

Hallo,

OK verstehe. Bei meinen ersten C# Übungen habe ich einen anderen Ansatzt
gewählt. Das dumme Struktur Nachbilden ist sowiso für die Katz da alles
variante Strukturen sind. Ich habe also eine Message definiert die einen
unmanged buffer enthält und die einzelnen Element per Marshall etc.
setzt. Hier mal mein alter Code als Idee. Ich programmiere CAPI und ISDN
Applikationen und Treiber seit nun fast 20 Jahren und langsam wird es langweilig 🙂

elli


using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;


namespace CAPI
{

 public class CapiMessage : IDisposable
  {
#region Constructor, Desctructor, Dispose
   public CapiMessage(CapiApplication application)
   {
    this.application = application;
    internalMemory   = Marshal.AllocHGlobal( 2048 + 32 );
    umBuffer         = internalMemory;
    writeIndex       = 8;
    length           = 0;
    streamBuffer     = new byte[2048 + 32 ];

    if ( streamBuffer == null )
     {
      //throw()
     }


   }

   public CapiMessage( IntPtr msg)
   {
    // Save unmanged message buffer position
    streamBuffer   = new byte[2048 + 32 ];

    Marshal.Copy( msg, streamBuffer,0,8);


    length  = readWord(0);
    command = (CapiCommand) (readWord(4) & 0xFFFF);

    // check for DATA B3 Indication

    Marshal.Copy( msg, streamBuffer,0,length);
   }

   ~CapiMessage()
   {
    if( internalMemory != IntPtr.Zero )
    {
     Marshal.FreeHGlobal( internalMemory );
    }
   }

   public void Dispose()
   {
    if ( ! fDisposed )
     {
      fDisposed = true;
      umBuffer = IntPtr.Zero;

      //umBuffer =  IntPtr.Zero;
      System.GC.SuppressFinalize( this );
     }

   }
#endregion


   public int sendMessage  (CapiCommand command)
    {
     this.command = command;
     writeIndex   = 0;
     length      += 8;

     writeWord( (ushort) length );
     writeWord( (ushort) application.ApplicationID );
     writeWord( (ushort) command );
     writeWord( (ushort) application.NextMessageNumber);

     return application.putMessage(this, command,length );
    }

#region message request section

   public int listenRequest(
                             int     controllerNumber,
                             Int32   infoMask,
                             CIPMask cipMask,
                             Int32   cipMask2
                           )
    {
     reset();
     writeDWord( controllerNumber);
     writeDWord( infoMask );
     writeDWord( (Int32) cipMask );
     writeDWord( cipMask2 );
     writeStruct( null );
     writeStruct( null );

     return sendMessage( CapiCommand.cmListenRequest );
    }

  public int connectRequest(
                            int       controllerNumber,
                            string    calledPartyNumber,
                            string    callingPartyNumber,
                            CIPValue  cipValue,
                            BProtocol bProtocol
                           )
  {
   reset();
   writeDWord( controllerNumber );
   writeWord( (ushort) cipValue );

   writeCalledPartyNumber(calledPartyNumber);
   writeCallingPartyNumber(callingPartyNumber,cipValue);

   writeStruct( null );               // Called  party subaddress
   writeStruct( null );               // Calling party subaddress
   writeStruct( null );               // param B protocol
   writeStruct( null );               // Bearer Capaility
   writeStruct( null );               // Low Layer Compatibility
   writeStruct( null );               // High Layer Compatibility
   writeStruct( null );               // Additional information elements

   return sendMessage(CAPI.CapiCommand.cmConnectRequest);
  }

  public int disconnectRequest(Int32 plci)
  {
   reset();
   writeDWord( plci );
   return sendMessage(CAPI.CapiCommand.cmDisconnectRequest);
  }

  public int alertRequest(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // Additional information elements
   return sendMessage(CAPI.CapiCommand.cmAlertRequest);
  }

  public int connectB3Request(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // NCPI
   return sendMessage(CAPI.CapiCommand.cmConnectB3Request);
  }

  public int disconnectB3Request(Int32 plci)
  {
   reset();
   writeDWord( plci );
   writeStruct( null );               // NCPI
   return sendMessage(CAPI.CapiCommand.cmDisconnectB3Request);
  }

#endregion

#region message response section


  public int connectResponse(Int32 plci, int reason)
   {
    reset();
    writeDWord( plci );
    writeWord( (ushort) reason );
    writeStruct( null );            // B protocol struct
    writeStruct( null );            // Connected number
    writeStruct( null );            // Connected subaddress
    writeStruct( null );            // Low Layer Compatibility
    writeStruct( null );            // Additional information elements

    return sendMessage(CAPI.CapiCommand.cmConnectResponse);
   }

  public int connectActiveResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmConnectActiveResponse);
  }

  public int disconnectResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmDisconnectResponse);
  }

  public int infoResponse(Int32 plci)
  {
   reset();
   writeDWord( plci );

   return sendMessage(CAPI.CapiCommand.cmDisconnectResponse);
  }

  public int disconnectB3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );
   writeStruct( null );               // NCPI

   return sendMessage(CAPI.CapiCommand.cmDisconnectB3Response);
  }

  public int connectB3Response(Int32 ncci,int reject)
  {
   reset();
   writeDWord( ncci );
   writeWord( (ushort) reject );
   writeStruct( null );               // NCPI

   return sendMessage(CAPI.CapiCommand.cmConnectResponse);
  }

  public int resetB3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmResetB3Response);
  }

  public int data3Response(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmDataB3Response);
  }

  public int connectB3ActiveResponse(Int32 ncci)
  {
   reset();
   writeDWord( ncci );

   return sendMessage(CAPI.CapiCommand.cmConnectB3ActiveResponse);
  }


#endregion

  protected void reset()
   {
    writeIndex = 8;
    length     = 0;
   }

  public byte[] MessageBuffer
  {
   get
   {
    return streamBuffer;
   }
  }

#region Message properties (member access section)
  public int ApplicationID
  {
   get
   {
    return readWord(0);
   }
  }

  public CapiCommand Command
  {
   get { return command;  }
   set { command = value; }
  }

  public ushort  MessageNumber
  {
   get
   {
    return readWord(6);
   }
  }

   public int  Length
   {
    get
    {
     return length;
    }
   }

    public Int32  Plci
     {
      get
      {
       switch ( command )
        {
         case CapiCommand.cmConnectRequest:
         case CapiCommand.cmListenRequest:
           return 0;  // Throw ??

        }
       return readDWord(8);
      }
     }

  public Int32  Ncci
  {
   get
   {
    return readDWord(8);
   }
  }

  public int ControllerNumber
   {
    get // todo check request type
     {
      return readDWord(8);
     }
   }

  public int InfoMask
   {
    get
     {
      return readDWord(12);
     }
   }

  public int CIPMask
  {
   get
   {
    return readDWord(16);
   }
  }

  public CapiInfo Info
  {
   get
   {
    switch ( command )
     {
      case CapiCommand.cmDataB3Confirmation:
        return (CapiInfo) readWord(14);
     }
    return (CapiInfo) readWord(12);
   }
  }

  public string CalledPartyNumber
   {
    get { return getCalledPartyNumber();  }
   }
   
  public string CalledPartySubAddress
   {
    get { return getCalledPartySubAddress();  }
   }

  public string CallingPartyNumber
   {
    get { return getCallingPartyNumber();  }
   }
   
  public string CallingPartySubAddress
   {
    get { return getCallingPartySubAddress();  }
   }
   
  public CIPValue CIP 
   {
    get { return (CIPValue) readWord(4);   }
    
   } 
   
   
  
#endregion

# region  Low level Message assembly stuff

  /// <summary>
  /// Write an byte into bufferStream, update the writeIndex and the written length
  /// </summary>
  protected void writeByte( byte val )
   {
    //Marshal.WriteByte( umBuffer, writeIndex, val );
    streamBuffer[writeIndex++] = val;
    length++;
   }

  /// <summary>
  /// Write an short into bufferStream in little endian byte order.
  /// </summary>
  protected void writeWord( ushort val )
   {
    writeByte( (byte) val );
    writeByte( (byte) (val >> 8) );
   }

  /// <summary>
  /// Write an int into bufferStream in little endian byte order.
  /// </summary>
  protected void writeDWord( int val )
   {
    writeWord( (ushort) val );
    writeWord( (ushort) (val >> 16));
   }

  protected void writeStruct( object val )
   {
    if( val == null )
    {
     writeByte(0);
     return;
    }

    int size = Marshal.SizeOf( val );
    if( size < 255 )
    {
     Marshal.WriteByte( umBuffer, writeIndex, (byte) size );
     writeIndex++;
     length++;
    }
    else
    {
     Marshal.WriteByte( (IntPtr) writeIndex, 0, 255 );
     writeIndex++;
     length++;
     Marshal.WriteInt16( (IntPtr) writeIndex, 0, (short) size );
     writeIndex += 2;
     length     += 2;
    }
    Marshal.StructureToPtr( val, (IntPtr) writeIndex, false );
    writeIndex += size;
    length     += size;
   }

  protected void writeCalledPartyNumber(string number)
   {
    writeByte( (byte) (1 + number.Length));    // length
    writeByte( 0x80 );                        // type

    foreach( char ch in number)
     {
      writeByte( (byte) ch );
     }

   }

  protected void writeCallingPartyNumber(string number, CIPValue cipValue)
  {
   writeByte( (byte) (2 + number.Length));    // length
   writeByte( 0x00 );                         // type

   if ( cipValue == CIPValue.cipValueNoProfile)
    {
     writeByte( 0x81 );                         // screen indicatior
    }
   else
    {
     writeByte( 0x80 );                         // screen indicatior
    }

   foreach( char ch in number)
   {
    writeByte( (byte) ch );
   }
  }

   protected void writeNCPI( NCPI ncpi )
    {
     if( ncpi  == null )
     {
      writeByte(0);
      return;
     }
    }





#endregion

#region Low Level Message disassembly section
  private string getCallingPartyNumber()
  {
   return "";
  }
   
  private string getCallingPartySubAddress()
  {
   return "";
  }
  
  private string getCalledPartySubAddress()
  {
   return "";
  }
   
   
  private string getCalledPartyNumber()
  {
   // BYTE      length
   // BYTE      type;
   // BYTE      digits[ capiMaxAddressLength ];

   int    offset;
   int    length;

   switch ( command )
   {
    case CapiCommand.cmConnectRequest:
    case CapiCommand.cmConnectIndication:
    case CapiCommand.cmInfoRequest:
     offset = seekStructure(0,1);
     length = readByte(offset++);
     return getString( length - 1, offset );


    default:
     return "";
   }
    
  }

  private int seekStructure(int offset, int index)
  {
   int length;

   offset += 8;  // byte offset into variable message part

   while ( --index > 0)
   {
    length = readByte( offset );

    if ( length + offset > Length )
    {
     Trace.WriteLine("! bad structure at index "+index);
     return 0;
    }

    offset += (length + 1);
   }


   return offset;
  }
   
  private string getString(int length, int offset)
  {
   string st;
    
   st = "";
    
   return st;
  }



  public byte readByte(int index )
  {
   return streamBuffer[index];
  }

  public ushort readWord(int index)
  {
   ushort lb;
   ushort hb;

   lb   = streamBuffer[index];
   hb   = streamBuffer[index+1];
   hb <<= 8;
   hb |= lb;
   return hb;
  }

  public int readDWord(int index  )
  {
   int lb;
   int hb;

   lb = readWord(index);
   hb = readWord(index+2);

   return (hb << 16)|lb;
  }
#endregion

#region Private identifier
   private IntPtr      umBuffer;
   private IntPtr      internalMemory;
   private int         writeIndex;
   private CapiCommand command;
   private int         length;
   bool                fDisposed;
   byte []             streamBuffer;
   CapiApplication     application;

#endregion
  }



}

25.10.2006 - 09:49 Uhr

hallo,

wenn Du schon C oder C++ Code hast, solltest Du ihn benutzen um ein
Assembly daraus zu machen. Mit entsprechenden C++ CLI Wrappern
kannst Du Deinen alten CAPI C/C++ Code wunderbar weiter benutzen
und eine managed Assembly daraus machen.

Es ist relativ blödsinning und unproduktiv mit C# alles von Grund auf
neu zu erfinden mit neuen Fehlern. Die Zeit spare ich mir, mein CAPI C++ Code läuft
seit über 10 Jahren in jeder Windows Umgebung (ob User oder Kernel Mode)
und jetzt auch in einem Managed Wrapper.

elli