package com.luxsin.app.service; import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.http.HttpUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.luxsin.app.bean.Model; import com.luxsin.app.bean.Target; import com.luxsin.app.bean.table.ModelTableDef; import com.luxsin.app.bean.table.TargetTableDef; import com.luxsin.app.config.MyConfig; import com.luxsin.app.dao.ModelMapper; import com.luxsin.app.dao.TargetMapper; import com.meilisearch.sdk.Index; import com.meilisearch.sdk.SearchRequest; import com.meilisearch.sdk.model.Searchable; import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.spring.service.impl.ServiceImpl; import lombok.extern.slf4j.Slf4j; import org.eafon.data.EResponseTool; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @Slf4j @Service public class ModelService extends ServiceImpl { private final RedisTemplate redisTemplate; private final TargetMapper targetMapper; private final MyConfig myConfig; private final Index modelIdx; public ModelService(RedisTemplate redisTemplate, TargetMapper targetMapper, MyConfig myConfig, Index modelIdx) { this.redisTemplate = redisTemplate; this.targetMapper = targetMapper; this.myConfig = myConfig; this.modelIdx = modelIdx; } /** * 获取手机列表 * @param key * @param count * @return */ public String modelList(String key, int count){ List list = Collections.emptyList(); Searchable searchable = modelIdx.search(SearchRequest.builder() .q(key) .attributesToRetrieve(new String[]{"rig", "form", "name", "brand_name", "source", "eq_key"}) .limit(count) .build()); if(!searchable.getHits().isEmpty()){ return JSONUtil.toJsonStr(searchable.getHits()); }else { return JSONUtil.toJsonStr(list); } } /** * 获取目标曲线 * @param brand * @param name * @param targetName * @param raw 是否只是需要机型的raw数据 * @return */ public String getCurvePoint(String brand, String name, String targetName){ if(StrUtil.isEmpty(brand) || StrUtil.isEmpty(name) || StrUtil.isEmpty(targetName)){ return EResponseTool.error(EResponseTool.ResultCode.VALIDATE_FAILED).toString(); } String cacheKey = brand + " " + name; String cache = String.valueOf(redisTemplate.opsForHash().get(cacheKey, targetName)); if(StrUtil.isEmpty(cache) || "null".equals(cache)){ String lockKey = cacheKey+":"+targetName+":lock"; boolean locked = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS)); if(locked){ try { cache = String.valueOf(redisTemplate.opsForHash().get(cacheKey, targetName)); if(StrUtil.isEmpty(cache) || "null".equals(cache)){ //redis中没有数据的,需要请求eq接口 JSONObject resp = getCurvePointFromPEQ(brand, name, targetName); if(null!=resp && resp.getInt("code") == EResponseTool.ResultCode.SUCCESS.getCode()){ //将请求接口得到的数据,放入缓存 redisTemplate.opsForHash().put(brand+" "+name, targetName, resp.toString()); return resp.toString(); } } }finally { redisTemplate.delete(lockKey); } }else{ try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { throw new RuntimeException(e); } return getCurvePoint(brand, name, targetName); } }else{ log.info("brand: {} name: {} target {} read cache.", brand, name, targetName); return cache; } return EResponseTool.error(EResponseTool.ResultCode.EMPTY).toString(); } private JSONObject getCurvePointFromPEQ(String brand, String name, String targetName){ Model model = getOne(QueryWrapper.create().where(ModelTableDef.MODEL.BRAND_NAME.eq(brand)). and(ModelTableDef.MODEL.NAME.eq(name))); if(!Objects.isNull(model)){ Target target = targetMapper.selectOneByQuery(QueryWrapper.create().where(TargetTableDef.TARGET.LABEL.eq(targetName))); if(!Objects.isNull(target)){ String eq_key = model.getEq_key(); String headPhone = brand+" "+name; if(StrUtil.isNotEmpty(eq_key) && eq_key.equals("name")){ headPhone = name; } JSONObject measurement = null; if("Eafonyoung".equals(model.getSource())){ measurement = getCSV(myConfig.getMeasurementBasePath()+"/Eafonyoung/data/"+model.getForm()+"/"+headPhone+".csv"); if(Objects.isNull(measurement)){ return null; } } JSONObject resp; if(target.isRead_csv()){ //读取自定义的target JSONObject targetRaw = getCSV(myConfig.getTargetBasePath()+"/"+target.getFile()); resp = reqEqualize(headPhone, measurement, targetRaw, JSONUtil.parseObj(target.getBassBoost()), model.getSource(), model.getRig()); }else{ resp = reqEqualize(headPhone, measurement, targetName, JSONUtil.parseObj(target.getBassBoost()), model.getSource(), model.getRig()); } if(resp.getInt("code") == EResponseTool.ResultCode.SUCCESS.getCode()){ return resp; }else{ log.info("请求reqEqualize接口失败,没有数据"); } } } return null; } private JSONObject getCSV(String path){ if(FileUtil.exist(path)){ List list = FileUtil.readLines(path, "UTF-8"); JSONArray frequency = new JSONArray(); JSONArray raw = new JSONArray(); for(int i=1;i reqMap = new HashMap<>(); reqMap.put("target", target); reqMap.put("sound_signature", null); reqMap.put("sound_signature_smoothing_window_size", 1); reqMap.put("bass_boost_gain", bassBoost.getFloat("gain")); reqMap.put("bass_boost_fc", bassBoost.getInt("fc")); reqMap.put("bass_boost_q", bassBoost.getFloat("q")); reqMap.put("treble_boost_gain",0); reqMap.put("treble_boost_fc", 10000); reqMap.put("treble_boost_q", 0.7); reqMap.put("tilt", 0); reqMap.put("fs",48000); reqMap.put("bit_depth", 16); reqMap.put("phase", "minimum"); reqMap.put("f_res", 16); reqMap.put("preamp",0); reqMap.put("max_gain" ,12); reqMap.put("max_slope", 18); reqMap.put("window_size", 0.08); reqMap.put("treble_window_size", 2); reqMap.put("treble_f_lower", 6000); reqMap.put("treble_f_upper", 8000); reqMap.put("treble_gain_k", 1); reqMap.put("graphic_eq", false); reqMap.put("parametric_eq", true); reqMap.put("fixed_band_eq", false); reqMap.put("convolution_eq", false); if(null!=measurement){ reqMap.put("measurement", measurement); } else if(StrUtil.isNotEmpty(headPhone)){ reqMap.put("name", headPhone); } reqMap.put("source", source); reqMap.put("rig", rig); reqMap.put("parametric_eq_config", "MINIDSP_IL_DSP"); Map response = new HashMap<>(); JSONArray fr_fields = new JSONArray(); fr_fields.addAll(List.of("raw")); response.put("fr_f_step", 1.02); response.put("base64fp16" ,false); response.put("fr_fields", fr_fields); reqMap.put("response", response); String resp = HttpUtil.post("https://autoeq.app/equalize", new JSONObject(reqMap).toString(), 10000); JSONObject result = new JSONObject(); if(StrUtil.isNotEmpty(resp)){ JSONObject respJson = JSONUtil.parseObj(resp); JSONObject parametric_eq = respJson.getJSONObject("parametric_eq"); if(Objects.isNull(parametric_eq)){ result.putOnce("code", 0); result.putOnce("msg", "req param error"); result.putOnce("param", reqMap); return result; }else { result.putOnce("parametric_eq", respJson.getJSONObject("parametric_eq")); result.putOnce("fr", respJson.getJSONObject("fr")); } } result.putOnce("code", EResponseTool.ResultCode.SUCCESS.getCode()); result.putOnce("msg", "ok"); return result; } public JSONObject reqEqualize(String headPhone, JSONObject measurement, String target, JSONObject bassBoost, String source, String rig) { return reqEqualizeInternal(headPhone, measurement, target, bassBoost, source, rig); } public JSONObject reqEqualize(String headPhone, JSONObject measurement, JSONObject target, JSONObject bassBoost, String source, String rig) { return reqEqualizeInternal(headPhone, measurement, target, bassBoost, source, rig); } public String getModels(String brandName, String modelName){ List list; if(StrUtil.isNotEmpty(brandName)){ list = list(QueryWrapper.create().where(ModelTableDef.MODEL.BRAND_NAME.eq(brandName)).orderBy(ModelTableDef.MODEL.NAME, true)); } else if(StrUtil.isNotEmpty(modelName)){ list = list(QueryWrapper.create().where(ModelTableDef.MODEL.NAME.like(modelName)).orderBy(ModelTableDef.MODEL.NAME, true)); } else{ list = Collections.emptyList(); } return JSONUtil.toJsonStr(list); } public String getModelCurve(String brand, String name){ return getCurvePoint(brand, name, "Harman over-ear 2018"); } }