audio.cpp: Split up into smaller parts
This commit is contained in:
		
							parent
							
								
									e341dca579
								
							
						
					
					
						commit
						f73b0ba4b5
					
				
					 13 changed files with 1830 additions and 1464 deletions
				
			
		| 
						 | 
					@ -135,6 +135,10 @@ set(MAIN_HEADERS
 | 
				
			||||||
	src/gl-fun.h
 | 
						src/gl-fun.h
 | 
				
			||||||
	src/gl-meta.h
 | 
						src/gl-meta.h
 | 
				
			||||||
	src/vertex.h
 | 
						src/vertex.h
 | 
				
			||||||
 | 
						src/soundemitter.h
 | 
				
			||||||
 | 
						src/aldatasource.h
 | 
				
			||||||
 | 
						src/alstream.h
 | 
				
			||||||
 | 
						src/audiostream.h
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
set(MAIN_SOURCE
 | 
					set(MAIN_SOURCE
 | 
				
			||||||
| 
						 | 
					@ -166,6 +170,10 @@ set(MAIN_SOURCE
 | 
				
			||||||
	src/gl-fun.cpp
 | 
						src/gl-fun.cpp
 | 
				
			||||||
	src/gl-meta.cpp
 | 
						src/gl-meta.cpp
 | 
				
			||||||
	src/vertex.cpp
 | 
						src/vertex.cpp
 | 
				
			||||||
 | 
						src/soundemitter.cpp
 | 
				
			||||||
 | 
						src/sdlsoundsource.cpp
 | 
				
			||||||
 | 
						src/alstream.cpp
 | 
				
			||||||
 | 
						src/audiostream.cpp
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
source_group("MKXP Source" FILES ${MAIN_SOURCE} ${MAIN_HEADERS})
 | 
					source_group("MKXP Source" FILES ${MAIN_SOURCE} ${MAIN_HEADERS})
 | 
				
			||||||
| 
						 | 
					@ -191,6 +199,10 @@ set(EMBEDDED_INPUT
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (RGSS2)
 | 
					if (RGSS2)
 | 
				
			||||||
 | 
						list(APPEND MAIN_SOURCE
 | 
				
			||||||
 | 
							src/vorbissource.cpp
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	list(APPEND EMBEDDED_INPUT
 | 
						list(APPEND EMBEDDED_INPUT
 | 
				
			||||||
		shader/blur.frag
 | 
							shader/blur.frag
 | 
				
			||||||
		shader/blurH.vert
 | 
							shader/blurH.vert
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								mkxp.pro
									
										
									
									
									
								
							
							
						
						
									
										15
									
								
								mkxp.pro
									
										
									
									
									
								
							| 
						 | 
					@ -126,7 +126,11 @@ HEADERS += \
 | 
				
			||||||
	src/debugwriter.h \
 | 
						src/debugwriter.h \
 | 
				
			||||||
	src/gl-fun.h \
 | 
						src/gl-fun.h \
 | 
				
			||||||
	src/gl-meta.h \
 | 
						src/gl-meta.h \
 | 
				
			||||||
	src/vertex.h
 | 
						src/vertex.h \
 | 
				
			||||||
 | 
						src/soundemitter.h \
 | 
				
			||||||
 | 
						src/aldatasource.h \
 | 
				
			||||||
 | 
						src/alstream.h \
 | 
				
			||||||
 | 
						src/audiostream.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SOURCES += \
 | 
					SOURCES += \
 | 
				
			||||||
	src/main.cpp \
 | 
						src/main.cpp \
 | 
				
			||||||
| 
						 | 
					@ -156,7 +160,11 @@ SOURCES += \
 | 
				
			||||||
	src/sharedstate.cpp \
 | 
						src/sharedstate.cpp \
 | 
				
			||||||
	src/gl-fun.cpp \
 | 
						src/gl-fun.cpp \
 | 
				
			||||||
	src/gl-meta.cpp \
 | 
						src/gl-meta.cpp \
 | 
				
			||||||
	src/vertex.cpp
 | 
						src/vertex.cpp \
 | 
				
			||||||
 | 
						src/soundemitter.cpp \
 | 
				
			||||||
 | 
						src/sdlsoundsource.cpp \
 | 
				
			||||||
 | 
						src/alstream.cpp \
 | 
				
			||||||
 | 
						src/audiostream.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EMBED = \
 | 
					EMBED = \
 | 
				
			||||||
	shader/transSimple.frag \
 | 
						shader/transSimple.frag \
 | 
				
			||||||
| 
						 | 
					@ -176,6 +184,9 @@ EMBED = \
 | 
				
			||||||
	assets/liberation.ttf
 | 
						assets/liberation.ttf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RGSS2 {
 | 
					RGSS2 {
 | 
				
			||||||
 | 
						SOURCES += \
 | 
				
			||||||
 | 
						src/vorbissource.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	EMBED += \
 | 
						EMBED += \
 | 
				
			||||||
	shader/blur.frag \
 | 
						shader/blur.frag \
 | 
				
			||||||
	shader/blurH.vert \
 | 
						shader/blurH.vert \
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,6 +23,8 @@
 | 
				
			||||||
#define ALUTIL_H
 | 
					#define ALUTIL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <al.h>
 | 
					#include <al.h>
 | 
				
			||||||
 | 
					#include <SDL_audio.h>
 | 
				
			||||||
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace AL
 | 
					namespace AL
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -185,4 +187,51 @@ namespace Source
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline uint8_t formatSampleSize(int sdlFormat)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (sdlFormat)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case AUDIO_U8 :
 | 
				
			||||||
 | 
						case AUDIO_S8 :
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						case AUDIO_U16LSB :
 | 
				
			||||||
 | 
						case AUDIO_U16MSB :
 | 
				
			||||||
 | 
						case AUDIO_S16LSB :
 | 
				
			||||||
 | 
						case AUDIO_S16MSB :
 | 
				
			||||||
 | 
							return 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						default :
 | 
				
			||||||
 | 
							assert(!"Unhandled sample format");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline ALenum chooseALFormat(int sampleSize, int channelCount)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						switch (sampleSize)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case 1 :
 | 
				
			||||||
 | 
							switch (channelCount)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							case 1 : return AL_FORMAT_MONO8;
 | 
				
			||||||
 | 
							case 2 : return AL_FORMAT_STEREO8;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case 2 :
 | 
				
			||||||
 | 
							switch (channelCount)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
							case 1 : return AL_FORMAT_MONO16;
 | 
				
			||||||
 | 
							case 2 : return AL_FORMAT_STEREO16;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default :
 | 
				
			||||||
 | 
							assert(!"Unhandled sample size / channel count");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define AUDIO_SLEEP 10
 | 
				
			||||||
 | 
					#define STREAM_BUF_SIZE 32768
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif // ALUTIL_H
 | 
					#endif // ALUTIL_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										64
									
								
								src/aldatasource.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/aldatasource.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** aldatasource.h
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ALDATASOURCE_H
 | 
				
			||||||
 | 
					#define ALDATASOURCE_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "al-util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct ALDataSource
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum Status
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							NoError,
 | 
				
			||||||
 | 
							EndOfStream,
 | 
				
			||||||
 | 
							WrapAround,
 | 
				
			||||||
 | 
							Error
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual ~ALDataSource() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Read/process next chunk of data, and attach it
 | 
				
			||||||
 | 
						 * to provided AL buffer */
 | 
				
			||||||
 | 
						virtual Status fillBuffer(AL::Buffer::ID alBuffer) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual int sampleRate() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						virtual void seekToOffset(float seconds) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Seek back to start */
 | 
				
			||||||
 | 
						virtual void reset() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* The frame count right after wrap around */
 | 
				
			||||||
 | 
						virtual uint32_t loopStartFrames() = 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALDataSource *createSDLSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
					                              const char *extension,
 | 
				
			||||||
 | 
								                  uint32_t maxBufSize,
 | 
				
			||||||
 | 
								                  bool looped);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef RGSS2
 | 
				
			||||||
 | 
					ALDataSource *createVorbisSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
					                                 bool looped);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ALDATASOURCE_H
 | 
				
			||||||
							
								
								
									
										425
									
								
								src/alstream.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								src/alstream.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,425 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** alstream.cpp
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "alstream.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sharedstate.h"
 | 
				
			||||||
 | 
					#include "filesystem.h"
 | 
				
			||||||
 | 
					#include "aldatasource.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL_mutex.h>
 | 
				
			||||||
 | 
					#include <SDL_thread.h>
 | 
				
			||||||
 | 
					#include <SDL_timer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALStream::ALStream(LoopMode loopMode,
 | 
				
			||||||
 | 
							           const std::string &threadId)
 | 
				
			||||||
 | 
						: looped(loopMode == Looped),
 | 
				
			||||||
 | 
						  state(Closed),
 | 
				
			||||||
 | 
						  source(0),
 | 
				
			||||||
 | 
						  thread(0),
 | 
				
			||||||
 | 
						  preemptPause(false),
 | 
				
			||||||
 | 
						  streamInited(false),
 | 
				
			||||||
 | 
						  needsRewind(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						alSrc = AL::Source::gen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::setVolume(alSrc, 1.0);
 | 
				
			||||||
 | 
						AL::Source::setPitch(alSrc, 1.0);
 | 
				
			||||||
 | 
						AL::Source::detachBuffer(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < STREAM_BUFS; ++i)
 | 
				
			||||||
 | 
							alBuf[i] = AL::Buffer::gen();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pauseMut = SDL_CreateMutex();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						threadName = std::string("al_stream (") + threadId + ")";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALStream::~ALStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						clearALQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::del(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < STREAM_BUFS; ++i)
 | 
				
			||||||
 | 
							AL::Buffer::del(alBuf[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_DestroyMutex(pauseMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::close()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case Playing:
 | 
				
			||||||
 | 
						case Paused:
 | 
				
			||||||
 | 
							stopStream();
 | 
				
			||||||
 | 
						case Stopped:
 | 
				
			||||||
 | 
							closeSource();
 | 
				
			||||||
 | 
							state = Closed;
 | 
				
			||||||
 | 
						case Closed:
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::open(const std::string &filename)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case Playing:
 | 
				
			||||||
 | 
						case Paused:
 | 
				
			||||||
 | 
							stopStream();
 | 
				
			||||||
 | 
						case Stopped:
 | 
				
			||||||
 | 
							closeSource();
 | 
				
			||||||
 | 
						case Closed:
 | 
				
			||||||
 | 
							openSource(filename);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						state = Stopped;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case Closed:
 | 
				
			||||||
 | 
						case Stopped:
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						case Playing:
 | 
				
			||||||
 | 
						case Paused:
 | 
				
			||||||
 | 
							stopStream();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						state = Stopped;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::play(float offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case Closed:
 | 
				
			||||||
 | 
						case Playing:
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						case Stopped:
 | 
				
			||||||
 | 
							startStream(offset);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case Paused :
 | 
				
			||||||
 | 
							resumeStream();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						state = Playing;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::pause()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (state)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case Closed:
 | 
				
			||||||
 | 
						case Stopped:
 | 
				
			||||||
 | 
						case Paused:
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						case Playing:
 | 
				
			||||||
 | 
							pauseStream();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						state = Paused;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::setVolume(float value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						AL::Source::setVolume(alSrc, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::setPitch(float value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						AL::Source::setPitch(alSrc, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALStream::State ALStream::queryState()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						checkStopped();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return state;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float ALStream::queryOffset()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (state == Closed)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float procOffset = static_cast<float>(procFrames) / source->sampleRate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return procOffset + AL::Source::getSecOffset(alSrc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::closeSource()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						delete source;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::openSource(const std::string &filename)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *ext;
 | 
				
			||||||
 | 
						shState->fileSystem().openRead(srcOps, filename.c_str(), FileSystem::Audio, false, &ext);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef RGSS2
 | 
				
			||||||
 | 
						/* Try to read ogg file signature */
 | 
				
			||||||
 | 
						char sig[5];
 | 
				
			||||||
 | 
						memset(sig, '\0', sizeof(sig));
 | 
				
			||||||
 | 
						SDL_RWread(&srcOps, sig, 1, 4);
 | 
				
			||||||
 | 
						SDL_RWseek(&srcOps, 0, RW_SEEK_SET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!strcmp(sig, "OggS"))
 | 
				
			||||||
 | 
							source = createVorbisSource(srcOps, looped);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							source = createSDLSource(srcOps, ext, STREAM_BUF_SIZE, looped);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						source = createSDLSource(srcOps, ext, STREAM_BUF_SIZE, looped);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						needsRewind = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::stopStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						threadTermReq = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::stop(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (thread)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							SDL_WaitThread(thread, 0);
 | 
				
			||||||
 | 
							thread = 0;
 | 
				
			||||||
 | 
							needsRewind = true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						procFrames = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::startStream(float offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						clearALQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						preemptPause = false;
 | 
				
			||||||
 | 
						streamInited = false;
 | 
				
			||||||
 | 
						sourceExhausted = false;
 | 
				
			||||||
 | 
						threadTermReq = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						startOffset = offset;
 | 
				
			||||||
 | 
						procFrames = offset * source->sampleRate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						thread = SDL_CreateThread(streamDataFun, threadName.c_str(), this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::pauseStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SDL_LockMutex(pauseMut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (AL::Source::getState(alSrc) != AL_PLAYING)
 | 
				
			||||||
 | 
							preemptPause = true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							AL::Source::pause(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_UnlockMutex(pauseMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::resumeStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SDL_LockMutex(pauseMut);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (preemptPause)
 | 
				
			||||||
 | 
							preemptPause = false;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							AL::Source::play(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_UnlockMutex(pauseMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::checkStopped()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* This only concerns the scenario where
 | 
				
			||||||
 | 
						 * state is still 'Playing', but the stream
 | 
				
			||||||
 | 
						 * has already ended on its own (EOF, Error) */
 | 
				
			||||||
 | 
						if (state != Playing)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If streaming thread hasn't queued up
 | 
				
			||||||
 | 
						 * buffers yet there's not point in querying
 | 
				
			||||||
 | 
						 * the AL source */
 | 
				
			||||||
 | 
						if (!streamInited)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If alSrc isn't playing, but we haven't
 | 
				
			||||||
 | 
						 * exhausted the data source yet, we're just
 | 
				
			||||||
 | 
						 * having a buffer underrun */
 | 
				
			||||||
 | 
						if (!sourceExhausted)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (AL::Source::getState(alSrc) == AL_PLAYING)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stopStream();
 | 
				
			||||||
 | 
						state = Stopped;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void ALStream::clearALQueue()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Unqueue all buffers */
 | 
				
			||||||
 | 
						ALint queuedBufs = AL::Source::getProcBufferCount(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (queuedBufs--)
 | 
				
			||||||
 | 
							AL::Source::unqueueBuffer(alSrc);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* thread func */
 | 
				
			||||||
 | 
					void ALStream::streamData()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Fill up queue */
 | 
				
			||||||
 | 
						bool firstBuffer = true;
 | 
				
			||||||
 | 
						ALDataSource::Status status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (needsRewind)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (startOffset > 0)
 | 
				
			||||||
 | 
								source->seekToOffset(startOffset);
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								source->reset();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < STREAM_BUFS; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							AL::Buffer::ID buf = alBuf[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							status = source->fillBuffer(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (status == ALDataSource::Error)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AL::Source::queueBuffer(alSrc, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (firstBuffer)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								resumeStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								firstBuffer = false;
 | 
				
			||||||
 | 
								streamInited = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (threadTermReq)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (status == ALDataSource::EndOfStream)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								sourceExhausted = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Wait for buffers to be consumed, then
 | 
				
			||||||
 | 
						 * refill and queue them up again */
 | 
				
			||||||
 | 
						while (true)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ALint procBufs = AL::Source::getProcBufferCount(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (procBufs--)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (threadTermReq)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								AL::Buffer::ID buf = AL::Source::unqueueBuffer(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* If something went wrong, try again later */
 | 
				
			||||||
 | 
								if (buf == AL::Buffer::ID(0))
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (buf == lastBuf)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* Reset the processed sample count so
 | 
				
			||||||
 | 
									 * querying the playback offset returns 0.0 again */
 | 
				
			||||||
 | 
									procFrames = source->loopStartFrames();
 | 
				
			||||||
 | 
									lastBuf = AL::Buffer::ID(0);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* Add the frame count contained in this
 | 
				
			||||||
 | 
									 * buffer to the total count */
 | 
				
			||||||
 | 
									ALint bits = AL::Buffer::getBits(buf);
 | 
				
			||||||
 | 
									ALint size = AL::Buffer::getSize(buf);
 | 
				
			||||||
 | 
									ALint chan = AL::Buffer::getChannels(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (bits != 0 && chan != 0)
 | 
				
			||||||
 | 
										procFrames += ((size / (bits / 8)) / chan);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (sourceExhausted)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								status = source->fillBuffer(buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (status == ALDataSource::Error)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									sourceExhausted = true;
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								AL::Source::queueBuffer(alSrc, buf);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* In case of buffer underrun,
 | 
				
			||||||
 | 
								 * start playing again */
 | 
				
			||||||
 | 
								if (AL::Source::getState(alSrc) == AL_STOPPED)
 | 
				
			||||||
 | 
									AL::Source::play(alSrc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* If this was the last buffer before the data
 | 
				
			||||||
 | 
								 * source loop wrapped around again, mark it as
 | 
				
			||||||
 | 
								 * such so we can catch it and reset the processed
 | 
				
			||||||
 | 
								 * sample count once it gets unqueued */
 | 
				
			||||||
 | 
								if (status == ALDataSource::WrapAround)
 | 
				
			||||||
 | 
									lastBuf = buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (status == ALDataSource::EndOfStream)
 | 
				
			||||||
 | 
									sourceExhausted = true;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (threadTermReq)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SDL_Delay(AUDIO_SLEEP);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int ALStream::streamDataFun(void *_self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ALStream &self = *static_cast<ALStream*>(_self);
 | 
				
			||||||
 | 
						self.streamData();
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										122
									
								
								src/alstream.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/alstream.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,122 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** alstream.h
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ALSTREAM_H
 | 
				
			||||||
 | 
					#define ALSTREAM_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "al-util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <SDL_rwops.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SDL_mutex;
 | 
				
			||||||
 | 
					struct SDL_thread;
 | 
				
			||||||
 | 
					struct ALDataSource;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define STREAM_BUFS 3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* State-machine like audio playback stream.
 | 
				
			||||||
 | 
					 * This class is NOT thread safe */
 | 
				
			||||||
 | 
					struct ALStream
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						enum State
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Closed,
 | 
				
			||||||
 | 
							Stopped,
 | 
				
			||||||
 | 
							Playing,
 | 
				
			||||||
 | 
							Paused
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool looped;
 | 
				
			||||||
 | 
						State state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALDataSource *source;
 | 
				
			||||||
 | 
						SDL_Thread *thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::string threadName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_mutex *pauseMut;
 | 
				
			||||||
 | 
						bool preemptPause;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* When this flag isn't set and alSrc is
 | 
				
			||||||
 | 
						 * in 'STOPPED' state, stream isn't over
 | 
				
			||||||
 | 
						 * (it just hasn't started yet) */
 | 
				
			||||||
 | 
						bool streamInited;
 | 
				
			||||||
 | 
						bool sourceExhausted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool threadTermReq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool needsRewind;
 | 
				
			||||||
 | 
						float startOffset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::ID alSrc;
 | 
				
			||||||
 | 
						AL::Buffer::ID alBuf[STREAM_BUFS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint64_t procFrames;
 | 
				
			||||||
 | 
						AL::Buffer::ID lastBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_RWops srcOps;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ALenum format;
 | 
				
			||||||
 | 
							ALsizei freq;
 | 
				
			||||||
 | 
						} stream;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum LoopMode
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Looped,
 | 
				
			||||||
 | 
							NotLooped
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALStream(LoopMode loopMode,
 | 
				
			||||||
 | 
						         const std::string &threadId);
 | 
				
			||||||
 | 
						~ALStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void close();
 | 
				
			||||||
 | 
						void open(const std::string &filename);
 | 
				
			||||||
 | 
						void stop();
 | 
				
			||||||
 | 
						void play(float offset = 0);
 | 
				
			||||||
 | 
						void pause();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setVolume(float value);
 | 
				
			||||||
 | 
						void setPitch(float value);
 | 
				
			||||||
 | 
						State queryState();
 | 
				
			||||||
 | 
						float queryOffset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void closeSource();
 | 
				
			||||||
 | 
						void openSource(const std::string &filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void stopStream();
 | 
				
			||||||
 | 
						void startStream(float offset);
 | 
				
			||||||
 | 
						void pauseStream();
 | 
				
			||||||
 | 
						void resumeStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void checkStopped();
 | 
				
			||||||
 | 
						void clearALQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* thread func */
 | 
				
			||||||
 | 
						void streamData();
 | 
				
			||||||
 | 
						static int streamDataFun(void *);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ALSTREAM_H
 | 
				
			||||||
							
								
								
									
										1464
									
								
								src/audio.cpp
									
										
									
									
									
								
							
							
						
						
									
										1464
									
								
								src/audio.cpp
									
										
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										303
									
								
								src/audiostream.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								src/audiostream.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,303 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** audiostream.cpp
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "audiostream.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					#include "exception.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL_mutex.h>
 | 
				
			||||||
 | 
					#include <SDL_thread.h>
 | 
				
			||||||
 | 
					#include <SDL_timer.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioStream::AudioStream(ALStream::LoopMode loopMode,
 | 
				
			||||||
 | 
					                         const std::string &threadId)
 | 
				
			||||||
 | 
						: baseVolume(1.0),
 | 
				
			||||||
 | 
						  fadeVolume(1.0),
 | 
				
			||||||
 | 
						  extVolume(1.0),
 | 
				
			||||||
 | 
						  extPaused(false),
 | 
				
			||||||
 | 
						  noResumeStop(false),
 | 
				
			||||||
 | 
						  stream(loopMode, threadId)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						current.volume = 1.0;
 | 
				
			||||||
 | 
						current.pitch = 1.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fade.active = false;
 | 
				
			||||||
 | 
						fade.thread = 0;
 | 
				
			||||||
 | 
						fade.threadName = std::string("audio_fade (") + threadId + ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						streamMut = SDL_CreateMutex();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioStream::~AudioStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (fade.thread)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fade.reqTerm = true;
 | 
				
			||||||
 | 
							SDL_WaitThread(fade.thread, 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stream.stop();
 | 
				
			||||||
 | 
						stream.close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDL_DestroyMutex(streamMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::play(const std::string &filename,
 | 
				
			||||||
 | 
					                       int volume,
 | 
				
			||||||
 | 
					                       int pitch,
 | 
				
			||||||
 | 
					                       float offset)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						finiFadeInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float _volume = clamp<int>(volume, 0, 100) / 100.f;
 | 
				
			||||||
 | 
						float _pitch  = clamp<int>(pitch, 50, 150) / 100.f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALStream::State sState = stream.queryState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If all parameters match the current ones and we're
 | 
				
			||||||
 | 
						 * still playing, there's nothing to do */
 | 
				
			||||||
 | 
						if (filename == current.filename
 | 
				
			||||||
 | 
						&&  _volume  == current.volume
 | 
				
			||||||
 | 
						&&  _pitch   == current.pitch
 | 
				
			||||||
 | 
						&&  (sState == ALStream::Playing || sState == ALStream::Paused))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If all parameters except volume match the current ones,
 | 
				
			||||||
 | 
						 * we update the volume and continue streaming */
 | 
				
			||||||
 | 
						if (filename == current.filename
 | 
				
			||||||
 | 
						&&  _pitch   == current.pitch
 | 
				
			||||||
 | 
						&&  (sState == ALStream::Playing || sState == ALStream::Paused))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							setBaseVolume(_volume);
 | 
				
			||||||
 | 
							current.volume = _volume;
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Requested audio file is different from current one */
 | 
				
			||||||
 | 
						bool diffFile = (filename != current.filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (sState)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						case ALStream::Paused :
 | 
				
			||||||
 | 
						case ALStream::Playing :
 | 
				
			||||||
 | 
							stream.stop();
 | 
				
			||||||
 | 
						case ALStream::Stopped :
 | 
				
			||||||
 | 
							if (diffFile)
 | 
				
			||||||
 | 
								stream.close();
 | 
				
			||||||
 | 
						case ALStream::Closed :
 | 
				
			||||||
 | 
							if (diffFile)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								try
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* This will throw on errors while
 | 
				
			||||||
 | 
									 * opening the data source */
 | 
				
			||||||
 | 
									stream.open(filename);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								catch (const Exception &e)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									unlockStream();
 | 
				
			||||||
 | 
									throw e;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						setBaseVolume(_volume);
 | 
				
			||||||
 | 
						stream.setPitch(_pitch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						current.filename = filename;
 | 
				
			||||||
 | 
						current.volume = _volume;
 | 
				
			||||||
 | 
						current.pitch = _pitch;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!extPaused)
 | 
				
			||||||
 | 
							stream.play(offset);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							noResumeStop = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unlockStream();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						finiFadeInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						lockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						noResumeStop = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stream.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unlockStream();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::fadeOut(int duration)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						lockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALStream::State sState = stream.queryState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fade.active)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sState == ALStream::Paused)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							stream.stop();
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sState != ALStream::Playing)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (fade.thread)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fade.reqFini = true;
 | 
				
			||||||
 | 
							SDL_WaitThread(fade.thread, 0);
 | 
				
			||||||
 | 
							fade.thread = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fade.active = true;
 | 
				
			||||||
 | 
						fade.msStep = (1.0) / duration;
 | 
				
			||||||
 | 
						fade.reqFini = false;
 | 
				
			||||||
 | 
						fade.reqTerm = false;
 | 
				
			||||||
 | 
						fade.startTicks = SDL_GetTicks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fade.thread = SDL_CreateThread(fadeThreadFun, fade.threadName.c_str(), this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unlockStream();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Any access to this classes 'stream' member,
 | 
				
			||||||
 | 
					 * whether state query or modification, must be
 | 
				
			||||||
 | 
					 * protected by a 'lock'/'unlock' pair */
 | 
				
			||||||
 | 
					void AudioStream::lockStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SDL_LockMutex(streamMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::unlockStream()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SDL_UnlockMutex(streamMut);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::setFadeVolume(float value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						fadeVolume = value;
 | 
				
			||||||
 | 
						updateVolume();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::setExtVolume1(float value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						extVolume = value;
 | 
				
			||||||
 | 
						updateVolume();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float AudioStream::playingOffset()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return stream.queryOffset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::finiFadeInt()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!fade.thread)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fade.reqFini = true;
 | 
				
			||||||
 | 
						SDL_WaitThread(fade.thread, 0);
 | 
				
			||||||
 | 
						fade.thread = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::updateVolume()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						stream.setVolume(baseVolume * fadeVolume * extVolume);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::setBaseVolume(float value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						baseVolume = value;
 | 
				
			||||||
 | 
						updateVolume();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void AudioStream::fadeThread()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						while (true)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Just immediately terminate on request */
 | 
				
			||||||
 | 
							if (fade.reqTerm)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							lockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							uint32_t curDur = SDL_GetTicks() - fade.startTicks;
 | 
				
			||||||
 | 
							float resVol = 1.0 - (curDur*fade.msStep);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ALStream::State state = stream.queryState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (state != ALStream::Playing
 | 
				
			||||||
 | 
							||  resVol < 0
 | 
				
			||||||
 | 
							||  fade.reqFini)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (state != ALStream::Paused)
 | 
				
			||||||
 | 
									stream.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								setFadeVolume(1.0);
 | 
				
			||||||
 | 
								unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							setFadeVolume(resVol);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SDL_Delay(AUDIO_SLEEP);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fade.active = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int AudioStream::fadeThreadFun(void *self)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						static_cast<AudioStream*>(self)->fadeThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										135
									
								
								src/audiostream.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/audiostream.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,135 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** audiostream.h
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef AUDIOSTREAM_H
 | 
				
			||||||
 | 
					#define AUDIOSTREAM_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "al-util.h"
 | 
				
			||||||
 | 
					#include "alstream.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SDL_mutex;
 | 
				
			||||||
 | 
					struct SDL_Thread;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct AudioStream
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::string filename;
 | 
				
			||||||
 | 
							float volume;
 | 
				
			||||||
 | 
							float pitch;
 | 
				
			||||||
 | 
						} current;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Volume set with 'play()' */
 | 
				
			||||||
 | 
						float baseVolume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Volume set by external threads,
 | 
				
			||||||
 | 
						 * such as for fade-in/out.
 | 
				
			||||||
 | 
						 * Multiplied with intVolume for final
 | 
				
			||||||
 | 
						 * playback volume.
 | 
				
			||||||
 | 
						 * fadeVolume: used by fade-out thread.
 | 
				
			||||||
 | 
						 * extVolume: used by MeWatch. */
 | 
				
			||||||
 | 
						float fadeVolume;
 | 
				
			||||||
 | 
						float extVolume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Note that 'extPaused' and 'noResumeStop' are
 | 
				
			||||||
 | 
						 * effectively only used with the AudioStream
 | 
				
			||||||
 | 
						 * instance representing the BGM */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Flag indicating that the MeWatch paused this
 | 
				
			||||||
 | 
						 * (BGM) stream because a ME started playing.
 | 
				
			||||||
 | 
						 * While this flag is set, calls to 'play()'
 | 
				
			||||||
 | 
						 * might open another file, but will not start
 | 
				
			||||||
 | 
						 * the playback stream (the MeWatch will start
 | 
				
			||||||
 | 
						 * it as soon as the ME finished playing). */
 | 
				
			||||||
 | 
						bool extPaused;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Flag indicating that this stream shouldn't be
 | 
				
			||||||
 | 
						 * started by the MeWatch when it is in stopped
 | 
				
			||||||
 | 
						 * state (eg. because the BGM stream was explicitly
 | 
				
			||||||
 | 
						 * stopped by the user script while the ME was playing.
 | 
				
			||||||
 | 
						 * When a new BGM is started (via 'play()') while an ME
 | 
				
			||||||
 | 
						 * is playing, the file will be loaded without starting
 | 
				
			||||||
 | 
						 * the stream, but we want the MeWatch to start it as
 | 
				
			||||||
 | 
						 * soon as the ME ends, so we unset this flag. */
 | 
				
			||||||
 | 
						bool noResumeStop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALStream stream;
 | 
				
			||||||
 | 
						SDL_mutex *streamMut;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Fade is in progress */
 | 
				
			||||||
 | 
							bool active;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Request fade thread to finish and
 | 
				
			||||||
 | 
							 * cleanup (like it normally would) */
 | 
				
			||||||
 | 
							bool reqFini;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Request fade thread to terminate
 | 
				
			||||||
 | 
							 * immediately */
 | 
				
			||||||
 | 
							bool reqTerm;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							SDL_Thread *thread;
 | 
				
			||||||
 | 
							std::string threadName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Amount of reduced absolute volume
 | 
				
			||||||
 | 
							 * per ms of fade time */
 | 
				
			||||||
 | 
							float msStep;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Ticks at start of fade */
 | 
				
			||||||
 | 
							uint32_t startTicks;
 | 
				
			||||||
 | 
						} fade;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AudioStream(ALStream::LoopMode loopMode,
 | 
				
			||||||
 | 
						            const std::string &threadId);
 | 
				
			||||||
 | 
						~AudioStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void play(const std::string &filename,
 | 
				
			||||||
 | 
						          int volume,
 | 
				
			||||||
 | 
						          int pitch,
 | 
				
			||||||
 | 
						          float offset = 0);
 | 
				
			||||||
 | 
						void stop();
 | 
				
			||||||
 | 
						void fadeOut(int duration);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Any access to this classes 'stream' member,
 | 
				
			||||||
 | 
						 * whether state query or modification, must be
 | 
				
			||||||
 | 
						 * protected by a 'lock'/'unlock' pair */
 | 
				
			||||||
 | 
						void lockStream();
 | 
				
			||||||
 | 
						void unlockStream();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void setFadeVolume(float value);
 | 
				
			||||||
 | 
						void setExtVolume1(float value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float playingOffset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						void finiFadeInt();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void updateVolume();
 | 
				
			||||||
 | 
						void setBaseVolume(float value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void fadeThread();
 | 
				
			||||||
 | 
						static int fadeThreadFun(void *);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // AUDIOSTREAM_H
 | 
				
			||||||
							
								
								
									
										127
									
								
								src/sdlsoundsource.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/sdlsoundsource.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,127 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** sdlsoundsource.cpp
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "aldatasource.h"
 | 
				
			||||||
 | 
					#include "exception.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL_sound.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SDLSoundSource : ALDataSource
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						Sound_Sample *sample;
 | 
				
			||||||
 | 
						SDL_RWops &srcOps;
 | 
				
			||||||
 | 
						uint8_t sampleSize;
 | 
				
			||||||
 | 
						bool looped;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ALenum alFormat;
 | 
				
			||||||
 | 
						ALsizei alFreq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SDLSoundSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
						               const char *extension,
 | 
				
			||||||
 | 
						               uint32_t maxBufSize,
 | 
				
			||||||
 | 
						               bool looped)
 | 
				
			||||||
 | 
						    : srcOps(ops),
 | 
				
			||||||
 | 
						      looped(looped)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							sample = Sound_NewSample(&srcOps, extension, 0, maxBufSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!sample)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SDL_RWclose(&ops);
 | 
				
			||||||
 | 
								throw Exception(Exception::SDLError, "SDL_sound: %s", Sound_GetError());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sampleSize = formatSampleSize(sample->actual.format);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							alFormat = chooseALFormat(sampleSize, sample->actual.channels);
 | 
				
			||||||
 | 
							alFreq = sample->actual.rate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~SDLSoundSource()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* This also closes 'srcOps' */
 | 
				
			||||||
 | 
							Sound_FreeSample(sample);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Status fillBuffer(AL::Buffer::ID alBuffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint32_t decoded = Sound_Decode(sample);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sample->flags & SOUND_SAMPLEFLAG_EAGAIN)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								/* Try to decode one more time on EAGAIN */
 | 
				
			||||||
 | 
								decoded = Sound_Decode(sample);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Give up */
 | 
				
			||||||
 | 
								if (sample->flags & SOUND_SAMPLEFLAG_EAGAIN)
 | 
				
			||||||
 | 
									return ALDataSource::Error;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sample->flags & SOUND_SAMPLEFLAG_ERROR)
 | 
				
			||||||
 | 
								return ALDataSource::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AL::Buffer::uploadData(alBuffer, alFormat, sample->buffer, decoded, alFreq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (sample->flags & SOUND_SAMPLEFLAG_EOF)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (looped)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									Sound_Rewind(sample);
 | 
				
			||||||
 | 
									return ALDataSource::WrapAround;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									return ALDataSource::EndOfStream;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return ALDataSource::NoError;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int sampleRate()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return sample->actual.rate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void seekToOffset(float seconds)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Sound_Seek(sample, static_cast<uint32_t>(seconds * 1000));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void reset()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							Sound_Rewind(sample);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t loopStartFrames()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Loops from the beginning of the file */
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALDataSource *createSDLSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
					                              const char *extension,
 | 
				
			||||||
 | 
								                  uint32_t maxBufSize,
 | 
				
			||||||
 | 
								                  bool looped)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return new SDLSoundSource(ops, extension, maxBufSize, looped);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										231
									
								
								src/soundemitter.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								src/soundemitter.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,231 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** soundemitter.cpp
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "soundemitter.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sharedstate.h"
 | 
				
			||||||
 | 
					#include "filesystem.h"
 | 
				
			||||||
 | 
					#include "exception.h"
 | 
				
			||||||
 | 
					#include "util.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <SDL_sound.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SE_CACHE_MEM (10*1024*1024) // 10 MB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SoundBuffer
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Uniquely identifies this or equal buffer */
 | 
				
			||||||
 | 
						std::string key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Buffer::ID alBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Link into the buffer cache priority list */
 | 
				
			||||||
 | 
						IntruListLink<SoundBuffer> link;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Buffer byte count */
 | 
				
			||||||
 | 
						uint32_t bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Reference count */
 | 
				
			||||||
 | 
						uint8_t refCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SoundBuffer()
 | 
				
			||||||
 | 
						    : link(this),
 | 
				
			||||||
 | 
						      refCount(1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							alBuffer = AL::Buffer::gen();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static SoundBuffer *ref(SoundBuffer *buffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							++buffer->refCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return buffer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						static void deref(SoundBuffer *buffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (--buffer->refCount == 0)
 | 
				
			||||||
 | 
								delete buffer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						~SoundBuffer()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							AL::Buffer::del(alBuffer);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Before: [a][b][c][d], After (index=1): [a][c][d][b] */
 | 
				
			||||||
 | 
					static void
 | 
				
			||||||
 | 
					arrayPushBack(size_t array[], size_t size, size_t index)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						size_t v = array[index];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t t = index; t < size-1; ++t)
 | 
				
			||||||
 | 
							array[t] = array[t+1];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						array[size-1] = v;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SoundEmitter::SoundEmitter()
 | 
				
			||||||
 | 
						: bufferBytes(0)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int i = 0; i < SE_SOURCES; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							alSrcs[i] = AL::Source::gen();
 | 
				
			||||||
 | 
							atchBufs[i] = 0;
 | 
				
			||||||
 | 
							srcPrio[i] = i;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SoundEmitter::~SoundEmitter()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int i = 0; i < SE_SOURCES; ++i)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							AL::Source::stop(alSrcs[i]);
 | 
				
			||||||
 | 
							AL::Source::del(alSrcs[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (atchBufs[i])
 | 
				
			||||||
 | 
								SoundBuffer::deref(atchBufs[i]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						BufferHash::const_iterator iter;
 | 
				
			||||||
 | 
						for (iter = bufferHash.cbegin(); iter != bufferHash.cend(); ++iter)
 | 
				
			||||||
 | 
							SoundBuffer::deref(iter->second);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SoundEmitter::play(const std::string &filename,
 | 
				
			||||||
 | 
					                        int volume,
 | 
				
			||||||
 | 
										    int pitch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						float _volume = clamp<int>(volume, 0, 100) / 100.f;
 | 
				
			||||||
 | 
						float _pitch  = clamp<int>(pitch, 50, 150) / 100.f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SoundBuffer *buffer = allocateBuffer(filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Try to find first free source */
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
						for (i = 0; i < SE_SOURCES; ++i)
 | 
				
			||||||
 | 
							if (AL::Source::getState(alSrcs[srcPrio[i]]) != AL_PLAYING)
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If we didn't find any, overtake the one with lowest priority */
 | 
				
			||||||
 | 
						if (i == SE_SOURCES)
 | 
				
			||||||
 | 
							i = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Push the used source to the back of the priority list */
 | 
				
			||||||
 | 
						size_t srcIndex = srcPrio[i];
 | 
				
			||||||
 | 
						arrayPushBack(srcPrio, SE_SOURCES, i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::ID src = alSrcs[srcIndex];
 | 
				
			||||||
 | 
						AL::Source::stop(src);
 | 
				
			||||||
 | 
						AL::Source::detachBuffer(src);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SoundBuffer *old = atchBufs[srcIndex];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (old)
 | 
				
			||||||
 | 
							SoundBuffer::deref(old);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						atchBufs[srcIndex] = SoundBuffer::ref(buffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::attachBuffer(src, buffer->alBuffer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::setVolume(src, _volume);
 | 
				
			||||||
 | 
						AL::Source::setPitch(src, _pitch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::play(src);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SoundEmitter::stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int i = 0; i < SE_SOURCES; i++)
 | 
				
			||||||
 | 
							AL::Source::stop(alSrcs[i]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SoundBuffer *SoundEmitter::allocateBuffer(const std::string &filename)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SoundBuffer *buffer = bufferHash.value(filename, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (buffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Buffer still in cashe.
 | 
				
			||||||
 | 
							 * Move to front of priority list */
 | 
				
			||||||
 | 
							buffers.remove(buffer->link);
 | 
				
			||||||
 | 
							buffers.append(buffer->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return buffer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/* Buffer not in cashe, needs to be loaded */
 | 
				
			||||||
 | 
							SDL_RWops dataSource;
 | 
				
			||||||
 | 
							const char *extension;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							shState->fileSystem().openRead(dataSource, filename.c_str(),
 | 
				
			||||||
 | 
														   FileSystem::Audio, false, &extension);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Sound_Sample *sampleHandle = Sound_NewSample(&dataSource, extension, 0, STREAM_BUF_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!sampleHandle)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SDL_RWclose(&dataSource);
 | 
				
			||||||
 | 
								throw Exception(Exception::SDLError, "SDL_sound: %s", Sound_GetError());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							uint32_t decBytes = Sound_DecodeAll(sampleHandle);
 | 
				
			||||||
 | 
							uint8_t sampleSize = formatSampleSize(sampleHandle->actual.format);
 | 
				
			||||||
 | 
							uint32_t sampleCount = decBytes / sampleSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							buffer = new SoundBuffer;
 | 
				
			||||||
 | 
							buffer->key = filename;
 | 
				
			||||||
 | 
							buffer->bytes = sampleSize * sampleCount;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ALenum alFormat = chooseALFormat(sampleSize, sampleHandle->actual.channels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							AL::Buffer::uploadData(buffer->alBuffer, alFormat, sampleHandle->buffer,
 | 
				
			||||||
 | 
												   buffer->bytes, sampleHandle->actual.rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Sound_FreeSample(sampleHandle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							uint32_t wouldBeBytes = bufferBytes + buffer->bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* If memory limit is reached, delete lowest priority buffer
 | 
				
			||||||
 | 
							 * until there is room or no buffers left */
 | 
				
			||||||
 | 
							while (wouldBeBytes > SE_CACHE_MEM && !buffers.isEmpty())
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SoundBuffer *last = buffers.tail();
 | 
				
			||||||
 | 
								bufferHash.erase(last->key);
 | 
				
			||||||
 | 
								buffers.remove(last->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								wouldBeBytes -= last->bytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								SoundBuffer::deref(last);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bufferHash.insert(filename, buffer);
 | 
				
			||||||
 | 
							buffers.prepend(buffer->link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bufferBytes = wouldBeBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return buffer;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								src/soundemitter.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/soundemitter.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,64 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** soundemitter.h
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef SOUNDEMITTER_H
 | 
				
			||||||
 | 
					#define SOUNDEMITTER_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "intrulist.h"
 | 
				
			||||||
 | 
					#include "al-util.h"
 | 
				
			||||||
 | 
					#include "boost-hash.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SE_SOURCES 6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SoundBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct SoundEmitter
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						typedef BoostHash<std::string, SoundBuffer*> BufferHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						IntruList<SoundBuffer> buffers;
 | 
				
			||||||
 | 
						BufferHash bufferHash;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Byte count sum of all cached / playing buffers */
 | 
				
			||||||
 | 
						uint32_t bufferBytes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AL::Source::ID alSrcs[SE_SOURCES];
 | 
				
			||||||
 | 
						SoundBuffer *atchBufs[SE_SOURCES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Indices of sources, sorted by priority (lowest first) */
 | 
				
			||||||
 | 
						size_t srcPrio[SE_SOURCES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						SoundEmitter();
 | 
				
			||||||
 | 
						~SoundEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void play(const std::string &filename,
 | 
				
			||||||
 | 
						          int volume,
 | 
				
			||||||
 | 
						          int pitch);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
						SoundBuffer *allocateBuffer(const std::string &filename);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // SOUNDEMITTER_H
 | 
				
			||||||
							
								
								
									
										283
									
								
								src/vorbissource.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								src/vorbissource.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,283 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					** vorbissource.cpp
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** This file is part of mkxp.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** Copyright (C) 2014 Jonas Kulla <Nyocurio@gmail.com>
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					** it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					** the Free Software Foundation, either version 2 of the License, or
 | 
				
			||||||
 | 
					** (at your option) any later version.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** mkxp is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					** GNU General Public License for more details.
 | 
				
			||||||
 | 
					**
 | 
				
			||||||
 | 
					** You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					** along with mkxp.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "aldatasource.h"
 | 
				
			||||||
 | 
					#include "exception.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define OV_EXCLUDE_STATIC_CALLBACKS
 | 
				
			||||||
 | 
					#include <vorbis/vorbisfile.h>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static size_t vfRead(void *ptr, size_t size, size_t nmemb, void *ops)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return SDL_RWread(static_cast<SDL_RWops*>(ops), ptr, size, nmemb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int vfSeek(void *ops, ogg_int64_t offset, int whence)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return SDL_RWseek(static_cast<SDL_RWops*>(ops), offset, whence);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static long vfTell(void *ops)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return SDL_RWtell(static_cast<SDL_RWops*>(ops));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static ov_callbacks OvCallbacks =
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    vfRead,
 | 
				
			||||||
 | 
					    vfSeek,
 | 
				
			||||||
 | 
					    0,
 | 
				
			||||||
 | 
					    vfTell
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct VorbisSource : ALDataSource
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						SDL_RWops &src;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						OggVorbis_File vf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t currentFrame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint32_t start;
 | 
				
			||||||
 | 
							uint32_t length;
 | 
				
			||||||
 | 
							uint32_t end;
 | 
				
			||||||
 | 
							bool valid;
 | 
				
			||||||
 | 
							bool requested;
 | 
				
			||||||
 | 
						} loop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int channels;
 | 
				
			||||||
 | 
							int rate;
 | 
				
			||||||
 | 
							int frameSize;
 | 
				
			||||||
 | 
							ALenum alFormat;
 | 
				
			||||||
 | 
						} info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<int16_t> sampleBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						VorbisSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
						             bool looped)
 | 
				
			||||||
 | 
						    : src(ops),
 | 
				
			||||||
 | 
						      currentFrame(0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							int error = ov_open_callbacks(&src, &vf, 0, 0, OvCallbacks);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								SDL_RWclose(&src);
 | 
				
			||||||
 | 
								throw Exception(Exception::MKXPError,
 | 
				
			||||||
 | 
								                "Vorbisfile: Cannot read ogg file");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Extract bitstream info */
 | 
				
			||||||
 | 
							info.channels = vf.vi->channels;
 | 
				
			||||||
 | 
							info.rate = vf.vi->rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (info.channels > 2)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								ov_clear(&vf);
 | 
				
			||||||
 | 
								SDL_RWclose(&src);
 | 
				
			||||||
 | 
								throw Exception(Exception::MKXPError,
 | 
				
			||||||
 | 
								                "Cannot handle audio with more than 2 channels");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							info.alFormat = chooseALFormat(sizeof(int16_t), info.channels);
 | 
				
			||||||
 | 
							info.frameSize = sizeof(int16_t) * info.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sampleBuf.resize(STREAM_BUF_SIZE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							loop.requested = looped;
 | 
				
			||||||
 | 
							loop.valid = false;
 | 
				
			||||||
 | 
							loop.start = loop.length = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!loop.requested)
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Try to extract loop info */
 | 
				
			||||||
 | 
							for (int i = 0; i < vf.vc->comments; ++i)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								char *comment = vf.vc->user_comments[i];
 | 
				
			||||||
 | 
								char *sep = strstr(comment, "=");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* No '=' found */
 | 
				
			||||||
 | 
								if (!sep)
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								/* Empty value */
 | 
				
			||||||
 | 
								if (!*(sep+1))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								*sep = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!strcmp(comment, "LOOPSTART"))
 | 
				
			||||||
 | 
									loop.start = strtol(sep+1, 0, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (!strcmp(comment, "LOOPLENGTH"))
 | 
				
			||||||
 | 
									loop.length = strtol(sep+1, 0, 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								*sep = '=';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							loop.end = loop.start + loop.length;
 | 
				
			||||||
 | 
							loop.valid = (loop.start && loop.length);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~VorbisSource()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ov_clear(&vf);
 | 
				
			||||||
 | 
							SDL_RWclose(&src);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int sampleRate()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return info.rate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void seekToOffset(float seconds)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							currentFrame = seconds * info.rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (loop.valid && currentFrame > loop.end)
 | 
				
			||||||
 | 
								currentFrame = loop.start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* If seeking fails, just seek back to start */
 | 
				
			||||||
 | 
							if (ov_pcm_seek(&vf, currentFrame) != 0)
 | 
				
			||||||
 | 
								ov_raw_seek(&vf, 0);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Status fillBuffer(AL::Buffer::ID alBuffer)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							void *bufPtr = sampleBuf.data();
 | 
				
			||||||
 | 
							int availBuf = sampleBuf.size();
 | 
				
			||||||
 | 
							int bufUsed  = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int canRead = availBuf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Status retStatus = ALDataSource::NoError;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							bool readAgain = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (loop.valid)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								int tilLoopEnd = loop.end * info.frameSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								canRead = std::min(availBuf, tilLoopEnd);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (canRead > 16)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								long res = ov_read(&vf, static_cast<char*>(bufPtr),
 | 
				
			||||||
 | 
								                   canRead, 0, sizeof(int16_t), 1, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (res < 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* Read error */
 | 
				
			||||||
 | 
									retStatus = ALDataSource::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (res == 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* EOF */
 | 
				
			||||||
 | 
									if (loop.requested)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										retStatus = ALDataSource::WrapAround;
 | 
				
			||||||
 | 
										reset();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									else
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										retStatus = ALDataSource::EndOfStream;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* If we sought right to the end of the file,
 | 
				
			||||||
 | 
									 * we might be EOF without actually having read
 | 
				
			||||||
 | 
									 * any data at all yet (which mustn't happen),
 | 
				
			||||||
 | 
									 * so we try to continue reading some data. */
 | 
				
			||||||
 | 
									if (bufUsed > 0)
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (readAgain)
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										/* We're still not getting data though.
 | 
				
			||||||
 | 
										 * Just error out to prevent an endless loop */
 | 
				
			||||||
 | 
										retStatus = ALDataSource::Error;
 | 
				
			||||||
 | 
										break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									readAgain = true;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								bufUsed += (res / sizeof(int16_t));
 | 
				
			||||||
 | 
								bufPtr = &sampleBuf[bufUsed];
 | 
				
			||||||
 | 
								currentFrame += (res / info.frameSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (loop.valid && currentFrame >= loop.end)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									/* Determine how many frames we're
 | 
				
			||||||
 | 
									 * over the loop end */
 | 
				
			||||||
 | 
									int discardFrames = currentFrame - loop.end;
 | 
				
			||||||
 | 
									bufUsed -= discardFrames * info.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									retStatus = ALDataSource::WrapAround;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									/* Seek to loop start */
 | 
				
			||||||
 | 
									currentFrame = loop.start;
 | 
				
			||||||
 | 
									if (ov_pcm_seek(&vf, currentFrame) != 0)
 | 
				
			||||||
 | 
										retStatus = ALDataSource::Error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								canRead -= res;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (retStatus != ALDataSource::Error)
 | 
				
			||||||
 | 
								AL::Buffer::uploadData(alBuffer, info.alFormat, sampleBuf.data(),
 | 
				
			||||||
 | 
								                       bufUsed*sizeof(int16_t), info.rate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return retStatus;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void reset()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							ov_raw_seek(&vf, 0);
 | 
				
			||||||
 | 
							currentFrame = 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t loopStartFrames()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (loop.valid)
 | 
				
			||||||
 | 
								return loop.start;
 | 
				
			||||||
 | 
							else
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALDataSource *createVorbisSource(SDL_RWops &ops,
 | 
				
			||||||
 | 
					                                 bool looped)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return new VorbisSource(ops, looped);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue