2010-09-03 Yavor Doganov Implement a threaded ALSA output bundle. * Bundles/ALSA/ALSA.h: * Bundles/ALSA/ALSA.m: * Bundles/ALSA/GNUmakefile: * Bundles/ALSA/GNUmakefile.preamble: New file. * GNUmakefile (BUNDLES): Conditionally add ALSA. * GeneralPreference.m: Make ALSA the default on GNU/Linux. (-[GeneralPreference _initDefaults]): Add migration code for existing users. --- cynthiune.app-0.9.5.orig/GNUmakefile +++ cynthiune.app-0.9.5/GNUmakefile @@ -94,6 +94,16 @@ else +# GNUSTEP_TARGET_OS is defined to `linux-gnueabi' on armel and +# `linux-gnuspe' on powerpcspe. +ifneq (,$(findstring linux-gnu,$(GNUSTEP_TARGET_OS))) + +ifneq (yes,$(disable-alsa)) +BUNDLES += ALSA +endif + +endif + ifneq (yes,$(disable-oss)) BUNDLES += OSS endif --- /dev/null +++ cynthiune.app-0.9.5/Bundles/ALSA/ALSA.h @@ -0,0 +1,40 @@ +/* ALSA.h - this file is part of Cynthiune -*-objc-*- + * + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * Author: Yavor Doganov + * + * Cynthiune is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * Cynthiune is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef ALSA_H +#define ALSA_H + address@hidden ALSA : NSObject +{ + id parentPlayer; + snd_pcm_t *pcm_handle; + + BOOL stopRequested; + + unsigned int channels; + unsigned long rate; + + unsigned char buffer[DEFAULT_BUFFER_SIZE]; +} address@hidden + +#endif /* ALSA_H */ --- /dev/null +++ cynthiune.app-0.9.5/Bundles/ALSA/ALSA.m @@ -0,0 +1,169 @@ +/* ALSA.m - this file is part of Cynthiune + * + * Copyright (C) 2010 Free Software Foundation, Inc. + * + * Author: Yavor Doganov + * + * Cynthiune is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * Cynthiune is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef _REENTRANT +#define _REENTRANT 1 +#endif + +#import + +#import +#import +#import + +#include + +#import "ALSA.h" + +#define LOCALIZED(X) _b ([ALSA class], X) + +static char *device = "default"; + address@hidden ALSA ++ (NSString *) bundleDescription +{ + return LOCALIZED (@"Output plug-in for ALSA"); +} + ++ (NSArray *) bundleCopyrightStrings +{ + return [NSArray arrayWithObjects: + LOCALIZED (@"Copyright (C) 2010 Free Software Foundation," + @" Inc."), + nil]; +} + ++ (BOOL) isThreaded +{ + return YES; +} + +- (void) setParentPlayer: (id) aPlayer; +{ + parentPlayer = aPlayer; +} + +- (id) init +{ + if ((self = [super init])) + { + parentPlayer = nil; + pcm_handle = NULL; + channels = 0; + rate = 0; + stopRequested = NO; + } + + return self; +} + +- (BOOL) openDevice +{ + int err; + BOOL result = NO; + + if ((err = snd_pcm_open (&pcm_handle, device, SND_PCM_STREAM_PLAYBACK, 0)) + < 0) + NSRunAlertPanel (LOCALIZED (@"Error"), + LOCALIZED (@"Failed to open the ALSA device:\n%s"), + LOCALIZED (@"OK"), NULL, NULL, snd_strerror (err)); + else if ((err = snd_pcm_set_params (pcm_handle, SND_PCM_FORMAT_S16, + SND_PCM_ACCESS_RW_INTERLEAVED, + channels, rate, 1, 100000)) < 0) + NSRunAlertPanel (LOCALIZED (@"Error"), + LOCALIZED (@"Failed to set device parameters:\n%s"), + LOCALIZED (@"OK"), NULL, NULL, snd_strerror (err)); + else if ((err = snd_pcm_prepare (pcm_handle)) < 0) + NSRunAlertPanel (LOCALIZED (@"Error"), + LOCALIZED (@"Failed to prepare the ALSA device for " + @"playing:\n%s"), + LOCALIZED (@"OK"), NULL, NULL, snd_strerror (err)); + else + result = YES; + + return result; +} + +- (BOOL) prepareDeviceWithChannels: (unsigned int) numberOfChannels + andRate: (unsigned long) sampleRate +{ + channels = numberOfChannels; + rate = sampleRate; + + return YES; +} + +- (void) closeDevice +{ + while (stopRequested) + [NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.1]]; + snd_pcm_close (pcm_handle); +} + +- (void) threadLoop +{ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + + while (!stopRequested) + { + snd_pcm_sframes_t written; + snd_pcm_uframes_t frames; + int bufferSize; + + bufferSize = [parentPlayer readNextChunk: buffer + withSize: DEFAULT_BUFFER_SIZE]; + + if (bufferSize > 0) + { + frames = snd_pcm_bytes_to_frames (pcm_handle, bufferSize); + written = snd_pcm_writei (pcm_handle, buffer, frames); + + if (written < 0) + { + NSLog (@"Failed writing to the ALSA device:\n" + @"%s, trying to recover.\n", snd_strerror (written)); + snd_pcm_recover (pcm_handle, written, 0); + } + } + + if ([pool autoreleaseCount] > 50) + [pool emptyPool]; + } + + stopRequested = NO; + [pool release]; +} + +- (BOOL) startThread +{ + [NSThread detachNewThreadSelector: @selector(threadLoop) + toTarget: self + withObject: nil]; + + return YES; +} + +- (void) stopThread +{ + stopRequested = YES; +} address@hidden --- /dev/null +++ cynthiune.app-0.9.5/Bundles/ALSA/GNUmakefile @@ -0,0 +1,44 @@ +# GNUmakefile - this file is part of Cynthiune +# +# Copyright (C) 2010 Free Software Foundation, Inc. +# +# Author: Yavor Doganov +# +# Cynthiune is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# Cynthiune is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +include $(GNUSTEP_MAKEFILES)/common.make + +PACKAGE_NAME = ALSA +BUNDLE_NAME = ALSA +BUNDLE_EXTENSION = .output +BUNDLE_INSTALL_DIR = $(GNUSTEP_LIBRARY)/Cynthiune +ALSA_PRINCIPAL_CLASS = ALSA + +ALSA_HEADERS = ALSA.h + +ALSA_OBJC_FILES = ALSA.m + +ADDITIONAL_INCLUDE_DIRS += -I../../Frameworks + +FRAMEWORKS_DIRS = ../../Frameworks/Cynthiune +FRAMEWORKS = Cynthiune + +include ../../frameworks.make + +-include GNUmakefile.preamble +-include GNUmakefile.local +include $(GNUSTEP_MAKEFILES)/bundle.make +-include GNUmakefile.postamble --- /dev/null +++ cynthiune.app-0.9.5/Bundles/ALSA/GNUmakefile.preamble @@ -0,0 +1,31 @@ +# GNUmakefile.preamble - this file is part of Cynthiune -*-makefile-gmake-*- +# +# Copyright (C) 2010 Free Software Foundation, Inc. +# +# Author: Yavor Doganov +# +# Cynthiune is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# Cynthiune is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301, USA. + +ALSA_CFLAGS := $(shell pkg-config --cflags alsa) +ALSA_LIBS := $(shell pkg-config --libs alsa) + +ADDITIONAL_INCLUDE_DIRS += $(ALSA_CFLAGS) +BUNDLE_LIBS += $(ALSA_LIBS) +ADDITIONAL_OBJCFLAGS += -Wall -Wno-import + +ifeq ($(debug), yes) +ADDITIONAL_LDFLAGS += -lmcheck +endif --- cynthiune.app-0.9.5.orig/GeneralPreference.m +++ cynthiune.app-0.9.5/GeneralPreference.m @@ -20,11 +20,7 @@ * Boston, MA 02111-1307, USA. */ -#import -#import -#import -#import -#import +#import #import #import @@ -45,12 +41,12 @@ #define defaultOutputBundle @"MacOSXPlayer" #else #ifdef __linux__ -#define defaultOutputBundle @"OSS" +#define defaultOutputBundle @"ALSA" #else #ifdef __WIN32__ #define defaultOutputBundle @"WaveOut" #else -#define defaultOutputBundle @"Esound" +#define defaultOutputBundle @"OSS" #endif #endif #endif @@ -90,6 +86,46 @@ [preference setObject: outputBundle forKey: @"OutputBundle"]; } +#ifdef __linux__ + if (![outputBundle isEqualToString: @"ALSA"]) + { + NSString *migrated; + int choice; + + /* Check if the user has already made her choice about which + output bundle to use. The default `SuggestedALSA' is to + ensure that the dialog does not appear every time on + startup, even if the user's preference is OSS or something + else. */ + migrated = [preference objectForKey: @"SuggestedALSA"]; + + if (!migrated) + { + choice = + NSRunAlertPanel (_(@"Deprecated output bundle detected"), + _(@"ALSA is the recommended output bundle\n" + @"on GNU/Linux systems, but %@ is " + @"currently used.\nWould you like to " + @"switch to ALSA now?"), + _(@"Yes"), _(@"No"), + _(@"No, don't ask anymore"), + outputBundle); + + switch (choice) + { + case NSAlertDefaultReturn: + [preference setObject: @"ALSA" forKey: @"OutputBundle"]; + [preference setObject: @"YES" forKey: @"SuggestedALSA"]; + break; + case NSAlertAlternateReturn: + break; + case NSAlertOtherReturn: + [preference setObject: @"YES" forKey: @"SuggestedALSA"]; + } + } + } +#endif + playlistFormat = [preference objectForKey: @"PlaylistFormat"]; if (!playlistFormat || !([playlistFormat isEqualToString: @"m3u"]