Forum: Mikrocontroller und Digitale Elektronik c/c++ array in bereits reserviertem Speicher anlegen


von Michael U. (amiga)


Lesenswert?

Hallo,

meine C/C++ Kenntnisse sind wohl zu bescheiden:

für meinen Streamplayer (ESP32/Arduino/ESP8266Audi-Lib) wird beim Start 
mit
1
  preallocateBuffer = malloc(preallocateBufferSize);        // Stream-File-Buffer

ein Speicher von 80kB fest reserviert.
Jetzt habe ich Play von SD-Card dazu gebaut, läuft auch soweit alles 
wunschgemäß. Der Stream-File-Buffer bleibt dabei reserviert (für das 
zurückschalte auf Stream-Play zur Laufzeit auch so nötig, selbst ein 
ESP32 hat begrenzten Ram und wenn ich den Freigebe wissen nur die 
Götter, ob ich ihn jemals wiederbekomme(Fragmentierung des Ram).

Jetzt zum Thema: ich möchte in diesem Bereich, der ja bei 
preallocateBuffer beginnt, ein String oder c-String array anlegen für 
die Titelliste der SD-Card.

Das darf beim Umschalten auf Stream-Play gern überschrieben oder 
entfernt werden, es kann bei SD-Card-Start neu angelegt und/oder neu 
beüfllt werden.

Es wird NIE gleichzeitig das Array und der Stream-File-Buffer benutzt.

Hier verläßt es mich jetzt komplett...

Nochmal aus dem aktuellen Source:
1
// hier wird der Bereich bei Streamply benutzt
2
    buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize);
3
4
// und hier das Playende:
5
  if (buff)
6
  {
7
    buff->close();
8
    delete buff;
9
    buff = NULL;
10
  }

Bei SD-Card wird das nie aufgerufen, ich hätte dort dann eben gern ein
String titelliste[500];
oder auch ein Array[500] of Char-Arrays[100];
angelegt ab preallocateBuffer
und am Ende wieder entfernt.

Gruß aus Berlin
Michael

Beitrag #5568400 wurde vom Autor gelöscht.
von mh (Gast)


Lesenswert?

Drei Dinge die evtl. relevant sind:
-std::variant (oder boost::variant oder union wenn std < c++17)
-allocator (vermutlich viel Arbeit)
-placement new (vermutlich keine gute Idee, wenn du fragen musst ;-) )

von Bernd K. (prof7bit)


Lesenswert?

Michael U. schrieb:
> Es wird NIE gleichzeitig das Array und der Stream-File-Buffer benutzt.

union

von Dirk K. (merciless)


Lesenswert?

Michael U. schrieb:
> // hier wird der Bereich bei Streamply benutzt
>     buff = new AudioFileSourceBuffer(file, preallocateBuffer,
> preallocateBufferSize);

Hier wird eine Instanz der Klasse AudioFileSourceBuffer
angelegt. Was hat die denn für Member? Wird in der Klasse
Speicher dynamisch reserviert oder als Value-Member?

merciless

von Michael U. (amiga)


Lesenswert?

Hallo,

es wird ein Char-Buffer angelegt, der bei preallocateBuffer beginnt und 
preallocateBufferSize lang ist. Der wird bei Playende eben mit
1
// und hier das Playende:
2
  if (buff)
3
  {
4
    buff->close();
5
    delete buff;
6
    buff = NULL;
7
  }
für die Klasse komplett beendet und beim nächsten Titel wieder erzeugt.
Das stört mich bei meinen Absichten nicht, Stop wurde da schon 
aufgerufen.

Ich habe jetzt mal mit wenig Sachkenntnis probiert:
1
const int preallocateBufferSize = 80*1024;
2
void *preallocateBuffer = NULL;
3
4
5
void setup()
6
{
7
  Serial.begin(115200);
8
  Serial.println("Reset");
9
10
  char * titelliste;  
11
  int i;
12
  preallocateBuffer = malloc(preallocateBufferSize);        // Stream-File-Buffer
13
  Serial.printf_P(PSTR("Adresse Buffer: %X\n"), preallocateBuffer);
14
  Serial.printf_P(PSTR("Buffer reserviert:  - Free mem=%d\n"), ESP.getFreeHeap());
15
16
  titelliste = (char*)preallocateBuffer;
17
  
18
  for (i=0; i<20; i++)
19
  {
20
    titelliste[i] = i + 'a';
21
    Serial.printf_P(PSTR("Adresse: %X Wert: %c\n"), titelliste+i, titelliste[i]);
22
  }
23
  titelliste[i]='\0';
24
25
  Serial.printf_P(PSTR("Array beschrieben:  - Free mem=%d\n"), ESP.getFreeHeap()); 
26
  Serial.print(titelliste);   
27
}
28
29
void loop()
30
{
31
  
32
}
Das macht soweit, was ich mir vorstelle, ich werde es erstmal als 
Char-Array benutzen, ich habe 80kB in der malloc-Reservierung, ins Array 
sollen max. 500 Strings mit maximal 100 Zeichen (99 und \0).
Die Zugriffpositionen kann ich ja problemlos berechnen.
Wenn ich aus dem SD-Card Mode rausgehe setze ich eben titelliste = NULL; 
dann kann ich beim Benutzen gegenprüfen, ob ich das Array schon gebaut 
habe.

Ein String-Array mit der Arduino Stringklasse wäre beim Füllen und 
benutzen nur meiner Bequemlichkeit entgegengekommen...

Danke für die Anregungen, ich werde mal meine Bildungslücken auf meine 
alten Tage noch ewas aufbessern.

Gruß aus Berlin
Michael

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Erstens:
Wenn du dem Buffer immer brauchst und nie freigeben willst, dann würde 
ich ihn fest anlegen, und nicht malloc() verwenden.

Zweitens:
Wenn du den Buffer wechselweise für verschiedene Zwecke nutzen möchtest,
Dann entweder eine Union verwenden, oder die zu verwendenden Pointer 
casten.

Hier die Cast Variante
1
const size_t bufferSize = 400;
2
3
uint8_t buffer[bufferSize];
4
5
6
7
8
9
void setup() 
10
{
11
  Serial.begin(9600);      
12
  Serial.println("Start");
13
14
// verwendung als String Buffer
15
  char *charPtr = reinterpret_cast<char*>(buffer);
16
17
  sprintf(charPtr,"%u ms",millis());
18
  Serial.println(charPtr);
19
  
20
// ---------------------
21
22
23
// verwendung als Int Buffer
24
  int *intPtr = reinterpret_cast<int*>(buffer);
25
26
  *intPtr = 4711;
27
  Serial.println(*intPtr);
28
29
  
30
}
31
32
void loop() 
33
{
34
35
}

von Michael U. (amiga)


Lesenswert?

Hallo,

Arduino Fanboy D. schrieb:
> Erstens:
> Wenn du dem Buffer immer brauchst und nie freigeben willst, dann würde
> ich ihn fest anlegen, und nicht malloc() verwenden.

naja, das ist in der ESP8266Audio Lib so drin und da habe ich keinen 
zwingenden Grund, das zu ändern. Er holt sich den Buffer sowieso in 
setup(), das stört mich auch direkt. Die Lib macht es beim Nutzen des 
Buffers mit
  buffer = (uint8_t*)inBuff;

bei .close(); setzt er den Zeiger wieder NULL.

Da habe meine Anleihe her und das scheint erstmal soweit 
zusammenzupassen.
Das ist daß, was ich oben probiert habe:
  char * titelliste;
  titelliste = (char*)preallocateBuffer;

Gruß aus Berlin
Michael

von S. R. (svenska)


Lesenswert?

Michael U. schrieb:
> Der Stream-File-Buffer bleibt dabei reserviert (für das
> zurückschalte auf Stream-Play zur Laufzeit auch so nötig, selbst ein
> ESP32 hat begrenzten Ram und wenn ich den Freigebe wissen nur die
> Götter, ob ich ihn jemals wiederbekomme(Fragmentierung des Ram).

Gibt es denn noch andere Teile des Systems, die Speicher alloziieren und 
freigeben? Falls nein, dann ist die Fragmentierung eigentlich nicht 
relevant: nach "x = malloc(size); free(x)" ist der alte Zustand 
wiederhergestellt, falls die libc nicht selbst kaputt ist.

Ansonsten kannst du auch einfach zwei Zeiger auf den gleichen Puffer 
anlegen:
1
void *buf = malloc(80000);
2
AudioBuf *playbuf = (AudioBuf*)buf;
3
SdBuf    *sdbuf = (SdBuf*)buf;

von Michael U. (amiga)


Lesenswert?

Hallo,

S. R. schrieb:
> Gibt es denn noch andere Teile des Systems, die Speicher alloziieren und
> freigeben? Falls nein, dann ist die Fragmentierung eigentlich nicht
> relevant: nach "x = malloc(size); free(x)" ist der alte Zustand
> wiederhergestellt, falls die libc nicht selbst kaputt ist.

Die anderen Teile gibt es prinzipbedingt, weil der Kram auf einem ESP32 
läuft und da irgendwo drunter ein RTOS ist, was auch WLAN usw. bedient. 
Dazu die Lib von Display und Touch, die prinzipiell auch machen kann, 
was sie will.

Deshalb hat der Autor der AudioLib auch diesen Weg (für sich) genommen.

> Ansonsten kannst du auch einfach zwei Zeiger auf den gleichen Puffer
> anlegen:void *buf = malloc(80000);
> AudioBuf *playbuf = (AudioBuf*)buf;
> SdBuf    *sdbuf = (SdBuf*)buf;

So hat es auch geendet und läuft hier jetzt seit einiger Zeit stabil.
Ich nehme den Buffer jetzt als Char-Buffer und berechne meine Offsets 
eben.
Es war ja nur der Gedanke, es als "Array of Array of Char" handeln zu 
können, also die Titeleinträger über z.B. titelliste[293] erreichen zu 
können statt über titelliste + 293 * 100. Das ist mir aber nicht 
gelungen.
Sieht eben z.B. ein strcpy(...); etwas "seltsam" aus.
Letztlich will ich ja nur max. 500 Titelnamen mit max. je 100 Zeichen 
incl. \0 verwalten...

Gruß aus Berlin
Michael

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Michael U. schrieb:
> Die anderen Teile gibt es prinzipbedingt,

Okay, das ist natürlich ein verdammt gutes Argument. :-)

Michael U. schrieb:
> Es war ja nur der Gedanke, es als "Array of Array of Char" handeln zu
> können, also die Titeleinträger über z.B. titelliste[293] erreichen zu
> können statt über titelliste + 293 * 100.

Wie wäre es mit (ungetestet):
1
union u_buf {
2
  char         rawData[80000];
3
  uint16_t     audioData[40000];
4
  struct titel titelData[128];
5
}

Ein "union u_buf" ist exakt 80000 Bytes groß, oder 128 * sizeof(struct 
titel), je nachdem was größer ist. :-)

von Michael U. (amiga)


Lesenswert?

Hallo,

S. R. schrieb:
> Ein "union u_buf" ist exakt 80000 Bytes groß, oder 128 * sizeof(struct
> titel), je nachdem was größer ist. :-)

Klingt erstmal gut, werde ich mal testen.

Gruß aus Berlin
Michael

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.