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