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
10charge, to any person obtaining a copy of this software module and associated
11documentation files (the "Software"), to deal in the Software without
12restriction, including without limitation the rights to use, copy, modify,
13merge, publish, distribute, sublicense, and/or sell copies of the Software, and
14to permit persons to whom the Software is furnished to do so, subject to the
15following conditions: The above copyright notice and this permission notice
16shall be included in all copies or substantial portions of the Software. THE
17SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
18INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
19PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22CONNECTION 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
25static 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
33Sound_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
43Sound_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
57int 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
63const 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
92inline 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
98void 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
120void 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
134void Sound_Queue::fill_buffer_( void* user_data, Uint8* out, int count )
135{
136 ((Sound_Queue*) user_data)->fill_buffer( out, count );
137}
138
139