]> git.unchartedbackwaters.co.uk Git - francis/winuae.git/commitdiff
mp3decoder separated
authorToni Wilen <twilen@winuae.net>
Fri, 16 Jul 2010 12:27:04 +0000 (15:27 +0300)
committerToni Wilen <twilen@winuae.net>
Fri, 16 Jul 2010 12:27:04 +0000 (15:27 +0300)
od-win32/mp3decoder.cpp [new file with mode: 0644]
od-win32/mp3decoder.h [new file with mode: 0644]

diff --git a/od-win32/mp3decoder.cpp b/od-win32/mp3decoder.cpp
new file mode 100644 (file)
index 0000000..95730ee
--- /dev/null
@@ -0,0 +1,218 @@
+
+/*
+* UAE
+*
+* mp3 decoder helper class
+*
+* Copyright 2010 Toni Wilen
+*
+*/
+
+#include "sysconfig.h"
+#include "sysdeps.h"
+
+#include <zfile.h>
+#include <mp3decoder.h>
+
+#include <windows.h>
+#include <mmreg.h>
+#include <msacm.h>
+
+#define MP3_BLOCK_SIZE 522
+
+static int mp3_bitrates[] = {
+  0,  32,  64,  96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1,
+  0,  32,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, -1,
+  0,  32,  40,  48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, -1,
+  0,  32,  48,  56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, -1,
+  0,   8,  16,  24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, -1
+};
+static int mp3_frequencies[] = {
+       44100, 48000, 32000, 0,
+       22050, 24000, 16000, 0,
+       11025, 12000,  8000, 0
+};
+static int mp3_samplesperframe[] = {
+        384,  384,  384,
+       1152, 1152, 1152,
+       1152,  576,  576
+};
+
+mp3decoder::~mp3decoder()
+{
+       if (g_mp3stream)
+               acmStreamClose((HACMSTREAM)g_mp3stream, 0);
+       g_mp3stream = NULL;
+}
+
+mp3decoder::mp3decoder()
+{
+       MMRESULT mmr;
+       LPWAVEFORMATEX waveFormat;
+       LPMPEGLAYER3WAVEFORMAT mp3format;
+       DWORD maxFormatSize;
+
+       // find the biggest format size
+       maxFormatSize = 0;
+       mmr = acmMetrics(NULL, ACM_METRIC_MAX_SIZE_FORMAT, &maxFormatSize);
+  
+       // define desired output format
+       waveFormat = (LPWAVEFORMATEX)LocalAlloc(LPTR, maxFormatSize);
+       waveFormat->wFormatTag = WAVE_FORMAT_PCM;
+       waveFormat->nChannels = 2; // stereo
+       waveFormat->nSamplesPerSec = 44100; // 44.1kHz
+       waveFormat->wBitsPerSample = 16; // 16 bits
+       waveFormat->nBlockAlign = 4; // 4 bytes of data at a time are useful (1 sample)
+       waveFormat->nAvgBytesPerSec = 4 * 44100; // byte-rate
+       waveFormat->cbSize = 0; // no more data to follow
+  
+       // define MP3 input format
+       mp3format = (LPMPEGLAYER3WAVEFORMAT)LocalAlloc(LPTR, maxFormatSize);
+       mp3format->wfx.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES;
+       mp3format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3;
+       mp3format->wfx.nChannels = 2;
+       mp3format->wfx.nAvgBytesPerSec = 128 * (1024 / 8);  // not really used but must be one of 64, 96, 112, 128, 160kbps
+       mp3format->wfx.wBitsPerSample = 0;                  // MUST BE ZERO
+       mp3format->wfx.nBlockAlign = 1;                     // MUST BE ONE
+       mp3format->wfx.nSamplesPerSec = 44100;              // 44.1kHz
+       mp3format->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;
+       mp3format->nBlockSize = MP3_BLOCK_SIZE;             // voodoo value #1
+       mp3format->nFramesPerBlock = 1;                     // MUST BE ONE
+       mp3format->nCodecDelay = 1393;                      // voodoo value #2
+       mp3format->wID = MPEGLAYER3_ID_MPEG;
+  
+       mmr = acmStreamOpen((LPHACMSTREAM)&g_mp3stream,               // open an ACM conversion stream
+                NULL,                       // querying all ACM drivers
+                (LPWAVEFORMATEX) mp3format, // converting from MP3
+                waveFormat,                 // to WAV
+                NULL,                       // with no filter
+                0,                          // or async callbacks
+                0,                          // (and no data for the callback)
+                0                           // and no flags
+                );
+  
+       LocalFree(mp3format);
+       LocalFree(waveFormat);
+       if (mmr != MMSYSERR_NOERROR) {
+               write_log(L"CUEMP3: couldn't open ACM mp3 decoder, %d\n", mmr);
+               throw exception();
+       }
+}
+
+uae_u8 *mp3decoder::get (struct zfile *zf, int maxsize)
+{
+       MMRESULT mmr;
+       unsigned long rawbufsize = 0;
+       LPBYTE mp3buf;
+       LPBYTE rawbuf;
+       uae_u8 *outbuf = NULL;
+       int outoffset = 0;
+       ACMSTREAMHEADER mp3streamHead;
+       HACMSTREAM h = (HACMSTREAM)g_mp3stream;
+
+       write_log(L"CUEMP3: decoding '%s'..\n", zfile_getname(zf));
+       mmr = acmStreamSize(h, MP3_BLOCK_SIZE, &rawbufsize, ACM_STREAMSIZEF_SOURCE);
+       if (mmr != MMSYSERR_NOERROR) {
+               write_log (L"CUEMP3: acmStreamSize, %d\n", mmr);
+               return NULL;
+       }
+       // allocate our I/O buffers
+       mp3buf = (LPBYTE)LocalAlloc(LPTR, MP3_BLOCK_SIZE);
+       rawbuf = (LPBYTE)LocalAlloc(LPTR, rawbufsize);
+  
+       // prepare the decoder
+       ZeroMemory(&mp3streamHead, sizeof (ACMSTREAMHEADER));
+       mp3streamHead.cbStruct = sizeof (ACMSTREAMHEADER);
+       mp3streamHead.pbSrc = mp3buf;
+       mp3streamHead.cbSrcLength = MP3_BLOCK_SIZE;
+       mp3streamHead.pbDst = rawbuf;
+       mp3streamHead.cbDstLength = rawbufsize;
+       mmr = acmStreamPrepareHeader(h, &mp3streamHead, 0);
+       if (mmr != MMSYSERR_NOERROR) {
+               write_log(L"CUEMP3: acmStreamPrepareHeader, %d\n", mmr);
+               return NULL;
+       }
+       zfile_fseek(zf, 0, SEEK_SET);
+       outbuf = xcalloc(uae_u8, maxsize);
+       for (;;) {
+               int count = zfile_fread(mp3buf, 1, MP3_BLOCK_SIZE, zf);
+               if (count != MP3_BLOCK_SIZE)
+                       break;
+               // convert the data
+               mmr = acmStreamConvert(h, &mp3streamHead, ACM_STREAMCONVERTF_BLOCKALIGN);
+               if (mmr != MMSYSERR_NOERROR) {
+                       write_log(L"CUEMP3: acmStreamConvert, %d\n", mmr);
+                       return NULL;
+               }
+               if (outoffset + mp3streamHead.cbDstLengthUsed > maxsize)
+                       break;
+               memcpy(outbuf + outoffset, rawbuf, mp3streamHead.cbDstLengthUsed);
+               outoffset += mp3streamHead.cbDstLengthUsed;
+       }
+       acmStreamUnprepareHeader(h, &mp3streamHead, 0);
+       LocalFree(rawbuf);
+       LocalFree(mp3buf);
+       write_log(L"CUEMP3: unpacked size %d bytes\n", outoffset);
+       return outbuf;
+}
+
+uae_u32 mp3decoder::getsize (struct zfile *zf)
+{
+       uae_u32 size;
+       int frames;
+
+       frames = 0;
+       size = 0;
+       for (;;) {
+               int ver, layer, bitrate, freq, padding, bitindex, iscrc;
+               int samplerate, framelen, bitrateidx, channelmode;
+               int isstereo;
+               uae_u8 header[4];
+
+               if (zfile_fread(header, sizeof header, 1, zf) != 1)
+                       return size;
+               if (header[0] != 0xff || ((header[1] & (0x80 | 0x40 | 0x20)) != (0x80 | 0x40 | 0x20))) {
+                       zfile_fseek (zf, -3, SEEK_CUR);
+                       continue;
+               }
+               ver = (header[1] >> 3) & 3;
+               if (ver == 1)
+                       return 0;
+               if (ver == 0)
+                       ver = 2;
+               else if (ver == 2)
+                       ver = 1;
+               else if (ver == 3)
+                       ver = 0;
+               layer = 4 - ((header[1] >> 1) & 3);
+               if (layer == 4)
+                       return 0;
+               iscrc = ((header[1] >> 0) & 1) ? 0 : 2;
+               bitrateidx = (header[2] >> 4) & 15;
+               freq = mp3_frequencies[(header[2] >> 2) & 3];
+               if (!freq)
+                       return 0;
+               channelmode = (header[3] >> 6) & 3;
+               isstereo = channelmode != 3;
+               if (ver == 0) {
+                       bitindex = layer - 1;
+               } else {
+                       if (layer == 1)
+                               bitindex = 3;
+                       else
+                               bitindex = 4;
+               }
+               bitrate = mp3_bitrates[bitindex * 16 + bitrateidx] * 1000;
+               if (bitrate <= 0)
+                       return 0;
+               padding = (header[2] >> 1) & 1;
+               samplerate = mp3_samplesperframe[(layer - 1) * 3 + ver];
+               framelen = ((samplerate / 8 * bitrate) / freq) + padding;
+               if (framelen <= 4)
+                       return 0;
+               zfile_fseek(zf, framelen + iscrc - 4, SEEK_CUR);
+               frames++;
+               size += samplerate * 2 * (isstereo ? 2 : 1);
+       }
+       return size;
+}
diff --git a/od-win32/mp3decoder.h b/od-win32/mp3decoder.h
new file mode 100644 (file)
index 0000000..7655c80
--- /dev/null
@@ -0,0 +1,10 @@
+
+class mp3decoder
+{
+       void *g_mp3stream;
+public:
+       mp3decoder();
+       ~mp3decoder();
+       uae_u8 *get(struct zfile *zf, int maxsize);
+       uae_u32 mp3decoder::getsize(struct zfile *zf);
+};