Permalink
Browse files

Merge pull request #3 from rainx86/master

New browser key and improvements to the CoreAudio audio ouput driver
  • Loading branch information...
2 parents 73d3b1b + 7a20d75 commit c6c1717eb9ea7766a68957e91bcd5ad5e7094a28 @EdSchouten committed Aug 25, 2015
Showing with 201 additions and 20 deletions.
  1. +10 −1 TODO
  2. +1 −1 herrie/README
  3. +7 −0 herrie/man/00-man
  4. +0 −4 herrie/man/02-man
  5. +170 −13 herrie/src/audio_output_coreaudio.c
  6. +7 −0 herrie/src/gui_browser.c
  7. +2 −1 herrie/src/gui_input.c
  8. +4 −0 herrie/src/gui_internal.h
View
@@ -16,7 +16,7 @@ Graphical user interface:
- Allow randomized playback in XMMS mode without shuffling the list
Audio abstraction:
-- Implement better buffering schemes for ALSA and CoreAudio
+- Implement better buffering scheme for ALSA
- Fully implement more audio file formats
- Apple Lossless
- WMA
@@ -41,3 +41,12 @@ Virtual filesystem:
Documentation:
- Usage guide
- Write a developers guide?
+
+CoreAudio (OS X) bugs:
+- Audio device properties cannot be set once the data source changes
+(e.g. if headphones are plugged in/out, the volume cannot be changed with
+herrie's controls anymore).
+Solution: ???
+- Gaps can sometimes be heard when music is playing.
+Solution: implement a better buffering scheme (maybe by using a queue with a
+few buffers?).
View
@@ -54,7 +54,7 @@ Herrie:
- dbus and dbus-glib (optional)
- gettext (NLS, optional)
-- glib (requires 2.10)
+- glib (requires 2.32)
- libasound (ALSA, optional)
- libao (AO, optional)
- libcurl (HTTP and AudioScrobbler, optional)
View
@@ -132,6 +132,10 @@ Remove all songs from the playlist.
.B R
Randomize the playlist.
.TP
+.B x
+When in XMMS mode, it starts the selected song. In party mode, this key
+will always start playback of the first song in the list.
+.TP
.B [
Move the currently selected song upward.
.TP
@@ -179,5 +183,8 @@ list. To remove an existing filter, go one directory up.
Change the current directory by entering a pathname. This pathname may
be relative to the current directory. When the address refers to a file
or web location, it will be displayed as well.
+.TP
+.B x
+Replace the contents of the playlist with the current selection.
.PP
And last but not least, there are also some general keyboard bindings:
View
@@ -35,10 +35,6 @@ Stop playback.
.B w
Write the current playlist to a playlist file.
.TP
-.B x
-When in XMMS mode, it starts the selected song. In party mode, this key
-will always start playback of the first song in the list.
-.TP
.B z
Go to the previous song.
.TP
@@ -88,12 +88,17 @@ int abufulen = 0;
* @brief A not really used mutex that is used for the conditional
* variable.
*/
-GMutex *abuflock;
+GMutex abuflock;
/**
* @brief The conditional variable that is used to inform the
* application that a buffer has been processed.
*/
-GCond *abufdrained;
+GCond abufdrained;
+/**
+ * @brief The data source currently being used by the CoreAudio device
+ * (headphones or speakers).
+ */
+UInt32 adscur;
#ifdef BUILD_VOLUME
/**
* @brief Preferred audio channels used for audio playback. We use it to
@@ -126,7 +131,7 @@ audio_output_ioproc(AudioDeviceID inDevice, const AudioTimeStamp *inNow,
/* Empty the buffer and notify that we can receive new data */
g_atomic_int_set(&abufulen, 0);
- g_cond_signal(abufdrained);
+ g_cond_signal(&abufdrained);
/* Fill the trailer with zero's */
for (; i < abuflen; i++)
@@ -135,23 +140,74 @@ audio_output_ioproc(AudioDeviceID inDevice, const AudioTimeStamp *inNow,
return (0);
}
+/**
+ * @brief Get the data source of the current CoreAudio audio device.
+ */
+static int
+audio_output_get_datasource(UInt32 *ds)
+{
+ UInt32 size;
+
+ size = sizeof *ds;
+#ifdef MAC_OS_X_VERSION_10_5
+ AudioObjectPropertyAddress address;
+
+ address = (AudioObjectPropertyAddress) {
+ kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ if (AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ ds) != 0)
+#else /* !MAC_OS_X_VERSION_10_5 */
+ if (AudioDeviceGetProperty(adid, 0, false,
+ kAudioDevicePropertyDataSource, &size, ds) != 0)
+#endif /* MAC_OS_X_VERSION_10_5 */
+ return (-1);
+
+ return (0);
+}
+
int
audio_output_open(void)
{
UInt32 size;
/* Obtain the audio device ID */
size = sizeof adid;
+#ifdef MAC_OS_X_VERSION_10_5
+ AudioObjectPropertyAddress address;
+
+ address = (AudioObjectPropertyAddress) {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
+
+ if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &address, 0, NULL, &size, &adid) != 0 ||
+ adid == kAudioDeviceUnknown)
+#else /* !MAC_OS_X_VERSION_10_5 */
if (AudioHardwareGetProperty(
kAudioHardwarePropertyDefaultOutputDevice,
&size, &adid) != 0 || adid == kAudioDeviceUnknown)
+#endif /* MAC_OS_X_VERSION_10_5 */
goto error;
/* Adjust the stream format */
size = sizeof afmt;
+#ifdef MAC_OS_X_VERSION_10_5
+ address.mSelector = kAudioDevicePropertyStreamFormat;
+ address.mScope = kAudioDevicePropertyScopeOutput;
+
+ if (AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ &afmt) != 0 || afmt.mFormatID != kAudioFormatLinearPCM)
+#else /* !MAC_OS_X_VERSION_10_5 */
if (AudioDeviceGetProperty(adid, 0, false,
kAudioDevicePropertyStreamFormat, &size, &afmt) != 0 ||
afmt.mFormatID != kAudioFormatLinearPCM)
+#endif /* MAC_OS_X_VERSION_10_5 */
goto error;
/* To be set on the first run */
@@ -163,17 +219,35 @@ audio_output_open(void)
/* Decide what buffer size to use */
size = sizeof abuflen;
abuflen = 32768;
+#ifdef MAC_OS_X_VERSION_10_5
+ address.mSelector = kAudioDevicePropertyBufferSize;
+
+ AudioObjectSetPropertyData(adid, &address, 0, NULL, size, &abuflen);
+ if (AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ &abuflen) != 0)
+#else /* !MAC_OS_X_VERSION_10_5 */
AudioDeviceSetProperty(adid, 0, 0, false,
kAudioDevicePropertyBufferSize, size, &abuflen);
if (AudioDeviceGetProperty(adid, 0, false,
kAudioDevicePropertyBufferSize, &size, &abuflen) != 0)
+#endif /* MAC_OS_X_VERSION_10_5 */
+ goto error;
+
+ /* Store the current data source */
+ if (audio_output_get_datasource(&adscur) != 0)
goto error;
#ifdef BUILD_VOLUME
/* Store the audio channels */
size = sizeof achans;
+#ifdef MAC_OS_X_VERSION_10_5
+ address.mSelector = kAudioDevicePropertyPreferredChannelsForStereo;
+
+ AudioObjectGetPropertyData(adid, &address, 0, NULL, &size, &achans);
+#else /* !MAC_OS_X_VERSION_10_5 */
AudioDeviceGetProperty(adid, 0, false,
kAudioDevicePropertyPreferredChannelsForStereo, &size, &achans);
+#endif /* MAC_OS_X_VERSION_10_5 */
#endif /* BUILD_VOLUME */
/* The buffer size reported is in floats */
@@ -182,8 +256,8 @@ audio_output_open(void)
abufcur = g_malloc(abuflen * sizeof(int16_t));
/* Locking down the buffer length */
- abuflock = g_mutex_new();
- abufdrained = g_cond_new();
+ g_mutex_init(&abuflock);
+ g_cond_init(&abufdrained);
/* Add our own I/O handling routine */
#ifdef MAC_OS_X_VERSION_10_5
@@ -203,7 +277,7 @@ audio_output_open(void)
int
audio_output_play(struct audio_file *fd)
{
- UInt32 len, size;
+ UInt32 len, size, adsnew;
int16_t *tmp;
/* Read data in our temporary buffer */
@@ -216,23 +290,39 @@ audio_output_play(struct audio_file *fd)
afmt.mSampleRate = fd->srate;
afmt.mChannelsPerFrame = fd->channels;
- if (AudioDeviceSetProperty(adid, 0, 0, 0,
- kAudioDevicePropertyStreamFormat, sizeof afmt, &afmt) != 0) {
+ size = sizeof afmt;
+#ifdef MAC_OS_X_VERSION_10_5
+ AudioObjectPropertyAddress address;
+
+ address = (AudioObjectPropertyAddress) {
+ kAudioDevicePropertyStreamFormat,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ if (AudioObjectSetPropertyData(adid, &address, 0, NULL, size,
+ &afmt) != 0) {
+ /* Get current settings back */
+ AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ &afmt);
+#else /* !MAC_OS_X_VERSION_10_5 */
+ if (AudioDeviceSetProperty(adid, 0, 0, false,
+ kAudioDevicePropertyStreamFormat, size, &afmt) != 0) {
/* Get current settings back */
- size = sizeof afmt;
AudioDeviceGetProperty(adid, 0, false,
kAudioDevicePropertyStreamFormat, &size, &afmt);
+#endif /* MAC_OS_X_VERSION_10_5 */
gui_msgbar_warn(_("Sample rate or amount of channels not supported."));
return (-1);
}
}
/* XXX: Mutex not actually needed - only for the condvar */
- g_mutex_lock(abuflock);
+ g_mutex_lock(&abuflock);
while (g_atomic_int_get(&abufulen) != 0)
- g_cond_wait(abufdrained, abuflock);
- g_mutex_unlock(abuflock);
+ g_cond_wait(&abufdrained, &abuflock);
+ g_mutex_unlock(&abuflock);
/* Toggle the buffers */
tmp = abufcur;
@@ -242,6 +332,16 @@ audio_output_play(struct audio_file *fd)
/* Atomically set the usage length */
g_atomic_int_set(&abufulen, len);
+ /* Check if the data source changed */
+ if (audio_output_get_datasource(&adsnew) != 0)
+ return (-1);
+
+ if (adscur != adsnew) {
+ /* Restart the device */
+ AudioDeviceStop(adid, aprocid);
+ adscur = adsnew;
+ }
+
/* Start processing of the data */
AudioDeviceStart(adid, aprocid);
@@ -258,6 +358,9 @@ audio_output_close(void)
AudioDeviceRemoveIOProc(adid, aprocid);
#endif /* MAC_OS_X_VERSION_10_5 */
+ g_mutex_clear(&abuflock);
+ g_cond_clear(&abufdrained);
+
g_free(abufnew);
g_free(abufcur);
}
@@ -271,26 +374,80 @@ static int
audio_output_volume_adjust(Float32 n)
{
Float32 vl, vr, vn;
- UInt32 size = sizeof vl;
+ UInt32 size;
+ UInt32 mute;
/*
* Merge left and right. On Mac OS X, we want to do this. My
* PowerBook has this odd bug that the built-in OS X volume
* applet makes the sound card go unbalanced after a long amount
* of time. We can prevent that over here...
*/
+ size = sizeof vl;
+#ifdef MAC_OS_X_VERSION_10_5
+ AudioObjectPropertyAddress address;
+ OSStatus vlstatus, vrstatus;
+
+ address = (AudioObjectPropertyAddress) {
+ kAudioDevicePropertyVolumeScalar,
+ kAudioDevicePropertyScopeOutput,
+ achans[0]
+ };
+ vlstatus = AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ &vl);
+
+ address.mElement = achans[1];
+ vrstatus = AudioObjectGetPropertyData(adid, &address, 0, NULL, &size,
+ &vr);
+
+ if (vlstatus != 0 || vrstatus != 0)
+#else /* !MAC_OS_X_VERSION_10_5 */
if (AudioDeviceGetProperty(adid, achans[0], false,
kAudioDevicePropertyVolumeScalar, &size, &vl) != 0 ||
AudioDeviceGetProperty(adid, achans[1], false,
kAudioDevicePropertyVolumeScalar, &size, &vr) != 0)
+#endif /* MAC_OS_X_VERSION_10_5 */
return (-1);
vn = CLAMP((vl + vr) / 2.0 + n, 0.0, 1.0);
+ /* Mute the audio device if the volume is at the minimum. */
+ if (vn == 0.0)
+ mute = 1;
+ else
+ mute = 0;
+
+ size = sizeof mute;
+#ifdef MAC_OS_X_VERSION_10_5
+ address.mSelector = kAudioDevicePropertyMute;
+ address.mElement = kAudioObjectPropertyElementMaster;
+
+ if (AudioObjectSetPropertyData(adid, &address, 0, NULL, size,
+ &mute) != 0)
+#else /* !MAC_OS_X_VERSION_10_5 */
+ if (AudioDeviceSetProperty(adid, 0, 0, false,
+ kAudioDevicePropertyMute, size, &mute) != 0)
+#endif /* MAC_OS_X_VERSION_10_5 */
+ return (-1);
+
/* Set the new volume */
+ size = sizeof vn;
+#ifdef MAC_OS_X_VERSION_10_5
+ address.mSelector = kAudioDevicePropertyVolumeScalar;
+ address.mElement = achans[0];
+ vlstatus = AudioObjectSetPropertyData(adid, &address, 0, NULL, size,
+ &vn);
+
+ address.mElement = achans[1];
+ vrstatus = AudioObjectSetPropertyData(adid, &address, 0, NULL, size,
+ &vn);
+
+ if (vlstatus != 0 || vrstatus != 0)
+#else /* !MAC_OS_X_VERSION_10_5 */
if (AudioDeviceSetProperty(adid, 0, achans[0], false,
kAudioDevicePropertyVolumeScalar, size, &vn) != 0 ||
AudioDeviceSetProperty(adid, 0, achans[1], false,
kAudioDevicePropertyVolumeScalar, size, &vn) != 0)
+#endif /* MAC_OS_X_VERSION_10_5 */
return (-1);
return (vn * 100.0);
@@ -351,6 +351,13 @@ gui_browser_playq_add_before(void)
gui_vfslist_cursor_down(win_browser, 1);
}
+void
+gui_browser_playq_replace(void)
+{
+ gui_playq_song_remove_all();
+ gui_browser_playq_add_tail();
+}
+
int
gui_browser_search(const struct vfsmatch *vm, int reverse)
{
Oops, something went wrong.

0 comments on commit c6c1717

Please sign in to comment.