Sound_Queue.cpp (4098B)
1 2// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/ 3 4#include "Sound_Queue.h" 5 6#include <assert.h> 7#include <string.h> 8#include <string> 9 10/* Copyright (C) 2005 by Shay Green. Permission is hereby granted, free of 11charge, to any person obtaining a copy of this software module and associated 12documentation files (the "Software"), to deal in the Software without 13restriction, including without limitation the rights to use, copy, modify, 14merge, publish, distribute, sublicense, and/or sell copies of the Software, and 15to permit persons to whom the Software is furnished to do so, subject to the 16following conditions: The above copyright notice and this permission notice 17shall be included in all copies or substantial portions of the Software. THE 18SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 19INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 21COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 22IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 23CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 24 25// Return current SDL_GetError() string, or str if SDL didn't have a string 26static const char* sdl_error( const char* str ) 27{ 28 const char* sdl_str = SDL_GetError(); 29 if ( sdl_str && *sdl_str ) 30 str = sdl_str; 31 return str; 32} 33 34Sound_Queue::Sound_Queue() 35{ 36 bufs = NULL; 37 free_sem = NULL; 38 sound_open = false; 39 sync_output = true; 40 41 std::string platform = SDL_GetPlatform(); 42 if (platform == "Linux") 43 { 44 SDL_InitSubSystem(SDL_INIT_AUDIO); 45 SDL_AudioInit("alsa"); 46 } 47 else 48 { 49 SDL_Init(SDL_INIT_AUDIO); 50 } 51 52 atexit(SDL_Quit); 53} 54 55Sound_Queue::~Sound_Queue() 56{ 57 stop(); 58} 59 60const char* Sound_Queue::start( long sample_rate, int chan_count ) 61{ 62 assert( !bufs ); // can only be initialized once 63 64 write_buf = 0; 65 write_pos = 0; 66 read_buf = 0; 67 68 bufs = new sample_t [(long) buf_size * buf_count]; 69 if ( !bufs ) 70 return "Out of memory"; 71 currently_playing_ = bufs; 72 73 for (long l = 0; l < ((long) buf_size * buf_count); l++) 74 bufs[0] = 0; 75 76 free_sem = SDL_CreateSemaphore( buf_count - 1 ); 77 if ( !free_sem ) 78 return sdl_error( "Couldn't create semaphore" ); 79 80 SDL_AudioSpec as; 81 as.freq = (int)sample_rate; 82 as.format = AUDIO_S16SYS; 83 as.channels = chan_count; 84 as.silence = 0; 85 as.samples = buf_size / chan_count; 86 as.size = 0; 87 as.callback = fill_buffer_; 88 as.userdata = this; 89 if ( SDL_OpenAudio( &as, NULL ) < 0 ) 90 return sdl_error( "Couldn't open SDL audio" ); 91 SDL_PauseAudio( false ); 92 sound_open = true; 93 94 return NULL; 95} 96 97void Sound_Queue::stop() 98{ 99 if ( sound_open ) 100 { 101 sound_open = false; 102 SDL_PauseAudio( true ); 103 SDL_CloseAudio(); 104 } 105 106 if ( free_sem ) 107 { 108 SDL_DestroySemaphore( free_sem ); 109 free_sem = NULL; 110 } 111 112 delete [] bufs; 113 bufs = NULL; 114} 115 116int Sound_Queue::sample_count() const 117{ 118 int buf_free = SDL_SemValue( free_sem ) * buf_size + (buf_size - write_pos); 119 return buf_size * buf_count - buf_free; 120} 121 122inline Sound_Queue::sample_t* Sound_Queue::buf( int index ) 123{ 124 assert( (unsigned) index < buf_count ); 125 return bufs + (long) index * buf_size; 126} 127 128void Sound_Queue::write( const sample_t* in, int count, bool sync ) 129{ 130 sync_output = sync; 131 132 while ( count ) 133 { 134 int n = buf_size - write_pos; 135 if ( n > count ) 136 n = count; 137 138 memcpy( buf( write_buf ) + write_pos, in, n * sizeof (sample_t) ); 139 in += n; 140 write_pos += n; 141 count -= n; 142 143 if ( write_pos >= buf_size ) 144 { 145 write_pos = 0; 146 write_buf = (write_buf + 1) % buf_count; 147 148 if (sync_output) 149 SDL_SemWait( free_sem ); 150 } 151 } 152} 153 154void Sound_Queue::fill_buffer( Uint8* out, int count ) 155{ 156 if ((SDL_SemValue( free_sem ) < buf_count - 1 ) || !sync_output) 157 { 158 currently_playing_ = buf( read_buf ); 159 memcpy( out, buf( read_buf ), count ); 160 read_buf = (read_buf + 1) % buf_count; 161 162 if (sync_output) 163 SDL_SemPost( free_sem ); 164 } 165 else 166 { 167 memset( out, 0, count ); 168 } 169} 170 171void Sound_Queue::fill_buffer_( void* user_data, Uint8* out, int count ) 172{ 173 ((Sound_Queue*) user_data)->fill_buffer( out, count ); 174}