winemp3.acm: Fix the Mac-specific code.
The previous code does not function on Snow Leopard, and does not conform
to Apple's documentation. This fix should function on all versions of OS X
later than 10.2.
diff --git a/configure b/configure
index cf2fc9d..bb83608 100755
--- a/configure
+++ b/configure
@@ -5781,8 +5781,6 @@
for ac_header in \
AL/al.h \
AudioToolbox/AudioConverter.h \
- AudioToolbox/AudioFile.h \
- AudioToolbox/AudioFileStream.h \
AudioUnit/AudioUnit.h \
Carbon/Carbon.h \
CoreAudio/CoreAudio.h \
@@ -6547,23 +6545,11 @@
COREAUDIO="-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI"
ac_save_LIBS="$LIBS"
- if test "$ac_cv_header_AudioToolbox_AudioFile_h" = "yes"
+ if test "$ac_cv_header_AudioToolbox_AudioConverter_h" = "yes"
then
LIBS="$LIBS $COREAUDIO"
- for ac_func in AudioFileStreamOpen
-do :
- ac_fn_c_check_func "$LINENO" "AudioFileStreamOpen" "ac_cv_func_AudioFileStreamOpen"
-if test "x$ac_cv_func_AudioFileStreamOpen" = x""yes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_AUDIOFILESTREAMOPEN 1
-_ACEOF
-
-fi
-done
-
LIBS="$ac_save_LIBS"
fi
- test "x$ac_cv_func_AudioFileStreamOpen" = xyes || as_fn_append wine_notices "|AudioToolbox version too old, mp3 codec won't be supported."
fi
if test "$ac_cv_header_OpenAL_al_h" = "yes"
then
@@ -11478,7 +11464,7 @@
This is an error since --with-mpg123 was requested." "$LINENO" 5 ;;
esac
fi
-test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_func_AudioFileStreamOpen" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no}
+test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_header_AudioToolbox_AudioConverter_h" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no}
if test "$ac_cv_header_AL_al_h" = "yes"
then
diff --git a/configure.ac b/configure.ac
index 8d2dffe..a338d8c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,8 +367,6 @@
AC_CHECK_HEADERS(\
AL/al.h \
AudioToolbox/AudioConverter.h \
- AudioToolbox/AudioFile.h \
- AudioToolbox/AudioFileStream.h \
AudioUnit/AudioUnit.h \
Carbon/Carbon.h \
CoreAudio/CoreAudio.h \
@@ -707,13 +705,11 @@
dnl CoreServices needed by AudioUnit
AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices -framework AudioToolbox -framework CoreMIDI")
ac_save_LIBS="$LIBS"
- if test "$ac_cv_header_AudioToolbox_AudioFile_h" = "yes"
+ if test "$ac_cv_header_AudioToolbox_AudioConverter_h" = "yes"
then
LIBS="$LIBS $COREAUDIO"
- AC_CHECK_FUNCS(AudioFileStreamOpen)
LIBS="$ac_save_LIBS"
fi
- test "x$ac_cv_func_AudioFileStreamOpen" = xyes || WINE_NOTICE([AudioToolbox version too old, mp3 codec won't be supported.])
fi
if test "$ac_cv_header_OpenAL_al_h" = "yes"
then
@@ -1534,7 +1530,7 @@
fi
WINE_NOTICE_WITH(mpg123,[test "x$ac_cv_lib_mpg123_mpg123_feed" != xyes -a x"$ac_cv_header_CoreAudio_CoreAudio_h" != xyes],
[libmpg123 ${notice_platform}development files not found (or too old), mp3 codec won't be supported.])
-test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_func_AudioFileStreamOpen" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no}
+test "x$ac_cv_lib_mpg123_mpg123_feed" = xyes -o "x$ac_cv_header_AudioToolbox_AudioConverter_h" = xyes || enable_winemp3_acm=${enable_winemp3_acm:-no}
dnl **** Check for OpenAL 1.1 ****
if test "$ac_cv_header_AL_al_h" = "yes"
diff --git a/dlls/winemp3.acm/mpegl3.c b/dlls/winemp3.acm/mpegl3.c
index 6a2dbee..92a3afc 100644
--- a/dlls/winemp3.acm/mpegl3.c
+++ b/dlls/winemp3.acm/mpegl3.c
@@ -37,12 +37,6 @@
# ifdef HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H
# include <AudioToolbox/AudioConverter.h>
# endif
-# ifdef HAVE_AUDIOTOOLBOX_AUDIOFILE_H
-# include <AudioToolbox/AudioFile.h>
-# endif
-# ifdef HAVE_AUDIOTOOLBOX_AUDIOFILESTREAM_H
-# include <AudioToolbox/AudioFileStream.h>
-# endif
#endif
#include "windef.h"
@@ -279,32 +273,36 @@
return MMSYSERR_NOERROR;
}
-#elif defined(HAVE_AUDIOFILESTREAMOPEN)
+#elif defined(HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H)
+
+static const unsigned short Mp3BitRates[2][16] =
+{
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0},
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}
+};
+
+static const unsigned short Mp3SampleRates[2][4] =
+{
+ {44100, 48000, 32000, 0},
+ {22050, 24000, 16000, 0}
+};
typedef struct tagAcmMpeg3Data
{
- LRESULT (*convert)(PACMDRVSTREAMINSTANCE adsi,
- const unsigned char*, LPDWORD, unsigned char*, LPDWORD);
+ LRESULT (*convert)(PACMDRVSTREAMINSTANCE adsi, const unsigned char*,
+ LPDWORD, unsigned char*, LPDWORD);
AudioConverterRef acr;
AudioStreamBasicDescription in,out;
- AudioFileStreamID afs;
AudioBufferList outBuffer;
AudioBuffer inBuffer;
+ SInt32 tagBytesLeft;
+
UInt32 NumberPackets;
AudioStreamPacketDescription *PacketDescriptions;
-
- OSStatus lastError;
} AcmMpeg3Data;
-static inline const char* wine_dbgstr_fourcc(ULONG fourcc)
-{
- char buf[4] = { (char) (fourcc >> 24), (char) (fourcc >> 16),
- (char) (fourcc >> 8), (char) fourcc };
- return wine_dbgstr_an(buf, sizeof(buf));
-}
-
/***********************************************************************
* MPEG3_drvOpen
*/
@@ -321,8 +319,12 @@
return 1;
}
-
-static OSStatus Mp3AudioConverterComplexInputDataProc (
+/*
+ When it asks for data, give it all we have. If we have no data, we assume
+ we will in the future, so give it no packets and return an error, which
+ signals that we will have more later.
+ */
+static OSStatus Mp3AudioConverterComplexInputDataProc(
AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
@@ -336,49 +338,218 @@
{
*ioNumberDataPackets = amd->NumberPackets;
ioData->mNumberBuffers = 1;
- ioData->mBuffers[0].mDataByteSize = amd->inBuffer.mDataByteSize;
- ioData->mBuffers[0].mData = amd->inBuffer.mData;
- ioData->mBuffers[0].mNumberChannels = amd->inBuffer.mNumberChannels;
+ ioData->mBuffers[0] = amd->inBuffer;
amd->inBuffer.mDataByteSize = 0;
if (outDataPacketDescription)
*outDataPacketDescription = amd->PacketDescriptions;
+ return noErr;
}
else
+ {
*ioNumberDataPackets = 0;
- return noErr;
+ return -74;
+ }
}
-static LRESULT mp3_leopard_horse(PACMDRVSTREAMINSTANCE adsi,
- const unsigned char* src, LPDWORD nsrc,
- unsigned char* dst, LPDWORD ndst)
+/*
+ Get the length of the current frame. We need to be at the start of a
+ frame now. The buffer must have at least the four bytes for the header.
+ */
+static SInt32 Mp3GetPacketLength(const unsigned char* src)
{
- AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver;
- OSStatus ret;
+ unsigned char mpegv;
+ unsigned short brate, srate;
+ unsigned int size;
- TRACE("ndst %u %p <- %u %p\n",*ndst,dst,*nsrc, src);
- amd->outBuffer.mNumberBuffers = 1;
- amd->outBuffer.mBuffers[0].mDataByteSize = *ndst;
- amd->outBuffer.mBuffers[0].mData = dst;
- amd->outBuffer.mBuffers[0].mNumberChannels = amd->out.mChannelsPerFrame;
+ /*
+ Check that our position looks like an MP3 header and see which type
+ of MP3 file we have.
+ */
+ if (src[0] == 0xff && src[1] >> 1 == 0x7d) mpegv = 0; /* MPEG-1 File */
+ else if (src[0] == 0xff && src[1] >> 1 == 0x79) mpegv = 1; /* MPEG-2 File */
+ else return -1;
- memset(dst,0xff,*ndst);
- ret = AudioFileStreamParseBytes( amd->afs, *nsrc, src, 0 );
+ /* Fill in bit rate and sample rate. */
+ brate = Mp3BitRates[mpegv][(src[2] & 0xf0) >> 4];
+ srate = Mp3SampleRates[mpegv][(src[2] & 0xc) >> 2];
- if (ret != noErr)
+ /* Certain values for bit rate and sample rate are invalid. */
+ if (brate == 0 || srate == 0) return -1;
+
+ /* Compute frame size, round down */
+ size = 72 * (2 - mpegv) * brate * 1000 / srate;
+
+ /* If there is padding, add one byte */
+ if (src[2] & 0x2) return size + 1;
+ else return size;
+}
+
+/*
+ Apple's AudioFileStream does weird things so we deal with parsing the
+ file ourselves. It was also designed for a different use case, so this
+ is not unexpected. We expect to have MP3 data as input (i.e. we can only
+ deal with MPEG-1 or MPEG-2 Layer III), which simplifies parsing a bit. We
+ understand the ID3v2 header and skip over it. Whenever we have data we
+ want to skip at the beginning of the input, we do this by setting *ndst=0
+ and *nsrc to the length of the unwanted data and return no error.
+ */
+static LRESULT mp3_leopard_horse(PACMDRVSTREAMINSTANCE adsi,
+ const unsigned char* src, LPDWORD nsrc,
+ unsigned char* dst, LPDWORD ndst)
+{
+ OSStatus err;
+ UInt32 size, aspdi, synci, syncSkip;
+ short framelen[4];
+ const unsigned char* psrc;
+ AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver;
+
+ TRACE("ndst %u %p <- %u %p\n", *ndst, dst, *nsrc, src);
+
+ TRACE("First 16 bytes to input: %s\n", wine_dbgstr_an(src, 16));
+
+ /* Parse ID3 tag */
+ if (!memcmp(src, "ID3", 3) && amd->tagBytesLeft == -1)
{
- *nsrc = 0;
- ERR("Feed Error %s\n", wine_dbgstr_fourcc(ret));
+ amd->tagBytesLeft = (src[6] << 21) + (src[7] << 14) + (src[8] << 7) + src[9];
+ if (src[5] & 0x10) amd->tagBytesLeft += 20; /* There is a footer */
+ else amd->tagBytesLeft += 10;
+ }
+
+ /* Consume the tag */
+ if (amd->tagBytesLeft >= (SInt32)*nsrc)
+ {
+ *ndst = 0;
+ amd->tagBytesLeft -= *nsrc;
+
+ TRACE("All %d bytes of source data is ID3 tag\n", *nsrc);
+ return MMSYSERR_NOERROR;
+ }
+ else if (amd->tagBytesLeft > 0)
+ {
+ src += amd->tagBytesLeft;
+ *nsrc -= amd->tagBytesLeft;
+ TRACE("Skipping %d for ID3 tag\n", amd->tagBytesLeft);
+ }
+
+ /*
+ Sync to initial MP3 frame. The largest possible MP3 frame is 1440.
+ Thus, in the first 1440 bytes we must find the beginning of 3 valid
+ frames in a row unless we reach the end of the file first.
+ */
+ syncSkip = 0;
+ for (psrc = src; psrc <= src + *nsrc - 4 && psrc < src + 1440; psrc++)
+ {
+ framelen[0] = 0;
+ for (synci = 1;
+ synci < 4 && psrc + framelen[synci-1] < src + *nsrc - 4;
+ synci++)
+ {
+ framelen[synci] = Mp3GetPacketLength(psrc + framelen[synci-1]);
+ if (framelen[synci] == -1)
+ {
+ synci = 0;
+ break;
+ }
+ framelen[synci] += framelen[synci-1];
+ }
+ if (synci > 0) /* We synced successfully */
+ {
+ if (psrc - src > 0)
+ {
+ syncSkip = psrc - src;
+ src += syncSkip;
+ *nsrc -= syncSkip;
+ TRACE("Skipping %d for frame sync\n", syncSkip);
+ }
+ break;
+ }
+ }
+
+ if (Mp3GetPacketLength(src) == -1)
+ {
+ *ndst = *nsrc = 0;
+ ERR("Frame sync failed. Cannot play file.\n");
return MMSYSERR_ERROR;
}
- else if (amd->lastError != noErr)
+
+ /*
+ Fill in frame descriptions for all frames. We use an extra pointer
+ to keep track of our position in the input.
+ */
+
+ amd->NumberPackets = 25; /* This is the initial array capacity */
+ amd->PacketDescriptions = HeapAlloc(GetProcessHeap(), 0, amd->NumberPackets * sizeof(AudioStreamPacketDescription));
+ if (amd->PacketDescriptions == 0) return MMSYSERR_NOMEM;
+
+ for (aspdi = 0, psrc = src;
+ psrc <= src + *nsrc - 4;
+ psrc += amd->PacketDescriptions[aspdi].mDataByteSize, aspdi++)
{
- *nsrc = 0;
- ERR("Error during feed %s\n", wine_dbgstr_fourcc(ret));
+ /* Return an error if we can't read the frame header */
+ if (Mp3GetPacketLength(psrc) == -1)
+ {
+ *ndst = *nsrc = 0;
+ ERR("Invalid header at %p.\n", psrc);
+ HeapFree(GetProcessHeap(), 0, amd->PacketDescriptions);
+ return MMSYSERR_ERROR;
+ }
+
+ /* If we run out of space, double size and reallocate */
+ if (aspdi >= amd->NumberPackets)
+ {
+ amd->NumberPackets *= 2;
+ amd->PacketDescriptions = HeapReAlloc(GetProcessHeap(), 0, amd->PacketDescriptions, amd->NumberPackets * sizeof(AudioStreamPacketDescription));
+ if (amd->PacketDescriptions == 0) return MMSYSERR_NOMEM;
+ }
+
+ /* Fill in packet data */
+ amd->PacketDescriptions[aspdi].mStartOffset = psrc - src;
+ amd->PacketDescriptions[aspdi].mVariableFramesInPacket = 0;
+ amd->PacketDescriptions[aspdi].mDataByteSize = Mp3GetPacketLength(psrc);
+
+ /* If this brings us past the end, the last one doesn't count */
+ if (psrc + amd->PacketDescriptions[aspdi].mDataByteSize > src + *nsrc) break;
+ }
+
+ /* Fill in correct number of frames */
+ amd->NumberPackets = aspdi;
+
+ /* Adjust nsrc to only include full frames */
+ *nsrc = psrc - src;
+
+ amd->inBuffer.mDataByteSize = *nsrc;
+ amd->inBuffer.mData = src;
+ amd->inBuffer.mNumberChannels = amd->in.mChannelsPerFrame;
+
+ amd->outBuffer.mNumberBuffers = 1;
+ amd->outBuffer.mBuffers[0].mDataByteSize = *ndst;
+ amd->outBuffer.mBuffers[0].mData = dst;
+ amd->outBuffer.mBuffers[0].mNumberChannels = amd->out.mChannelsPerFrame;
+
+ /* Convert the data */
+ size = amd->outBuffer.mBuffers[0].mDataByteSize / amd->out.mBytesPerPacket;
+ err = AudioConverterFillComplexBuffer(amd->acr, Mp3AudioConverterComplexInputDataProc, amd, &size, &amd->outBuffer, 0);
+
+ HeapFree(GetProcessHeap(), 0, amd->PacketDescriptions);
+
+ /* Add skipped bytes back into *nsrc */
+ if (amd->tagBytesLeft > 0)
+ {
+ *nsrc += amd->tagBytesLeft;
+ amd->tagBytesLeft = 0;
+ }
+ *nsrc += syncSkip;
+
+ if (err != noErr && err != -74)
+ {
+ *ndst = *nsrc = 0;
+ ERR("Feed Error: %ld\n", err);
return MMSYSERR_ERROR;
}
*ndst = amd->outBuffer.mBuffers[0].mDataByteSize;
- *nsrc = amd->inBuffer.mDataByteSize;
+
+ TRACE("convert %d -> %d\n", *nsrc, *ndst);
return MMSYSERR_NOERROR;
}
@@ -389,33 +560,7 @@
*/
static void MPEG3_Reset(PACMDRVSTREAMINSTANCE adsi, AcmMpeg3Data* aad)
{
- SInt64 offset;
AudioConverterReset(aad->acr);
- AudioFileStreamSeek(aad->afs, 0, &offset, 0);
-}
-
-static void Mp3PropertyListenerProc ( void *inClientData,
- AudioFileStreamID inAudioFileStream, UInt32 inPropertyID, UInt32 *ioFlags)
-{
- /* No operation at this time */
-}
-
-static void Mp3PacketsProc ( void *inClientData, UInt32 inNumberBytes,
- UInt32 inNumberPackets, const void *inInputData,
- AudioStreamPacketDescription *inPacketDescriptions)
-{
- AcmMpeg3Data *amd = (AcmMpeg3Data*)inClientData;
- UInt32 size;
-
- amd->inBuffer.mDataByteSize = inNumberBytes;
- amd->inBuffer.mData = (void*)inInputData;
- amd->inBuffer.mNumberChannels = amd->in.mChannelsPerFrame;
-
- amd->NumberPackets = inNumberPackets;
- amd->PacketDescriptions = inPacketDescriptions;
-
- size = amd->outBuffer.mBuffers[0].mDataByteSize / amd->out.mBytesPerPacket;
- amd->lastError = AudioConverterFillComplexBuffer(amd->acr, Mp3AudioConverterComplexInputDataProc, inClientData, &size, &amd->outBuffer, NULL);
}
/***********************************************************************
@@ -437,14 +582,9 @@
adsi->dwDriver = (DWORD_PTR)aad;
- if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM &&
+ if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_MPEGLAYER3 &&
adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
{
- goto theEnd;
- }
- else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_MPEGLAYER3 &&
- adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM)
- {
OSStatus err;
aad->in.mSampleRate = adsi->pwfxSrc->nSamplesPerSec;
@@ -458,7 +598,6 @@
aad->in.mFormatFlags = 0;
aad->out.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
aad->in.mBytesPerFrame = 0;
-
aad->out.mBytesPerFrame = (aad->out.mBitsPerChannel * aad->out.mChannelsPerFrame) / 8;
aad->in.mBytesPerPacket = 0;
aad->out.mBytesPerPacket = aad->out.mBytesPerFrame;
@@ -466,33 +605,26 @@
aad->out.mFramesPerPacket = 1;
aad->in.mReserved = aad->out.mReserved = 0;
- aad->acr = NULL;
+ aad->tagBytesLeft = -1;
- err = AudioConverterNew(&aad->in, &aad->out ,&aad->acr);
+ aad->convert = mp3_leopard_horse;
+
+ err = AudioConverterNew(&aad->in, &aad->out, &aad->acr);
if (err != noErr)
{
- ERR("Create failed: %s\n", wine_dbgstr_fourcc(err));
- goto theEnd;
+ ERR("Create failed: %ld\n", err);
}
else
{
- aad->convert = mp3_leopard_horse;
- err = AudioFileStreamOpen(aad, Mp3PropertyListenerProc, Mp3PacketsProc, kAudioFormatMPEGLayer3, &aad->afs);
- if (err != noErr)
- {
- ERR("Stream Open failed: %s\n", wine_dbgstr_fourcc(err));
- goto theEnd;
- }
+ MPEG3_Reset(adsi, aad);
+
+ return MMSYSERR_NOERROR;
}
}
- else goto theEnd;
- MPEG3_Reset(adsi, aad);
- return MMSYSERR_NOERROR;
-
- theEnd:
HeapFree(GetProcessHeap(), 0, aad);
- adsi->dwDriver = 0L;
+ adsi->dwDriver = 0;
+
return MMSYSERR_NOTSUPPORTED;
}
@@ -502,9 +634,13 @@
*/
static LRESULT MPEG3_StreamClose(PACMDRVSTREAMINSTANCE adsi)
{
- AudioConverterDispose(((AcmMpeg3Data*)adsi->dwDriver)->acr);
- AudioFileStreamClose(((AcmMpeg3Data*)adsi->dwDriver)->afs);
- HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver);
+ AcmMpeg3Data* amd = (AcmMpeg3Data*)adsi->dwDriver;
+
+ AudioConverterDispose(amd->acr);
+
+ HeapFree(GetProcessHeap(), 0, amd);
+ adsi->dwDriver = 0;
+
return MMSYSERR_NOERROR;
}
diff --git a/include/config.h.in b/include/config.h.in
index 5d8806d..f365fca 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -33,19 +33,9 @@
/* Define to 1 if you have the <asm/types.h> header file. */
#undef HAVE_ASM_TYPES_H
-/* Define to 1 if you have the `AudioFileStreamOpen' function. */
-#undef HAVE_AUDIOFILESTREAMOPEN
-
/* Define to 1 if you have the <AudioToolbox/AudioConverter.h> header file. */
#undef HAVE_AUDIOTOOLBOX_AUDIOCONVERTER_H
-/* Define to 1 if you have the <AudioToolbox/AudioFileStream.h> header file.
- */
-#undef HAVE_AUDIOTOOLBOX_AUDIOFILESTREAM_H
-
-/* Define to 1 if you have the <AudioToolbox/AudioFile.h> header file. */
-#undef HAVE_AUDIOTOOLBOX_AUDIOFILE_H
-
/* Define to 1 if you have the <AudioUnit/AudioUnit.h> header file. */
#undef HAVE_AUDIOUNIT_AUDIOUNIT_H