Exemplo n.º 1
0
bool AUD_OpenALDevice::AUD_OpenALHandle::seek(float position)
{
	if(!m_status)
		return false;

	m_device->lock();

	if(m_isBuffered)
		alSourcef(m_source, AL_SEC_OFFSET, position);
	else
	{
		m_reader->seek((int)(position * m_reader->getSpecs().rate));
		m_eos = false;

		ALint info;

		alGetSourcei(m_source, AL_SOURCE_STATE, &info);

		if(info != AL_PLAYING)
		{
			if(info == AL_PAUSED)
				alSourceStop(m_source);

			alSourcei(m_source, AL_BUFFER, 0);
			m_current = 0;

			ALenum err;
			if((err = alGetError()) == AL_NO_ERROR)
			{
				int length;
				AUD_DeviceSpecs specs = m_device->m_specs;
				specs.specs = m_reader->getSpecs();
				m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));

				for(int i = 0; i < CYCLE_BUFFERS; i++)
				{
					length = m_device->m_buffersize;
					m_reader->read(length, m_eos, m_device->m_buffer.getBuffer());
					alBufferData(m_buffers[i], m_format, m_device->m_buffer.getBuffer(),
								 length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);

					if(alGetError() != AL_NO_ERROR)
						break;
				}

				if(m_loopcount != 0)
					m_eos = false;

				alSourceQueueBuffers(m_source, CYCLE_BUFFERS, m_buffers);
			}

			alSourceRewind(m_source);
		}
	}

	m_device->unlock();

	return true;
}
Exemplo n.º 2
0
AUD_OpenALDevice::AUD_OpenALHandle::AUD_OpenALHandle(AUD_OpenALDevice* device, ALenum format, AUD_Reference<AUD_IReader> reader, bool keep) :
	m_isBuffered(false), m_reader(reader), m_keep(keep), m_format(format), m_current(0),
	m_eos(false), m_loopcount(0), m_stop(NULL), m_stop_data(NULL), m_status(AUD_STATUS_PLAYING),
	m_device(device)
{
	AUD_DeviceSpecs specs = m_device->m_specs;
	specs.specs = m_reader->getSpecs();

	// OpenAL playback code
	alGenBuffers(CYCLE_BUFFERS, m_buffers);
	if(alGetError() != AL_NO_ERROR)
		AUD_THROW(AUD_ERROR_OPENAL, genbuffer_error);

	try
	{
		m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
		int length;
		bool eos;

		for(int i = 0; i < CYCLE_BUFFERS; i++)
		{
			length = m_device->m_buffersize;
			reader->read(length, eos, m_device->m_buffer.getBuffer());
			alBufferData(m_buffers[i], m_format, m_device->m_buffer.getBuffer(),
						 length * AUD_DEVICE_SAMPLE_SIZE(specs),
						 specs.rate);
			if(alGetError() != AL_NO_ERROR)
				AUD_THROW(AUD_ERROR_OPENAL, bufferdata_error);
		}

		alGenSources(1, &m_source);
		if(alGetError() != AL_NO_ERROR)
			AUD_THROW(AUD_ERROR_OPENAL, gensource_error);

		try
		{
			alSourceQueueBuffers(m_source, CYCLE_BUFFERS,
								 m_buffers);
			if(alGetError() != AL_NO_ERROR)
				AUD_THROW(AUD_ERROR_OPENAL, queue_error);
		}
		catch(AUD_Exception&)
		{
			alDeleteSources(1, &m_source);
			throw;
		}
	}
	catch(AUD_Exception&)
	{
		alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
		throw;
	}
	alSourcei(m_source, AL_SOURCE_RELATIVE, 1);
}
Exemplo n.º 3
0
bool AUD_ReadDevice::read(data_t* buffer, int length)
{
	if(m_playing)
		mix(buffer, length);
	else
		if(m_specs.format == AUD_FORMAT_U8)
			memset(buffer, 0x80, length * AUD_DEVICE_SAMPLE_SIZE(m_specs));
		else
			memset(buffer, 0, length * AUD_DEVICE_SAMPLE_SIZE(m_specs));
	return m_playing;
}
Exemplo n.º 4
0
AUD_FFMPEGWriter::~AUD_FFMPEGWriter()
{
	// writte missing data
	if(m_input_samples)
	{
		sample_t* buf = m_input_buffer.getBuffer();
		memset(buf + m_specs.channels * m_input_samples, 0,
			   (m_input_size - m_input_samples) * AUD_DEVICE_SAMPLE_SIZE(m_specs));

		encode(buf);
	}

	av_write_trailer(m_formatCtx);

	avcodec_close(m_codecCtx);

	av_freep(&m_formatCtx->streams[0]->codec);
	av_freep(&m_formatCtx->streams[0]);

#ifdef FFMPEG_HAVE_ENCODE_AUDIO2
	av_frame_free(&m_frame);
#endif

	avio_close(m_formatCtx->pb);
	av_free(m_formatCtx);
}
Exemplo n.º 5
0
OpenALDevice::OpenALHandle::OpenALHandle(OpenALDevice* device, ALenum format, std::shared_ptr<IReader> reader, bool keep) :
	m_isBuffered(false), m_reader(reader), m_keep(keep), m_format(format),
	m_eos(false), m_loopcount(0), m_stop(nullptr), m_stop_data(nullptr), m_status(STATUS_PLAYING),
	m_device(device)
{
	DeviceSpecs specs = m_device->m_specs;
	specs.specs = m_reader->getSpecs();

	// OpenAL playback code
	alGenBuffers(CYCLE_BUFFERS, m_buffers);
	if(alGetError() != AL_NO_ERROR)
		AUD_THROW(DeviceException, "Buffer generation failed while staring playback with OpenAL.");

	try
	{
		m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));
		int length;
		bool eos;

		for(m_current = 0; m_current < CYCLE_BUFFERS; m_current++)
		{
			length = m_device->m_buffersize;
			reader->read(length, eos, m_device->m_buffer.getBuffer());

			if(length == 0)
				break;

			alBufferData(m_buffers[m_current], m_format, m_device->m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);

			if(alGetError() != AL_NO_ERROR)
				AUD_THROW(DeviceException, "Filling the buffer with data failed while starting playback with OpenAL.");
		}

		alGenSources(1, &m_source);
		if(alGetError() != AL_NO_ERROR)
			AUD_THROW(DeviceException, "Source generation failed while starting playback with OpenAL.");

		try
		{
			alSourceQueueBuffers(m_source, m_current, m_buffers);
			if(alGetError() != AL_NO_ERROR)
				AUD_THROW(DeviceException, "Buffer queuing failed while starting playback with OpenAL.");
		}
		catch(Exception&)
		{
			alDeleteSources(1, &m_source);
			throw;
		}
	}
	catch(Exception&)
	{
		alDeleteBuffers(CYCLE_BUFFERS, m_buffers);
		throw;
	}
	alSourcei(m_source, AL_SOURCE_RELATIVE, 1);
}
void AUD_FFMPEGReader::read(int& length, bool& eos, sample_t* buffer)
{
	// read packages and decode them
	AVPacket packet;
	int data_size = 0;
	int pkgbuf_pos;
	int left = length;
	int sample_size = AUD_DEVICE_SAMPLE_SIZE(m_specs);

	sample_t* buf = buffer;
	pkgbuf_pos = m_pkgbuf_left;
	m_pkgbuf_left = 0;

	// there may still be data in the buffer from the last call
	if(pkgbuf_pos > 0)
	{
		data_size = AUD_MIN(pkgbuf_pos, left * sample_size);
		m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(),
		          data_size / AUD_FORMAT_SIZE(m_specs.format));
		buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
		left -= data_size/sample_size;
	}

	// for each frame read as long as there isn't enough data already
	while((left > 0) && (av_read_frame(m_formatCtx, &packet) >= 0))
	{
		// is it a frame from the audio stream?
		if(packet.stream_index == m_stream)
		{
			// decode the package
			pkgbuf_pos = decode(packet, m_pkgbuf);

			// copy to output buffer
			data_size = AUD_MIN(pkgbuf_pos, left * sample_size);
			m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(),
					  data_size / AUD_FORMAT_SIZE(m_specs.format));
			buf += data_size / AUD_FORMAT_SIZE(m_specs.format);
			left -= data_size/sample_size;
		}
		av_free_packet(&packet);
	}
	// read more data than necessary?
	if(pkgbuf_pos > data_size)
	{
		m_pkgbuf_left = pkgbuf_pos-data_size;
		memmove(m_pkgbuf.getBuffer(),
				((data_t*)m_pkgbuf.getBuffer())+data_size,
				pkgbuf_pos-data_size);
	}

	if((eos = (left > 0)))
		length -= left;

	m_position += length;
}
Exemplo n.º 7
0
void FFMPEGWriter::write(unsigned int length, sample_t* buffer)
{
	unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs);

	if(m_input_size)
	{
		sample_t* inbuf = m_input_buffer.getBuffer();

		while(length)
		{
			unsigned int len = std::min(m_input_size - m_input_samples, length);

			std::memcpy(inbuf + m_input_samples * m_specs.channels, buffer, len * samplesize);

			buffer += len * m_specs.channels;
			m_input_samples += len;
			m_position += len;
			length -= len;

			if(m_input_samples == m_input_size)
			{
				encode();

				m_input_samples = 0;
			}
		}
	}
	else // PCM data, can write directly!
	{
		int samplesize = AUD_SAMPLE_SIZE(m_specs);
		m_input_buffer.assureSize(length * std::max(AUD_DEVICE_SAMPLE_SIZE(m_specs), samplesize));

		sample_t* buf = m_input_buffer.getBuffer();
		m_convert(reinterpret_cast<data_t*>(buf), reinterpret_cast<data_t*>(buffer), length * m_specs.channels);

		m_input_samples = length;

		m_position += length;

		encode();
	}
}
Exemplo n.º 8
0
void AUD_FFMPEGWriter::write(unsigned int length, sample_t* buffer)
{
	unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs);

	if(m_input_size)
	{
		sample_t* inbuf = m_input_buffer.getBuffer();

		while(length)
		{
			unsigned int len = AUD_MIN(m_input_size - m_input_samples, length);

			memcpy(inbuf + m_input_samples * m_specs.channels, buffer, len * samplesize);

			buffer += len * m_specs.channels;
			m_input_samples += len;
			m_position += len;
			length -= len;

			if(m_input_samples == m_input_size)
			{
				encode(inbuf);

				m_input_samples = 0;
			}
		}
	}
	else // PCM data, can write directly!
	{
		int samplesize = AUD_SAMPLE_SIZE(m_specs);
		if(m_output_buffer.getSize() != length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8)
			m_output_buffer.resize(length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8);
		m_input_buffer.assureSize(length * AUD_MAX(AUD_DEVICE_SAMPLE_SIZE(m_specs), samplesize));

		sample_t* buf = m_input_buffer.getBuffer();
		m_convert(reinterpret_cast<data_t*>(buf), reinterpret_cast<data_t*>(buffer), length * m_specs.channels);

		encode(buf);

		m_position += length;
	}
}
Exemplo n.º 9
0
FFMPEGWriter::FFMPEGWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) :
	m_position(0),
	m_specs(specs),
	m_input_samples(0),
	m_deinterleave(false)
{
	static const char* formats[] = { nullptr, "ac3", "flac", "matroska", "mp2", "mp3", "ogg", "wav" };

	if(avformat_alloc_output_context2(&m_formatCtx, nullptr, formats[format], filename.c_str()) < 0)
		AUD_THROW(FileException, "File couldn't be written, format couldn't be found with ffmpeg.");

	m_outputFmt = m_formatCtx->oformat;

	if(!m_outputFmt) {
		avformat_free_context(m_formatCtx);
		AUD_THROW(FileException, "File couldn't be written, output format couldn't be found with ffmpeg.");
	}

	m_outputFmt->audio_codec = AV_CODEC_ID_NONE;

	switch(codec)
	{
	case CODEC_AAC:
		m_outputFmt->audio_codec = AV_CODEC_ID_AAC;
		break;
	case CODEC_AC3:
		m_outputFmt->audio_codec = AV_CODEC_ID_AC3;
		break;
	case CODEC_FLAC:
		m_outputFmt->audio_codec = AV_CODEC_ID_FLAC;
		break;
	case CODEC_MP2:
		m_outputFmt->audio_codec = AV_CODEC_ID_MP2;
		break;
	case CODEC_MP3:
		m_outputFmt->audio_codec = AV_CODEC_ID_MP3;
		break;
	case CODEC_OPUS:
		m_outputFmt->audio_codec = AV_CODEC_ID_OPUS;
		break;
	case CODEC_PCM:
		switch(specs.format)
		{
		case FORMAT_U8:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_U8;
			break;
		case FORMAT_S16:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
			break;
		case FORMAT_S24:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S24LE;
			break;
		case FORMAT_S32:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S32LE;
			break;
		case FORMAT_FLOAT32:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F32LE;
			break;
		case FORMAT_FLOAT64:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F64LE;
			break;
		default:
			m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
			break;
		}
		break;
	case CODEC_VORBIS:
		m_outputFmt->audio_codec = AV_CODEC_ID_VORBIS;
		break;
	default:
		m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
		break;
	}

	try
	{
		if(m_outputFmt->audio_codec == AV_CODEC_ID_NONE)
			AUD_THROW(FileException, "File couldn't be written, audio codec not found with ffmpeg.");

		AVCodec* codec = avcodec_find_encoder(m_outputFmt->audio_codec);
		if(!codec)
			AUD_THROW(FileException, "File couldn't be written, audio encoder couldn't be found with ffmpeg.");

		m_stream = avformat_new_stream(m_formatCtx, codec);
		if(!m_stream)
			AUD_THROW(FileException, "File couldn't be written, stream creation failed with ffmpeg.");

		m_stream->id = m_formatCtx->nb_streams - 1;

		m_codecCtx = m_stream->codec;

		switch(m_specs.format)
		{
		case FORMAT_U8:
			m_convert = convert_float_u8;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
			break;
		case FORMAT_S16:
			m_convert = convert_float_s16;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
			break;
		case FORMAT_S32:
			m_convert = convert_float_s32;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S32;
			break;
		case FORMAT_FLOAT64:
			m_convert = convert_float_double;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
			break;
		default:
			m_convert = convert_copy<sample_t>;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
			break;
		}

		if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
			m_codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;

		bool format_supported = false;

		for(int i = 0; codec->sample_fmts[i] != -1; i++)
		{
			if(av_get_alt_sample_fmt(codec->sample_fmts[i], false) == m_codecCtx->sample_fmt)
			{
				m_deinterleave = av_sample_fmt_is_planar(codec->sample_fmts[i]);
				m_codecCtx->sample_fmt = codec->sample_fmts[i];
				format_supported = true;
			}
		}

		if(!format_supported)
		{
			int chosen_index = 0;
			auto chosen = av_get_alt_sample_fmt(codec->sample_fmts[chosen_index], false);
			for(int i = 1; codec->sample_fmts[i] != -1; i++)
			{
				auto fmt = av_get_alt_sample_fmt(codec->sample_fmts[i], false);
				if((fmt > chosen && chosen < m_codecCtx->sample_fmt) || (fmt > m_codecCtx->sample_fmt && fmt < chosen))
				{
					chosen = fmt;
					chosen_index = i;
				}
			}

			m_codecCtx->sample_fmt = codec->sample_fmts[chosen_index];
			m_deinterleave = av_sample_fmt_is_planar(m_codecCtx->sample_fmt);
			switch(av_get_alt_sample_fmt(m_codecCtx->sample_fmt, false))
			{
			case AV_SAMPLE_FMT_U8:
				specs.format = FORMAT_U8;
				m_convert = convert_float_u8;
				break;
			case AV_SAMPLE_FMT_S16:
				specs.format = FORMAT_S16;
				m_convert = convert_float_s16;
				break;
			case AV_SAMPLE_FMT_S32:
				specs.format = FORMAT_S32;
				m_convert = convert_float_s32;
				break;
			case AV_SAMPLE_FMT_FLT:
				specs.format = FORMAT_FLOAT32;
				m_convert = convert_copy<sample_t>;
				break;
			case AV_SAMPLE_FMT_DBL:
				specs.format = FORMAT_FLOAT64;
				m_convert = convert_float_double;
				break;
			default:
				AUD_THROW(FileException, "File couldn't be written, sample format not supported with ffmpeg.");
			}
		}

		m_codecCtx->sample_rate = 0;

		if(codec->supported_samplerates)
		{
			for(int i = 0; codec->supported_samplerates[i]; i++)
			{
				if(codec->supported_samplerates[i] == m_specs.rate)
				{
					m_codecCtx->sample_rate = codec->supported_samplerates[i];
					break;
				}
				else if((codec->supported_samplerates[i] > m_codecCtx->sample_rate && m_specs.rate > m_codecCtx->sample_rate) ||
						(codec->supported_samplerates[i] < m_codecCtx->sample_rate && m_specs.rate < codec->supported_samplerates[i]))
				{
					m_codecCtx->sample_rate = codec->supported_samplerates[i];
				}
			}
		}

		if(m_codecCtx->sample_rate == 0)
			m_codecCtx->sample_rate = m_specs.rate;

		m_specs.rate = m_codecCtx->sample_rate;

		m_codecCtx->codec_id = m_outputFmt->audio_codec;
		m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
		m_codecCtx->bit_rate = bitrate;
		m_codecCtx->channels = m_specs.channels;
		m_stream->time_base.num = m_codecCtx->time_base.num = 1;
		m_stream->time_base.den = m_codecCtx->time_base.den = m_codecCtx->sample_rate;

		if(avcodec_open2(m_codecCtx, codec, nullptr) < 0)
			AUD_THROW(FileException, "File couldn't be written, encoder couldn't be opened with ffmpeg.");

		int samplesize = std::max(int(AUD_SAMPLE_SIZE(m_specs)), AUD_DEVICE_SAMPLE_SIZE(m_specs));

		if((m_input_size = m_codecCtx->frame_size))
			m_input_buffer.resize(m_input_size * samplesize);

		if(avio_open(&m_formatCtx->pb, filename.c_str(), AVIO_FLAG_WRITE))
			AUD_THROW(FileException, "File couldn't be written, file opening failed with ffmpeg.");

		avformat_write_header(m_formatCtx, nullptr);
	}
	catch(Exception&)
	{
		avformat_free_context(m_formatCtx);
		throw;
	}
}
Exemplo n.º 10
0
void AUD_OpenALDevice::updateStreams()
{
	AUD_Reference<AUD_OpenALHandle> sound;

	int length;

	ALint info;
	AUD_DeviceSpecs specs = m_specs;
	ALCenum cerr;
	std::list<AUD_Reference<AUD_OpenALHandle> > stopSounds;
	std::list<AUD_Reference<AUD_OpenALHandle> > pauseSounds;
	AUD_HandleIterator it;

	while(1)
	{
		lock();

		alcSuspendContext(m_context);
		cerr = alcGetError(m_device);
		if(cerr == ALC_NO_ERROR)
		{
			// for all sounds
			for(it = m_playingSounds.begin(); it != m_playingSounds.end(); it++)
			{
				sound = *it;

				// is it a streamed sound?
				if(!sound->m_isBuffered)
				{
					// check for buffer refilling
					alGetSourcei(sound->m_source, AL_BUFFERS_PROCESSED, &info);

					if(info)
					{
						specs.specs = sound->m_reader->getSpecs();
						m_buffer.assureSize(m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));

						// for all empty buffers
						while(info--)
						{
							// if there's still data to play back
							if(!sound->m_eos)
							{
								// read data
								length = m_buffersize;
								sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());

								// looping necessary?
								if(length == 0 && sound->m_loopcount)
								{
									if(sound->m_loopcount > 0)
										sound->m_loopcount--;

									sound->m_reader->seek(0);

									length = m_buffersize;
									sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
								}

								if(sound->m_loopcount != 0)
									sound->m_eos = false;

								// read nothing?
								if(length == 0)
								{
									break;
								}

								// unqueue buffer
								alSourceUnqueueBuffers(sound->m_source, 1,
												&sound->m_buffers[sound->m_current]);
								ALenum err;
								if((err = alGetError()) != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}

								// fill with new data
								alBufferData(sound->m_buffers[sound->m_current],
											 sound->m_format,
											 m_buffer.getBuffer(), length *
											 AUD_DEVICE_SAMPLE_SIZE(specs),
											 specs.rate);

								if((err = alGetError()) != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}

								// and queue again
								alSourceQueueBuffers(sound->m_source, 1,
												&sound->m_buffers[sound->m_current]);
								if(alGetError() != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}

								sound->m_current = (sound->m_current+1) %
												 AUD_OpenALHandle::CYCLE_BUFFERS;
							}
							else
								break;
						}
					}
				}

				// check if the sound has been stopped
				alGetSourcei(sound->m_source, AL_SOURCE_STATE, &info);

				if(info != AL_PLAYING)
				{
					// if it really stopped
					if(sound->m_eos)
					{
						if(sound->m_stop)
							sound->m_stop(sound->m_stop_data);

						// pause or
						if(sound->m_keep)
							pauseSounds.push_back(sound);
						// stop
						else
							stopSounds.push_back(sound);
					}
					// continue playing
					else
						alSourcePlay(sound->m_source);
				}
			}

			for(it = pauseSounds.begin(); it != pauseSounds.end(); it++)
				(*it)->pause();

			for(it = stopSounds.begin(); it != stopSounds.end(); it++)
				(*it)->stop();

			pauseSounds.clear();
			stopSounds.clear();

			alcProcessContext(m_context);
		}

		// stop thread
		if(m_playingSounds.empty() || (cerr != ALC_NO_ERROR))
		{
			m_playing = false;
			unlock();
			pthread_exit(NULL);
		}

		unlock();

#ifdef WIN32
		Sleep(20);
#else
		usleep(20000);
#endif
	}
}
Exemplo n.º 11
0
void AUD_SDLDevice::SDL_mix(void *data, Uint8* buffer, int length)
{
	AUD_SDLDevice* device = (AUD_SDLDevice*)data;

	device->mix((data_t*)buffer,length/AUD_DEVICE_SAMPLE_SIZE(device->m_specs));
}
Exemplo n.º 12
0
void OpenALDevice::updateStreams()
{
	int length;

	ALint info;
	DeviceSpecs specs = m_specs;
	ALCenum cerr;
	std::list<std::shared_ptr<OpenALHandle> > stopSounds;
	std::list<std::shared_ptr<OpenALHandle> > pauseSounds;

	auto sleepDuration = std::chrono::milliseconds(20);

	for(;;)
	{
		lock();

		alcSuspendContext(m_context);
		cerr = alcGetError(m_device);
		if(cerr == ALC_NO_ERROR)
		{
			// for all sounds
			for(auto& sound : m_playingSounds)
			{
				// is it a streamed sound?
				if(!sound->m_isBuffered)
				{
					// check for buffer refilling
					alGetSourcei(sound->m_source, AL_BUFFERS_PROCESSED, &info);

					info += (OpenALHandle::CYCLE_BUFFERS - sound->m_current);

					if(info)
					{
						specs.specs = sound->m_reader->getSpecs();
						m_buffer.assureSize(m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));

						// for all empty buffers
						while(info--)
						{
							// if there's still data to play back
							if(!sound->m_eos)
							{
								// read data
								length = m_buffersize;

								try
								{
									sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());

									// looping necessary?
									if(length == 0 && sound->m_loopcount)
									{
										if(sound->m_loopcount > 0)
											sound->m_loopcount--;

										sound->m_reader->seek(0);

										length = m_buffersize;
										sound->m_reader->read(length, sound->m_eos, m_buffer.getBuffer());
									}
								}
								catch(Exception& e)
								{
									length = 0;
									std::cerr << "Caught exception while reading sound data during playback with OpenAL: " << e.getMessage() << std::endl;
								}

								if(sound->m_loopcount != 0)
									sound->m_eos = false;

								// read nothing?
								if(length == 0)
								{
									break;
								}

								ALuint buffer;

								if(sound->m_current < OpenALHandle::CYCLE_BUFFERS)
									buffer = sound->m_buffers[sound->m_current++];
								else
									alSourceUnqueueBuffers(sound->m_source, 1, &buffer);

								ALenum err;
								if((err = alGetError()) != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}

								// fill with new data
								alBufferData(buffer, sound->m_format, m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);

								if((err = alGetError()) != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}

								// and queue again
								alSourceQueueBuffers(sound->m_source, 1,&buffer);
								if(alGetError() != AL_NO_ERROR)
								{
									sound->m_eos = true;
									break;
								}
							}
							else
								break;
						}
					}
				}

				// check if the sound has been stopped
				alGetSourcei(sound->m_source, AL_SOURCE_STATE, &info);

				if(info != AL_PLAYING)
				{
					// if it really stopped
					if(sound->m_eos && info != AL_INITIAL)
					{
						if(sound->m_stop)
							sound->m_stop(sound->m_stop_data);

						// pause or
						if(sound->m_keep)
							pauseSounds.push_back(sound);
						// stop
						else
							stopSounds.push_back(sound);
					}
					// continue playing
					else
						alSourcePlay(sound->m_source);
				}
			}

			for(auto& sound : pauseSounds)
				sound->pause(true);

			for(auto& sound : stopSounds)
				sound->stop();

			pauseSounds.clear();
			stopSounds.clear();

			alcProcessContext(m_context);
		}

		// stop thread
		if(m_playingSounds.empty() || (cerr != ALC_NO_ERROR))
		{
			m_playing = false;
			unlock();

			return;
		}

		unlock();

		std::this_thread::sleep_for(sleepDuration);
	}
}
Exemplo n.º 13
0
bool OpenALDevice::OpenALHandle::seek(float position)
{
	if(!m_status)
		return false;

	std::lock_guard<ILockable> lock(*m_device);

	if(!m_status)
		return false;

	if(m_isBuffered)
		alSourcef(m_source, AL_SEC_OFFSET, position);
	else
	{
		m_reader->seek((int)(position * m_reader->getSpecs().rate));
		m_eos = false;

		ALint info;

		alGetSourcei(m_source, AL_SOURCE_STATE, &info);

		// we need to stop playing sounds as well to clear the buffers
		// this might cause clicks, but fixes a bug regarding position determination
		if(info == AL_PAUSED || info == AL_PLAYING)
			alSourceStop(m_source);

		alSourcei(m_source, AL_BUFFER, 0);

		ALenum err;
		if((err = alGetError()) == AL_NO_ERROR)
		{
			int length;
			DeviceSpecs specs = m_device->m_specs;
			specs.specs = m_reader->getSpecs();
			m_device->m_buffer.assureSize(m_device->m_buffersize * AUD_DEVICE_SAMPLE_SIZE(specs));

			for(m_current = 0; m_current < CYCLE_BUFFERS; m_current++)
			{
				length = m_device->m_buffersize;

				m_reader->read(length, m_eos, m_device->m_buffer.getBuffer());

				if(length == 0)
					break;

				alBufferData(m_buffers[m_current], m_format, m_device->m_buffer.getBuffer(), length * AUD_DEVICE_SAMPLE_SIZE(specs), specs.rate);

				if(alGetError() != AL_NO_ERROR)
					break;
			}

			if(m_loopcount != 0)
				m_eos = false;

			alSourceQueueBuffers(m_source, m_current, m_buffers);
		}

		alSourceRewind(m_source);
	}

	if(m_status == STATUS_STOPPED)
		m_status = STATUS_PAUSED;

	return true;
}
Exemplo n.º 14
0
AUD_FFMPEGWriter::AUD_FFMPEGWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate) :
	m_position(0),
	m_specs(specs),
	m_input_samples(0)
{
	static const char* formats[] = { NULL, "ac3", "flac", "matroska", "mp2", "mp3", "ogg", "wav" };

	m_formatCtx = avformat_alloc_context();
	if (!m_formatCtx) AUD_THROW(AUD_ERROR_FFMPEG, context_error);

	strcpy(m_formatCtx->filename, filename.c_str());
	m_outputFmt = m_formatCtx->oformat = av_guess_format(formats[format], filename.c_str(), NULL);
	if (!m_outputFmt) {
		avformat_free_context(m_formatCtx);
		AUD_THROW(AUD_ERROR_FFMPEG, context_error);
	}

	switch(codec)
	{
	case AUD_CODEC_AAC:
		m_outputFmt->audio_codec = AV_CODEC_ID_AAC;
		break;
	case AUD_CODEC_AC3:
		m_outputFmt->audio_codec = AV_CODEC_ID_AC3;
		break;
	case AUD_CODEC_FLAC:
		m_outputFmt->audio_codec = AV_CODEC_ID_FLAC;
		break;
	case AUD_CODEC_MP2:
		m_outputFmt->audio_codec = AV_CODEC_ID_MP2;
		break;
	case AUD_CODEC_MP3:
		m_outputFmt->audio_codec = AV_CODEC_ID_MP3;
		break;
	case AUD_CODEC_PCM:
		switch(specs.format)
		{
		case AUD_FORMAT_U8:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_U8;
			break;
		case AUD_FORMAT_S16:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
			break;
		case AUD_FORMAT_S24:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S24LE;
			break;
		case AUD_FORMAT_S32:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_S32LE;
			break;
		case AUD_FORMAT_FLOAT32:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F32LE;
			break;
		case AUD_FORMAT_FLOAT64:
			m_outputFmt->audio_codec = AV_CODEC_ID_PCM_F64LE;
			break;
		default:
			m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
			break;
		}
		break;
	case AUD_CODEC_VORBIS:
		m_outputFmt->audio_codec = AV_CODEC_ID_VORBIS;
		break;
	default:
		m_outputFmt->audio_codec = AV_CODEC_ID_NONE;
		break;
	}

	try
	{
		if(m_outputFmt->audio_codec == AV_CODEC_ID_NONE)
			AUD_THROW(AUD_ERROR_SPECS, codec_error);

		m_stream = avformat_new_stream(m_formatCtx, NULL);
		if(!m_stream)
			AUD_THROW(AUD_ERROR_FFMPEG, stream_error);

		m_codecCtx = m_stream->codec;
		m_codecCtx->codec_id = m_outputFmt->audio_codec;
		m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO;
		m_codecCtx->bit_rate = bitrate;
		m_codecCtx->sample_rate = int(m_specs.rate);
		m_codecCtx->channels = m_specs.channels;
		m_codecCtx->time_base.num = 1;
		m_codecCtx->time_base.den = m_codecCtx->sample_rate;

		switch(m_specs.format)
		{
		case AUD_FORMAT_U8:
			m_convert = AUD_convert_float_u8;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
			break;
		case AUD_FORMAT_S16:
			m_convert = AUD_convert_float_s16;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
			break;
		case AUD_FORMAT_S32:
			m_convert = AUD_convert_float_s32;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_S32;
			break;
		case AUD_FORMAT_FLOAT32:
			m_convert = AUD_convert_copy<float>;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
			break;
		case AUD_FORMAT_FLOAT64:
			m_convert = AUD_convert_float_double;
			m_codecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
			break;
		default:
			AUD_THROW(AUD_ERROR_FFMPEG, format_error);
		}

		try
		{
			if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER)
				m_codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER;

			AVCodec* codec = avcodec_find_encoder(m_codecCtx->codec_id);
			if(!codec)
				AUD_THROW(AUD_ERROR_FFMPEG, codec_error);

			if(codec->sample_fmts) {
				// Check if the prefered sample format for this codec is supported.
				const enum AVSampleFormat *p = codec->sample_fmts;
				for(; *p != -1; p++) {
					if(*p == m_stream->codec->sample_fmt)
						break;
				}
				if(*p == -1) {
					// Sample format incompatible with codec. Defaulting to a format known to work.
					m_stream->codec->sample_fmt = codec->sample_fmts[0];
				}
			}

			if(avcodec_open2(m_codecCtx, codec, NULL))
				AUD_THROW(AUD_ERROR_FFMPEG, codec_error);

			m_output_buffer.resize(FF_MIN_BUFFER_SIZE);
			int samplesize = AUD_MAX(AUD_SAMPLE_SIZE(m_specs), AUD_DEVICE_SAMPLE_SIZE(m_specs));

			if(m_codecCtx->frame_size <= 1) {
				m_input_size = FF_MIN_BUFFER_SIZE * 8 / m_codecCtx->bits_per_coded_sample / m_codecCtx->channels;
				m_input_buffer.resize(m_input_size * samplesize);
			}
			else
			{
				m_input_buffer.resize(m_codecCtx->frame_size * samplesize);
				m_input_size = m_codecCtx->frame_size;
			}

#ifdef FFMPEG_HAVE_ENCODE_AUDIO2
			m_frame = av_frame_alloc();
			if (!m_frame)
				AUD_THROW(AUD_ERROR_FFMPEG, codec_error);
			avcodec_get_frame_defaults(m_frame);
			m_frame->linesize[0]    = m_input_size * samplesize;
			m_frame->format         = m_codecCtx->sample_fmt;
			m_frame->nb_samples     = m_input_size;
#  ifdef FFMPEG_HAVE_AVFRAME_SAMPLE_RATE
			m_frame->sample_rate    = m_codecCtx->sample_rate;
#  endif
#  ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
			m_frame->channel_layout = m_codecCtx->channel_layout;
#  endif
			m_sample_size = av_get_bytes_per_sample(m_codecCtx->sample_fmt);
			m_frame_pts = 0;
			m_deinterleave = av_sample_fmt_is_planar(m_codecCtx->sample_fmt);
			if(m_deinterleave)
				m_deinterleave_buffer.resize(m_input_size * m_codecCtx->channels * m_sample_size);
#endif

			try
			{
				if(avio_open(&m_formatCtx->pb, filename.c_str(), AVIO_FLAG_WRITE))
					AUD_THROW(AUD_ERROR_FILE, file_error);

				avformat_write_header(m_formatCtx, NULL);
			}
			catch(AUD_Exception&)
			{
				avcodec_close(m_codecCtx);
				av_freep(&m_formatCtx->streams[0]->codec);
				throw;
			}
		}
		catch(AUD_Exception&)
		{
			av_freep(&m_formatCtx->streams[0]);
			throw;
		}
	}
	catch(AUD_Exception&)
	{
		av_free(m_formatCtx);
		throw;
	}
}