aboutsummaryrefslogtreecommitdiffstats
path: root/hw/goldfish_audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/goldfish_audio.c')
-rw-r--r--hw/goldfish_audio.c268
1 files changed, 181 insertions, 87 deletions
diff --git a/hw/goldfish_audio.c b/hw/goldfish_audio.c
index d0a44b5..c8a6712 100644
--- a/hw/goldfish_audio.c
+++ b/hw/goldfish_audio.c
@@ -62,21 +62,25 @@ enum {
AUDIO_INT_READ_BUFFER_FULL = 1U << 2,
};
+struct goldfish_audio_buff {
+ uint32_t address;
+ uint32_t length;
+ uint8* data;
+ uint32_t capacity;
+ uint32_t offset;
+};
+
struct goldfish_audio_state {
struct goldfish_device dev;
- // pointers to our two write buffers
- uint32_t buffer_1, buffer_2;
- uint32_t read_buffer;
// buffer flags
uint32_t int_status;
// irq enable mask for int_status
uint32_t int_enable;
-#if USE_QEMU_AUDIO_IN
- uint32_t read_pos;
- uint32_t read_size;
-#else
+#ifndef USE_QEMU_AUDIO_IN
+ // address of the read buffer
+ uint32_t read_buffer;
// path to file or device to use for input
const char* input_source;
// true if input is a wav file
@@ -94,11 +98,9 @@ struct goldfish_audio_state {
int current_buffer;
// current data to write
- uint8* data_1;
- uint32_t data_1_length;
- uint8* data_2;
- uint32_t data_2_length;
-
+ struct goldfish_audio_buff out_buff1[1];
+ struct goldfish_audio_buff out_buff2[1];
+ struct goldfish_audio_buff in_buff[1];
// for QEMU sound output
QEMUSoundCard card;
@@ -108,24 +110,132 @@ struct goldfish_audio_state {
#endif
};
+static void
+goldfish_audio_buff_init( struct goldfish_audio_buff* b )
+{
+ b->address = 0;
+ b->length = 0;
+ b->data = NULL;
+ b->capacity = 0;
+ b->offset = 0;
+}
+
+static void
+goldfish_audio_buff_reset( struct goldfish_audio_buff* b )
+{
+ b->offset = 0;
+ b->length = 0;
+}
+
+static uint32_t
+goldfish_audio_buff_length( struct goldfish_audio_buff* b )
+{
+ return b->length;
+}
+
+static void
+goldfish_audio_buff_ensure( struct goldfish_audio_buff* b, uint32_t size )
+{
+ if (b->capacity < size) {
+ b->data = qemu_realloc(b->data, size);
+ b->capacity = size;
+ }
+}
+
+static void
+goldfish_audio_buff_set_address( struct goldfish_audio_buff* b, uint32_t addr )
+{
+ b->address = addr;
+}
+
+static void
+goldfish_audio_buff_set_length( struct goldfish_audio_buff* b, uint32_t len )
+{
+ b->length = len;
+ b->offset = 0;
+ goldfish_audio_buff_ensure(b, len);
+}
+
+static void
+goldfish_audio_buff_read( struct goldfish_audio_buff* b )
+{
+ cpu_physical_memory_read(b->address, b->data, b->length);
+}
+
+static void
+goldfish_audio_buff_write( struct goldfish_audio_buff* b )
+{
+ cpu_physical_memory_write(b->address, b->data, b->length);
+}
+
+static int
+goldfish_audio_buff_send( struct goldfish_audio_buff* b, int free, struct goldfish_audio_state* s )
+{
+ int ret, write = b->length;
+
+ if (write > free)
+ write = free;
+
+ ret = AUD_write(s->voice, b->data + b->offset, write);
+ b->offset += ret;
+ b->length -= ret;
+ return ret;
+}
+
+static int
+goldfish_audio_buff_available( struct goldfish_audio_buff* b )
+{
+ return b->length - b->offset;
+}
+
+static int
+goldfish_audio_buff_recv( struct goldfish_audio_buff* b, int avail, struct goldfish_audio_state* s )
+{
+ int missing = b->length - b->offset;
+ int avail2 = (avail > missing) ? missing : avail;
+ int read;
+
+ read = AUD_read(s->voicein, b->data + b->offset, avail2 );
+ if (read == 0)
+ return 0;
+
+ if (avail2 > 0)
+ D("%s: AUD_read(%d) returned %d", __FUNCTION__, avail2, read);
+
+ cpu_physical_memory_write( b->address + b->offset, b->data, read );
+ b->offset += read;
+
+ return read;
+}
+
+static void
+goldfish_audio_buff_put( struct goldfish_audio_buff* b, QEMUFile* f )
+{
+ qemu_put_be32(f, b->address );
+ qemu_put_be32(f, b->length );
+ qemu_put_be32(f, b->offset );
+ qemu_put_buffer(f, b->data, b->length );
+}
+
+static int
+goldfish_audio_buff_get( struct goldfish_audio_buff* b, QEMUFile* f )
+{
+ b->address = qemu_get_be32(f);
+ b->length = qemu_get_be32(f);
+ b->offset = qemu_get_be32(f);
+ goldfish_audio_buff_ensure(b, b->length);
+ qemu_get_buffer(f, b->data, b->length);
+}
+
/* update this whenever you change the goldfish_audio_state structure */
-#define AUDIO_STATE_SAVE_VERSION 1
+#define AUDIO_STATE_SAVE_VERSION 2
#define QFIELD_STRUCT struct goldfish_audio_state
QFIELD_BEGIN(audio_state_fields)
- QFIELD_INT32(buffer_1),
- QFIELD_INT32(buffer_2),
- QFIELD_INT32(read_buffer),
QFIELD_INT32(int_status),
QFIELD_INT32(int_enable),
-#if USE_QEMU_AUDIO_IN
- QFIELD_INT32(read_pos),
- QFIELD_INT32(read_size),
-#endif
QFIELD_INT32(read_buffer_available),
QFIELD_INT32(current_buffer),
- QFIELD_INT32(data_1_length),
- QFIELD_INT32(data_2_length),
QFIELD_END
static void audio_state_save( QEMUFile* f, void* opaque )
@@ -134,9 +244,9 @@ static void audio_state_save( QEMUFile* f, void* opaque )
qemu_put_struct(f, audio_state_fields, s);
- /* we can't write data_1 and data_2 directly */
- qemu_put_be32( f, s->data_1 - phys_ram_base );
- qemu_put_be32( f, s->data_2 - phys_ram_base );
+ goldfish_audio_buff_put (s->out_buff1, f);
+ goldfish_audio_buff_put (s->out_buff2, f);
+ goldfish_audio_buff_put (s->in_buff, f);
}
static int audio_state_load( QEMUFile* f, void* opaque, int version_id )
@@ -149,8 +259,9 @@ static int audio_state_load( QEMUFile* f, void* opaque, int version_id )
ret = qemu_get_struct(f, audio_state_fields, s);
if (!ret) {
- s->data_1 = qemu_get_be32(f) + phys_ram_base;
- s->data_2 = qemu_get_be32(f) + phys_ram_base;
+ goldfish_audio_buff_get( s->out_buff1, f );
+ goldfish_audio_buff_get( s->out_buff2, f );
+ goldfish_audio_buff_get (s->in_buff, f);
}
return -1;
}
@@ -158,25 +269,25 @@ static int audio_state_load( QEMUFile* f, void* opaque, int version_id )
static void enable_audio(struct goldfish_audio_state *s, int enable)
{
// enable or disable the output voice
- if (s->voice != NULL)
+ if (s->voice != NULL) {
AUD_set_active_out(s->voice, (enable & (AUDIO_INT_WRITE_BUFFER_1_EMPTY | AUDIO_INT_WRITE_BUFFER_2_EMPTY)) != 0);
+ goldfish_audio_buff_reset( s->out_buff1 );
+ goldfish_audio_buff_reset( s->out_buff2 );
+ }
- if (s->voicein)
+ if (s->voicein) {
AUD_set_active_in (s->voicein, (enable & AUDIO_INT_READ_BUFFER_FULL) != 0);
- // reset buffer information
- s->data_1_length = 0;
- s->data_2_length = 0;
+ goldfish_audio_buff_reset( s->in_buff );
+ }
s->current_buffer = 0;
- s->read_pos = 0;
}
#if USE_QEMU_AUDIO_IN
static void start_read(struct goldfish_audio_state *s, uint32_t count)
{
//printf( "... goldfish audio start_read, count=%d\n", count );
- s->read_size = count;
- s->read_buffer_available = 0;
- s->read_pos = 0;
+ goldfish_audio_buff_set_length( s->in_buff, count );
+ s->read_buffer_available = count;
}
#else
static void start_read(struct goldfish_audio_state *s, uint32_t count)
@@ -255,7 +366,6 @@ static uint32_t goldfish_audio_read(void *opaque, target_phys_addr_t offset)
{
uint32_t ret;
struct goldfish_audio_state *s = opaque;
- offset -= s->dev.base;
switch(offset) {
case AUDIO_INT_STATUS:
// return current buffer status flags
@@ -277,6 +387,7 @@ static uint32_t goldfish_audio_read(void *opaque, target_phys_addr_t offset)
case AUDIO_READ_BUFFER_AVAILABLE:
D("%s: AUDIO_READ_BUFFER_AVAILABLE returns %d", __FUNCTION__,
s->read_buffer_available);
+ goldfish_audio_buff_write( s->in_buff );
return s->read_buffer_available;
default:
@@ -288,7 +399,6 @@ static uint32_t goldfish_audio_read(void *opaque, target_phys_addr_t offset)
static void goldfish_audio_write(void *opaque, target_phys_addr_t offset, uint32_t val)
{
struct goldfish_audio_state *s = opaque;
- offset -= s->dev.base;
switch(offset) {
case AUDIO_INT_ENABLE:
@@ -301,30 +411,34 @@ static void goldfish_audio_write(void *opaque, target_phys_addr_t offset, uint32
break;
case AUDIO_SET_WRITE_BUFFER_1:
/* save pointer to buffer 1 */
- s->buffer_1 = val;
+ D( "%s: AUDIO_SET_WRITE_BUFFER_1 %08x", __FUNCTION__, val);
+ goldfish_audio_buff_set_address( s->out_buff1, val );
break;
case AUDIO_SET_WRITE_BUFFER_2:
/* save pointer to buffer 2 */
- s->buffer_2 = val;
+ D( "%s: AUDIO_SET_WRITE_BUFFER_2 %08x", __FUNCTION__, val);
+ goldfish_audio_buff_set_address( s->out_buff2, val );
break;
case AUDIO_WRITE_BUFFER_1:
/* record that data in buffer 1 is ready to write */
+ //D( "%s: AUDIO_WRITE_BUFFER_1 %08x", __FUNCTION__, val);
if (s->current_buffer == 0) s->current_buffer = 1;
- s->data_1 = phys_ram_base + s->buffer_1;
- s->data_1_length = val;
+ goldfish_audio_buff_set_length( s->out_buff1, val );
+ goldfish_audio_buff_read( s->out_buff1 );
s->int_status &= ~AUDIO_INT_WRITE_BUFFER_1_EMPTY;
break;
case AUDIO_WRITE_BUFFER_2:
/* record that data in buffer 2 is ready to write */
+ //D( "%s: AUDIO_WRITE_BUFFER_2 %08x", __FUNCTION__, val);
if (s->current_buffer == 0) s->current_buffer = 2;
- s->data_2 = phys_ram_base + s->buffer_2;
- s->data_2_length = val;
+ goldfish_audio_buff_set_length( s->out_buff2, val );
+ goldfish_audio_buff_read( s->out_buff2 );
s->int_status &= ~AUDIO_INT_WRITE_BUFFER_2_EMPTY;
break;
- case AUDIO_SET_READ_BUFFER:
+ case AUDIO_SET_READ_BUFFER:
/* save pointer to the read buffer */
- s->read_buffer = val;
+ goldfish_audio_buff_set_address( s->in_buff, val );
D( "%s: AUDIO_SET_READ_BUFFER %p", __FUNCTION__, (void*)val );
break;
@@ -350,19 +464,14 @@ static void goldfish_audio_callback(void *opaque, int free)
/* write data in buffer 1 */
while (free && s->current_buffer == 1) {
- int write = s->data_1_length;
- if (write > free) write = free;
-
- int written = AUD_write(s->voice, s->data_1, write);
+ int written = goldfish_audio_buff_send( s->out_buff1, free, s );
if (written) {
- D("%s: sent %d bytes to audio output", __FUNCTION__, write);
- s->data_1 += written;
- s->data_1_length -= written;
+ D("%s: sent %5d bytes to audio output (buffer 1)", __FUNCTION__, written);
free -= written;
- if (s->data_1_length == 0) {
+ if (goldfish_audio_buff_length( s->out_buff1 ) == 0) {
new_status |= AUDIO_INT_WRITE_BUFFER_1_EMPTY;
- s->current_buffer = (s->data_2_length ? 2 : 0);
+ s->current_buffer = (goldfish_audio_buff_length( s->out_buff2 ) ? 2 : 0);
}
} else {
break;
@@ -371,19 +480,14 @@ static void goldfish_audio_callback(void *opaque, int free)
/* write data in buffer 2 */
while (free && s->current_buffer == 2) {
- int write = s->data_2_length;
- if (write > free) write = free;
-
- int written = AUD_write(s->voice, s->data_2, write);
+ int written = goldfish_audio_buff_send( s->out_buff2, free, s );
if (written) {
- D("%s: sent %d bytes to audio output", __FUNCTION__, write);
- s->data_2 += written;
- s->data_2_length -= written;
+ D("%s: sent %5d bytes to audio output (buffer 2)", __FUNCTION__, written);
free -= written;
- if (s->data_2_length == 0) {
+ if (goldfish_audio_buff_length( s->out_buff2 ) == 0) {
new_status |= AUDIO_INT_WRITE_BUFFER_2_EMPTY;
- s->current_buffer = (s->data_1_length ? 1 : 0);
+ s->current_buffer = (goldfish_audio_buff_length( s->out_buff1 ) ? 1 : 0);
}
} else {
break;
@@ -404,36 +508,22 @@ goldfish_audio_in_callback(void *opaque, int avail)
struct goldfish_audio_state *s = opaque;
int new_status = 0;
- if (s->read_pos >= s->read_size)
+ if (goldfish_audio_buff_available( s->in_buff ) == 0 )
return;
- if (0 && s->read_size > 0)
- D("%s: in %d (pos=%d size=%d)", __FUNCTION__,
- avail, s->read_pos, s->read_size );
-
while (avail > 0) {
- int pos = s->read_pos;
- int missing = s->read_size - pos;
- uint8* buffer = (uint8*)phys_ram_base + s->read_buffer + pos;
- int read;
- int avail2 = (avail > missing) ? missing : avail;
-
- read = AUD_read(s->voicein, buffer, avail2);
+ int read = goldfish_audio_buff_recv( s->in_buff, avail, s );
if (read == 0)
break;
- if (avail2 > 0)
- D("%s: AUD_read(%d) returned %d", __FUNCTION__, avail2, read);
-
- s->read_buffer_available += read;
-
avail -= read;
- pos += read;
- if (pos == s->read_size) {
+
+ if (goldfish_audio_buff_available( s->in_buff) == 0) {
new_status |= AUDIO_INT_READ_BUFFER_FULL;
- D("%s: AUDIO_INT_READ_BUFFER_FULL available=%d", __FUNCTION__, s->read_buffer_available);
+ D("%s: AUDIO_INT_READ_BUFFER_FULL available=%d",
+ __FUNCTION__, goldfish_audio_buff_length( s->in_buff ));
+ break;
}
- s->read_pos = pos;
}
if (new_status && new_status != s->int_status) {
@@ -458,7 +548,7 @@ static CPUWriteMemoryFunc *goldfish_audio_writefn[] = {
void goldfish_audio_init(uint32_t base, int id, const char* input_source)
{
struct goldfish_audio_state *s;
- audsettings_t as;
+ struct audsettings as;
/* nothing to do if no audio input and output */
if (!android_hw->hw_audioOutput && !android_hw->hw_audioInput)
@@ -482,7 +572,7 @@ void goldfish_audio_init(uint32_t base, int id, const char* input_source)
}
#endif
- AUD_register_card( &glob_audio_state, "goldfish_audio", &s->card);
+ AUD_register_card( "goldfish_audio", &s->card);
as.freq = 44100;
as.nchannels = 2;
@@ -525,6 +615,10 @@ void goldfish_audio_init(uint32_t base, int id, const char* input_source)
}
#endif
+ goldfish_audio_buff_init( s->out_buff1 );
+ goldfish_audio_buff_init( s->out_buff2 );
+ goldfish_audio_buff_init( s->in_buff );
+
goldfish_device_add(&s->dev, goldfish_audio_readfn, goldfish_audio_writefn, s);
register_savevm( "audio_state", 0, AUDIO_STATE_SAVE_VERSION,