aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Adlib.cpp145
-rw-r--r--Makefile7
-rw-r--r--Mpu401.cpp7
-rw-r--r--README.md18
-rw-r--r--midialsa.cpp6
-rw-r--r--pcmalsa.cpp42
-rw-r--r--pcmalsa.h8
7 files changed, 112 insertions, 121 deletions
diff --git a/Adlib.cpp b/Adlib.cpp
index cd4d6e2..3033c4e 100644
--- a/Adlib.cpp
+++ b/Adlib.cpp
@@ -51,9 +51,6 @@
#define LOG_ENABLE_FLOW 1
#define LOG_GROUP LOG_GROUP_DEV_SB16
#include <VBox/vmm/pdmdev.h>
-#ifndef IN_RING3
-# include <VBox/vmm/pdmapi.h>
-#endif
#include <VBox/AssertGuest.h>
#include <VBox/version.h>
#include <iprt/assert.h>
@@ -61,6 +58,10 @@
#include "opl3.h"
+#ifndef IN_RING3
+#error "R3-only driver"
+#endif
+
#if RT_OPSYS == RT_OPSYS_LINUX
#include "pcmalsa.h"
typedef PCMOutAlsa PCMOutBackend;
@@ -77,7 +78,7 @@ typedef PCMOutWin PCMOutBackend;
#define ADLIB_DEFAULT_OUT_DEVICE "default"
#define ADLIB_DEFAULT_SAMPLE_RATE 22055 /* Hz */
-#define ADLIB_NUM_CHANNELS 2 /* as we are actually using OPL3 */
+#define ADLIB_NUM_CHANNELS 2 /* as we are actually supporting OPL3 */
enum {
ADLIB_PORT_ADDR = 0,
@@ -90,8 +91,11 @@ enum {
/** The saved state version. */
#define ADLIB_SAVED_STATE_VERSION 1
+/** Maximum number of sound samples render in one batch by render thread. */
+#define ADLIB_RENDER_BLOCK_TIME 5 /* in millisec */
+
/** The render thread will shutdown if this time passes since the last OPL register write. */
-#define ADLIB_RENDER_THREAD_TIMEOUT 5000 /* in millisec */
+#define ADLIB_RENDER_SUSPEND_TIMEOUT 5000 /* in millisec */
#define OPL2_NUM_IO_PORTS 2
#define OPL3_NUM_IO_PORTS 4
@@ -126,9 +130,8 @@ typedef struct {
PCMOutBackend pcmOut;
/** Thread that connects to PCM out, renders and pushes audio data. */
RTTHREAD hRenderThread;
- /** Buffer for the rendering thread to use. */
+ /** Buffer for the rendering thread to use, size defined by ADLIB_RENDER_BLOCK_TIME. */
R3PTRTYPE(uint8_t *) pbRenderBuf;
- size_t uRenderBufSize;
/** Flag to signal render thread to shut down. */
bool volatile fShutdown;
/** Flag from render thread indicated it has shutdown (e.g. due to error or timeout). */
@@ -156,6 +159,18 @@ typedef ADLIBSTATE *PADLIBSTATE;
#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+static inline uint64_t adlibCalculateFramesFromMilli(PADLIBSTATE pThis, uint64_t milli)
+{
+ uint64_t rate = pThis->uSampleRate;
+ return (rate * milli) / 1000;
+}
+
+static inline size_t adlibCalculateBytesFromFrames(PADLIBSTATE pThis, uint64_t frames)
+{
+ NOREF(pThis);
+ return frames * sizeof(uint16_t) * ADLIB_NUM_CHANNELS;
+}
+
static uint64_t adlibCalculateTimerExpire(PPDMDEVINS pDevIns, uint8_t value, uint64_t period)
{
uint64_t delay_usec = (0x100 - value) * period;
@@ -168,6 +183,12 @@ static uint64_t adlibCalculateTimerExpire(PPDMDEVINS pDevIns, uint8_t value, uin
}
/**
+ * The render thread calls into the emulator to render audio frames, and then pushes them
+ * on the PCM output device.
+ * We rely on the PCM output device's blocking writes behavior to avoid running continously.
+ * A small block size (ADLIB_RENDER_BLOCK_TIME) is also used to give the main thread some
+ * opportunities to run.
+ *
* @callback_method_impl{FNRTTHREAD}
*/
static DECLCALLBACK(int) adlibRenderThread(RTTHREAD ThreadSelf, void *pvUser)
@@ -178,52 +199,37 @@ static DECLCALLBACK(int) adlibRenderThread(RTTHREAD ThreadSelf, void *pvUser)
// Compute the max number of frames we can store on our temporary buffer.
int16_t *buf = (int16_t*) pThis->pbRenderBuf;
- ssize_t buf_size = pThis->uRenderBufSize;
- ssize_t buf_samples = buf_size / sizeof(int16_t);
- ssize_t buf_frames = buf_samples / ADLIB_NUM_CHANNELS;
+ uint64_t buf_frames = adlibCalculateFramesFromMilli(pThis, ADLIB_RENDER_BLOCK_TIME);
- Log(("adlib: Starting render thread\n"));
+ Log(("adlib: Starting render thread with buf_frames=%lld\n", buf_frames));
int rc = pPcmOut->open(pThis->pszOutDevice, pThis->uSampleRate, ADLIB_NUM_CHANNELS);
AssertLogRelRCReturn(rc, rc);
while (!ASMAtomicReadBool(&pThis->fShutdown)
- && ASMAtomicReadU64(&pThis->tmLastWrite) + ADLIB_RENDER_THREAD_TIMEOUT >= RTTimeSystemMilliTS()) {
- ssize_t avail = pPcmOut->avail();
-
- if (avail < 0) {
- LogWarn(("adlib: render thread avail err=%d\n", avail));
- break;
- }
- if (avail == 0) {
- rc = pPcmOut->wait();
- AssertLogRelRCBreak(rc);
- avail = pPcmOut->avail();
- if (avail < 0) {
- LogWarn(("adlib: render thread wait avail err=%d\n", avail));
- break;
- }
- }
-
- avail = RT_MIN(avail, buf_frames);
-
- Log3(("rendering %ld frames\n", avail));
+ && ASMAtomicReadU64(&pThis->tmLastWrite) + ADLIB_RENDER_SUSPEND_TIMEOUT >= RTTimeSystemMilliTS()) {
+ Log3(("rendering %lld frames\n", buf_frames));
RTCritSectEnter(&pThis->critSect);
- OPL3_GenerateStream(&pThis->opl, buf, avail);
+ OPL3_GenerateStream(&pThis->opl, buf, buf_frames);
RTCritSectLeave(&pThis->critSect);
- ssize_t written_frames = pPcmOut->write(buf, avail);
+ Log3(("writing %lld frames\n", buf_frames));
+
+ ssize_t written_frames = pPcmOut->write(buf, buf_frames);
if (written_frames < 0) {
- LogWarn(("adlib: render thread write err=%d\n", written_frames));
- break;
+ rc = written_frames;
+ AssertLogRelMsgFailedBreak(("adlib: render thread write err=%Rrc\n", written_frames));
}
+
+ RTThreadYield();
}
- rc = pPcmOut->close();
- AssertLogRelRC(rc);
+ int rcClose = pPcmOut->close();
+ AssertLogRelRC(rcClose);
+ if (RT_SUCCESS(rc)) rc = rcClose;
- Log(("adlib: Stopping render thread\n"));
+ Log(("adlib: Stopping render thread with rc=%Rrc\n", rc));
ASMAtomicWriteBool(&pThis->fStopped, true);
@@ -231,7 +237,7 @@ static DECLCALLBACK(int) adlibRenderThread(RTTHREAD ThreadSelf, void *pvUser)
}
/** Waits for the render thread to finish and reaps it. */
-static int adlibReapRenderThread(PPDMDEVINS pDevIns, RTMSINTERVAL millies = 1000)
+static int adlibReapRenderThread(PPDMDEVINS pDevIns, RTMSINTERVAL millies = 100)
{
PADLIBSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PADLIBSTATE);
@@ -253,6 +259,12 @@ static int adlibStopRenderThread(PPDMDEVINS pDevIns, bool wait = false)
{
PADLIBSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PADLIBSTATE);
+ if (pThis->hRenderThread == NIL_RTTHREAD) {
+ // Already stopped & reaped
+ return VINF_SUCCESS;
+ }
+
+ // Raise the flag for the thread
ASMAtomicWriteBool(&pThis->fShutdown, true);
if (wait) {
@@ -263,36 +275,19 @@ static int adlibStopRenderThread(PPDMDEVINS pDevIns, bool wait = false)
return VINF_SUCCESS;
}
-static int adlibResetRenderThread(PPDMDEVINS pDevIns)
-{
- PADLIBSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PADLIBSTATE);
-
- // Thread has to be shutdown
- AssertReturn(ASMAtomicReadBool(&pThis->fShutdown), VERR_INVALID_STATE);
-
- int rc = adlibReapRenderThread(pDevIns);
- if (RT_SUCCESS(rc)) {
- pThis->fShutdown = false;
- pThis->fStopped = false;
- } else {
- LogWarn(("adlib%d: can't reset render thread, it did not terminate (%Rrc)\n", rc));
- }
-
- return rc;
-}
-
static void adlibWakeRenderThread(PPDMDEVINS pDevIns)
{
PADLIBSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PADLIBSTATE);
ASMAtomicWriteU64(&pThis->tmLastWrite, RTTimeSystemMilliTS());
- AssertReturnVoid(!ASMAtomicReadBool(&pThis->fShutdown));
-
// Reap any existing render thread if it had stopped
if (ASMAtomicReadBool(&pThis->fStopped)) {
int rc = adlibReapRenderThread(pDevIns);
- AssertRCReturnVoid(rc);
+ AssertLogRelRCReturnVoid(rc);
+ } else if (ASMAtomicReadBool(&pThis->fShutdown)
+ && pThis->hRenderThread != NIL_RTTHREAD) {
+ AssertLogRelMsgFailedReturnVoid(("can't wake render thread -- it's shutting down!\n"));
}
// If there is no existing render thread, start a new one
@@ -305,10 +300,7 @@ static void adlibWakeRenderThread(PPDMDEVINS pDevIns)
int rc = RTThreadCreateF(&pThis->hRenderThread, adlibRenderThread, pThis, 0,
RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE,
"adlib%u_render", pDevIns->iInstance);
- if (RT_FAILURE(rc)) {
- LogWarn(("adlib%d: could not start render thread (%Rrc)\n", pDevIns->iInstance, rc));
- AssertRCReturnVoid(rc);
- }
+ AssertLogRelRCReturnVoid(rc);
}
}
@@ -334,6 +326,10 @@ static uint8_t adlibReadStatus(PPDMDEVINS pDevIns)
if (pThis->timer2Enable && tmNow > pThis->timer2Expire) {
status |= RT_BIT(7) | RT_BIT(5);
}
+ if (!pThis->fOPL3) {
+ // OPL2 seems to have this as special signature.
+ status |= 0x6;
+ }
Log2Func(("status=0x%x\n", status));
@@ -553,14 +549,6 @@ static DECLCALLBACK(void) adlibR3Suspend(PPDMDEVINS pDevIns)
}
/**
- * @interface_method_impl{PDMDEVREG,pfnResume}
- */
-static DECLCALLBACK(void) adlibR3Resume(PPDMDEVINS pDevIns)
-{
- adlibResetRenderThread(pDevIns);
-}
-
-/**
* @interface_method_impl{PDMDEVREG,pfnPowerOff}
*/
static DECLCALLBACK(void) adlibR3PowerOff(PPDMDEVINS pDevIns)
@@ -611,18 +599,17 @@ static DECLCALLBACK(int) adlibR3Construct(PPDMDEVINS pDevIns, int iInstance, PCF
adlibR3Reset(pDevIns);
/* Initialize now the buffer that will be used by the render thread. */
- // Give it as much space as it could possibly need, like half a second
- pThis->uRenderBufSize = (pThis->uSampleRate * ADLIB_NUM_CHANNELS * sizeof(uint16_t)) / 2;
- pThis->pbRenderBuf = (uint8_t *) RTMemAllocZ(pThis->uRenderBufSize);
+ size_t renderBlockSize = adlibCalculateBytesFromFrames(pThis, adlibCalculateFramesFromMilli(pThis, ADLIB_RENDER_BLOCK_TIME));
+ pThis->pbRenderBuf = (uint8_t *) RTMemAlloc(renderBlockSize);
AssertReturn(pThis->pbRenderBuf, VERR_NO_MEMORY);
/* Prepare the render thread, but not create it yet. */
- pThis->fShutdown = false;
+ pThis->fShutdown = false;
pThis->fStopped = false;
pThis->hRenderThread = NIL_RTTHREAD;
pThis->tmLastWrite = 0;
- rc = RTCritSectInit(&pThis->critSect);
- AssertRCReturn(rc, rc);
+ rc = RTCritSectInit(&pThis->critSect);
+ AssertRCReturn(rc, rc);
/*
* Register I/O ports.
@@ -716,7 +703,7 @@ static const PDMDEVREG g_DeviceAdlib =
/* .pfnPowerOn = */ NULL,
/* .pfnReset = */ adlibR3Reset,
/* .pfnSuspend = */ adlibR3Suspend,
- /* .pfnResume = */ adlibR3Resume,
+ /* .pfnResume = */ NULL,
/* .pfnAttach = */ NULL,
/* .pfnDetach = */ NULL,
/* .pfnQueryInterface = */ NULL,
diff --git a/Makefile b/Makefile
index 55d57d5..188ea1b 100644
--- a/Makefile
+++ b/Makefile
@@ -89,14 +89,17 @@ $(OUTDIR)/ExtPack.xml: ExtPack.xml
$(OUTDIR)/ExtPack.signature:
echo "todo" > $@
-$(OUTDIR)/ExtPack.manifest: $(OUTDIR)
+$(OUTDIR)/ExtPack.manifest: $(OUTDIR) $(OUTOSDIR)
cd $(OUTDIR) ;\
find -type f -printf '%P\n' | xargs ../build_manifest.sh > $(@F)
pack: $(OUTDIR)/ExtPack.xml $(OUTDIR)/ExtPack.signature $(OUTDIR)/ExtPack.manifest
tar --format=ustar --numeric-owner --owner=0 --group=0 --mode='a=rX,u+w' --sort=name -C $(OUTDIR) -f VMusic.vbox-extpack -v -z -c .
+strip:
+ strip $(OUTOSDIR)/*.$(SO)
+
clean:
rm -rf $(OUTDIR) $(OBJDIR) VMusic.vbox-extpack
-.PHONY: all build clean pack
+.PHONY: all build clean strip pack
diff --git a/Mpu401.cpp b/Mpu401.cpp
index 1f9e8ec..d4ca931 100644
--- a/Mpu401.cpp
+++ b/Mpu401.cpp
@@ -51,14 +51,15 @@
#define LOG_ENABLE_FLOW 1
#define LOG_GROUP LOG_GROUP_DEV_SB16
#include <VBox/vmm/pdmdev.h>
-#ifndef IN_RING3
-# include <VBox/vmm/pdmapi.h>
-#endif
#include <VBox/AssertGuest.h>
#include <VBox/version.h>
#include <iprt/assert.h>
#include <iprt/mem.h>
+#ifndef IN_RING3
+#error "R3-only driver"
+#endif
+
#if RT_OPSYS == RT_OPSYS_LINUX
#include "midialsa.h"
typedef MIDIOutAlsa MIDIOutBackend;
diff --git a/README.md b/README.md
index e58b421..57148c8 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ VBoxManage setextradata "$vm" VBoxInternal/Devices/mpu401/0/Trusted 1
If the devices have been correctly enabled, you should see the following messages in the
VBox.log file of a virtual machine after it has been powered on:
-```
+```{ use_pygments=false }
00:00:00.799849 Installed Extension Packs:
00:00:00.799866 VMusic (Version: 0.2 r0; VRDE Module: )
...
@@ -77,7 +77,7 @@ or [Munt](https://sourceforge.net/projects/munt/).
First, start the virtual machine. Second, start and configure your software synthesizer.
If you run `aconnect -l` at this point, you will see a list of all your real/virtual MIDI devices in the system. Sample output from `aconnect -l`:
-```
+```{ use_pygments=false }
client 0: 'System' [type=kernel]
0 'Timer '
Connecting To: 142:0
@@ -94,7 +94,7 @@ This indicates that there is a `Munt MT-32` synthesizer at port 129:0 , and a `V
The latter is the virtual MIDI device used by the MPU-401 emulation. So, to send the virtual machine's MIDI output to Munt,
connect the two ports by running:
-```
+```{ use_pygments=false }
aconnect 128:0 129:0
```
@@ -105,16 +105,16 @@ all virtual MIDI ports to it, in which case you may not need to connect anything
# Building
-You need the standard C++ building tools, _make_, _libasound_ and headers (e.g. `libasound2-dev` in Ubuntu).
+You need the standard C++ building tools, make, libasound and headers (e.g. `libasound2-dev` in Ubuntu).
First, ensure that, in the directory where the VMusic source resides, you add two extra directories:
-* _VirtualBox.src_ containing the unpacked pristine VirtualBox source from [virtualbox.org](https://www.virtualbox.org/wiki/Downloads), e.g. the contents of [VirtualBox-6.1.32.tar.bz2](https://download.virtualbox.org/virtualbox/6.1.32/VirtualBox-6.1.32.tar.bz2).
+* `VirtualBox.src` containing the unpacked pristine VirtualBox source from [virtualbox.org](https://www.virtualbox.org/wiki/Downloads), e.g. the contents of [VirtualBox-6.1.32.tar.bz2](https://download.virtualbox.org/virtualbox/6.1.32/VirtualBox-6.1.32.tar.bz2).
-* _VirtualBox.linux.amd64_ containing at least the following VirtualBox Linux.amd64 libraries,
-either from an official installation or your distribution's package: _VBoxRT.so_ and _VBoxVMM.so_.
-E.g. copy _/usr/lib/virtualbox/VBoxRT.so_ into _VirtualBox.linux.amd64/VBoxRT.so_.
+* `VirtualBox.linux.amd64` containing at least the following VirtualBox Linux.amd64 libraries,
+either from an official installation or your distribution's package: `VBoxRT.so` and `VBoxVMM.so`.
+E.g. copy `/usr/lib/virtualbox/VBoxRT.so` into `VirtualBox.linux.amd64/VBoxRT.so`.
-After this, just type `make` followed by `make pack` and _VMusic.vbox-extpack_ should be generated.
+After this, just type `make` followed by `make pack` and `VMusic.vbox-extpack` should be generated.
diff --git a/midialsa.cpp b/midialsa.cpp
index e8e653a..ef07d8a 100644
--- a/midialsa.cpp
+++ b/midialsa.cpp
@@ -36,7 +36,7 @@ MIDIOutAlsa::~MIDIOutAlsa()
int MIDIOutAlsa::open(const char *dev)
{
- int err;
+ int err;
if ((err = snd_rawmidi_open(NULL, &_out, "virtual", SND_RAWMIDI_NONBLOCK))) {
LogWarn(("ALSA rawmidi open error: %s\n", snd_strerror(err)));
@@ -46,7 +46,7 @@ int MIDIOutAlsa::open(const char *dev)
// TODO: Connect somewhere
NOREF(dev);
- return VINF_SUCCESS;
+ return VINF_SUCCESS;
}
int MIDIOutAlsa::close()
@@ -55,7 +55,7 @@ int MIDIOutAlsa::close()
snd_rawmidi_close(_out);
_out = NULL;
}
- return VINF_SUCCESS;
+ return VINF_SUCCESS;
}
ssize_t MIDIOutAlsa::write(uint8_t *data, size_t len)
diff --git a/pcmalsa.cpp b/pcmalsa.cpp
index eec9b94..5708509 100644
--- a/pcmalsa.cpp
+++ b/pcmalsa.cpp
@@ -36,13 +36,13 @@ PCMOutAlsa::~PCMOutAlsa()
int PCMOutAlsa::open(const char *dev, unsigned int sampleRate, unsigned int channels)
{
- int err;
+ int err;
err = snd_pcm_open(&_pcm, dev, SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0) {
- LogWarn(("ALSA playback open error: %s\n", snd_strerror(err)));
- return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
- }
+ LogWarn(("ALSA playback open error: %s\n", snd_strerror(err)));
+ return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
+ }
// TODO: Right now, setting a too large period size means we will not let the main
// thread run long enough to actually change notes/voices. Need some actual synchronization.
@@ -57,16 +57,16 @@ int PCMOutAlsa::open(const char *dev, unsigned int sampleRate, unsigned int chan
return VERR_AUDIO_STREAM_COULD_NOT_CREATE;
}
- return VINF_SUCCESS;
+ return VINF_SUCCESS;
}
int PCMOutAlsa::close()
{
- if (_pcm) {
+ if (_pcm) {
snd_pcm_drain(_pcm);
- snd_pcm_close(_pcm);
- }
- return VINF_SUCCESS;
+ snd_pcm_close(_pcm);
+ }
+ return VINF_SUCCESS;
}
ssize_t PCMOutAlsa::avail()
@@ -104,16 +104,16 @@ int PCMOutAlsa::wait()
ssize_t PCMOutAlsa::write(int16_t *buf, size_t n)
{
- snd_pcm_sframes_t frames = snd_pcm_writei(_pcm, buf, n);
- if (frames < 0) {
- LogFlow(("ALSA trying to recover from error: %s\n", snd_strerror(frames)));
- frames = snd_pcm_recover(_pcm, frames, 0);
- }
- if (frames < 0) {
+ snd_pcm_sframes_t frames = snd_pcm_writei(_pcm, buf, n);
+ if (frames < 0) {
+ LogFlow(("ALSA trying to recover from error: %s\n", snd_strerror(frames)));
+ frames = snd_pcm_recover(_pcm, frames, 0);
+ }
+ if (frames < 0) {
LogWarn(("ALSA write error: %s\n", snd_strerror(frames)));
- return VERR_AUDIO_STREAM_NOT_READY;
- }
- return frames;
+ return VERR_AUDIO_STREAM_NOT_READY;
+ }
+ return frames;
}
int PCMOutAlsa::setParams(unsigned int sampleRate, unsigned int channels, unsigned int bufferTime, unsigned int periodTime)
@@ -175,19 +175,19 @@ int PCMOutAlsa::setParams(unsigned int sampleRate, unsigned int channels, unsign
}
err = snd_pcm_hw_params_get_buffer_size(hwparams, &_bufferSize);
if (err < 0) {
- printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
+ LogWarnFunc(("Unable to get buffer size for playback: %s\n", snd_strerror(err)));
return err;
}
/* set the period time */
err = snd_pcm_hw_params_set_period_time_near(_pcm, hwparams, &periodTime, NULL);
if (err < 0) {
- printf("Unable to set period time %u for playback: %s\n", periodTime, snd_strerror(err));
+ LogWarnFunc(("Unable to set period time %u for playback: %s\n", periodTime, snd_strerror(err)));
return err;
}
err = snd_pcm_hw_params_get_period_size(hwparams, &_periodSize, NULL);
if (err < 0) {
- printf("Unable to get period size for playback: %s\n", snd_strerror(err));
+ LogWarnFunc(("Unable to get period size for playback: %s\n", snd_strerror(err)));
return err;
}
diff --git a/pcmalsa.h b/pcmalsa.h
index d9837c5..c314faf 100644
--- a/pcmalsa.h
+++ b/pcmalsa.h
@@ -27,11 +27,11 @@ typedef struct _snd_pcm snd_pcm_t;
class PCMOutAlsa
{
public:
- PCMOutAlsa();
- ~PCMOutAlsa();
+ PCMOutAlsa();
+ ~PCMOutAlsa();
int open(const char *dev, unsigned int sampleRate, unsigned int channels);
- int close();
+ int close();
ssize_t avail();
int wait();
@@ -42,7 +42,7 @@ private:
int setParams(unsigned int sampleRate, unsigned int channels, unsigned int bufferTime, unsigned int periodTime);
private:
- snd_pcm_t * _pcm;
+ snd_pcm_t * _pcm;
size_t _bufferSize;
size_t _periodSize;
};