# This patch makes it possible to delay either channel. I made this when # I was working on getting MoC into my car which sadly never happened. # Anyways, in a car you sit closer to one speaker than the other and # it isn't just a matter of balancing the sound level as you normally # do. You also need to adjust the delay so the audio hits your ears # from both channels at the same time. This affects where the sound # seems to be coming from and the overall sound image. # For different reasons this never made it into the official MoC but # maybe someone will find it cool and take up the project. # diff -ru moc-2.4.0/audio.c moc-2.4.0-boost/audio.c --- moc-2.4.0/audio.c 2006-03-12 01:36:37.000000000 +0100 +++ moc-2.4.0-boost/audio.c 2006-03-12 13:17:53.000000000 +0100 @@ -102,6 +102,13 @@ #define sample_rate_compat(sound, device) ((device) * 1.05 >= sound \ && (device) * 0.95 <= sound) +#define AUDIO_DELAY_MAX 200 +/* Left channel delay in samples, negative values allowed */ +static int audio_delay = 0; +static struct fifo_buf audio_delay_fifo = { NULL, 0, 0, 0 }; +static char *audio_delay_buf = NULL; +static size_t audio_delay_buf_size = (32 * 1024); + /* Make a human readable description of the sound sample format(s). * Put the description in msg which is of size buf_size. * Return msg. */ @@ -144,6 +151,7 @@ return msg; } + /* Return != 0 if fmt1 and fmt2 have the same sample width. */ int sfmt_same_bps (const long fmt1, const long fmt2) { @@ -706,7 +714,10 @@ driver_sound_params.channels, driver_sound_params.rate); } - + + /* Init audio delay buffer */ + fifo_buf_init (&audio_delay_fifo, sfmt_Bps (driver_sound_params.fmt) * AUDIO_DELAY_MAX / driver_sound_params.channels ); + audio_delay_buf = xmalloc (audio_delay_buf_size); return res; } @@ -746,11 +757,76 @@ return hw.get_buff_fill (); } +static void process_delay (const char *buf, const size_t size) +{ + size_t pos; + /* The delay, always positive */ + int abs_delay = audio_delay >= 0 ?audio_delay : -audio_delay; + size_t sample_size = sfmt_Bps (driver_sound_params.fmt); + /* Delay this many bytes */ + int delay_samples = abs_delay * sample_size; + /* How many samples to process */ + size_t samples = size / sample_size; + /* Delay left or right? */ + int delay_left = audio_delay >= 0; + + assert(size>0); + + /* If we have too small buffer, increase it to double the size of 'size' */ + if (size > audio_delay_buf_size) + { + audio_delay_buf_size = size * 2; + audio_delay_buf = xrealloc (audio_delay_buf, audio_delay_buf_size); + } + + /* We can only handle two channels. If it's not we just copy the inbuffer */ + if (driver_sound_params.channels != 2) + { + memcpy (audio_delay_buf, buf, size); + return; + } + + /* If we have more delay than what is in the buffer we fill with zeroes */ + if( audio_delay_fifo.fill < delay_samples ) + { + char *tmpbuf = (char *) xcalloc (1, delay_samples-audio_delay_fifo.fill); + fifo_buf_put (&audio_delay_fifo, tmpbuf, delay_samples-audio_delay_fifo.fill ); + free (tmpbuf); + } + /* If we have less delay than what is in the buffer we throw away the difference */ + else if( audio_delay_fifo.fill > delay_samples ) + { + char *tmpbuf = (char *) xmalloc (audio_delay_fifo.fill-delay_samples); + fifo_buf_get (&audio_delay_fifo, tmpbuf, audio_delay_fifo.fill-delay_samples); + free (tmpbuf); + } + + /* Copy each sample */ + for (pos=0; pos < samples; pos += 2) + { + if (delay_left) + { + /* Put one in and take one out of the end */ + fifo_buf_put (&audio_delay_fifo, buf+(pos*sample_size), sample_size); + fifo_buf_get (&audio_delay_fifo, audio_delay_buf+(pos*sample_size), sample_size); + memcpy (audio_delay_buf + ( (pos + 1) * sample_size), buf + ( (pos + 1) * sample_size), sample_size); + } + else + { + memcpy (audio_delay_buf + (pos * sample_size), buf + (pos * sample_size), sample_size); + fifo_buf_put (&audio_delay_fifo, buf + ( (pos + 1) * sample_size), sample_size); + fifo_buf_get (&audio_delay_fifo, audio_delay_buf + ( (pos + 1) * sample_size), sample_size); + } + } +} + int audio_send_pcm (const char *buf, const size_t size) { int played; + + process_delay (buf, size), - played = hw.play (buf, size); + played = hw.play (audio_delay_buf, size); if (played == 0) fatal ("Audio output error."); @@ -774,6 +850,10 @@ audio_conv_destroy (&sound_conv); need_audio_conversion = 0; } + + free (audio_delay_buf); + fifo_buf_destroy( &audio_delay_fifo ); + audio_opened = 0; } } @@ -947,6 +1027,37 @@ logit ("Tried to set mixer to volume out of range."); } +/* Set the amount of delay in samples between channels. + * Positivei values delays left, negative values delays right */ +void audio_set_delay (const int val) +{ + if (val >= -AUDIO_DELAY_MAX && val <= AUDIO_DELAY_MAX) + { + audio_delay=val; + } + else + logit ("Tried to set delay to value out of range."); +} + +int audio_get_delay () +{ + return audio_delay; +} + +/* Returns the delay in milimetres */ +int audio_get_mm () +{ + int mm; + /* Velocity of sound can be calculated roughly with the formula + * c = 331.5 + (0.6 * temperature in degrees Celsius). + * In 22 degrees that's 334.7. We use fixed point calculation for this + */ + mm = 3347000 * audio_delay / driver_sound_params.rate; + if (audio_delay >= 0) mm += 5; + else mm -= 5; + return mm / 10; /* One decimal precision */ +} + void audio_plist_delete (const char *file) { int num; diff -ru moc-2.4.0/audio.h moc-2.4.0-boost/audio.h --- moc-2.4.0/audio.h 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/audio.h 2006-03-12 01:19:15.000000000 +0100 @@ -250,5 +250,7 @@ char *audio_get_mixer_channel_name (); void audio_toggle_mixer_channel (); void audio_plist_move (const char *file1, const char *file2); - +void audio_set_delay (const int val); +int audio_get_delay (); +int audio_get_mm (); #endif Endast i moc-2.4.0-boost: gdb.cmds diff -ru moc-2.4.0/interface.c moc-2.4.0-boost/interface.c --- moc-2.4.0/interface.c 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/interface.c 2006-03-12 01:23:03.000000000 +0100 @@ -1613,6 +1613,36 @@ set_mixer (get_mixer_value() + diff); } +static int get_delay_value () +{ + send_int_to_srv (CMD_GET_DELAY); + return get_data_int (); +} + +static int get_delay_mm () +{ + send_int_to_srv (CMD_GET_MM); + return get_data_int (); +} + +static void set_delay (int val) +{ + if (val < -200) + val = -200; + else if (val > 200) + val = 200; + + send_int_to_srv (CMD_SET_DELAY); + send_int_to_srv (val); + + interface_message ("Differance between left and right: %d mm", get_delay_mm ()); +} + +static void adjust_delay (const int diff) +{ + set_delay (get_delay_value() + diff); +} + /* Add the currently selected file to the playlist. */ static void add_file_plist () { @@ -3151,6 +3181,12 @@ case KEY_CMD_EXEC10: exec_custom_command ("ExecCommand10"); break; + case KEY_CMD_DELAY_LEFT: + adjust_delay(1); + break; + case KEY_CMD_DELAY_RIGHT: + adjust_delay(-1); + break; default: abort (); } diff -ru moc-2.4.0/keys.c moc-2.4.0-boost/keys.c --- moc-2.4.0/keys.c 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/keys.c 2006-03-11 23:49:36.000000000 +0100 @@ -719,7 +719,23 @@ CON_MENU, { KEY_F(10), -1 }, 1 - } + }, + { + KEY_CMD_DELAY_LEFT, + "delay_left", + "Move audio position to the left", + CON_MENU, + { 'O', -1 }, + 1 + }, + { + KEY_CMD_DELAY_RIGHT, + "delay_right", + "Move audio position to the right", + CON_MENU, + { 'P', -1 }, + 1 + } }; static struct special_keys diff -ru moc-2.4.0/keys.h moc-2.4.0-boost/keys.h --- moc-2.4.0/keys.h 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/keys.h 2006-03-11 23:49:36.000000000 +0100 @@ -86,6 +86,8 @@ KEY_CMD_EXEC8, KEY_CMD_EXEC9, KEY_CMD_EXEC10, + KEY_CMD_DELAY_LEFT, + KEY_CMD_DELAY_RIGHT, KEY_CMD_WRONG }; Endast i moc-2.4.0-boost: moc-2.4.0.tar.bz2 Endast i moc-2.4.0: moc.spec diff -ru moc-2.4.0/protocol.h moc-2.4.0-boost/protocol.h --- moc-2.4.0/protocol.h 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/protocol.h 2006-03-12 01:19:53.000000000 +0100 @@ -119,6 +119,10 @@ #define CMD_CLI_PLIST_MOVE 0x31 /* move an item */ #define CMD_LIST_MOVE 0x32 /* move an item */ +#define CMD_GET_DELAY 0x033 /* Get delay value */ +#define CMD_SET_DELAY 0x034 /* Set delay value */ +#define CMD_GET_MM 0x035 /* Get delay value in millimeters */ + char *socket_name (); int get_int (int sock, int *i); enum noblock_io_status get_int_noblock (int sock, int *i); diff -ru moc-2.4.0/server.c moc-2.4.0-boost/server.c --- moc-2.4.0/server.c 2006-02-12 12:55:10.000000000 +0100 +++ moc-2.4.0-boost/server.c 2006-03-12 14:05:23.000000000 +0100 @@ -280,6 +280,38 @@ fatal ("dup2() failed: %s", strerror(errno)); } +static void audio_delay_load (char *filename) +{ + FILE *file=NULL; + int delay=0; + + asert (filename!=NULL); + asert (strlen(filename)>0); + + if ( (file=fopen (filename,"r")) ) + { + if (fscanf (file, "%d\n", &delay)>0) + { + audio_set_delay (delay); + } + fclose (fp); + } +} + +static void audio_delay_save (char *filename) +{ + FILE *file=NULL; + + asert (filename!=NULL); + asert (strlen(filename)>0); + + if ( (file=fopen (filename,"w")) ) + { + fprintf (file, "%d\n", audio_get_delay ()); + fclose (fp); + } +} + /* Initialize the server - return fd of the listening socket or -1 on error */ int server_init (int debug, int foreground) { @@ -327,6 +359,7 @@ audio_initialize (); tags_cache_init (&tags_cache, options_get_int("TagsCacheSize") * 1024); tags_cache_load (&tags_cache, create_file_name("tags_cache")); + audio_delay_load (create_file_name ("audio_delay")); clients_init (); server_tid = pthread_self (); @@ -446,6 +479,7 @@ { logit ("Server exiting..."); + audio_delay_save (create_file_name ("audio_delay")); audio_exit (); tags_cache_save (&tags_cache, create_file_name("tags_cache")); tags_cache_destroy (&tags_cache); @@ -604,6 +638,18 @@ return 1; } +/* Set the delay to the value provided by the client. Return 0 on error. */ +static int set_delay (struct client *cli) +{ + int val; + + if (!get_int(cli->socket, &val)) + return 0; + + audio_set_delay (val); + return 1; +} + /* Delete an item from the playlist. Return 0 on error. */ static int delete_item (struct client *cli) { @@ -1131,6 +1177,18 @@ if (!req_list_move(cli)) err = 1; break; + case CMD_GET_DELAY: + if (!send_data_int(cli, audio_get_delay())) + err = 1; + break; + case CMD_SET_DELAY: + if (!set_delay(cli)) + err = 1; + break; + case CMD_GET_MM: + if (!send_data_int(cli, audio_get_mm())) + err = 1; + break; default: logit ("Bad command (0x%x) from the client.", cmd); err = 1;