diff --git a/.gitignore b/.gitignore index e43b0f98..6b2cb231 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ .DS_Store +android/build +**/.* +!.gitignore +local.properties diff --git a/android/.gradle/6.7.1/executionHistory/executionHistory.bin b/android/.gradle/6.7.1/executionHistory/executionHistory.bin deleted file mode 100644 index 6e4a8ee3..00000000 Binary files a/android/.gradle/6.7.1/executionHistory/executionHistory.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/executionHistory/executionHistory.lock b/android/.gradle/6.7.1/executionHistory/executionHistory.lock deleted file mode 100644 index 20ea9764..00000000 Binary files a/android/.gradle/6.7.1/executionHistory/executionHistory.lock and /dev/null differ diff --git a/android/.gradle/6.7.1/fileChanges/last-build.bin b/android/.gradle/6.7.1/fileChanges/last-build.bin deleted file mode 100644 index f76dd238..00000000 Binary files a/android/.gradle/6.7.1/fileChanges/last-build.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/fileHashes/fileHashes.bin b/android/.gradle/6.7.1/fileHashes/fileHashes.bin deleted file mode 100644 index d8dcd8f0..00000000 Binary files a/android/.gradle/6.7.1/fileHashes/fileHashes.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/fileHashes/fileHashes.lock b/android/.gradle/6.7.1/fileHashes/fileHashes.lock deleted file mode 100644 index 6ea411fc..00000000 Binary files a/android/.gradle/6.7.1/fileHashes/fileHashes.lock and /dev/null differ diff --git a/android/.gradle/6.7.1/fileHashes/resourceHashesCache.bin b/android/.gradle/6.7.1/fileHashes/resourceHashesCache.bin deleted file mode 100644 index 82f3b20d..00000000 Binary files a/android/.gradle/6.7.1/fileHashes/resourceHashesCache.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/gc.properties b/android/.gradle/6.7.1/gc.properties deleted file mode 100644 index e69de29b..00000000 diff --git a/android/.gradle/6.7.1/javaCompile/classAnalysis.bin b/android/.gradle/6.7.1/javaCompile/classAnalysis.bin deleted file mode 100644 index ff81896a..00000000 Binary files a/android/.gradle/6.7.1/javaCompile/classAnalysis.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/javaCompile/jarAnalysis.bin b/android/.gradle/6.7.1/javaCompile/jarAnalysis.bin deleted file mode 100644 index b5edd528..00000000 Binary files a/android/.gradle/6.7.1/javaCompile/jarAnalysis.bin and /dev/null differ diff --git a/android/.gradle/6.7.1/javaCompile/javaCompile.lock b/android/.gradle/6.7.1/javaCompile/javaCompile.lock deleted file mode 100644 index 653850f3..00000000 Binary files a/android/.gradle/6.7.1/javaCompile/javaCompile.lock and /dev/null differ diff --git a/android/.gradle/6.7.1/javaCompile/taskHistory.bin b/android/.gradle/6.7.1/javaCompile/taskHistory.bin deleted file mode 100644 index 43a8f51f..00000000 Binary files a/android/.gradle/6.7.1/javaCompile/taskHistory.bin and /dev/null differ diff --git a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index fb590349..00000000 Binary files a/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/android/.gradle/buildOutputCleanup/cache.properties b/android/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 911de871..00000000 --- a/android/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Fri Apr 16 16:48:05 CEST 2021 -gradle.version=6.7.1 diff --git a/android/.gradle/buildOutputCleanup/outputFiles.bin b/android/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 5a8a2e38..00000000 Binary files a/android/.gradle/buildOutputCleanup/outputFiles.bin and /dev/null differ diff --git a/android/.gradle/checksums/checksums.lock b/android/.gradle/checksums/checksums.lock deleted file mode 100644 index d47d3613..00000000 Binary files a/android/.gradle/checksums/checksums.lock and /dev/null differ diff --git a/android/.gradle/checksums/md5-checksums.bin b/android/.gradle/checksums/md5-checksums.bin deleted file mode 100644 index 2c65a403..00000000 Binary files a/android/.gradle/checksums/md5-checksums.bin and /dev/null differ diff --git a/android/.gradle/checksums/sha1-checksums.bin b/android/.gradle/checksums/sha1-checksums.bin deleted file mode 100644 index 25144836..00000000 Binary files a/android/.gradle/checksums/sha1-checksums.bin and /dev/null differ diff --git a/android/.gradle/checksums/sha256-checksums.bin b/android/.gradle/checksums/sha256-checksums.bin deleted file mode 100644 index a157a210..00000000 Binary files a/android/.gradle/checksums/sha256-checksums.bin and /dev/null differ diff --git a/android/.gradle/checksums/sha512-checksums.bin b/android/.gradle/checksums/sha512-checksums.bin deleted file mode 100644 index 06437d0b..00000000 Binary files a/android/.gradle/checksums/sha512-checksums.bin and /dev/null differ diff --git a/android/.gradle/configuration-cache/gc.properties b/android/.gradle/configuration-cache/gc.properties deleted file mode 100644 index e69de29b..00000000 diff --git a/android/.gradle/vcs-1/gc.properties b/android/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29b..00000000 diff --git a/android/local.properties b/android/local.properties deleted file mode 100644 index d2e7944b..00000000 --- a/android/local.properties +++ /dev/null @@ -1,8 +0,0 @@ -## This file must *NOT* be checked into Version Control Systems, -# as it contains information specific to your local configuration. -# -# Location of the SDK. This is only used by Gradle. -# For customization when using a Version Control System, please read the -# header note. -#Sun Jul 11 21:17:08 CEST 2021 -sdk.dir=/Volumes/Home/larpoux/Library/Android/sdk diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoMediaPlayer.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoMediaPlayer.java index 59627333..20cee7fc 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoMediaPlayer.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoMediaPlayer.java @@ -50,6 +50,10 @@ void _play() mediaPlayer.start(); } + int _getAudioSessionId(){ + return mediaPlayer.getAudioSessionId(); + } + int feed(byte[] data) throws Exception { throw new Exception("Cannot feed a Media Player"); diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayer.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayer.java index 2174988f..a8dc016c 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayer.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayer.java @@ -20,22 +20,25 @@ import android.media.MediaPlayer; +import android.media.audiofx.AudioEffect; +import android.media.audiofx.Equalizer; +import android.media.audiofx.LoudnessEnhancer; import android.os.Build; import android.os.Handler; import android.os.Looper; -import android.media.AudioFocusRequest; - import java.io.File; import java.io.FileOutputStream; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; +import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Timer; import java.util.TimerTask; -import java.lang.Thread; import com.dooboolab.TauEngine.Flauto.*; -import com.dooboolab.TauEngine.Flauto; public class FlautoPlayer implements MediaPlayer.OnErrorListener { @@ -102,6 +105,12 @@ public class FlautoPlayer implements MediaPlayer.OnErrorListener static int currentPlayerID = 0; private int myPlayerId = 0; + //--------------- EQ related -----------------------// + private List rawAudioEffects = new ArrayList(); + private List audioEffects = new ArrayList(); + private Map audioEffectsMap = new HashMap(); + private Integer audioSessionId; + static final String ERR_UNKNOWN = "ERR_UNKNOWN"; static final String ERR_PLAYER_IS_NULL = "ERR_PLAYER_IS_NULL"; @@ -130,6 +139,13 @@ public boolean openPlayer () return true; } + public int getAudioSessionId() throws Exception { + if (player == null) { + throw new Exception("getAudioSessionId() : player is null"); + } + return player._getAudioSessionId(); + } + public void closePlayer ( ) { stop(); @@ -188,7 +204,6 @@ public boolean startPlayer (t_CODEC codec, String fromURI, byte[] dataBuffer, in } } - try { if (fromURI == null && codec == t_CODEC.pcm16) @@ -318,34 +333,32 @@ void setTimer(long duration) TimerTask task = new TimerTask() { @Override public void run() { - mainHandler.post(new Runnable() + mainHandler.post(new Runnable() + { + @Override + public void run() { - @Override - public void run() + try { - try + if (player != null) { - if (player != null) - { - long position = player._getCurrentPosition(); - long duration = player._getDuration(); - if (position > duration) - { - position = duration; - } - m_callBack.updateProgress(position, duration); + long position = player._getCurrentPosition(); + long duration = player._getDuration(); + if (position > duration) + { + position = duration; } - } catch (Exception e) - { - logDebug( "Exception: " + e.toString()); - stopPlayer(); + m_callBack.updateProgress(position, duration); } + } catch (Exception e) + { + logDebug( "Exception: " + e.toString()); + stopPlayer(); } - }); - - - } + } + }); + } }; mTimer = new Timer(); @@ -391,34 +404,34 @@ void stop() public boolean play() { - if (player == null) + if (player == null) + { + return false; + } + try + { + if (latentVolume >= 0) { - return false; + setVolume(latentVolume); } - try + if (latentSpeed >= 0) { - if (latentVolume >= 0) - { - setVolume(latentVolume); - } - if (latentSpeed >= 0) - { - setSpeed(latentSpeed); - } - if (subsDurationMillis > 0) - setTimer(subsDurationMillis); - if (latentSeek >= 0) - { - seekToPlayer(latentSeek); - } + setSpeed(latentSpeed); + } + if (subsDurationMillis > 0) + setTimer(subsDurationMillis); + if (latentSeek >= 0) + { + seekToPlayer(latentSeek); + } - }catch (Exception e) - { + }catch (Exception e) + { - } - player._play(); - return true; + } + player._play(); + return true; } public boolean isDecoderSupported (t_CODEC codec ) @@ -561,9 +574,104 @@ public Map getProgress ( ) return dic; } + // ----------------------------Equalizer related----------------------------------------// + + public void initEqualizer(Integer audioSessionId) throws Exception { + System.out.printf("audioSessionId: %d", audioSessionId); + if(audioSessionId == null) { + this.audioSessionId = this.getAudioSessionId(); + } else { + this.audioSessionId = audioSessionId; + } + + // Init with basic info + this.rawAudioEffects.add(mapOf("type","AndroidEqualizer", "enabled", false)); + + clearAudioEffects(); + if (this.audioSessionId != null) { + for (Object rawAudioEffect : this.rawAudioEffects) { + Map json = (Map)rawAudioEffect; + AudioEffect audioEffect = decodeAudioEffect(rawAudioEffect, this.audioSessionId); + if ((Boolean)json.get("enabled")) { + audioEffect.setEnabled(true); + } + this.audioEffects.add(audioEffect); + this.audioEffectsMap.put((String)json.get("type"), audioEffect); + } + } + } + + public void setAudioSessionId(Integer audioSessionId) { + this.audioSessionId = audioSessionId; + } + + public void equalizerBandSetGain(int bandIndex, double gain) { + ((Equalizer) Objects.requireNonNull(this.audioEffectsMap.get("AndroidEqualizer"))).setBandLevel((short)bandIndex, + (short)(Math.round(gain * 1000.0))); + } + + public void audioEffectSetEnabled(String type, boolean enabled) { + Objects.requireNonNull(this.audioEffectsMap.get(type)).setEnabled(enabled); + } + + public Map equalizerAudioEffectGetParameters() { + Equalizer equalizer = (Equalizer)this.audioEffectsMap.get("AndroidEqualizer"); + ArrayList rawBands = new ArrayList<>(); + for (short i = 0; i < Objects.requireNonNull(equalizer).getNumberOfBands(); i++) { + rawBands.add(mapOf( + "index", i, + "lowerFrequency", (double)equalizer.getBandFreqRange(i)[0] / 1000.0, + "upperFrequency", (double)equalizer.getBandFreqRange(i)[1] / 1000.0, + "centerFrequency", (double)equalizer.getCenterFreq(i) / 1000.0, + "gain", equalizer.getBandLevel(i) / 1000.0 + )); + } + return mapOf( + "parameters", mapOf( + "minDecibels", equalizer.getBandLevelRange()[0] / 1000.0, + "maxDecibels", equalizer.getBandLevelRange()[1] / 1000.0, + "bands", rawBands + ) + ); + } + + private AudioEffect decodeAudioEffect(final Object json, int audioSessionId) { + Map map = (Map) json; + String type = (String) map.get("type"); + switch (Objects.requireNonNull(type)) { + case "AndroidLoudnessEnhancer": + if (Build.VERSION.SDK_INT < 19) + throw new RuntimeException("AndroidLoudnessEnhancer requires minSdkVersion >= 19"); + int targetGain = (int) Math.round((((Double) map.get("targetGain")) * 1000.0)); + LoudnessEnhancer loudnessEnhancer = new LoudnessEnhancer(audioSessionId); + loudnessEnhancer.setTargetGain(targetGain); + return loudnessEnhancer; + case "AndroidEqualizer": + return new Equalizer(0, audioSessionId); + default: + throw new IllegalArgumentException("Unknown AudioEffect type: " + map.get("type")); + } + } + + private void clearAudioEffects() { + for (Iterator it = this.audioEffects.iterator(); it.hasNext();) { + AudioEffect audioEffect = it.next(); + audioEffect.release(); + it.remove(); + } + this.audioEffectsMap.clear(); + } + + static Map mapOf(Object... args) { + Map map = new HashMap<>(); + for (int i = 0; i < args.length; i += 2) { + map.put((String)args[i], args[i + 1]); + } + return map; + } - void logDebug (String msg) + void logDebug (String msg) { m_callBack.log ( t_LOG_LEVEL.DBG , msg); } diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngine.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngine.java index dacf2db7..197fefb9 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngine.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngine.java @@ -150,6 +150,10 @@ void _stop() blockThread = null; } + int _getAudioSessionId() { + return sessionId; + } + void _finish() { } diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineFromMic.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineFromMic.java index 712f3a3e..5a40cfea 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineFromMic.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineFromMic.java @@ -282,6 +282,10 @@ void _stop() } } + int _getAudioSessionId() { + return sessionId; + } + void _finish() { } diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineInterface.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineInterface.java index 3a5af843..1d399e87 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineInterface.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerEngineInterface.java @@ -32,6 +32,7 @@ abstract class FlautoPlayerEngineInterface abstract long _getCurrentPosition(); abstract int feed(byte[] data) throws Exception; abstract void _play(); + abstract int _getAudioSessionId() throws Exception; } diff --git a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerMedia.java b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerMedia.java index 53549cd4..a92b01e1 100644 --- a/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerMedia.java +++ b/android/src/main/java/com/dooboolab/TauEngine/FlautoPlayerMedia.java @@ -114,7 +114,9 @@ void _stop() { } - + int _getAudioSessionId() { + return mediaPlayer.getAudioSessionId(); + } void _pausePlayer() throws Exception { if (mediaPlayer == null) { diff --git a/ios/Classes/FlautoPlayer.h b/ios/Classes/FlautoPlayer.h index 0f393afb..efab9e7b 100644 --- a/ios/Classes/FlautoPlayer.h +++ b/ios/Classes/FlautoPlayer.h @@ -85,6 +85,9 @@ - (long)getPosition; - (long)getDuration; - (void)logDebug: (NSString*)msg; +- (void)initEqualizer:(NSDictionary*) params; +- (void)enableEqualizer:(bool)enable; +- (void) setEqualizerBandGain: (int) bandIndex gain: (float) gain; diff --git a/ios/Classes/FlautoPlayer.mm b/ios/Classes/FlautoPlayer.mm index b6d79648..50d1df1a 100644 --- a/ios/Classes/FlautoPlayer.mm +++ b/ios/Classes/FlautoPlayer.mm @@ -62,6 +62,7 @@ @implementation FlautoPlayer double latentSpeed; long latentSeek; bool voiceProcessing; + NSDictionary* equalizerParams; } @@ -185,7 +186,7 @@ - (bool)startPlayerCodec: (t_CODEC)codec [self stop]; // To start a fresh new playback if ( (path == nil || [path class] == [NSNull class] ) && codec == pcm16) - m_playerEngine = [[AudioEngine alloc] init: self ]; + m_playerEngine = [[AudioEngine alloc] init: self eqParams: equalizerParams ]; else m_playerEngine = [[AudioPlayerFlauto alloc]init: self]; @@ -475,6 +476,21 @@ - (long)getDuration return [m_playerEngine getDuration]; } +- (void)initEqualizer:(NSDictionary*) params +{ + equalizerParams = params; +} + +- (void)enableEqualizer:(bool) enable +{ + [m_playerEngine enableEqualizer: enable]; +} + +- (void) setEqualizerBandGain: (int) bandIndex gain: (float) gain +{ + [m_playerEngine setEqualizerBandGain: bandIndex gain: gain]; +} + - (NSDictionary*)getProgress { @@ -546,7 +562,6 @@ - (void)logDebug: (NSString*)msg [m_callBack log: DBG msg: msg]; } - @end //--------------------------------------------------------------------------------------------- diff --git a/ios/Classes/FlautoPlayerEngine.h b/ios/Classes/FlautoPlayerEngine.h index b392959a..05820c24 100644 --- a/ios/Classes/FlautoPlayerEngine.h +++ b/ios/Classes/FlautoPlayerEngine.h @@ -35,40 +35,43 @@ @protocol FlautoPlayerEngineInterface - - (void) startPlayerFromBuffer: (NSData*)data ; - - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; - - (long) getDuration; - - (long) getPosition; - - (void) stop; - - (bool) play; - - (bool) resume; - - (bool) pause; - - (bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)fadeDuration; // Volume is between 0.0 and 1.0 - - (bool) setSpeed: (double) speed ; // Speed is between 0.0 and 1.0 to go slower - - (bool) seek: (double) pos; - - (t_PLAYER_STATE) getStatus; - - (int) feed: (NSData*)data; + - (void) startPlayerFromBuffer: (NSData*)data ; + - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; + - (long) getDuration; + - (long) getPosition; + - (void) stop; + - (bool) play; + - (bool) resume; + - (bool) pause; + - (bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)fadeDuration; // Volume is between 0.0 and 1.0 + - (bool) setSpeed: (double) speed ; // Speed is between 0.0 and 1.0 to go slower + - (bool) seek: (double) pos; + - (t_PLAYER_STATE) getStatus; + - (int) feed: (NSData*)data; + - (void) enableEqualizer:(bool) enabled; + - (void) setEqualizerBandGain: (int) bandIndex gain: (float) gain; @end @interface AudioPlayerFlauto : NSObject { } -- (AudioPlayerFlauto*) init: (NSObject*)owner ;// FlutterSoundPlayer* - - - (void) startPlayerFromBuffer: (NSData*)data ; - - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; - - (void) stop; - - (bool) play; - - (bool) resume; - - (bool) pause; - - (bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)duration ;// Volume is between 0.0 and 1.0 - - (bool) setSpeed: (double) speed ;// Volume is between 0.0 and 1.0 - - (bool) seek: (double) pos; - - (t_PLAYER_STATE) getStatus; - - (AVAudioPlayer*) getAudioPlayer; - - (void) setAudioPlayer: (AVAudioPlayer*)thePlayer; - - (int) feed: (NSData*)data; + - (AudioPlayerFlauto*) init: (NSObject*)owner ;// FlutterSoundPlayer* + + - (void) startPlayerFromBuffer: (NSData*)data ; + - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; + - (void) stop; + - (bool) play; + - (bool) resume; + - (bool) pause; + - (bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)duration ;// Volume is between 0.0 and 1.0 + - (bool) setSpeed: (double) speed ;// Volume is between 0.0 and 1.0 + - (bool) seek: (double) pos; + - (t_PLAYER_STATE) getStatus; + - (AVAudioPlayer*) getAudioPlayer; + - (void) setAudioPlayer: (AVAudioPlayer*)thePlayer; + - (int) feed: (NSData*)data; + - (AVAudioUnitEQ *) setEqualizer:(NSDictionary*) arguments; @end @@ -77,19 +80,20 @@ { // TODO FlutterSoundPlayer* flutterSoundPlayer; // Owner } - - (AudioEngine*) init: (NSObject*)owner; // FlutterSoundPlayer* - - - (void) startPlayerFromBuffer: (NSData*)data ; - - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; - - (bool) play; - - (void) stop; - - (bool) resume; - - (bool) pause; - - (bool) setVolume: (double) volume fadeDuration:(NSTimeInterval)duration ; - - (bool) setSpeed: (double) speed ; - - (bool) seek: (double) pos; - - (int) getStatus; - - (int) feed: (NSData*)data; + - (AudioEngine*)init: (NSObject*)owner eqParams: (NSDictionary*) params; + + - (void) startPlayerFromBuffer: (NSData*)data ; + - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; + - (bool) play; + - (void) stop; + - (bool) resume; + - (bool) pause; + - (bool) setVolume: (double) volume fadeDuration:(NSTimeInterval)duration ; + - (bool) setSpeed: (double) speed ; + - (bool) seek: (double) pos; + - (int) getStatus; + - (int) feed: (NSData*)data; + - (float)gainFrom:(float)value; @end @@ -98,22 +102,67 @@ { // TODO FlutterSoundPlayer* flutterSoundPlayer; // Owner } - - (AudioEngineFromMic*) init: (NSObject*)owner; // FlutterSoundPlayer* - - - (void) startPlayerFromBuffer: (NSData*)data ; - - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; - - (void) stop; - - (bool) play; - - (bool) resume; - - (bool) pause; - - (bool) setVolume: (double) volume fadeDuration:(NSTimeInterval)duration ; - - (bool) setSpeed: (double) speed; - - (bool) seek: (double) pos; - - (int) getStatus; - - (int) feed: (NSData*)data; + - (AudioEngineFromMic*) init: (NSObject*)owner; // FlutterSoundPlayer* + + - (void) startPlayerFromBuffer: (NSData*)data ; + - (void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate ; + - (void) stop; + - (bool) play; + - (bool) resume; + - (bool) pause; + - (bool) setVolume: (double) volume fadeDuration:(NSTimeInterval)duration ; + - (bool) setSpeed: (double) speed; + - (bool) seek: (double) pos; + - (int) getStatus; + - (int) feed: (NSData*)data; @end +//#pragma mark - Enum EffectType +//typedef NS_ENUM(NSUInteger, EffectType) { +// EffectTypeDarwinEqualizer, NotImplemented +// }; +// +//NSString *NSStringFromEffectType(EffectType type) { +// switch (type) { +// case EffectTypeDarwinEqualizer: +// return @"DarwinEqualizer"; +// default: +// return @""; +// } +//} +//EffectType EffectTypeFromNSString(NSString *string) { +// if ([string isEqualToString:@"DarwinEqualizer"]) { +// return EffectTypeDarwinEqualizer; +// } +// return NotImplemented; +//} +// +//#pragma mark - Protocol EffectData +//@protocol EffectData +// @property (nonatomic, assign, readonly) EffectType type; +//@end +// +//#pragma mark - Struct BandEqualizerData +//@interface BandEqualizerData : NSObject +// @property (nonatomic, assign) NSUInteger index; +// @property (nonatomic, assign) float centerFrequency; +// @property (nonatomic, assign) float gain; +//@end +// +//#pragma mark - Struct ParamsEqualizerData +//@interface ParamsEqualizerData : NSObject +// @property (nonatomic, strong) NSArray *bands; +//@end +// +//#pragma mark - Struct EqualizerEffectData +//@interface EqualizerEffectData : NSObject +// @property (nonatomic, assign) EffectType type; +// @property (nonatomic, assign) BOOL enabled; +// @property (nonatomic, strong) ParamsEqualizerData *parameters; +// - (instancetype)fromJson:(NSDictionary *)map; +// - (id)effectFrom:(NSDictionary *)map error:(NSError **)error; +//@end #endif /* PlayerEngine_h */ diff --git a/ios/Classes/FlautoPlayerEngine.mm b/ios/Classes/FlautoPlayerEngine.mm index e071cfe2..a2e29740 100644 --- a/ios/Classes/FlautoPlayerEngine.mm +++ b/ios/Classes/FlautoPlayerEngine.mm @@ -36,59 +36,55 @@ @implementation AudioPlayerFlauto - (AVAudioPlayer*) getAudioPlayer { - return player; + return player; } - (void) setAudioPlayer: (AVAudioPlayer*)thePlayer { - player = thePlayer; + player = thePlayer; } + - (AudioPlayerFlauto*)init: (FlautoPlayer*)owner + { + flautoPlayer = owner; + return [super init]; + } - - - (AudioPlayerFlauto*)init: (FlautoPlayer*)owner - { - flautoPlayer = owner; - return [super init]; - } - - -(void) startPlayerFromBuffer: (NSData*) dataBuffer - { - NSError* error = [[NSError alloc] init]; - [self setAudioPlayer: [[AVAudioPlayer alloc] initWithData: dataBuffer error: &error]]; - [self getAudioPlayer].delegate = flautoPlayer; - } - - -(void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate - - { - [self setAudioPlayer: [[AVAudioPlayer alloc] initWithContentsOfURL: url error: nil] ]; - [self getAudioPlayer].delegate = flautoPlayer; + -(void) startPlayerFromBuffer: (NSData*) dataBuffer + { + NSError* error = [[NSError alloc] init]; + [self setAudioPlayer: [[AVAudioPlayer alloc] initWithData: dataBuffer error: &error]]; + [self getAudioPlayer].delegate = flautoPlayer; } + -(void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)numChannels sampleRate: (long)sampleRate + { + [self setAudioPlayer: [[AVAudioPlayer alloc] initWithContentsOfURL: url error: nil] ]; + [self getAudioPlayer].delegate = flautoPlayer; + } - -(long) getDuration - { - double duration = [self getAudioPlayer].duration; - return (long)(duration * 1000.0); - } + -(long) getDuration + { + double duration = [self getAudioPlayer].duration; + return (long)(duration * 1000.0); + } - -(long) getPosition - { - double position = [self getAudioPlayer].currentTime ; - return (long)( position * 1000.0); - } + -(long) getPosition + { + double position = [self getAudioPlayer].currentTime ; + return (long)( position * 1000.0); + } - -(void) stop - { - [ [self getAudioPlayer] stop]; - [self setAudioPlayer: nil]; - } + -(void) stop + { + [ [self getAudioPlayer] stop]; + [self setAudioPlayer: nil]; + } -(bool) play { - bool b = [ [self getAudioPlayer] play]; - return b; + bool b = [ [self getAudioPlayer] play]; + return b; } @@ -153,57 +149,183 @@ @implementation AudioEngine { FlautoPlayer* flutterSoundPlayer; // Owner AVAudioEngine* engine; + AVAudioUnitEQ* eq; AVAudioPlayerNode* playerNode; AVAudioFormat* playerFormat; AVAudioFormat* outputFormat; AVAudioOutputNode* outputNode; AVAudioConverter* converter; CFTimeInterval mStartPauseTime ; // The time when playback was paused - CFTimeInterval systemTime ; //The time when StartPlayer() ; + CFTimeInterval systemTime ; //The time when StartPlayer() ; double mPauseTime ; // The number of seconds during the total Pause mode NSData* waitingBlock; long m_sampleRate ; int m_numChannels; } - - (AudioEngine*)init: (FlautoPlayer*)owner + - (AudioEngine*)init: (FlautoPlayer*)owner eqParams: (NSDictionary*) params { - flutterSoundPlayer = owner; - waitingBlock = nil; - engine = [[AVAudioEngine alloc] init]; - outputNode = [engine outputNode]; + flutterSoundPlayer = owner; + waitingBlock = nil; + engine = [[AVAudioEngine alloc] init]; + outputNode = [engine outputNode]; + playerNode = [[AVAudioPlayerNode alloc] init]; - if (@available(iOS 13.0, *)) { - if ([flutterSoundPlayer isVoiceProcessingEnabled]) { - NSError* err; - if (![outputNode setVoiceProcessingEnabled:YES error:&err]) { - [flutterSoundPlayer logDebug:[NSString stringWithFormat:@"error enabling voiceProcessing => %@", err]]; - } else { - [flutterSoundPlayer logDebug: @"VoiceProcessing enabled"]; - } + if (@available(iOS 13.0, *)) { + if ([flutterSoundPlayer isVoiceProcessingEnabled]) { + NSError* err; + if (![outputNode setVoiceProcessingEnabled:YES error:&err]) { + [flutterSoundPlayer logDebug:[NSString stringWithFormat:@"error enabling voiceProcessing => %@", err]]; + } else { + [flutterSoundPlayer logDebug: @"VoiceProcessing enabled"]; } - } else { - [flutterSoundPlayer logDebug: @"WARNING! VoiceProcessing is only available on iOS13+"]; } - - outputFormat = [outputNode inputFormatForBus: 0]; - playerNode = [[AVAudioPlayerNode alloc] init]; + } else { + [flutterSoundPlayer logDebug: @"WARNING! VoiceProcessing is only available on iOS13+"]; + } + + outputFormat = [outputNode inputFormatForBus: 0]; + + eq = [self setEqualizer: params]; + + if(eq){ + [flutterSoundPlayer logDebug: @"EQ found! Attach it"]; + [engine attachNode: eq]; + } else { + [flutterSoundPlayer logDebug: @"EQ NOT FOUND!!!"]; + } + + [engine attachNode: playerNode]; + AVAudioMixerNode *mixerNode = [engine mainMixerNode]; +// AVAudioFormat * format = [[engine mainMixerNode] outputFormatForBus:0]; + + if(eq){ + [flutterSoundPlayer logDebug: @"EQ found! Connect it"]; + [engine connect: playerNode to: eq format: outputFormat]; + [engine connect: eq to: mixerNode format: outputFormat]; + } else { + [engine connect: playerNode to: outputNode format: outputFormat]; + } + + [engine prepare]; - [engine attachNode: playerNode]; + bool b = [engine startAndReturnError: nil]; + if (!b) + { + NSLog(@"Cannot start the audio engine!"); + [flutterSoundPlayer logDebug: @"Cannot start the audio engine"]; + } - [engine connect: playerNode to: outputNode format: outputFormat]; - bool b = [engine startAndReturnError: nil]; - if (!b) - { - [flutterSoundPlayer logDebug: @"Cannot start the audio engine"]; - } + mPauseTime = 0.0; // Total number of seconds in pause mode + mStartPauseTime = -1; // Not in paused mode + systemTime = CACurrentMediaTime(); // The time when started + return [super init]; + } - mPauseTime = 0.0; // Total number of seconds in pause mode - mStartPauseTime = -1; // Not in paused mode - systemTime = CACurrentMediaTime(); // The time when started - return [super init]; + - (void) enableEqualizer:(bool) enabled + { + if (eq == nil) + { + [flutterSoundPlayer logDebug: @"Cannot enable the equalizer because it is not initialized"]; + return; + } + + [engine prepare]; + eq.bypass = !enabled; + + // Start the engine. + NSError *error; + [engine startAndReturnError:&error]; + if (error) { + NSLog(@"error:%@", error); + } + } + + - (void) setEqualizerBandGain: (int) bandIndex gain: (float) gain + { + if (eq == nil) + { + [flutterSoundPlayer logDebug: @"Cannot set the equalizer band gain because the equalizer is not initialized"]; + NSLog(@"Cannot set the equalizer band gain because the equalizer is not initialized"); + return; + } + if (bandIndex < 0 || bandIndex >= eq.bands.count) + { + [flutterSoundPlayer logDebug: @"Cannot set the equalizer band gain because the band index is out of range"]; + NSLog(@"Cannot set the equalizer band gain because the band index is out of range"); + return; + } + + [engine prepare]; + +// NSLog(@"Received (flutter): band %d: to: %f ",bandIndex, gain); + +// NSLog(@"Setting gain for band: %d to %f", bandIndex, [self gainFrom: gain]); + + AVAudioUnitEQFilterParameters* band = eq.bands[bandIndex]; + band.gain = [self gainFrom: gain]; + + // Start the engine. + NSError *error; + [engine startAndReturnError:&error]; + if (error) { + NSLog(@"error:%@", error); + } } + - (AVAudioUnitEQ *) setEqualizer:(NSDictionary*) arguments; { + [flutterSoundPlayer logDebug:@"Initialize darwin EQ!"]; + + AVAudioUnitEQ* _eq; + + // Collect all DarwinEqulizer related + NSArray *effectsRaw = [arguments objectForKey:@"DarwinEqualizer"]; + if (!effectsRaw) { + [flutterSoundPlayer logDebug:@"no DarwinEqualizer type found"]; + throw @"no DarwinEqualizer type found"; + } + + // + NSDictionary *equalizerRaw = [effectsRaw filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary * _Nullable bindings) { + return [[evaluatedObject objectForKey:@"type"] isEqualToString:@"DarwinEqualizer"]; + }]].firstObject; + + // Collect parameters + NSDictionary *parameters = [equalizerRaw objectForKey:@"parameters"]; + // Collect bands + NSArray *rawBands = [parameters objectForKey:@"bands"]; + // Get enabled status + BOOL enabled = [[equalizerRaw objectForKey:@"enabled"] boolValue]; + + [flutterSoundPlayer logDebug: @"Setting Equalizer!"]; + + // Init equalizer + _eq = [[AVAudioUnitEQ alloc] initWithNumberOfBands:(unsigned int)rawBands.count + 1]; + [_eq setBypass : !enabled]; + [_eq setGlobalGain : 1]; +// // Set bands + for (int i = 0; i < rawBands.count; i++) { + NSDictionary *band = rawBands[i]; + _eq.bands[i].frequency = [[band objectForKey:@"centerFrequency"] floatValue]; +// _eq.bands[i].bandwidth = 1.0f; + _eq.bands[i].gain = [self gainFrom: ([[band objectForKey:@" Gain"] floatValue])]; + _eq.bands[i].bypass = false; + _eq.bands[i].filterType = AVAudioUnitEQFilterTypeParametric; + } + +// //Band pass filter +// AVAudioUnitEQFilterParameters *bandPassFilter; +// bandPassFilter = _eq.bands[(unsigned int)rawBands.count]; +// bandPassFilter.frequency = 2000; +//// bandPassFilter.bandwidth = 2.0f; +// bandPassFilter.gain = -60; +// bandPassFilter.bypass = false; +// bandPassFilter.filterType = AVAudioUnitEQFilterTypeLowPass; + + return _eq; + } + + -(void) startPlayerFromBuffer: (NSData*) dataBuffer { [self feed: dataBuffer] ; @@ -252,13 +374,16 @@ -(void) stop { converter = nil; // ARC will dealloc the converter (I hope ;-) ) } + if(eq != nil){ + eq = nil; + } } } -(bool) play { - [playerNode play]; - return true; + [playerNode play]; + return true; } -(bool) resume @@ -301,10 +426,16 @@ - (int) feed: (NSData*)data if (ready < NB_BUFFERS ) { int ln = (int)[data length]; - int frameLn = ln/2; - int frameLength = 8*frameLn;// Two octets for a frame (Monophony, INT Linear 16) + int frameLn = ln/4; + int frameLength = frameLn;// Two octets for a frame (Monophony, INT Linear 16) - playerFormat = [[AVAudioFormat alloc] initWithCommonFormat: AVAudioPCMFormatInt16 sampleRate: (double)m_sampleRate channels: m_numChannels interleaved: NO]; + AVAudioChannelLayout *chLayout = [[AVAudioChannelLayout alloc] initWithLayoutTag:kAudioChannelLayoutTag_Stereo]; + playerFormat = [[AVAudioFormat alloc] + initWithCommonFormat: AVAudioPCMFormatInt16 + sampleRate: (double)m_sampleRate + // channels: m_numChannels + interleaved: YES + channelLayout: chLayout]; AVAudioPCMBuffer* thePCMInputBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat: playerFormat frameCapacity: frameLn]; memcpy((unsigned char*)(thePCMInputBuffer.int16ChannelData[0]), [data bytes], ln); @@ -359,17 +490,24 @@ - (int) feed: (NSData*)data waitingBlock = data; return 0; } - } + } --(bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)fadeDuration// TODO -{ - return true; // TODO -} + -(bool) setVolume: (double) volume fadeDuration: (NSTimeInterval)fadeDuration// TODO + { + return true; // TODO + } -- (bool) setSpeed: (double) speed -{ - return true; // TODO -} + - (bool) setSpeed: (double) speed + { + return true; // TODO + } + + + - (float) gainFrom:(float) value + { + //Equalize the level between iOS and Android + return value * 2.8; + } @end @@ -444,7 +582,6 @@ -(void) startPlayerFromURL: (NSURL*) url codec: (t_CODEC)codec channels: (int)n -(void) stop { - if (engine != nil) { [engine stop]; @@ -496,14 +633,22 @@ -(bool) setSpeed: (double) speed // TODO return true; // TODO } - - (int) feed: (NSData*)data - { - return 0; - } + - (int) feed: (NSData*)data + { + return 0; + } +- (void)enableEqualizer:(bool)enabled { + NSLog(@"enableEqualizer not implemented (MIC)"); +} + + +- (void)setEqualizerBandGain:(int)bandIndex gain:(float)gain { + NSLog(@"setEqualizerBandGain not implemented (MIC)"); +} -//------------------------------------------------------------------------------------------------------------------------------------------- +//------------------------------------------------------------------------------------------------------------------------------------------- @end