Ajouts de musique et irrKlang.
This commit is contained in:
		
							
								
								
									
										397
									
								
								SQCSim2021/external/irrKlang-1.6.0/plugins/ikpMP3/CIrrKlangAudioStreamMP3.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								SQCSim2021/external/irrKlang-1.6.0/plugins/ikpMP3/CIrrKlangAudioStreamMP3.cpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,397 @@
 | 
			
		||||
// Copyright (C) 2002-2007 Nikolaus Gebhardt
 | 
			
		||||
// Part of the code for this plugin for irrKlang is based on:
 | 
			
		||||
//  MP3 input for Audiere by Matt Campbell <mattcampbell@pobox.com>, based on
 | 
			
		||||
//  libavcodec from ffmpeg (http://ffmpeg.sourceforge.net/).
 | 
			
		||||
// See license.txt for license details of this plugin.
 | 
			
		||||
 | 
			
		||||
#include "CIrrKlangAudioStreamMP3.h"
 | 
			
		||||
#include <memory.h>
 | 
			
		||||
#include <stdlib.h> // free, malloc and realloc
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
namespace irrklang
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
CIrrKlangAudioStreamMP3::CIrrKlangAudioStreamMP3(IFileReader* file)
 | 
			
		||||
: File(file), TheMPAuDecContext(0), InputPosition(0), InputLength(0),
 | 
			
		||||
	DecodeBuffer(0), FirstFrameRead(false), EndOfFileReached(0),
 | 
			
		||||
	FileBegin(0), Position(0)
 | 
			
		||||
{
 | 
			
		||||
	if (File)
 | 
			
		||||
	{
 | 
			
		||||
		File->grab();
 | 
			
		||||
 | 
			
		||||
		TheMPAuDecContext = new MPAuDecContext();
 | 
			
		||||
 | 
			
		||||
		if (!TheMPAuDecContext || mpaudec_init(TheMPAuDecContext) < 0)
 | 
			
		||||
		{
 | 
			
		||||
			File->drop();
 | 
			
		||||
			File = 0;
 | 
			
		||||
			delete TheMPAuDecContext;
 | 
			
		||||
			TheMPAuDecContext = 0;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// init, get format
 | 
			
		||||
 | 
			
		||||
		DecodeBuffer = new ik_u8[MPAUDEC_MAX_AUDIO_FRAME_SIZE];
 | 
			
		||||
 | 
			
		||||
		if (File->getSize()>0)
 | 
			
		||||
		{
 | 
			
		||||
			// seekable file, now parse file to get size
 | 
			
		||||
			// (needed to make it possible for the engine to loop a stream correctly)
 | 
			
		||||
 | 
			
		||||
			skipID3IfNecessary();
 | 
			
		||||
 | 
			
		||||
			TheMPAuDecContext->parse_only = 1;
 | 
			
		||||
			Format.FrameCount = 0;
 | 
			
		||||
 | 
			
		||||
			while(!EndOfFileReached)
 | 
			
		||||
			{
 | 
			
		||||
				if (!decodeFrame())
 | 
			
		||||
					break;
 | 
			
		||||
 | 
			
		||||
				Format.FrameCount += TheMPAuDecContext->frame_size;
 | 
			
		||||
 | 
			
		||||
				if (!EndOfFileReached /*&& File->isSeekable()*/ )
 | 
			
		||||
				{
 | 
			
		||||
					// to be able to seek in the stream, store offsets and sizes
 | 
			
		||||
 | 
			
		||||
					SFramePositionData data;
 | 
			
		||||
					data.size = TheMPAuDecContext->frame_size;
 | 
			
		||||
					data.offset = File->getPos() - (InputLength - InputPosition) - TheMPAuDecContext->coded_frame_size;
 | 
			
		||||
 | 
			
		||||
					FramePositionData.push_back(data);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			TheMPAuDecContext->parse_only = 0;
 | 
			
		||||
			setPosition(0);
 | 
			
		||||
		}
 | 
			
		||||
		else
 | 
			
		||||
			decodeFrame(); // decode first frame to read audio format
 | 
			
		||||
 | 
			
		||||
		if (!TheMPAuDecContext->channels ||
 | 
			
		||||
			!TheMPAuDecContext->sample_rate )
 | 
			
		||||
		{
 | 
			
		||||
			File->drop();
 | 
			
		||||
			File = 0;
 | 
			
		||||
			delete TheMPAuDecContext;
 | 
			
		||||
			TheMPAuDecContext = 0;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CIrrKlangAudioStreamMP3::~CIrrKlangAudioStreamMP3()
 | 
			
		||||
{
 | 
			
		||||
	if (File)
 | 
			
		||||
		File->drop();
 | 
			
		||||
 | 
			
		||||
	if (TheMPAuDecContext)
 | 
			
		||||
	{
 | 
			
		||||
		mpaudec_clear(TheMPAuDecContext);
 | 
			
		||||
		delete TheMPAuDecContext;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete [] DecodeBuffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//! returns format of the audio stream
 | 
			
		||||
SAudioStreamFormat CIrrKlangAudioStreamMP3::getFormat()
 | 
			
		||||
{
 | 
			
		||||
	return Format;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//! tells the audio stream to read n audio frames into the specified buffer
 | 
			
		||||
ik_s32 CIrrKlangAudioStreamMP3::readFrames(void* target, ik_s32 frameCountToRead)
 | 
			
		||||
{
 | 
			
		||||
	const int frameSize = Format.getFrameSize();
 | 
			
		||||
 | 
			
		||||
	int framesRead = 0;
 | 
			
		||||
	ik_u8* out = (ik_u8*)target;
 | 
			
		||||
 | 
			
		||||
	while (framesRead < frameCountToRead)
 | 
			
		||||
	{
 | 
			
		||||
		// no more samples?  ask the MP3 for more
 | 
			
		||||
		if (DecodedQueue.getSize() < frameSize)
 | 
			
		||||
		{
 | 
			
		||||
			if (!decodeFrame() || EndOfFileReached)
 | 
			
		||||
				return framesRead;
 | 
			
		||||
 | 
			
		||||
			// if the buffer is still empty, we are done
 | 
			
		||||
			if (DecodedQueue.getSize() < frameSize)
 | 
			
		||||
				return framesRead;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const int framesLeft = frameCountToRead - framesRead;
 | 
			
		||||
		const int dequeSize = DecodedQueue.getSize() / frameSize;
 | 
			
		||||
		const int framesToRead = framesLeft < dequeSize ? framesLeft : dequeSize;
 | 
			
		||||
 | 
			
		||||
		DecodedQueue.read(out, framesToRead * frameSize);
 | 
			
		||||
 | 
			
		||||
		out += framesToRead * frameSize;
 | 
			
		||||
		framesRead += framesToRead;
 | 
			
		||||
		Position += framesToRead;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return framesRead;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
bool CIrrKlangAudioStreamMP3::decodeFrame()
 | 
			
		||||
{
 | 
			
		||||
    int outputSize = 0;
 | 
			
		||||
 | 
			
		||||
	while (!outputSize)
 | 
			
		||||
	{
 | 
			
		||||
		if (InputPosition == InputLength)
 | 
			
		||||
		{
 | 
			
		||||
			InputPosition = 0;
 | 
			
		||||
			InputLength = File->read(InputBuffer, IKP_MP3_INPUT_BUFFER_SIZE);
 | 
			
		||||
 | 
			
		||||
			if (InputLength == 0)
 | 
			
		||||
			{
 | 
			
		||||
				EndOfFileReached = true;
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int rv = mpaudec_decode_frame( TheMPAuDecContext, (ik_s16*)DecodeBuffer,
 | 
			
		||||
									   &outputSize,
 | 
			
		||||
									   (ik_u8*)InputBuffer + InputPosition,
 | 
			
		||||
									   InputLength - InputPosition);
 | 
			
		||||
 | 
			
		||||
		if (rv < 0)
 | 
			
		||||
			return false;
 | 
			
		||||
 | 
			
		||||
		InputPosition += rv;
 | 
			
		||||
	} // end while
 | 
			
		||||
 | 
			
		||||
	if (!FirstFrameRead)
 | 
			
		||||
	{
 | 
			
		||||
		Format.ChannelCount = TheMPAuDecContext->channels;
 | 
			
		||||
		Format.SampleRate = TheMPAuDecContext->sample_rate;
 | 
			
		||||
		Format.SampleFormat = ESF_S16;
 | 
			
		||||
		Format.FrameCount = -1; // unknown lenght
 | 
			
		||||
 | 
			
		||||
		FirstFrameRead = true;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	if (TheMPAuDecContext->channels != Format.ChannelCount ||
 | 
			
		||||
		TheMPAuDecContext->sample_rate != Format.SampleRate)
 | 
			
		||||
	{
 | 
			
		||||
		// Can't handle format changes mid-stream.
 | 
			
		||||
		return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	if (!TheMPAuDecContext->parse_only)
 | 
			
		||||
	{
 | 
			
		||||
		if (outputSize < 0)
 | 
			
		||||
		{
 | 
			
		||||
			// Couldn't decode this frame.  Too bad, already lost it.
 | 
			
		||||
			// This should only happen when seeking.
 | 
			
		||||
 | 
			
		||||
			outputSize = TheMPAuDecContext->frame_size;
 | 
			
		||||
			memset(DecodeBuffer, 0, outputSize * Format.getFrameSize());
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DecodedQueue.write(DecodeBuffer, outputSize);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
//! sets the position of the audio stream.
 | 
			
		||||
/** For example to let the stream be read from the beginning of the file again,
 | 
			
		||||
setPosition(0) would be called. This is usually done be the sound engine to
 | 
			
		||||
loop a stream after if has reached the end. Return true if sucessful and 0 if not. */
 | 
			
		||||
bool CIrrKlangAudioStreamMP3::setPosition(ik_s32 pos)
 | 
			
		||||
{
 | 
			
		||||
	if (!File || !TheMPAuDecContext)
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (pos == 0)
 | 
			
		||||
	{
 | 
			
		||||
		// usually done for looping, just reset to start
 | 
			
		||||
 | 
			
		||||
		File->seek(FileBegin); // skip possible ID3 header
 | 
			
		||||
 | 
			
		||||
		EndOfFileReached = false;
 | 
			
		||||
 | 
			
		||||
		DecodedQueue.clear();
 | 
			
		||||
 | 
			
		||||
		MPAuDecContext oldContext = *TheMPAuDecContext;
 | 
			
		||||
 | 
			
		||||
		mpaudec_clear(TheMPAuDecContext);
 | 
			
		||||
		mpaudec_init(TheMPAuDecContext);
 | 
			
		||||
 | 
			
		||||
		TheMPAuDecContext->bit_rate = oldContext.bit_rate;
 | 
			
		||||
		TheMPAuDecContext->channels = oldContext.channels;
 | 
			
		||||
		TheMPAuDecContext->frame_size = oldContext.frame_size;
 | 
			
		||||
		TheMPAuDecContext->sample_rate = oldContext.sample_rate;
 | 
			
		||||
 | 
			
		||||
		InputPosition = 0;
 | 
			
		||||
		InputLength = 0;
 | 
			
		||||
		Position = 0;
 | 
			
		||||
		CurrentFramePosition = 0;
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		// user wants to seek in the stream, so do this here
 | 
			
		||||
 | 
			
		||||
		int scan_position = 0;
 | 
			
		||||
		int target_frame = 0;
 | 
			
		||||
		int frame_count = (int)FramePositionData.size();
 | 
			
		||||
 | 
			
		||||
		while (target_frame < frame_count)
 | 
			
		||||
		{
 | 
			
		||||
			int frame_size = FramePositionData[target_frame].size;
 | 
			
		||||
 | 
			
		||||
			if (pos <= scan_position + frame_size)
 | 
			
		||||
				break;
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				scan_position += frame_size;
 | 
			
		||||
				target_frame++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		const int MAX_FRAME_DEPENDENCY = 10;
 | 
			
		||||
		target_frame = std::max(0, target_frame - MAX_FRAME_DEPENDENCY);
 | 
			
		||||
		setPosition(0);
 | 
			
		||||
 | 
			
		||||
		File->seek(FramePositionData[target_frame].offset, false);
 | 
			
		||||
 | 
			
		||||
		int i;
 | 
			
		||||
		for (i = 0; i < target_frame; i++)
 | 
			
		||||
		{
 | 
			
		||||
			if (i>=(int)FramePositionData.size())
 | 
			
		||||
			{
 | 
			
		||||
				// internal error
 | 
			
		||||
				setPosition(0);
 | 
			
		||||
				return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			Position += FramePositionData[i].size;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!decodeFrame() || EndOfFileReached)
 | 
			
		||||
		{
 | 
			
		||||
			setPosition(0);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		int frames_to_consume = pos - Position; // PCM frames now
 | 
			
		||||
		if (frames_to_consume > 0)
 | 
			
		||||
		{
 | 
			
		||||
			ik_u8 *buf = new ik_u8[frames_to_consume * Format.getFrameSize()];
 | 
			
		||||
			readFrames(buf, frames_to_consume);
 | 
			
		||||
			delete[] buf;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
      	return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CIrrKlangAudioStreamMP3::QueueBuffer::QueueBuffer()
 | 
			
		||||
{
 | 
			
		||||
	Capacity = 256;
 | 
			
		||||
	Size = 0;
 | 
			
		||||
 | 
			
		||||
	Buffer = (ik_u8*)malloc(Capacity);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CIrrKlangAudioStreamMP3::QueueBuffer::~QueueBuffer()
 | 
			
		||||
{
 | 
			
		||||
	free(Buffer);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int CIrrKlangAudioStreamMP3::QueueBuffer::getSize()
 | 
			
		||||
{
 | 
			
		||||
	return Size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CIrrKlangAudioStreamMP3::QueueBuffer::write(const void* buffer, int size)
 | 
			
		||||
{
 | 
			
		||||
	bool needRealloc = false;
 | 
			
		||||
 | 
			
		||||
	while (size + Size > Capacity)
 | 
			
		||||
	{
 | 
			
		||||
		Capacity *= 2;
 | 
			
		||||
		needRealloc = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    if (needRealloc)
 | 
			
		||||
	{
 | 
			
		||||
        Buffer = (ik_u8*)realloc(Buffer, Capacity);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	memcpy(Buffer + Size, buffer, size);
 | 
			
		||||
	Size += size;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
int CIrrKlangAudioStreamMP3::QueueBuffer::read(void* buffer, int size)
 | 
			
		||||
{
 | 
			
		||||
	int toRead = size < Size ? size : Size;
 | 
			
		||||
 | 
			
		||||
	memcpy(buffer, Buffer, toRead);
 | 
			
		||||
	memmove(Buffer, Buffer + toRead, Size - toRead);
 | 
			
		||||
 | 
			
		||||
	Size -= toRead;
 | 
			
		||||
	return toRead;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CIrrKlangAudioStreamMP3::QueueBuffer::clear()
 | 
			
		||||
{
 | 
			
		||||
	Size = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void CIrrKlangAudioStreamMP3::skipID3IfNecessary()
 | 
			
		||||
{
 | 
			
		||||
	char header[10];
 | 
			
		||||
	int read = File->read(&header, 10);
 | 
			
		||||
 | 
			
		||||
	if (read == 10 &&
 | 
			
		||||
		header[0] == 'I' && header[1] == 'D' && header[2] == '3')
 | 
			
		||||
	{
 | 
			
		||||
		int versionMajor = header[3];
 | 
			
		||||
		int versionMinor = header[4];
 | 
			
		||||
		int flags = header[5];
 | 
			
		||||
 | 
			
		||||
		// IDv2 size looks like the following: ID3v2 size  4 * %0xxxxxxx.
 | 
			
		||||
		// Sick, but that's how it works.
 | 
			
		||||
 | 
			
		||||
		int size = 0;
 | 
			
		||||
		size  = (header[6] & 0x7f) << (3*7);
 | 
			
		||||
		size |= (header[7] & 0x7f) << (2*7);
 | 
			
		||||
		size |= (header[8] & 0x7f) << (1*7);
 | 
			
		||||
		size |= (header[9] & 0x7f) ;
 | 
			
		||||
 | 
			
		||||
		size += 10; // header size
 | 
			
		||||
 | 
			
		||||
		FileBegin = size;
 | 
			
		||||
		File->seek(FileBegin);
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
		File->seek(0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
} // end namespace irrklang
 | 
			
		||||
		Reference in New Issue
	
	Block a user