Gluon Mobile iOS 音频播放器
Gluon Mobile iOS Audio Player
由于 JavaFx Media 尚未移植到移动平台,任何人都可以帮助我使用本机 iOS APi 来播放将存储在中的声音 mp3 文件我的 gluon 项目中的 main/resources 文件夹。
虽然在 Android 上,我们可以轻松地将本机 API 添加到 Gluon 项目的 Android 文件夹中(检查此 solution 以在 [=131] 上使用本机媒体=]), 使用本机代码 (Objetive-C) 和 iOS 文件夹上的媒体 API 是不够的,因为它必须被编译,并且编译后的文件必须包含在ipa.
目前,Charm Down is doing this for a bunch of services. If you have a look at the build.gradle
script for iOS,它包括 xcodebuild
编译和构建本机库 libCharm.a
的任务,以后应该包含在任何 iOS 项目中使用任何这些服务。
我的建议是克隆 Charm Down,并提供一项新服务:AudioService
,以及一些基本方法,例如:
public interface AudioService {
void play(String audioName);
void pause();
void resume();
void stop();
}
此服务将添加到 Platform
class:
public abstract class Platform {
...
public abstract AudioService getAudioService();
}
并且您应该为桌面(空)、Android(如 here)和 iOS.
提供实现
IOS 实施 - Java
您必须将此添加到 IOSPlatform
class:
public class IOSPlatform extends Platform {
...
@Override
public AudioService getAudioService() {
if (audioService == null) {
audioService = new IOSAudioService();
}
return audioService;
}
}
并创建 IOSAudioService
class:
public class IOSAudioService implements AudioService {
@Override
public void play(String audioName) {
CharmApplication.play(audioName);
}
@Override
public void pause() {
CharmApplication.pause();
}
@Override
public void resume() {
CharmApplication.resume();
}
@Override
public void stop() {
CharmApplication.stop();
}
}
最后,您必须在 CharmApplication
中添加本机调用:
public class CharmApplication {
static {
System.loadLibrary("Charm");
_initIDs();
}
...
public static native void play(String audioName);
public static native void pause();
public static native void resume();
public static native void stop();
}
IOS 实施 - Objective-C
现在,在本机文件夹 CharmApplication.m
上,添加这些调用的实现:
#include "CharmApplication.h"
...
#include "Audio.h"
// Audio
Audio *_audio;
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_play
(JNIEnv *env, jclass jClass, jstring jTitle)
{
NSLog(@"Play audio");
const jchar *charsTitle = (*env)->GetStringChars(env, jTitle, NULL);
NSString *name = [NSString stringWithCharacters:(UniChar *)charsTitle length:(*env)->GetStringLength(env, jTitle)];
(*env)->ReleaseStringChars(env, jTitle, charsTitle);
_audio = [[Audio alloc] init];
[_audio playAudio:name];
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_pause
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio pauseAudio];
}
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_resume
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio resumeAudio];
}
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_stop
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio stopAudio];
}
return;
}
并创建头文件Audio.h
:
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
@interface Audio :UIViewController <AVAudioPlayerDelegate>
{
}
- (void) playAudio: (NSString *) audioName;
- (void) pauseAudio;
- (void) resumeAudio;
- (void) stopAudio;
@end
和实施 Audio.m
。这个是基于这个 tutorial:
#include "Audio.h"
#include "CharmApplication.h"
@implementation Audio
AVAudioPlayer* player;
- (void)playAudio:(NSString *) audioName
{
NSString* fileName = [audioName stringByDeletingPathExtension];
NSString* extension = [audioName pathExtension];
NSURL* url = [[NSBundle mainBundle] URLForResource:[NSString stringWithFormat:@"%@",fileName] withExtension:[NSString stringWithFormat:@"%@",extension]];
NSError* error = nil;
if(player)
{
[player stop];
player = nil;
}
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if(!player)
{
NSLog(@"Error creating player: %@", error);
return;
}
player.delegate = self;
[player prepareToPlay];
[player play];
}
- (void)pauseAudio
{
if(!player)
{
return;
}
[player pause];
}
- (void)resumeAudio
{
if(!player)
{
return;
}
[player play];
}
- (void)stopAudio
{
if(!player)
{
return;
}
[player stop];
player = nil;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(@"%s successfully=%@", __PRETTY_FUNCTION__, flag ? @"YES" : @"NO");
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
NSLog(@"%s error=%@", __PRETTY_FUNCTION__, error);
}
@end
构建本机库
编辑 iOS 模块的 build.gradle
,并将音频服务添加到 xcodebuild
任务:
def nativeSources = ["$project.projectDir/src/main/native/CharmApplication.m",
...,
"$project.projectDir/src/main/native/Audio.m"]
...
def compileOutputs = [
"$project.buildDir/native/$arch/CharmApplication.o",
"$project.buildDir/native/$arch/Charm.o",
...,
"$project.buildDir/native/$arch/Audio.o"]
构建项目
保存项目,并从 Charm Down 项目的根目录,在命令行 运行:
./gradlew clean build
如果一切正常,您应该:
- Common/build/libs/charm-down-common-2.1.0-SNAPSHOT.jar
- Desktop/build/libs/charm-down-desktop-2.1.0-SNAPSHOT.jar
- Android/build/libs/charm-down-android-2.1.0-SNAPSHOT.jar
- IOS/build/libs/charm-down-ios-2.1.0-SNAPSHOT.jar
- IOS/build/native/libCharm.a
Gluon 项目
有了这些依赖项和本机库,您将能够创建一个新的 Gluon 项目,并将 jars 添加为本地依赖项(添加到 libs
文件夹)。
至于原生库,应该添加到这个路径:src/ios/jniLibs/libCharm.a
.
更新 build.gradle
脚本:
repositories {
flatDir {
dirs 'libs'
}
jcenter()
...
}
dependencies {
compile 'com.gluonhq:charm-glisten:3.0.0'
compile 'com.gluonhq:charm-down-common:2.1.0-SNAPSHOT'
compile 'com.gluonhq:charm-glisten-connect-view:3.0.0'
iosRuntime 'com.gluonhq:charm-glisten-ios:3.0.0'
iosRuntime 'com.gluonhq:charm-down-ios:2.1.0-SNAPSHOT'
}
在您的视图中,检索服务并提供一些基本的 UI 来调用 play("audio.mp3")
、pause()
、resume()
和 stop()
方法:
private boolean pause;
public BasicView(String name) {
super(name);
AudioService audioService = PlatformFactory.getPlatform().getAudioService();
final HBox hBox = new HBox(10,
MaterialDesignIcon.PLAY_ARROW.button(e -> audioService.play("audio.mp3")),
MaterialDesignIcon.PAUSE.button(e -> {
if (!pause) {
audioService.pause();
pause = true;
} else {
audioService.resume();
pause = false;
}
}),
MaterialDesignIcon.STOP.button(e -> audioService.stop()));
hBox.setAlignment(Pos.CENTER);
setCenter(new StackPane(hBox));
}
最后,在 src/ios/assets/audio.mp3
下放置一个类似 audio.mp3
的音频文件,构建并部署到 iOS。
希望 Charm Down 能尽快提供这个 API。此外,如果您提出了一个不错的实现,请随时分享并创建一个 Pull Request.
由于 JavaFx Media 尚未移植到移动平台,任何人都可以帮助我使用本机 iOS APi 来播放将存储在中的声音 mp3 文件我的 gluon 项目中的 main/resources 文件夹。
虽然在 Android 上,我们可以轻松地将本机 API 添加到 Gluon 项目的 Android 文件夹中(检查此 solution 以在 [=131] 上使用本机媒体=]), 使用本机代码 (Objetive-C) 和 iOS 文件夹上的媒体 API 是不够的,因为它必须被编译,并且编译后的文件必须包含在ipa.
目前,Charm Down is doing this for a bunch of services. If you have a look at the build.gradle
script for iOS,它包括 xcodebuild
编译和构建本机库 libCharm.a
的任务,以后应该包含在任何 iOS 项目中使用任何这些服务。
我的建议是克隆 Charm Down,并提供一项新服务:AudioService
,以及一些基本方法,例如:
public interface AudioService {
void play(String audioName);
void pause();
void resume();
void stop();
}
此服务将添加到 Platform
class:
public abstract class Platform {
...
public abstract AudioService getAudioService();
}
并且您应该为桌面(空)、Android(如 here)和 iOS.
提供实现IOS 实施 - Java
您必须将此添加到 IOSPlatform
class:
public class IOSPlatform extends Platform {
...
@Override
public AudioService getAudioService() {
if (audioService == null) {
audioService = new IOSAudioService();
}
return audioService;
}
}
并创建 IOSAudioService
class:
public class IOSAudioService implements AudioService {
@Override
public void play(String audioName) {
CharmApplication.play(audioName);
}
@Override
public void pause() {
CharmApplication.pause();
}
@Override
public void resume() {
CharmApplication.resume();
}
@Override
public void stop() {
CharmApplication.stop();
}
}
最后,您必须在 CharmApplication
中添加本机调用:
public class CharmApplication {
static {
System.loadLibrary("Charm");
_initIDs();
}
...
public static native void play(String audioName);
public static native void pause();
public static native void resume();
public static native void stop();
}
IOS 实施 - Objective-C
现在,在本机文件夹 CharmApplication.m
上,添加这些调用的实现:
#include "CharmApplication.h"
...
#include "Audio.h"
// Audio
Audio *_audio;
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_play
(JNIEnv *env, jclass jClass, jstring jTitle)
{
NSLog(@"Play audio");
const jchar *charsTitle = (*env)->GetStringChars(env, jTitle, NULL);
NSString *name = [NSString stringWithCharacters:(UniChar *)charsTitle length:(*env)->GetStringLength(env, jTitle)];
(*env)->ReleaseStringChars(env, jTitle, charsTitle);
_audio = [[Audio alloc] init];
[_audio playAudio:name];
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_pause
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio pauseAudio];
}
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_resume
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio resumeAudio];
}
return;
}
JNIEXPORT void JNICALL Java_com_gluonhq_charm_down_ios_CharmApplication_stop
(JNIEnv *env, jclass jClass)
{
if (_audio)
{
[_audio stopAudio];
}
return;
}
并创建头文件Audio.h
:
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
@interface Audio :UIViewController <AVAudioPlayerDelegate>
{
}
- (void) playAudio: (NSString *) audioName;
- (void) pauseAudio;
- (void) resumeAudio;
- (void) stopAudio;
@end
和实施 Audio.m
。这个是基于这个 tutorial:
#include "Audio.h"
#include "CharmApplication.h"
@implementation Audio
AVAudioPlayer* player;
- (void)playAudio:(NSString *) audioName
{
NSString* fileName = [audioName stringByDeletingPathExtension];
NSString* extension = [audioName pathExtension];
NSURL* url = [[NSBundle mainBundle] URLForResource:[NSString stringWithFormat:@"%@",fileName] withExtension:[NSString stringWithFormat:@"%@",extension]];
NSError* error = nil;
if(player)
{
[player stop];
player = nil;
}
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
if(!player)
{
NSLog(@"Error creating player: %@", error);
return;
}
player.delegate = self;
[player prepareToPlay];
[player play];
}
- (void)pauseAudio
{
if(!player)
{
return;
}
[player pause];
}
- (void)resumeAudio
{
if(!player)
{
return;
}
[player play];
}
- (void)stopAudio
{
if(!player)
{
return;
}
[player stop];
player = nil;
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
NSLog(@"%s successfully=%@", __PRETTY_FUNCTION__, flag ? @"YES" : @"NO");
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError *)error
{
NSLog(@"%s error=%@", __PRETTY_FUNCTION__, error);
}
@end
构建本机库
编辑 iOS 模块的 build.gradle
,并将音频服务添加到 xcodebuild
任务:
def nativeSources = ["$project.projectDir/src/main/native/CharmApplication.m",
...,
"$project.projectDir/src/main/native/Audio.m"]
...
def compileOutputs = [
"$project.buildDir/native/$arch/CharmApplication.o",
"$project.buildDir/native/$arch/Charm.o",
...,
"$project.buildDir/native/$arch/Audio.o"]
构建项目
保存项目,并从 Charm Down 项目的根目录,在命令行 运行:
./gradlew clean build
如果一切正常,您应该:
- Common/build/libs/charm-down-common-2.1.0-SNAPSHOT.jar
- Desktop/build/libs/charm-down-desktop-2.1.0-SNAPSHOT.jar
- Android/build/libs/charm-down-android-2.1.0-SNAPSHOT.jar
- IOS/build/libs/charm-down-ios-2.1.0-SNAPSHOT.jar
- IOS/build/native/libCharm.a
Gluon 项目
有了这些依赖项和本机库,您将能够创建一个新的 Gluon 项目,并将 jars 添加为本地依赖项(添加到 libs
文件夹)。
至于原生库,应该添加到这个路径:src/ios/jniLibs/libCharm.a
.
更新 build.gradle
脚本:
repositories {
flatDir {
dirs 'libs'
}
jcenter()
...
}
dependencies {
compile 'com.gluonhq:charm-glisten:3.0.0'
compile 'com.gluonhq:charm-down-common:2.1.0-SNAPSHOT'
compile 'com.gluonhq:charm-glisten-connect-view:3.0.0'
iosRuntime 'com.gluonhq:charm-glisten-ios:3.0.0'
iosRuntime 'com.gluonhq:charm-down-ios:2.1.0-SNAPSHOT'
}
在您的视图中,检索服务并提供一些基本的 UI 来调用 play("audio.mp3")
、pause()
、resume()
和 stop()
方法:
private boolean pause;
public BasicView(String name) {
super(name);
AudioService audioService = PlatformFactory.getPlatform().getAudioService();
final HBox hBox = new HBox(10,
MaterialDesignIcon.PLAY_ARROW.button(e -> audioService.play("audio.mp3")),
MaterialDesignIcon.PAUSE.button(e -> {
if (!pause) {
audioService.pause();
pause = true;
} else {
audioService.resume();
pause = false;
}
}),
MaterialDesignIcon.STOP.button(e -> audioService.stop()));
hBox.setAlignment(Pos.CENTER);
setCenter(new StackPane(hBox));
}
最后,在 src/ios/assets/audio.mp3
下放置一个类似 audio.mp3
的音频文件,构建并部署到 iOS。
希望 Charm Down 能尽快提供这个 API。此外,如果您提出了一个不错的实现,请随时分享并创建一个 Pull Request.